Conversation
Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Adds a new gh-aw agentic workflow that records daily GHCR download totals for ghcr.io/github/gh-aw-mcpg, stores a rolling history in cache-memory, and posts a daily GitHub issue containing a QuickChart-rendered trend graph plus summary stats.
Changes:
- Introduces the
ghcr-download-trackeragent prompt/workflow definition (frontmatter + instructions). - Adds the compiled/locked GitHub Actions workflow generated by
gh aw compile.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 7 comments.
| File | Description |
|---|---|
.github/workflows/ghcr-download-tracker.md |
Defines the agent’s steps: fetch download totals, update 90-day cache history, generate a QuickChart URL, and create a daily report issue. |
.github/workflows/ghcr-download-tracker.lock.yml |
Compiled workflow that executes the Copilot agent, safe-outputs handling, and cache-memory persistence. |
Comments suppressed due to low confidence (1)
.github/workflows/ghcr-download-tracker.md:241
- Same table-formatting issue in the history table template: the header/separator rows begin with
||, which introduces an unintended empty column. Use single leading|so the history table renders as intended.
| Date | Total Downloads | Daily Δ |
|------|----------------|---------|
<one row per entry, most recent first, showing daily change>
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| step = len(history) / 30 | ||
| history = [history[int(i * step)] for i in range(30)] |
There was a problem hiding this comment.
The downsampling logic can drop the most recent datapoint: int(i * step) often won’t select the last index, so the chart may omit “today” once history exceeds 30 entries. Use an index calculation that guarantees inclusion of both endpoints (first and last), e.g., spread indices over [0, len(history)-1].
| step = len(history) / 30 | |
| history = [history[int(i * step)] for i in range(30)] | |
| n = len(history) | |
| step = (n - 1) / (30 - 1) | |
| indices = [round(i * step) for i in range(30)] | |
| history = [history[idx] for idx in indices] |
|
|
||
| | Metric | Value | | ||
| |--------|-------| | ||
| | **Current Total Downloads** | <currentCount> | | ||
| | **7-Day Growth** | +<delta7> downloads | | ||
| | **30-Day Growth** | +<delta30> downloads | | ||
| | **Tracking Since** | <earliestDate> | | ||
|
|
There was a problem hiding this comment.
The Markdown table rows in the issue template start with double pipes (|| Metric | Value |), which will render as an extra empty column. Change these to single-pipe table syntax (| Metric | Value |, etc.) so the Summary table renders correctly in the created issue.
This issue also appears on line 239 of the same file.
| GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }} | ||
| GH_AW_NOOP_MESSAGE: ${{ steps.noop.outputs.noop_message }} | ||
| GH_AW_NOOP_REPORT_AS_ISSUE: "true" | ||
| with: |
There was a problem hiding this comment.
The PR description says the workflow "falls back to noop on API failure — no issue created", but the compiled workflow sets GH_AW_NOOP_REPORT_AS_ISSUE: "true", which likely turns noop messages into issues via handle_noop_message.cjs. If the intent is truly “no issue on API failure”, set this to false (or adjust the prompt/output type so failures don’t route through noop-as-issue).
| needs: activation | ||
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read |
There was a problem hiding this comment.
The agent job only has contents: read, but it needs to call the GitHub Packages API to read GHCR download counts. Add packages: read (either at workflow level or for the agent job) so gh api /orgs/github/packages/... can succeed with the workflow token.
| contents: read | |
| contents: read | |
| packages: read |
| workflow_dispatch: | ||
|
|
||
| permissions: | ||
| contents: read |
There was a problem hiding this comment.
The workflow queries the GitHub Packages API ("/orgs/github/packages/..."), which requires packages: read permission on the workflow token. With only contents: read set here, gh api is likely to 403 and the tracker will never collect data. Add packages: read (ideally scoped at the job level if possible in gh-aw frontmatter) so the download-count query can succeed.
| contents: read | |
| contents: read | |
| packages: read |
| gh api '/orgs/github/packages/container/gh-aw-mcpg/versions?per_page=100' \ | ||
| | python3 -c " | ||
| import sys, json | ||
| versions = json.load(sys.stdin) | ||
| total = sum(v.get('download_count', 0) for v in versions) |
There was a problem hiding this comment.
The download count query only fetches the first page of versions (per_page=100) and will undercount if the container has >100 versions. Use gh api --paginate ... (or loop on the Link header) to ensure all version pages are included before summing download_count.
| gh api '/orgs/github/packages/container/gh-aw-mcpg/versions?per_page=100' \ | |
| | python3 -c " | |
| import sys, json | |
| versions = json.load(sys.stdin) | |
| total = sum(v.get('download_count', 0) for v in versions) | |
| gh api --paginate '/orgs/github/packages/container/gh-aw-mcpg/versions?per_page=100' \ | |
| | python3 -c " | |
| import sys, json | |
| total = 0 | |
| for line in sys.stdin: | |
| line = line.strip() | |
| if not line: | |
| continue | |
| page = json.loads(line) | |
| if isinstance(page, dict): | |
| page = [page] | |
| for v in page: | |
| total += v.get('download_count', 0) |
| cache_file = os.path.join(cache_dir, 'download-history.json') | ||
|
|
||
| today = datetime.utcnow().strftime('%Y-%m-%d') | ||
| current_count = <REPLACE_WITH_currentCount> |
There was a problem hiding this comment.
This Python snippet is not runnable as written (current_count = <REPLACE_WITH_currentCount> is invalid syntax). In strict: true workflows, the agent may execute this verbatim and fail. Prefer passing the value via an env var or stdin (e.g., CURRENT_COUNT in the shell) and reading it in Python, so the example is directly executable.
| current_count = <REPLACE_WITH_currentCount> | |
| current_count = int(os.environ.get("CURRENT_COUNT", "0")) |
Adds an agentic workflow that tracks
ghcr.io/github/gh-aw-mcpgcontainer image downloads over time and posts a graph as a GitHub issue daily.Workflow behavior
gh api /orgs/github/packages/container/gh-aw-mcpg/versionscache-memorywith a 90-day rolling windowclose-older-issues: truekeeps only the latest report open;expires: 8auto-closes stale onesnoopon API failure — no issue createdFiles
.github/workflows/ghcr-download-tracker.md— workflow definition.github/workflows/ghcr-download-tracker.lock.yml— compiled GitHub Actions YAML (generated bygh aw compile)