diff --git a/.gitignore b/.gitignore index 2b2b24a..273d6ad 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,7 @@ # Generated reports from local testing report.md weekly-report.md +comment-template.md *.md.bak # macOS diff --git a/CHANGELOG.md b/CHANGELOG.md index 8c3e488..013a2fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,17 +7,46 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added +- **Comment summary template**: Auto-generate an editable highlights template from merged PR data + - New `--summary-template` CLI flag and `summary-template` action input + - New `--summary-template-file=FILE` CLI flag and `summary-template-file` action input + - Collects merged PR titles, URLs, authors, and labels during report generation + - Groups PRs by repository with placeholder sections for human curation + - New `comment-template` action output for downstream workflow steps + - Labels from PRs are included to aid thematic categorization + - Auto-detects contributors from merged PRs (filters bots and Copilot) + - Uses markdown link format for clean GitHub rendering + - See [Comment Summary Template docs](docs/comment-summary-template.md) for details +- **Release tracking in summary template**: Repos that publish releases now show them in the template + - Fetches releases published in the report date range via GitHub API + - Displays a single releases table at the top of the template (omitted if no releases) + - Table columns: Repository, Release (name/tag with link), Date + - Sorted by publication date (newest first) + - Works for both organization repos and external tracked repos + - Helps summarize important milestones alongside PR activity +- **Releases column in main report**: Include release activity directly in the primary report table + - Adds a **Releases** column to the main activity table for each repository + - Counts non-draft releases published in the report date range and includes them in the totals row + - Clickable link to repo releases page when count > 0 + - Keeps release metrics alongside issues, PRs, and commits for a unified overview + ### Fixed - **Corrupted documentation**: Rewrote `docs/testing.md` to remove duplicated/interleaved content and restore a clean testing guide - **Corrupted tests README**: Rewrote `tests/README.md` to remove duplicated content and ensure it only references existing test files - **CLI error message**: Changed misleading `ERROR: GITHUB_OUTPUT environment variable not set!` to a debug message in CLI mode (GITHUB_OUTPUT is only relevant in GitHub Actions) - **Hyperlinks documentation**: Fixed `docs/hyperlinks.md` overview that incorrectly said "metrics greater than 1" when the actual behavior is "metrics greater than 0" +- **`set -e` safety for API calls**: Restructured all `api_call` assignments to use `if var=$(api_call ...); then` pattern so that API failures are handled gracefully instead of aborting the script +- **Date formatting in releases table**: Use non-zero-padded day format (`Feb 5` instead of `Feb 05`) for consistent display with documentation examples +- **Clickable links comment**: Fixed comment that said "if > 1" to match actual `format_metric()` behavior of linking when count is > 0 ### Changed - **CONTRIBUTING.md**: Updated from generic Python template (referenced `pip install`, `pytest`, PEP 8) to accurate shell-project instructions with correct tooling (`bash`, `curl`, `jq`) and test commands - **Argument parsing**: Removed no-op `shift` statements inside `for arg in "$@"` loop in `generate-report.sh` (shift has no effect inside a for loop) - **Copilot instructions**: Added `.tmp/` file staging guidelines to prevent heredoc/shell escaping issues - **`.gitignore`**: Added `.tmp/` working directory for file staging +- **Debug output**: All debug messages are now gated behind `--debug` CLI flag / `debug` action input (silent by default) +- **Comment template docs**: Softened wording from "all merged PRs" to "merged PRs" and documented the 100-item-per-page API limit ## [2.2.0] - 2025-10-23 diff --git a/README.md b/README.md index e7d6fdb..9653410 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ A powerful GitHub Action that generates comprehensive activity reports across Gi - **📊 Comprehensive Analytics**: Issues, PRs, commits, and activity summaries - **🔗 Clickable Metrics**: Numbers > 0 link directly to GitHub search results for quick navigation +- **📝 Comment Summary Template**: Auto-generates an editable highlights template with merged PR details and labels - **🌐 External Repository Tracking**: Monitor repositories outside your primary organization - **⚡ Smart Activity Detection**: Captures ALL repository activity (commits, issues, PRs, updates) with intelligent filtering - **🔄 Complete Coverage**: Handles organizations with hundreds of repositories via pagination @@ -86,6 +87,12 @@ export GITHUB_TOKEN=ghp_xxxxx # Track external repositories (from other organizations) ./generate-report.sh --token=ghp_xxxxx --track-external-repos=executablebooks/sphinx-proof,executablebooks/sphinx-exercise +# Generate report with comment summary template +./generate-report.sh --token=ghp_xxxxx --summary-template + +# Custom summary template output file +./generate-report.sh --token=ghp_xxxxx --summary-template --summary-template-file=highlights.md + # View the generated report cat report.md ``` @@ -105,6 +112,9 @@ cat report.md - `--exclude=REPOS` - Comma-separated list of repos or regex patterns to exclude (e.g., `repo1,lecture-.*\.notebooks`) - `--track-external-repos=LIST` - Comma-separated list of external repos to track (format: `org/repo`, e.g., `executablebooks/sphinx-proof,executablebooks/sphinx-exercise`) - `--delay=SECONDS` - Delay between API calls (default: 0) +- `--summary-template` - Generate a comment summary template with merged PR details +- `--summary-template-file=FILE` - Output filename for summary template (default: `comment-template.md`) +- `--debug` - Enable verbose debug output The report is saved to `report.md` (or your specified output file) in the current directory. @@ -118,6 +128,9 @@ The report is saved to `report.md` (or your specified output file) in the curren | `exclude-repos` | Comma-separated list of repository names or regex patterns to exclude (e.g., `repo1,lecture-.*\.notebooks`) | No | `''` | | `track-external-repos` | Comma-separated list of external repositories to track (format: `org/repo`, e.g., `executablebooks/sphinx-proof,executablebooks/sphinx-exercise`) | No | `''` | | `api-delay` | Delay in seconds between API calls to avoid rate limits (0 = no delay) | No | `0` | +| `summary-template` | Generate a comment summary template with merged PR details (`true`/`false`) | No | `false` | +| `summary-template-file` | Output filename for the comment summary template | No | `comment-template.md` | +| `debug` | Enable verbose debug output (`true`/`false`) | No | `false` | ## Outputs @@ -125,6 +138,7 @@ The report is saved to `report.md` (or your specified output file) in the curren |--------|-------------| | `report-content` | The full generated report content | | `report-summary` | A brief summary of the report metrics | +| `comment-template` | The generated comment summary template content (when `summary-template` is enabled) | ## How It Works @@ -171,23 +185,35 @@ jobs: weekly-report: runs-on: ubuntu-latest steps: - # Step 1: Generate the report (our action) + # Step 1: Generate the report and comment summary template - name: Generate weekly report - uses: QuantEcon/action-weekly-report@v2 # Always uses latest v2.x.x release + uses: QuantEcon/action-weekly-report@v2 with: github-token: ${{ secrets.GITHUB_TOKEN }} organization: 'QuantEcon' track-external-repos: 'executablebooks/sphinx-proof,executablebooks/sphinx-exercise' + summary-template: 'true' - # Step 2: Create issue with report (separate action - optional) + # Step 2: Create issue from report - name: Create issue from report + id: create-issue uses: peter-evans/create-issue-from-file@v4 with: title: Weekly Activity Report - content-filepath: weekly-report.md # Action outputs to weekly-report.md + content-filepath: weekly-report.md labels: report, automated + + # Step 3: Post the comment summary template on the new issue + - name: Post comment summary template + if: hashFiles('comment-template.md') != '' + uses: peter-evans/create-or-update-comment@v4 + with: + issue-number: ${{ steps.create-issue.outputs.issue-number }} + body-path: comment-template.md ``` +**How it works:** Step 1 generates both `weekly-report.md` and `comment-template.md`. Step 2 creates the issue. Step 3 posts the template as the first comment on that issue — ready for a maintainer to edit into a curated highlights summary. + **Note:** The action outputs to `weekly-report.md` by default. In CLI mode, it uses `report.md`. ## Report Format @@ -207,6 +233,7 @@ For large organizations, use the `api-delay` parameter to add delays between req ## Documentation - **[Testing Guide](docs/testing.md)** - How to test and validate the action +- **[Comment Summary Template](docs/comment-summary-template.md)** - Auto-generated highlights from merged PRs - **[Technical Details](docs/improvements.md)** - Implementation details - **[Validation Examples](docs/validation.md)** - Real-world validation - **[Release Notes](docs/releases/)** - Version-specific changes diff --git a/action.yml b/action.yml index 7ff09d9..fdeb6d6 100644 --- a/action.yml +++ b/action.yml @@ -26,6 +26,18 @@ inputs: description: 'Delay in seconds between API calls to avoid rate limits (0 = no delay)' required: false default: '0' + summary-template: + description: 'Generate a comment summary template with merged PR details (true/false)' + required: false + default: 'false' + summary-template-file: + description: 'Output filename for the comment summary template' + required: false + default: 'comment-template.md' + debug: + description: 'Enable verbose debug output (true/false)' + required: false + default: 'false' outputs: report-content: @@ -34,6 +46,9 @@ outputs: report-summary: description: 'A brief summary of the report metrics' value: ${{ steps.generate.outputs.report-summary }} + comment-template: + description: 'The generated comment summary template content (when summary-template is enabled)' + value: ${{ steps.generate.outputs.comment-template }} runs: using: 'composite' @@ -48,4 +63,7 @@ runs: INPUT_OUTPUT_FORMAT: ${{ inputs.output-format }} INPUT_EXCLUDE_REPOS: ${{ inputs.exclude-repos }} INPUT_TRACK_EXTERNAL_REPOS: ${{ inputs.track-external-repos }} - INPUT_API_DELAY: ${{ inputs.api-delay }} \ No newline at end of file + INPUT_API_DELAY: ${{ inputs.api-delay }} + INPUT_SUMMARY_TEMPLATE: ${{ inputs.summary-template }} + INPUT_SUMMARY_TEMPLATE_FILE: ${{ inputs.summary-template-file }} + INPUT_DEBUG: ${{ inputs.debug }} diff --git a/docs/README.md b/docs/README.md index 8abf4af..22eb5e2 100644 --- a/docs/README.md +++ b/docs/README.md @@ -4,6 +4,7 @@ - [Testing Guide](testing.md) - How to test and validate the action - [Hyperlink Feature](hyperlinks.md) - Interactive metrics with clickable links +- [Comment Summary Template](comment-summary-template.md) - Auto-generated highlights from merged PRs - [Technical Details](improvements.md) - Implementation and architecture - [Validation](validation.md) - Real-world examples and verification - [Release Notes](releases/) - Version history and changes @@ -24,6 +25,12 @@ - URL formats and destinations - Usage examples +**[Comment Summary Template](comment-summary-template.md)** +- Auto-generate highlights from merged PR data +- Label-aware categorization +- Editable template workflow +- Action outputs for downstream steps + ### Technical Documentation **[Implementation Details](improvements.md)** diff --git a/docs/comment-summary-template.md b/docs/comment-summary-template.md new file mode 100644 index 0000000..0e28147 --- /dev/null +++ b/docs/comment-summary-template.md @@ -0,0 +1,169 @@ +# Comment Summary Template + +The comment summary template feature auto-generates an editable markdown template containing merged PR details from the report period. This helps write the "highlights" comment that accompanies each activity report. + +> **Note:** The template includes up to 100 merged PRs and 100 releases per repository (the GitHub API default page size). For most repositories and weekly report windows, this covers all activity. + +## Background + +Activity reports generated by this action contain a metrics table (issues, PRs, commits). The accompanying comment — written by a maintainer — provides high-level context about what was accomplished. This feature bridges the gap by collecting the raw PR data needed to write that comment. + +See [QuantEcon/meta#280](https://github.com/QuantEcon/meta/issues/280) for an example of a report with a hand-written highlights comment. + +## Usage + +### CLI + +```bash +# Generate report with comment summary template +./generate-report.sh --token=ghp_xxxxx --summary-template + +# Custom output filename +./generate-report.sh --token=ghp_xxxxx --summary-template --summary-template-file=highlights.md +``` + +### GitHub Action + +```yaml +- name: Generate activity report + uses: QuantEcon/action-weekly-report@v2 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + summary-template: 'true' + summary-template-file: 'comment-template.md' +``` + +## Output Format + +The template is written to `comment-template.md` (default) and contains: + +1. **Placeholder summary** — Editable topic bullets for high-level themes +2. **Releases table** — All releases published in the period in a single table (omitted if none) +3. **Merged PRs by repository** — Merged PRs with title, URL, labels, and author +4. **Contributor acknowledgement** — Auto-generated from PR authors (bots and Copilot excluded) + +### Example Output + +```markdown +**High Level Summary:** + +1. **[Topic 1]** _Add a high-level summary of work in this area._ +2. **[Topic 2]** _Add another thematic summary._ + +--- + +## 📦 Releases + +| Repository | Release | Date | +|------------|---------|------| +| lecture-python.myst | [publish-2026feb19b](https://github.com/.../publish-2026feb19b) | Feb 19 | +| quantecon-book-theme | [v0.17.0](https://github.com/.../v0.17.0) | Feb 19 | +| QuantEcon.py | [v0.11.0](https://github.com/.../v0.11.0) | Feb 15 | + +**lecture-python.myst:** + - [Update consumer problem lecture](https://github.com/QuantEcon/lecture-python.myst/pull/757) [maintenance] (@HumphreyYang) + - [Fix typo in GDP lecture](https://github.com/QuantEcon/lecture-python.myst/pull/756) (@contributor) + +**quantecon-book-theme:** + - [Major refactor for responsiveness](https://github.com/QuantEcon/quantecon-book-theme/pull/335) [infrastructure, priority: high] (@mmcky) + +--- + +thanks @HumphreyYang, @mmcky, and @contributor for all your contributions. +``` + +## How Labels Help + +When PRs have labels (e.g., `maintenance`, `infrastructure`, `bug`, `priority: high`), they appear in brackets next to each PR. This makes it easier to: + +- **Group by theme** — Quickly identify which PRs relate to maintenance vs. new features +- **Prioritize highlights** — Spot high-priority work at a glance +- **Categorize** — Reorganize the flat per-repo list into thematic sections + +For maximum benefit, use standardized labels across repositories. See [QuantEcon/meta#178](https://github.com/QuantEcon/meta/issues/178) for the labels standardization initiative. + +## Workflow + +### Automated (GitHub Actions) + +The template can be auto-posted as a comment on the activity report issue: + +```yaml +# Step 1: Generate report + template +- name: Generate weekly report + uses: QuantEcon/action-weekly-report@v2 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + summary-template: 'true' + +# Step 2: Create issue from report +- name: Create issue from report + id: create-issue + uses: peter-evans/create-issue-from-file@v4 + with: + title: Weekly Activity Report + content-filepath: weekly-report.md + labels: report, automated + +# Step 3: Post template as first comment +- name: Post comment summary template + if: hashFiles('comment-template.md') != '' + uses: peter-evans/create-or-update-comment@v4 + with: + issue-number: ${{ steps.create-issue.outputs.issue-number }} + body-path: comment-template.md +``` + +The template lands as the first comment on the new issue. A maintainer then edits that comment to curate the highlights. + +### Manual + +1. Run the action or CLI with `summary-template` enabled +2. Open `comment-template.md` — merged PRs are listed by repo +3. Reorganize into thematic sections (Lectures, Infrastructure, Libraries, etc.) +4. Add high-level context for each section +5. Remove PRs that don't need highlighting (e.g., automated dependency bumps) +6. Post as a comment on the activity report issue + +## Action Outputs + +The template content is also available as the `comment-template` output for use in downstream workflow steps: + +```yaml +- name: Generate report + id: report + uses: QuantEcon/action-weekly-report@v2 + with: + github-token: ${{ secrets.GITHUB_TOKEN }} + summary-template: 'true' + +# Option 1: Pass to an action input (recommended — inherently safe) +- name: Post comment + uses: peter-evans/create-or-update-comment@v4 + with: + issue-number: 1 + body: ${{ steps.report.outputs.comment-template }} + +# Option 2: Use via environment variable (safe for run steps) +- name: Use template content + env: + TEMPLATE: ${{ steps.report.outputs.comment-template }} + run: echo "$TEMPLATE" +``` + +> **Security note:** Never interpolate `${{ steps.report.outputs.comment-template }}` +> directly in a `run:` block. The output contains PR titles, labels, and author names +> which could include shell metacharacters. Always pass it through an `env:` variable +> or an action `with:` input to avoid shell injection. + +## Inputs + +| Input | Description | Default | +|-------|-------------|---------| +| `summary-template` | Enable template generation (`true`/`false`) | `false` | +| `summary-template-file` | Output filename | `comment-template.md` | + +| CLI Flag | Description | +|----------|-------------| +| `--summary-template` | Enable template generation | +| `--summary-template-file=FILE` | Output filename (default: `comment-template.md`) | diff --git a/generate-report.sh b/generate-report.sh index 1dce81f..0662d08 100755 --- a/generate-report.sh +++ b/generate-report.sh @@ -7,6 +7,9 @@ END_DATE="" CLI_TOKEN="" CLI_ORG="" CLI_OUTPUT="" +CLI_SUMMARY_TEMPLATE="" +CLI_SUMMARY_TEMPLATE_FILE="" +CLI_DEBUG="" show_usage() { cat << EOF @@ -23,6 +26,9 @@ OPTIONS: --exclude=REPOS Comma-separated list of repos to exclude (supports regex patterns) --track-external-repos=LIST Comma-separated list of external repos (format: org/repo,org/repo) --delay=SECONDS Delay between API calls (default: 0) + --summary-template Generate a comment summary template with merged PR details + --summary-template-file=FILE Output filename for summary template (default: comment-template.md) + --debug Enable verbose debug output --help Show this help message EXAMPLES: @@ -50,6 +56,12 @@ EXAMPLES: # Track external repositories $0 --token=ghp_xxxxx --track-external-repos=executablebooks/sphinx-proof,executablebooks/sphinx-exercise + # Generate report with comment summary template + $0 --token=ghp_xxxxx --summary-template + + # Custom summary template filename + $0 --token=ghp_xxxxx --summary-template --summary-template-file=highlights.md + ENVIRONMENT VARIABLES: GITHUB_TOKEN or INPUT_GITHUB_TOKEN GitHub token INPUT_ORGANIZATION Organization name @@ -57,6 +69,9 @@ ENVIRONMENT VARIABLES: INPUT_EXCLUDE_REPOS Repositories to exclude INPUT_TRACK_EXTERNAL_REPOS External repositories to track INPUT_API_DELAY API delay in seconds + INPUT_SUMMARY_TEMPLATE Enable summary template generation (true/false) + INPUT_SUMMARY_TEMPLATE_FILE Summary template output filename + INPUT_DEBUG Enable debug output (true/false) OUTPUT: Report is saved to: report.md (or specified --output file) @@ -91,6 +106,15 @@ for arg in "$@"; do --output=*) CLI_OUTPUT="${arg#*=}" ;; + --summary-template) + CLI_SUMMARY_TEMPLATE="true" + ;; + --summary-template-file=*) + CLI_SUMMARY_TEMPLATE_FILE="${arg#*=}" + ;; + --debug) + CLI_DEBUG="true" + ;; --help) show_usage exit 0 @@ -103,8 +127,17 @@ for arg in "$@"; do esac done -echo "DEBUG: Starting report generation" -echo "DEBUG: Environment check - GITHUB_OUTPUT: ${GITHUB_OUTPUT:-NOT_SET}" +# Resolve debug flag early so debug() function works +DEBUG_MODE="${CLI_DEBUG:-${INPUT_DEBUG:-false}}" + +debug() { + if [ "$DEBUG_MODE" = "true" ]; then + echo "DEBUG: $*" + fi +} + +debug "Starting report generation" +debug "Environment check - GITHUB_OUTPUT: ${GITHUB_OUTPUT:-NOT_SET}" # Get inputs - CLI args take precedence over environment variables GITHUB_TOKEN="${CLI_TOKEN:-${GITHUB_TOKEN:-${INPUT_GITHUB_TOKEN}}}" @@ -113,6 +146,8 @@ OUTPUT_FORMAT="${INPUT_OUTPUT_FORMAT:-markdown}" EXCLUDE_REPOS="${CLI_EXCLUDE:-${INPUT_EXCLUDE_REPOS:-}}" TRACK_EXTERNAL_REPOS="${CLI_EXTERNAL_REPOS:-${INPUT_TRACK_EXTERNAL_REPOS:-}}" API_DELAY="${CLI_DELAY:-${INPUT_API_DELAY:-0}}" +SUMMARY_TEMPLATE="${CLI_SUMMARY_TEMPLATE:-${INPUT_SUMMARY_TEMPLATE:-false}}" +SUMMARY_TEMPLATE_FILE="${CLI_SUMMARY_TEMPLATE_FILE:-${INPUT_SUMMARY_TEMPLATE_FILE:-comment-template.md}}" # Set output file based on context # - GitHub Actions: Use weekly-report.md for backward compatibility @@ -138,9 +173,10 @@ if [ -z "$GITHUB_TOKEN" ]; then exit 1 fi -echo "DEBUG: Inputs - ORG: $ORGANIZATION, FORMAT: $OUTPUT_FORMAT, EXCLUDE: $EXCLUDE_REPOS" -echo "DEBUG: External repos: $TRACK_EXTERNAL_REPOS" -echo "DEBUG: Output file: $OUTPUT_FILE" +debug "Inputs - ORG: $ORGANIZATION, FORMAT: $OUTPUT_FORMAT, EXCLUDE: $EXCLUDE_REPOS" +debug "External repos: $TRACK_EXTERNAL_REPOS" +debug "Output file: $OUTPUT_FILE" +debug "Summary template: $SUMMARY_TEMPLATE (file: $SUMMARY_TEMPLATE_FILE)" # Validate external repos format (must be org/repo) if [ -n "$TRACK_EXTERNAL_REPOS" ]; then @@ -281,39 +317,91 @@ process_external_repo() { # Count total current open issues local current_issues_response - current_issues_response=$(api_call "/repos/${full_repo}/issues?state=open") local current_issues=0 - current_issues=$(echo "$current_issues_response" | jq 'if type == "array" then [.[] | select(.pull_request == null)] | length else 0 end' 2>/dev/null || echo 0) + if current_issues_response=$(api_call "/repos/${full_repo}/issues?state=open"); then + current_issues=$(echo "$current_issues_response" | jq 'if type == "array" then [.[] | select(.pull_request == null)] | length else 0 end' 2>/dev/null || echo 0) + fi # Count opened issues in the date range local opened_response - opened_response=$(api_call "/repos/${full_repo}/issues?state=all") local opened_issues=0 - opened_issues=$(echo "$opened_response" | jq --arg since "$WEEK_AGO" --arg until "$NOW" 'if type == "array" then [.[] | select(.created_at >= $since and .created_at <= $until and .pull_request == null)] | length else 0 end' 2>/dev/null || echo 0) + if opened_response=$(api_call "/repos/${full_repo}/issues?state=all"); then + opened_issues=$(echo "$opened_response" | jq --arg since "$WEEK_AGO" --arg until "$NOW" 'if type == "array" then [.[] | select(.created_at >= $since and .created_at <= $until and .pull_request == null)] | length else 0 end' 2>/dev/null || echo 0) + fi # Count closed issues in the date range local closed_response - closed_response=$(api_call "/repos/${full_repo}/issues?state=closed") local closed_issues=0 - closed_issues=$(echo "$closed_response" | jq --arg since "$WEEK_AGO" --arg until "$NOW" 'if type == "array" then [.[] | select(.closed_at != null and .closed_at >= $since and .closed_at <= $until and .pull_request == null)] | length else 0 end' 2>/dev/null || echo 0) + if closed_response=$(api_call "/repos/${full_repo}/issues?state=closed"); then + closed_issues=$(echo "$closed_response" | jq --arg since "$WEEK_AGO" --arg until "$NOW" 'if type == "array" then [.[] | select(.closed_at != null and .closed_at >= $since and .closed_at <= $until and .pull_request == null)] | length else 0 end' 2>/dev/null || echo 0) + fi # Count merged PRs in the date range local prs_response - prs_response=$(api_call "/repos/${full_repo}/pulls?state=closed") local merged_prs=0 - merged_prs=$(echo "$prs_response" | jq --arg since "$WEEK_AGO" --arg until "$NOW" 'if type == "array" then [.[] | select(.merged_at != null and .merged_at >= $since and .merged_at <= $until)] | length else 0 end' 2>/dev/null || echo 0) + if prs_response=$(api_call "/repos/${full_repo}/pulls?state=closed"); then + merged_prs=$(echo "$prs_response" | jq --arg since "$WEEK_AGO" --arg until "$NOW" 'if type == "array" then [.[] | select(.merged_at != null and .merged_at >= $since and .merged_at <= $until)] | length else 0 end' 2>/dev/null || echo 0) + fi + + # Collect merged PR details for summary template + local local_ext_pr_details="" + if [ "$SUMMARY_TEMPLATE" = "true" ] && [ "$merged_prs" -gt 0 ]; then + local_ext_pr_details=$(echo "$prs_response" | jq -c --arg since "$WEEK_AGO" --arg until "$NOW" --arg repo "$repo_name" --arg org "$repo_org" ' + if type == "array" then + [.[] | select(.merged_at != null and .merged_at >= $since and .merged_at <= $until) | + { + repo: ($org + "/" + $repo), + org: $org, + number: .number, + title: .title, + url: .html_url, + author: .user.login, + labels: [.labels[]?.name], + merged_at: .merged_at + }] | .[] + else empty end + ' 2>/dev/null) + fi # Count opened PRs in the date range local all_prs_response - all_prs_response=$(api_call "/repos/${full_repo}/pulls?state=all") local opened_prs=0 - opened_prs=$(echo "$all_prs_response" | jq --arg since "$WEEK_AGO" --arg until "$NOW" 'if type == "array" then [.[] | select(.created_at >= $since and .created_at <= $until)] | length else 0 end' 2>/dev/null || echo 0) + if all_prs_response=$(api_call "/repos/${full_repo}/pulls?state=all"); then + opened_prs=$(echo "$all_prs_response" | jq --arg since "$WEEK_AGO" --arg until "$NOW" 'if type == "array" then [.[] | select(.created_at >= $since and .created_at <= $until)] | length else 0 end' 2>/dev/null || echo 0) + fi # Count commits in the date range local commits_response - commits_response=$(api_call "/repos/${full_repo}/commits?since=${WEEK_AGO}&until=${NOW}") local commits=0 - commits=$(echo "$commits_response" | jq 'if type == "array" then length else 0 end' 2>/dev/null || echo 0) + if commits_response=$(api_call "/repos/${full_repo}/commits?since=${WEEK_AGO}&until=${NOW}"); then + commits=$(echo "$commits_response" | jq 'if type == "array" then length else 0 end' 2>/dev/null || echo 0) + fi + + # Count releases published in the date range + local releases_response + local releases=0 + if releases_response=$(api_call "/repos/${full_repo}/releases"); then + releases=$(echo "$releases_response" | jq --arg since "$WEEK_AGO" --arg until "$NOW" 'if type == "array" then [.[] | select(.published_at != null and .published_at >= $since and .published_at <= $until and .draft == false)] | length else 0 end' 2>/dev/null || echo 0) + fi + + # Collect release details for summary template + local local_ext_release_details="" + if [ "$SUMMARY_TEMPLATE" = "true" ] && [ "$releases" -gt 0 ]; then + local_ext_release_details=$(echo "$releases_response" | jq -c --arg since "$WEEK_AGO" --arg until "$NOW" --arg repo "$full_repo" --arg org "$repo_org" ' + if type == "array" then + [.[] | select(.published_at != null and .published_at >= $since and .published_at <= $until and .draft == false) | + { + repo: $repo, + org: $org, + tag: .tag_name, + name: (.name // .tag_name), + url: .html_url, + published_at: .published_at, + body: (.body // "" | split("\n")[0:3] | join(" ")) + }] | .[] + else empty end + ' 2>/dev/null) + fi # Handle null/empty values current_issues=${current_issues:-0} @@ -322,9 +410,19 @@ process_external_repo() { merged_prs=${merged_prs:-0} opened_prs=${opened_prs:-0} commits=${commits:-0} + releases=${releases:-0} # Return values as JSON for easy parsing - echo "{\"repo\":\"$full_repo\",\"current_issues\":$current_issues,\"opened_issues\":$opened_issues,\"closed_issues\":$closed_issues,\"opened_prs\":$opened_prs,\"merged_prs\":$merged_prs,\"commits\":$commits}" + # Include PR details and release details as nested arrays for summary template + local pr_details_json="[]" + if [ -n "$local_ext_pr_details" ]; then + pr_details_json=$(echo "$local_ext_pr_details" | jq -s '.') + fi + local release_details_json="[]" + if [ -n "$local_ext_release_details" ]; then + release_details_json=$(echo "$local_ext_release_details" | jq -s '.') + fi + echo "{\"repo\":\"$full_repo\",\"current_issues\":$current_issues,\"opened_issues\":$opened_issues,\"closed_issues\":$closed_issues,\"opened_prs\":$opened_prs,\"merged_prs\":$merged_prs,\"releases\":$releases,\"commits\":$commits,\"pr_details\":$pr_details_json,\"release_details\":$release_details_json}" } # Get all repositories and filter by activity @@ -485,6 +583,7 @@ total_closed_issues=0 total_merged_prs=0 total_commits=0 total_opened_prs=0 +total_releases=0 failed_repos=0 rate_limited_repos=0 report_content="" @@ -514,11 +613,16 @@ if [ "$OUTPUT_FORMAT" = "markdown" ]; then ## Summary -| Repository | Current Issues | Opened Issues | Closed Issues | Opened PRs | Merged PRs | Commits | -|------------|----------------|---------------|---------------|------------|------------|---------|" - echo "DEBUG: Initial report content set, length: ${#report_content}" +| Repository | Current Issues | Opened Issues | Closed Issues | Opened PRs | Merged PRs | Releases | Commits | +|------------|----------------|---------------|---------------|------------|------------|----------|---------|" + debug "Initial report content set, length: ${#report_content}" fi +# Initialize summary template data collection +# We collect merged PR details and release info as newline-delimited JSON records +summary_template_data="" +summary_release_data="" + # Process each repository repo_count=0 repos_with_activity=0 @@ -529,8 +633,7 @@ while IFS= read -r repo; do echo "Processing repository: $repo" # Count total current open issues - current_issues_response=$(api_call "/repos/${ORGANIZATION}/${repo}/issues?state=open") - if [ $? -eq 0 ]; then + if current_issues_response=$(api_call "/repos/${ORGANIZATION}/${repo}/issues?state=open"); then current_issues=$(echo "$current_issues_response" | jq 'if type == "array" then [.[] | select(.pull_request == null)] | length else 0 end') else current_issues=0 @@ -542,8 +645,7 @@ while IFS= read -r repo; do fi # Count opened issues in the date range - opened_response=$(api_call "/repos/${ORGANIZATION}/${repo}/issues?state=all") - if [ $? -eq 0 ]; then + if opened_response=$(api_call "/repos/${ORGANIZATION}/${repo}/issues?state=all"); then opened_issues=$(echo "$opened_response" | jq --arg since "$WEEK_AGO" --arg until "$NOW" 'if type == "array" then [.[] | select(.created_at >= $since and .created_at <= $until and .pull_request == null)] | length else 0 end') else opened_issues=0 @@ -555,8 +657,7 @@ while IFS= read -r repo; do fi # Count closed issues in the date range - closed_response=$(api_call "/repos/${ORGANIZATION}/${repo}/issues?state=closed") - if [ $? -eq 0 ]; then + if closed_response=$(api_call "/repos/${ORGANIZATION}/${repo}/issues?state=closed"); then closed_issues=$(echo "$closed_response" | jq --arg since "$WEEK_AGO" --arg until "$NOW" 'if type == "array" then [.[] | select(.closed_at != null and .closed_at >= $since and .closed_at <= $until and .pull_request == null)] | length else 0 end') else closed_issues=0 @@ -568,9 +669,35 @@ while IFS= read -r repo; do fi # Count merged PRs in the date range - prs_response=$(api_call "/repos/${ORGANIZATION}/${repo}/pulls?state=closed") - if [ $? -eq 0 ]; then + if prs_response=$(api_call "/repos/${ORGANIZATION}/${repo}/pulls?state=closed"); then merged_prs=$(echo "$prs_response" | jq --arg since "$WEEK_AGO" --arg until "$NOW" 'if type == "array" then [.[] | select(.merged_at != null and .merged_at >= $since and .merged_at <= $until)] | length else 0 end') + + # Collect merged PR details for summary template + if [ "$SUMMARY_TEMPLATE" = "true" ] && [ "$merged_prs" -gt 0 ]; then + local_pr_details=$(echo "$prs_response" | jq -c --arg since "$WEEK_AGO" --arg until "$NOW" --arg repo "$repo" --arg org "$ORGANIZATION" ' + if type == "array" then + [.[] | select(.merged_at != null and .merged_at >= $since and .merged_at <= $until) | + { + repo: $repo, + org: $org, + number: .number, + title: .title, + url: .html_url, + author: .user.login, + labels: [.labels[]?.name], + merged_at: .merged_at + }] | .[] + else empty end + ' 2>/dev/null) + if [ -n "$local_pr_details" ]; then + if [ -n "$summary_template_data" ]; then + summary_template_data="${summary_template_data} +${local_pr_details}" + else + summary_template_data="$local_pr_details" + fi + fi + fi else merged_prs=0 if echo "$prs_response" | jq -e '.error' 2>/dev/null | grep -q "rate_limit"; then @@ -581,8 +708,7 @@ while IFS= read -r repo; do fi # Count opened PRs in the date range (both open and closed) - all_prs_response=$(api_call "/repos/${ORGANIZATION}/${repo}/pulls?state=all") - if [ $? -eq 0 ]; then + if all_prs_response=$(api_call "/repos/${ORGANIZATION}/${repo}/pulls?state=all"); then opened_prs=$(echo "$all_prs_response" | jq --arg since "$WEEK_AGO" --arg until "$NOW" 'if type == "array" then [.[] | select(.created_at >= $since and .created_at <= $until)] | length else 0 end') else opened_prs=0 @@ -594,8 +720,7 @@ while IFS= read -r repo; do fi # Count commits in the date range - commits_response=$(api_call "/repos/${ORGANIZATION}/${repo}/commits?since=${WEEK_AGO}&until=${NOW}") - if [ $? -eq 0 ]; then + if commits_response=$(api_call "/repos/${ORGANIZATION}/${repo}/commits?since=${WEEK_AGO}&until=${NOW}"); then commits=$(echo "$commits_response" | jq 'if type == "array" then length else 0 end') else commits=0 @@ -606,6 +731,44 @@ while IFS= read -r repo; do fi fi + # Count releases published in the date range + if releases_response=$(api_call "/repos/${ORGANIZATION}/${repo}/releases"); then + releases=$(echo "$releases_response" | jq --arg since "$WEEK_AGO" --arg until "$NOW" 'if type == "array" then [.[] | select(.published_at != null and .published_at >= $since and .published_at <= $until and .draft == false)] | length else 0 end') + + # Collect release details for summary template + if [ "$SUMMARY_TEMPLATE" = "true" ] && [ "$releases" -gt 0 ]; then + local_release_details=$(echo "$releases_response" | jq -c --arg since "$WEEK_AGO" --arg until "$NOW" --arg repo "$repo" --arg org "$ORGANIZATION" ' + if type == "array" then + [.[] | select(.published_at != null and .published_at >= $since and .published_at <= $until and .draft == false) | + { + repo: $repo, + org: $org, + tag: .tag_name, + name: (.name // .tag_name), + url: .html_url, + published_at: .published_at, + body: (.body // "" | split("\n")[0:3] | join(" ")) + }] | .[] + else empty end + ' 2>/dev/null) + if [ -n "$local_release_details" ]; then + if [ -n "$summary_release_data" ]; then + summary_release_data="${summary_release_data} +${local_release_details}" + else + summary_release_data="$local_release_details" + fi + fi + fi + else + releases=0 + if echo "$releases_response" | jq -e '.error' 2>/dev/null | grep -q "rate_limit"; then + rate_limited_repos=$((rate_limited_repos + 1)) + else + failed_repos=$((failed_repos + 1)) + fi + fi + # Handle null/empty values current_issues=${current_issues:-0} opened_issues=${opened_issues:-0} @@ -613,6 +776,7 @@ while IFS= read -r repo; do merged_prs=${merged_prs:-0} opened_prs=${opened_prs:-0} commits=${commits:-0} + releases=${releases:-0} # Add to totals total_current_issues=$((total_current_issues + current_issues)) @@ -621,10 +785,11 @@ while IFS= read -r repo; do total_merged_prs=$((total_merged_prs + merged_prs)) total_opened_prs=$((total_opened_prs + opened_prs)) total_commits=$((total_commits + commits)) + total_releases=$((total_releases + releases)) # Only include repos with actual activity (exclude repos with all zeros in activity columns) # Note: We check activity metrics only, not current_issues (which is current state, not activity) - if [ $((opened_issues + closed_issues + opened_prs + merged_prs + commits)) -gt 0 ]; then + if [ $((opened_issues + closed_issues + opened_prs + merged_prs + commits + releases)) -gt 0 ]; then repos_with_activity=$((repos_with_activity + 1)) if [ "$OUTPUT_FORMAT" = "markdown" ]; then # Create GitHub search URLs for clickable metrics @@ -634,34 +799,36 @@ while IFS= read -r repo; do closed_issues_url="https://github.com/${ORGANIZATION}/${repo}/issues?q=is:issue+closed:${start_display}..${end_display}" opened_prs_url="https://github.com/${ORGANIZATION}/${repo}/pulls?q=is:pr+created:${start_display}..${end_display}" merged_prs_url="https://github.com/${ORGANIZATION}/${repo}/pulls?q=is:pr+merged:${start_display}..${end_display}" + releases_url="https://github.com/${ORGANIZATION}/${repo}/releases" commits_url="https://github.com/${ORGANIZATION}/${repo}/commits?since=${start_display}&until=${end_display}" - # Format metrics as clickable links if > 1 + # Format metrics as clickable links if > 0 current_issues_display=$(format_metric "$current_issues" "$current_issues_url") opened_issues_display=$(format_metric "$opened_issues" "$opened_issues_url") closed_issues_display=$(format_metric "$closed_issues" "$closed_issues_url") opened_prs_display=$(format_metric "$opened_prs" "$opened_prs_url") merged_prs_display=$(format_metric "$merged_prs" "$merged_prs_url") + releases_display=$(format_metric "$releases" "$releases_url") commits_display=$(format_metric "$commits" "$commits_url") report_content="${report_content} -| $repo | $current_issues_display | $opened_issues_display | $closed_issues_display | $opened_prs_display | $merged_prs_display | $commits_display |" +| $repo | $current_issues_display | $opened_issues_display | $closed_issues_display | $opened_prs_display | $merged_prs_display | $releases_display | $commits_display |" fi else - echo "DEBUG: Skipping $repo from report (no activity: all metrics are zero)" + debug "Skipping $repo from report (no activity: all metrics are zero)" fi done <<< "$repo_names" -echo "DEBUG: Processed $repo_count repositories" -echo "DEBUG: Repositories with activity (included in report): $repos_with_activity" -echo "DEBUG: Repositories skipped (no activity): $((repo_count - repos_with_activity))" -echo "DEBUG: Final report content length: ${#report_content}" +debug "Processed $repo_count repositories" +debug "Repositories with activity (included in report): $repos_with_activity" +debug "Repositories skipped (no activity): $((repo_count - repos_with_activity))" +debug "Final report content length: ${#report_content}" # Add summary to report if [ "$OUTPUT_FORMAT" = "markdown" ]; then report_content="${report_content} -|**Total**|**$total_current_issues**|**$total_opened_issues**|**$total_closed_issues**|**$total_opened_prs**|**$total_merged_prs**|**$total_commits**| +|**Total**|**$total_current_issues**|**$total_opened_issues**|**$total_closed_issues**|**$total_opened_prs**|**$total_merged_prs**|**$total_releases**|**$total_commits**| ## Details @@ -671,6 +838,7 @@ if [ "$OUTPUT_FORMAT" = "markdown" ]; then - **Total Issues Closed:** $total_closed_issues - **Total PRs Opened:** $total_opened_prs - **Total PRs Merged:** $total_merged_prs +- **Total Releases:** $total_releases - **Total Commits:** $total_commits" # Add warnings about incomplete data if any API calls failed @@ -704,6 +872,7 @@ if [ "$OUTPUT_FORMAT" = "markdown" ]; then ext_total_merged_prs=0 ext_total_commits=0 ext_total_opened_prs=0 + ext_total_releases=0 ext_repos_with_activity=0 ext_report_content="" @@ -722,8 +891,33 @@ if [ "$OUTPUT_FORMAT" = "markdown" ]; then closed_issues=$(echo "$repo_result" | jq -r '.closed_issues') opened_prs=$(echo "$repo_result" | jq -r '.opened_prs') merged_prs=$(echo "$repo_result" | jq -r '.merged_prs') + releases=$(echo "$repo_result" | jq -r '.releases') commits=$(echo "$repo_result" | jq -r '.commits') + # Extract PR details for summary template + if [ "$SUMMARY_TEMPLATE" = "true" ]; then + ext_pr_details=$(echo "$repo_result" | jq -c '.pr_details[]?' 2>/dev/null) + if [ -n "$ext_pr_details" ]; then + if [ -n "$summary_template_data" ]; then + summary_template_data="${summary_template_data} +${ext_pr_details}" + else + summary_template_data="$ext_pr_details" + fi + fi + + # Extract release details for summary template + ext_release_details=$(echo "$repo_result" | jq -c '.release_details[]?' 2>/dev/null) + if [ -n "$ext_release_details" ]; then + if [ -n "$summary_release_data" ]; then + summary_release_data="${summary_release_data} +${ext_release_details}" + else + summary_release_data="$ext_release_details" + fi + fi + fi + # Add to totals ext_total_current_issues=$((ext_total_current_issues + current_issues)) ext_total_opened_issues=$((ext_total_opened_issues + opened_issues)) @@ -731,9 +925,10 @@ if [ "$OUTPUT_FORMAT" = "markdown" ]; then ext_total_merged_prs=$((ext_total_merged_prs + merged_prs)) ext_total_opened_prs=$((ext_total_opened_prs + opened_prs)) ext_total_commits=$((ext_total_commits + commits)) + ext_total_releases=$((ext_total_releases + releases)) # Only include repos with activity - if [ $((opened_issues + closed_issues + opened_prs + merged_prs + commits)) -gt 0 ]; then + if [ $((opened_issues + closed_issues + opened_prs + merged_prs + commits + releases)) -gt 0 ]; then ext_repos_with_activity=$((ext_repos_with_activity + 1)) # Create GitHub search URLs @@ -742,6 +937,7 @@ if [ "$OUTPUT_FORMAT" = "markdown" ]; then closed_issues_url="https://github.com/${full_repo}/issues?q=is:issue+closed:${start_display}..${end_display}" opened_prs_url="https://github.com/${full_repo}/pulls?q=is:pr+created:${start_display}..${end_display}" merged_prs_url="https://github.com/${full_repo}/pulls?q=is:pr+merged:${start_display}..${end_display}" + releases_url="https://github.com/${full_repo}/releases" commits_url="https://github.com/${full_repo}/commits?since=${start_display}&until=${end_display}" # Format metrics as clickable links if > 0 @@ -750,12 +946,13 @@ if [ "$OUTPUT_FORMAT" = "markdown" ]; then closed_issues_display=$(format_metric "$closed_issues" "$closed_issues_url") opened_prs_display=$(format_metric "$opened_prs" "$opened_prs_url") merged_prs_display=$(format_metric "$merged_prs" "$merged_prs_url") + releases_display=$(format_metric "$releases" "$releases_url") commits_display=$(format_metric "$commits" "$commits_url") ext_report_content="${ext_report_content} -| $full_repo | $current_issues_display | $opened_issues_display | $closed_issues_display | $opened_prs_display | $merged_prs_display | $commits_display |" +| $full_repo | $current_issues_display | $opened_issues_display | $closed_issues_display | $opened_prs_display | $merged_prs_display | $releases_display | $commits_display |" else - echo "DEBUG: Skipping $full_repo from report (no activity: all metrics are zero)" + debug "Skipping $full_repo from report (no activity: all metrics are zero)" fi done @@ -765,14 +962,14 @@ if [ "$OUTPUT_FORMAT" = "markdown" ]; then ## External Repositories -| Repository | Current Issues | Opened Issues | Closed Issues | Opened PRs | Merged PRs | Commits | -|------------|----------------|---------------|---------------|------------|------------|---------|${ext_report_content} -|**Total**|**$ext_total_current_issues**|**$ext_total_opened_issues**|**$ext_total_closed_issues**|**$ext_total_opened_prs**|**$ext_total_merged_prs**|**$ext_total_commits**|" +| Repository | Current Issues | Opened Issues | Closed Issues | Opened PRs | Merged PRs | Releases | Commits | +|------------|----------------|---------------|---------------|------------|------------|----------|---------|${ext_report_content} +|**Total**|**$ext_total_current_issues**|**$ext_total_opened_issues**|**$ext_total_closed_issues**|**$ext_total_opened_prs**|**$ext_total_merged_prs**|**$ext_total_releases**|**$ext_total_commits**|" - echo "DEBUG: External repositories with activity: $ext_repos_with_activity" - echo "DEBUG: External repos totals - Issues: $ext_total_opened_issues opened, $ext_total_closed_issues closed | PRs: $ext_total_opened_prs opened, $ext_total_merged_prs merged | Commits: $ext_total_commits" + debug "External repositories with activity: $ext_repos_with_activity" + debug "External repos totals - Issues: $ext_total_opened_issues opened, $ext_total_closed_issues closed | PRs: $ext_total_opened_prs opened, $ext_total_merged_prs merged | Commits: $ext_total_commits" else - echo "DEBUG: No external repository activity in date range" + debug "No external repository activity in date range" fi fi @@ -782,7 +979,133 @@ if [ "$OUTPUT_FORMAT" = "markdown" ]; then fi # Create summary -summary="Summary: $total_current_issues current open issues, $total_opened_issues issues opened, $total_closed_issues issues closed, $total_opened_prs PRs opened, $total_merged_prs PRs merged, $total_commits commits" +summary="Summary: $total_current_issues current open issues, $total_opened_issues issues opened, $total_closed_issues issues closed, $total_opened_prs PRs opened, $total_merged_prs PRs merged, $total_releases releases, $total_commits commits" + +# Generate comment summary template if enabled +if [ "$SUMMARY_TEMPLATE" = "true" ]; then + echo "Generating comment summary template..." + + template_content="**High Level Summary:** + + + +1. **[Topic 1]** _Add a high-level summary of work in this area._ +2. **[Topic 2]** _Add another thematic summary._ + +---" + + # --- Releases table (Option A: single table at top, omitted if no releases) --- + if [ -n "$summary_release_data" ]; then + template_content="${template_content} + +## 📦 Releases + +| Repository | Release | Date | +|------------|---------|------|" + + # Sort all releases by published_at descending + sorted_releases=$(echo "$summary_release_data" | jq -s 'sort_by(.published_at) | reverse | .[]' -c 2>/dev/null) + + while IFS= read -r release_json; do + [ -z "$release_json" ] && continue + release_repo=$(echo "$release_json" | jq -r '.repo') + release_name=$(echo "$release_json" | jq -r '.name') + release_tag=$(echo "$release_json" | jq -r '.tag') + release_url=$(echo "$release_json" | jq -r '.url') + release_date=$(echo "$release_json" | jq -r '.published_at' | cut -d'T' -f1) + # Format the date as "Feb 5" style — non-zero-padded day (GNU %-d first, then BSD %e trimmed, then fallback) + release_date_display=$(date -d "$release_date" "+%b %-d" 2>/dev/null || date -j -f "%Y-%m-%d" "$release_date" "+%b %e" 2>/dev/null | sed 's/ / /' || echo "$release_date") + # Use name if different from tag, otherwise just show tag + if [ "$release_name" != "$release_tag" ] && [ -n "$release_name" ]; then + release_label="${release_name} (${release_tag})" + else + release_label="${release_tag}" + fi + template_content="${template_content} +| ${release_repo} | [${release_label}](${release_url}) | ${release_date_display} |" + done <<< "$sorted_releases" + fi + + # --- PR listings per repository --- + if [ -n "$summary_template_data" ]; then + # Get unique repos from the collected PR data + repo_list=$(echo "$summary_template_data" | jq -r '.repo' | sort -u) + + while IFS= read -r template_repo; do + [ -z "$template_repo" ] && continue + + template_content="${template_content} + +**${template_repo}:**" + + # Get PRs for this repo, sorted by merged_at + repo_prs=$(echo "$summary_template_data" | jq -c --arg repo "$template_repo" 'select(.repo == $repo)' | jq -s 'sort_by(.merged_at) | reverse | .[]' -c 2>/dev/null) + + while IFS= read -r pr_json; do + [ -z "$pr_json" ] && continue + pr_title=$(echo "$pr_json" | jq -r '.title') + pr_url=$(echo "$pr_json" | jq -r '.url') + pr_author=$(echo "$pr_json" | jq -r '.author') + pr_labels=$(echo "$pr_json" | jq -r '.labels | if length > 0 then " [" + join(", ") + "]" else "" end') + + template_content="${template_content} + - [${pr_title}](${pr_url})${pr_labels} (@${pr_author})" + done <<< "$repo_prs" + + done <<< "$repo_list" + elif [ -z "$summary_release_data" ]; then + # No PRs and no releases + template_content="${template_content} + +_No merged PRs or releases found in the report period._" + fi + + # Build contributors line from actual PR authors + contributors_line="" + if [ -n "$summary_template_data" ]; then + # Get unique non-bot authors, sorted alphabetically + contributors=$(echo "$summary_template_data" | jq -r '.author' | grep -v '\[bot\]$' | grep -vi '^copilot$' | sort -uf) + contributor_count=$(echo "$contributors" | grep -c . || true) + + if [ "$contributor_count" -gt 0 ]; then + # Format as @user1, @user2, and @user3 + formatted="" + i=0 + while IFS= read -r author; do + [ -z "$author" ] && continue + i=$((i + 1)) + if [ $i -eq 1 ]; then + formatted="@${author}" + elif [ $i -eq "$contributor_count" ]; then + formatted="${formatted}, and @${author}" + else + formatted="${formatted}, @${author}" + fi + done <<< "$contributors" + contributors_line="thanks ${formatted} for all your contributions." + fi + fi + + if [ -z "$contributors_line" ]; then + contributors_line="" + fi + + template_content="${template_content} + +--- + +${contributors_line}" + + # Save template to file + echo "$template_content" > "$SUMMARY_TEMPLATE_FILE" + echo "Comment summary template generated: $SUMMARY_TEMPLATE_FILE" + debug "Summary template length: ${#template_content}" +fi # Save report to file echo "$report_content" > "$OUTPUT_FILE" @@ -790,15 +1113,15 @@ echo "$report_content" > "$OUTPUT_FILE" echo "Report generated: $OUTPUT_FILE" # Debug: Check if GITHUB_OUTPUT is set and accessible -echo "DEBUG: GITHUB_OUTPUT environment variable: ${GITHUB_OUTPUT:-NOT_SET}" -echo "DEBUG: Report content length: ${#report_content}" -echo "DEBUG: Summary: $summary" +debug "GITHUB_OUTPUT environment variable: ${GITHUB_OUTPUT:-NOT_SET}" +debug "Report content length: ${#report_content}" +debug "Summary: $summary" # Set outputs if [ -n "$GITHUB_OUTPUT" ]; then - echo "DEBUG: Writing to GITHUB_OUTPUT file" - echo "DEBUG: Content preview (first 100 chars): ${report_content:0:100}" - echo "DEBUG: Summary preview: $summary" + debug "Writing to GITHUB_OUTPUT file" + debug "Content preview (first 100 chars): ${report_content:0:100}" + debug "Summary preview: $summary" # Use a unique delimiter to avoid conflicts with content delimiter="QUANTECON_REPORT_END_$(date +%s)" @@ -808,10 +1131,18 @@ if [ -n "$GITHUB_OUTPUT" ]; then echo "report-summary=$summary" >> "$GITHUB_OUTPUT" - echo "DEBUG: Outputs written to GITHUB_OUTPUT" - echo "DEBUG: GITHUB_OUTPUT file size: $(wc -c < "$GITHUB_OUTPUT")" + # Add summary template output if enabled + if [ "$SUMMARY_TEMPLATE" = "true" ] && [ -n "$template_content" ]; then + template_delimiter="QUANTECON_TEMPLATE_END_$(date +%s)" + echo "comment-template<<${template_delimiter}" >> "$GITHUB_OUTPUT" + echo "$template_content" >> "$GITHUB_OUTPUT" + echo "${template_delimiter}" >> "$GITHUB_OUTPUT" + fi + + debug "Outputs written to GITHUB_OUTPUT" + debug "GITHUB_OUTPUT file size: $(wc -c < "$GITHUB_OUTPUT")" else - echo "DEBUG: GITHUB_OUTPUT not set (expected in CLI mode, outputs are for GitHub Actions only)" + debug "GITHUB_OUTPUT not set (expected in CLI mode, outputs are for GitHub Actions only)" fi echo "Report generated successfully!"