diff --git a/.github/linters/.markdown-lint.yml b/.github/linters/.markdown-lint.yml index c796544..615c339 100644 --- a/.github/linters/.markdown-lint.yml +++ b/.github/linters/.markdown-lint.yml @@ -20,7 +20,7 @@ ############### # line length MD013: false -# singe h1 +# single h1 MD025: false # duplicate headers MD024: false diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml index ed4fae7..6ad6a56 100644 --- a/.github/workflows/copilot-setup-steps.yml +++ b/.github/workflows/copilot-setup-steps.yml @@ -26,12 +26,12 @@ jobs: # If you do not check out your code, Copilot will do this for you. steps: - name: Checkout code - uses: actions/checkout@v6.0.2 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Set up Python - uses: actions/setup-python@v6.2.0 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: 3.12 diff --git a/.github/workflows/docker-ci.yml b/.github/workflows/docker-ci.yml index 1f627ff..bbb93d4 100644 --- a/.github/workflows/docker-ci.yml +++ b/.github/workflows/docker-ci.yml @@ -14,7 +14,7 @@ jobs: build: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v6.0.2 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Build the Docker image diff --git a/.github/workflows/python-ci.yml b/.github/workflows/python-ci.yml index 45a7e78..dcac5fe 100644 --- a/.github/workflows/python-ci.yml +++ b/.github/workflows/python-ci.yml @@ -20,11 +20,11 @@ jobs: matrix: python-version: [3.11, 3.12] steps: - - uses: actions/checkout@v6.0.2 + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v6.2.0 + uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6.2.0 with: python-version: ${{ matrix.python-version }} - name: Install dependencies diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 36491ad..fa0f8ca 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -25,7 +25,7 @@ jobs: steps: - name: "Checkout code" - uses: actions/checkout@v6.0.2 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: persist-credentials: false @@ -36,7 +36,7 @@ jobs: results_format: sarif publish_results: true - name: "Upload artifact" - uses: actions/upload-artifact@v6.0.0 + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 with: name: SARIF file path: results.sarif diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml index 59b2411..cdf7790 100644 --- a/.github/workflows/stale.yml +++ b/.github/workflows/stale.yml @@ -11,7 +11,7 @@ jobs: stale: runs-on: ubuntu-latest steps: - - uses: actions/stale@v10.1.1 + - uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1 with: stale-issue-message: "This issue is stale because it has been open 21 days with no activity. Remove stale label or comment or this will be closed in 14 days." close-issue-message: "This issue was closed because it has been stalled for 35 days with no activity." diff --git a/.github/workflows/super-linter.yaml b/.github/workflows/super-linter.yaml index a1bb627..bab1cb9 100644 --- a/.github/workflows/super-linter.yaml +++ b/.github/workflows/super-linter.yaml @@ -18,7 +18,7 @@ jobs: statuses: write steps: - name: Checkout Code - uses: actions/checkout@v6.0.2 + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 persist-credentials: false @@ -27,7 +27,7 @@ jobs: python -m pip install --upgrade pip pip install -r requirements.txt -r requirements-test.txt - name: Lint Code Base - uses: super-linter/super-linter@502f4fe48a81a392756e173e39a861f8c8efe056 + uses: super-linter/super-linter@61abc07d755095a68f4987d1c2c3d1d64408f1f9 # v8.5.0 env: DEFAULT_BRANCH: main GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/contributor_stats.py b/contributor_stats.py index ffd960e..2a77353 100644 --- a/contributor_stats.py +++ b/contributor_stats.py @@ -104,7 +104,7 @@ def merge_contributors(contributors: list) -> list: merged_contributors: List[ContributorStats] = [] for contributor_list in contributors: for contributor in contributor_list: - # if the contributor is already in the merged list, merge their relavent attributes + # if the contributor is already in the merged list, merge their relevant attributes if contributor.username in [c.username for c in merged_contributors]: for merged_contributor in merged_contributors: if merged_contributor.username == contributor.username: diff --git a/env.py b/env.py index b160dc2..486f536 100644 --- a/env.py +++ b/env.py @@ -70,6 +70,36 @@ def validate_date_format(env_var_name: str) -> str: return date_to_validate +def validate_date_range(start_date: str, end_date: str) -> None: + """Validate that start_date is before end_date. + + Does nothing if either date is not set. + + Args: + start_date: The start date string in YYYY-MM-DD format. + end_date: The end date string in YYYY-MM-DD format. + + Raises: + ValueError: If end_date is before or equal to start_date. + """ + if not start_date or not end_date: + return + + pattern = "%Y-%m-%d" + try: + start = datetime.datetime.strptime(start_date, pattern).date() + end = datetime.datetime.strptime(end_date, pattern).date() + except ValueError as exc: + raise ValueError( + "start_date and end_date must be in the format YYYY-MM-DD" + ) from exc + + if end <= start: + raise ValueError( + f"END_DATE ('{end_date}') must be after START_DATE ('{start_date}')" + ) + + def get_env_vars( test: bool = False, ) -> tuple[ @@ -142,6 +172,7 @@ def get_env_vars( start_date = validate_date_format("START_DATE") end_date = validate_date_format("END_DATE") + validate_date_range(start_date, end_date) sponsor_info = get_bool_env_var("SPONSOR_INFO", False) link_to_profile = get_bool_env_var("LINK_TO_PROFILE", False) diff --git a/test_env.py b/test_env.py index 638e6e5..fb1734d 100644 --- a/test_env.py +++ b/test_env.py @@ -222,6 +222,78 @@ def test_get_env_vars_auth_with_github_app_installation_missing_inputs(self): "GH_APP_ID set and GH_APP_INSTALLATION_ID or GH_APP_PRIVATE_KEY variable not set", ) + @patch.dict( + os.environ, + { + "ORGANIZATION": "org", + "REPOSITORY": "repo", + "GH_TOKEN": "token", + "START_DATE": "2025-01-01", + "END_DATE": "2024-01-01", + }, + clear=True, + ) + def test_get_env_vars_end_date_before_start_date(self): + """Test that an error is raised when END_DATE is before START_DATE""" + with self.assertRaises(ValueError) as cm: + env.get_env_vars() + the_exception = cm.exception + self.assertEqual( + str(the_exception), + "END_DATE ('2024-01-01') must be after START_DATE ('2025-01-01')", + ) + + @patch.dict( + os.environ, + { + "ORGANIZATION": "org", + "REPOSITORY": "repo", + "GH_TOKEN": "token", + "START_DATE": "2024-01-01", + "END_DATE": "2024-01-01", + }, + clear=True, + ) + def test_get_env_vars_equal_start_and_end_date(self): + """Test that an error is raised when START_DATE equals END_DATE""" + with self.assertRaises(ValueError) as cm: + env.get_env_vars() + the_exception = cm.exception + self.assertEqual( + str(the_exception), + "END_DATE ('2024-01-01') must be after START_DATE ('2024-01-01')", + ) + + @patch.dict( + os.environ, + { + "ORGANIZATION": "org", + "REPOSITORY": "repo", + "GH_TOKEN": "token", + "START_DATE": "2024-01-01", + "END_DATE": "2025-01-01", + }, + clear=True, + ) + def test_get_env_vars_valid_date_range(self): + """Test that valid date range (START_DATE before END_DATE) is accepted""" + ( + _organization, + _repository_list, + _gh_app_id, + _gh_app_installation_id, + _gh_app_private_key, + _gh_app_enterprise_only, + _token, + _ghe, + start_date, + end_date, + _sponsor_info, + _link_to_profile, + ) = env.get_env_vars() + self.assertEqual(start_date, "2024-01-01") + self.assertEqual(end_date, "2025-01-01") + if __name__ == "__main__": unittest.main()