diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 2cf3c83..e2dd8b5 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -42,6 +42,6 @@ jobs: path: results.sarif retention-days: 5 - name: "Upload to code-scanning" - uses: github/codeql-action/upload-sarif@b56ba49b26e50535fa1e7f7db0f4f7b4bf65d80d # v3.24.9 + uses: github/codeql-action/upload-sarif@6bb031afdd8eb862ea3fc1848194185e076637e5 # v3.24.9 with: sarif_file: results.sarif diff --git a/issue_metrics.py b/issue_metrics.py index 055618a..ff3c6ca 100644 --- a/issue_metrics.py +++ b/issue_metrics.py @@ -161,7 +161,10 @@ def get_per_issue_metrics( elif issue.state == "open": # type: ignore num_issues_open += 1 if not env_vars.hide_created_at: - issue_with_metrics.created_at = issue["created_at"] + if isinstance(issue, github3.search.IssueSearchResult): # type: ignore + issue_with_metrics.created_at = issue.issue.created_at # type: ignore + elif isinstance(issue, dict): # type: ignore + issue_with_metrics.created_at = issue["createdAt"] # type: ignore issues_with_metrics.append(issue_with_metrics) return issues_with_metrics, num_issues_open, num_issues_closed @@ -171,15 +174,16 @@ def evaluate_markdown_file_size(output_file: str) -> None: """ Evaluate the size of the markdown file and split it, if it is too large. """ - file_name_without_extension = Path(output_file).stem + output_file_name = output_file if output_file else "issue_metrics.md" + file_name_without_extension = Path(output_file_name).stem max_char_count = 65535 - if markdown_too_large_for_issue_body(output_file, max_char_count): - split_markdown_file(output_file, max_char_count) - shutil.move(output_file, f"{file_name_without_extension}_full.md") - shutil.move(f"{file_name_without_extension}_0.md", output_file) + if markdown_too_large_for_issue_body(output_file_name, max_char_count): + split_markdown_file(output_file_name, max_char_count) + shutil.move(output_file_name, f"{file_name_without_extension}_full.md") + shutil.move(f"{file_name_without_extension}_0.md", output_file_name) print( f"Issue metrics markdown file is too large for GitHub issue body and has been \ -split into multiple files. ie. {output_file}, {file_name_without_extension}_1.md, etc. \ +split into multiple files. ie. {output_file_name}, {file_name_without_extension}_1.md, etc. \ The full file is saved as {file_name_without_extension}_full.md\n\ See https://github.com/github/issue-metrics/blob/main/docs/dealing-with-large-issue-metrics.md" ) diff --git a/requirements-test.txt b/requirements-test.txt index 75e7512..985fbd9 100644 --- a/requirements-test.txt +++ b/requirements-test.txt @@ -6,4 +6,4 @@ pylint==3.3.4 pytest==8.3.5 pytest-cov==6.0.0 types-pytz==2025.1.0.20250204 -types-requests==2.32.0.20250301 +types-requests==2.32.0.20250306 diff --git a/test_config.py b/test_config.py index 7a41ad1..327f851 100644 --- a/test_config.py +++ b/test_config.py @@ -72,6 +72,7 @@ def setUp(self): "GH_TOKEN", "GHE", "HIDE_AUTHOR", + "HIDE_CREATED_AT", "HIDE_ITEMS_CLOSED_COUNT", "HIDE_LABEL_METRICS", "HIDE_TIME_TO_ANSWER", diff --git a/test_issue_metrics.py b/test_issue_metrics.py index 4f67d39..5a6e9de 100644 --- a/test_issue_metrics.py +++ b/test_issue_metrics.py @@ -374,9 +374,7 @@ def setUp(self): self.issue1 = { "title": "Issue 1", "url": "github.com/user/repo/issues/1", - "user": { - "login": "alice", - }, + "user": {"login": "alice"}, "createdAt": "2023-01-01T00:00:00Z", "comments": { "nodes": [ @@ -392,9 +390,7 @@ def setUp(self): self.issue2 = { "title": "Issue 2", "url": "github.com/user/repo/issues/2", - "user": { - "login": "bob", - }, + "user": {"login": "bob"}, "createdAt": "2023-01-01T00:00:00Z", "comments": {"nodes": [{"createdAt": "2023-01-03T00:00:00Z"}]}, "answerChosenAt": "2023-01-05T00:00:00Z", @@ -441,6 +437,7 @@ def test_get_per_issue_metrics_with_discussion(self): "GH_TOKEN": "test_token", "SEARCH_QUERY": "is:issue is:open repo:user/repo", "HIDE_AUTHOR": "true", + "HIDE_CREATED_AT": "false", "HIDE_LABEL_METRICS": "true", "HIDE_TIME_TO_ANSWER": "true", "HIDE_TIME_TO_CLOSE": "true", @@ -482,6 +479,18 @@ def test_get_per_issue_metrics_with_discussion_with_hide_envs(self): class TestEvaluateMarkdownFileSize(unittest.TestCase): """Test suite for the evaluate_markdown_file_size function.""" + @patch("issue_metrics.markdown_too_large_for_issue_body") + def test_markdown_too_large_for_issue_body_called_with_empty_output_file( + self, mock_evaluate + ): + """ + Test that the function uses the output_file. + """ + mock_evaluate.return_value = False + evaluate_markdown_file_size("") + + mock_evaluate.assert_called_with("issue_metrics.md", 65535) + @patch("issue_metrics.markdown_too_large_for_issue_body") def test_markdown_too_large_for_issue_body_called_with_output_file( self, mock_evaluate diff --git a/test_markdown_writer.py b/test_markdown_writer.py index 25aec52..1d63db5 100644 --- a/test_markdown_writer.py +++ b/test_markdown_writer.py @@ -22,6 +22,7 @@ "SEARCH_QUERY": "is:open repo:user/repo", "GH_TOKEN": "test_token", "DRAFT_PR_TRACKING": "True", + "HIDE_CREATED_AT": "False", }, ) class TestWriteToMarkdown(unittest.TestCase): @@ -44,6 +45,7 @@ def test_write_to_markdown(self): title="Issue 1", html_url="https://github.com/user/repo/issues/1", author="alice", + created_at=timedelta(days=-5), time_to_first_response=timedelta(days=1), time_to_close=timedelta(days=2), time_to_answer=timedelta(days=3), @@ -54,6 +56,7 @@ def test_write_to_markdown(self): title="Issue 2\r", html_url="https://github.com/user/repo/issues/2", author="bob", + created_at=timedelta(days=-5), time_to_first_response=timedelta(days=3), time_to_close=timedelta(days=4), time_to_answer=timedelta(days=5), @@ -129,12 +132,12 @@ def test_write_to_markdown(self): "| Number of most active mentors | 5 |\n" "| Total number of items created | 2 |\n\n" "| Title | URL | Author | Time to first response | Time to close |" - " Time to answer | Time in draft | Time spent in bug |\n" - "| --- | --- | --- | --- | --- | --- | --- | --- |\n" + " Time to answer | Time in draft | Time spent in bug | Created At |\n" + "| --- | --- | --- | --- | --- | --- | --- | --- | --- |\n" "| Issue 1 | https://github.com/user/repo/issues/1 | [alice](https://github.com/alice) | 1 day, 0:00:00 | " - "2 days, 0:00:00 | 3 days, 0:00:00 | 1 day, 0:00:00 | 4 days, 0:00:00 |\n" + "2 days, 0:00:00 | 3 days, 0:00:00 | 1 day, 0:00:00 | 4 days, 0:00:00 | -5 days, 0:00:00 |\n" "| Issue 2 | https://github.com/user/repo/issues/2 | [bob](https://github.com/bob) | 3 days, 0:00:00 | " - "4 days, 0:00:00 | 5 days, 0:00:00 | 1 day, 0:00:00 | 2 days, 0:00:00 |\n\n" + "4 days, 0:00:00 | 5 days, 0:00:00 | 1 day, 0:00:00 | 2 days, 0:00:00 | -5 days, 0:00:00 |\n\n" "_This report was generated with the [Issue Metrics Action](https://github.com/github/issue-metrics)_\n" "Search query used to find these items: `is:issue is:open label:bug`\n" ) @@ -156,6 +159,7 @@ def test_write_to_markdown_with_vertical_bar_in_title(self): title="Issue 1", html_url="https://github.com/user/repo/issues/1", author="alice", + created_at=timedelta(days=-5), time_to_first_response=timedelta(days=1), time_to_close=timedelta(days=2), time_to_answer=timedelta(days=3), @@ -166,6 +170,7 @@ def test_write_to_markdown_with_vertical_bar_in_title(self): title="feat| Issue 2", # title contains a vertical bar html_url="https://github.com/user/repo/issues/2", author="bob", + created_at=timedelta(days=-5), time_to_first_response=timedelta(days=3), time_to_close=timedelta(days=4), time_to_answer=timedelta(days=5), @@ -238,12 +243,12 @@ def test_write_to_markdown_with_vertical_bar_in_title(self): "| Number of most active mentors | 5 |\n" "| Total number of items created | 2 |\n\n" "| Title | URL | Author | Time to first response | Time to close |" - " Time to answer | Time in draft | Time spent in bug |\n" - "| --- | --- | --- | --- | --- | --- | --- | --- |\n" + " Time to answer | Time in draft | Time spent in bug | Created At |\n" + "| --- | --- | --- | --- | --- | --- | --- | --- | --- |\n" "| Issue 1 | https://github.com/user/repo/issues/1 | [alice](https://github.com/alice) | 1 day, 0:00:00 | " - "2 days, 0:00:00 | 3 days, 0:00:00 | 1 day, 0:00:00 | 1 day, 0:00:00 |\n" + "2 days, 0:00:00 | 3 days, 0:00:00 | 1 day, 0:00:00 | 1 day, 0:00:00 | -5 days, 0:00:00 |\n" "| feat| Issue 2 | https://github.com/user/repo/issues/2 | [bob](https://github.com/bob) | 3 days, 0:00:00 | " - "4 days, 0:00:00 | 5 days, 0:00:00 | None | 2 days, 0:00:00 |\n\n" + "4 days, 0:00:00 | 5 days, 0:00:00 | None | 2 days, 0:00:00 | -5 days, 0:00:00 |\n\n" "_This report was generated with the [Issue Metrics Action](https://github.com/github/issue-metrics)_\n" ) self.assertEqual(content, expected_content) @@ -287,6 +292,7 @@ def test_write_to_markdown_no_issues(self): { "SEARCH_QUERY": "is:open repo:user/repo", "GH_TOKEN": "test_token", + "HIDE_CREATED_AT": "False", "HIDE_TIME_TO_FIRST_RESPONSE": "True", "HIDE_TIME_TO_CLOSE": "True", "HIDE_TIME_TO_ANSWER": "True", @@ -309,6 +315,7 @@ def test_writes_markdown_file_with_non_hidden_columns_only(self): title="Issue 1", html_url="https://github.com/user/repo/issues/1", author="alice", + created_at=timedelta(days=-5), time_to_first_response=timedelta(minutes=10), time_to_close=timedelta(days=1), time_to_answer=timedelta(hours=2), @@ -321,6 +328,7 @@ def test_writes_markdown_file_with_non_hidden_columns_only(self): title="Issue 2", html_url="https://github.com/user/repo/issues/2", author="bob", + created_at=timedelta(days=-5), time_to_first_response=timedelta(minutes=20), time_to_close=timedelta(days=2), time_to_answer=timedelta(hours=4), @@ -363,6 +371,7 @@ def test_writes_markdown_file_with_non_hidden_columns_only(self): # Check that the function writes the correct markdown file with open("issue_metrics.md", "r", encoding="utf-8") as file: content = file.read() + expected_content = ( "# Issue Metrics\n\n" "| Metric | Count |\n" @@ -370,10 +379,10 @@ def test_writes_markdown_file_with_non_hidden_columns_only(self): "| Number of items that remain open | 2 |\n" "| Number of most active mentors | 5 |\n" "| Total number of items created | 2 |\n\n" - "| Title | URL | Author |\n" - "| --- | --- | --- |\n" - "| Issue 1 | https://www.github.com/user/repo/issues/1 | [alice](https://github.com/alice) |\n" - "| Issue 2 | https://www.github.com/user/repo/issues/2 | [bob](https://github.com/bob) |\n\n" + "| Title | URL | Author | Created At |\n" + "| --- | --- | --- | --- |\n" + "| Issue 1 | https://www.github.com/user/repo/issues/1 | [alice](https://github.com/alice) | -5 days, 0:00:00 |\n" + "| Issue 2 | https://www.github.com/user/repo/issues/2 | [bob](https://github.com/bob) | -5 days, 0:00:00 |\n\n" "_This report was generated with the [Issue Metrics Action](https://github.com/github/issue-metrics)_\n" "Search query used to find these items: `repo:user/repo is:issue`\n" )