Skip to content

Add daily GHCR download tracker workflow#1560

Merged
lpcox merged 2 commits intomainfrom
copilot/create-workflow-for-downloads-graph
Mar 3, 2026
Merged

Add daily GHCR download tracker workflow#1560
lpcox merged 2 commits intomainfrom
copilot/create-workflow-for-downloads-graph

Conversation

Copy link
Contributor

Copilot AI commented Mar 3, 2026

Adds an agentic workflow that tracks ghcr.io/github/gh-aw-mcpg container image downloads over time and posts a graph as a GitHub issue daily.

Workflow behavior

  • Fetches total download count by summing across all package versions via gh api /orgs/github/packages/container/gh-aw-mcpg/versions
  • Persists daily snapshots in cache-memory with a 90-day rolling window
  • Generates a line chart as a quickchart.io URL — rendered client-side in the issue, no outbound request from the runner
  • Creates a GitHub issue with the embedded chart, a summary table (current total, 7-day and 30-day growth), and a collapsible full history table
  • close-older-issues: true keeps only the latest report open; expires: 8 auto-closes stale ones
  • Falls back to noop on API failure — no issue created

Files

  • .github/workflows/ghcr-download-tracker.md — workflow definition
  • .github/workflows/ghcr-download-tracker.lock.yml — compiled GitHub Actions YAML (generated by gh aw compile)

Copilot AI and others added 2 commits March 3, 2026 13:14
Co-authored-by: lpcox <15877973+lpcox@users.noreply.github.com>
@lpcox lpcox marked this pull request as ready for review March 3, 2026 13:21
Copilot AI review requested due to automatic review settings March 3, 2026 13:21
@lpcox lpcox merged commit e65228a into main Mar 3, 2026
3 checks passed
@lpcox lpcox deleted the copilot/create-workflow-for-downloads-graph branch March 3, 2026 13:22
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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-tracker agent 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.

Comment on lines +164 to +165
step = len(history) / 30
history = [history[int(i * step)] for i in range(30)]
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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].

Suggested change
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]

Copilot uses AI. Check for mistakes.
Comment on lines +226 to +233

| Metric | Value |
|--------|-------|
| **Current Total Downloads** | <currentCount> |
| **7-Day Growth** | +<delta7> downloads |
| **30-Day Growth** | +<delta30> downloads |
| **Tracking Since** | <earliestDate> |

Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copilot uses AI. Check for mistakes.
Comment on lines +1016 to +1019
GH_AW_AGENT_CONCLUSION: ${{ needs.agent.result }}
GH_AW_NOOP_MESSAGE: ${{ steps.noop.outputs.noop_message }}
GH_AW_NOOP_REPORT_AS_ISSUE: "true"
with:
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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).

Copilot uses AI. Check for mistakes.
needs: activation
runs-on: ubuntu-latest
permissions:
contents: read
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
contents: read
contents: read
packages: read

Copilot uses AI. Check for mistakes.
workflow_dispatch:

permissions:
contents: read
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
contents: read
contents: read
packages: read

Copilot uses AI. Check for mistakes.
Comment on lines +53 to +57
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)
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
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)

Copilot uses AI. Check for mistakes.
cache_file = os.path.join(cache_dir, 'download-history.json')

today = datetime.utcnow().strftime('%Y-%m-%d')
current_count = <REPLACE_WITH_currentCount>
Copy link

Copilot AI Mar 3, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Suggested change
current_count = <REPLACE_WITH_currentCount>
current_count = int(os.environ.get("CURRENT_COUNT", "0"))

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants