From fcbce7e5900f0c9bff29d31253421ceafa61a0d2 Mon Sep 17 00:00:00 2001 From: Gonzalo Riestra Date: Wed, 9 Sep 2026 14:01:34 +0200 Subject: [PATCH 1/4] Add scheduled maintenance PR workflow --- .github/workflows/maintenance-prs.yml | 120 ++++++++++++++++++++++++++ 1 file changed, 120 insertions(+) create mode 100644 .github/workflows/maintenance-prs.yml diff --git a/.github/workflows/maintenance-prs.yml b/.github/workflows/maintenance-prs.yml new file mode 100644 index 00000000000..7d7c3056fbe --- /dev/null +++ b/.github/workflows/maintenance-prs.yml @@ -0,0 +1,120 @@ +name: maintenance-prs + +on: + schedule: + # GitHub schedules use UTC: midnight is 02:00 at a fixed GMT+2 offset. + - cron: '0 0 * * 0,2-6' + - cron: '0 0 * * 1' + +permissions: + contents: write + issues: read + pull-requests: write + +env: + PNPM_VERSION: '10.11.1' + SHOPIFY_CLI_ENV: development + SHOPIFY_CONFIG: debug + +jobs: + maintenance: + name: Maintenance - ${{ matrix.task }} + if: github.repository == 'Shopify/cli' + runs-on: ubuntu-latest + timeout-minutes: 120 + strategy: + fail-fast: false + matrix: + task: ${{ fromJSON(github.event.schedule == '0 0 * * 1' && '["refactor","tests","performance","security"]' || '["refactor","tests"]') }} + concurrency: + group: maintenance-prs-${{ matrix.task }} + cancel-in-progress: false + env: + MAINTENANCE_TASK: ${{ matrix.task }} + MAINTENANCE_BRANCH: ${{ matrix.task }}-maintenance-${{ github.run_id }} + steps: + - uses: actions/checkout@v6 + with: + # Include remote branches and history for the prompts' duplicate checks. + fetch-depth: 0 + + - name: Setup deps + uses: ./.github/actions/setup-cli-deps + with: + node-version: '26.1.0' + + - name: Create maintenance branch + run: | + git config user.name 'github-actions[bot]' + git config user.email '41898282+github-actions[bot]@users.noreply.github.com' + git checkout -b "$MAINTENANCE_BRANCH" + + - name: Run maintenance task + id: maintenance + uses: anthropics/claude-code-action@36a69b6a90b850823f86de06fdfd56264772ad98 # v1 + env: + ANTHROPIC_BASE_URL: ${{ secrets.ANTHROPIC_BASE_URL }} + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + CLAUDE_BRANCH: ${{ env.MAINTENANCE_BRANCH }} + with: + anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }} + github_token: ${{ secrets.GITHUB_TOKEN }} + bot_name: 'github-actions[bot]' + bot_id: '41898282' + prompt: | + Perform the maintenance task in `.agents/automated-tasks/${{ matrix.task }}.md`. + Read the entire file, `AGENTS.md`, and `.github/PULL_REQUEST_TEMPLATE.md` + before choosing a change. Carefully follow every phase and boundary in the task file. + + Work on the existing branch `${{ env.MAINTENANCE_BRANCH }}`. It already has + the required task prefix. Create at most ONE draft PR against the repository's + default branch. Use `gh pr create --draft` and the PR template exactly as instructed. + Never merge, approve, or mark a PR ready for review. + + For duplicate checks, inspect remote branches and search open, merged, and closed + PRs with `gh pr list --state all`, then read the bodies and diffs of related PRs. + This includes previous maintenance PRs whose branches have been deleted. + If no worthwhile, non-duplicate change exists after the required candidate checks, + stop successfully without opening a PR. + + Dependencies are installed. Run all verification required by the task before + opening the PR. This repo uses `pnpm test` for its unit suite; use it wherever + the task says `pnpm test:unit`. Do not open a PR if the required checks fail. + Keep duplicate-check notes out of the PR body and all template checkboxes unchecked. + Do not post to Slack; the workflow will announce the PR after it exists. + claude_args: | + --allowedTools Read,Glob,Grep,Edit,Write,Bash + + - name: Find the created PR + id: pull-request + # Claude may create a PR before a later step in its session fails. + if: ${{ !cancelled() && steps.maintenance.outcome != 'skipped' }} + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + with: + script: | + const {data: pullRequests} = await github.rest.pulls.list({ + ...context.repo, + state: 'open', + head: `${context.repo.owner}:${process.env.MAINTENANCE_BRANCH}`, + base: context.payload.repository.default_branch, + }); + const pullRequest = pullRequests[0]; + if (!pullRequest) { + core.info('No maintenance PR was created.'); + return; + } + + core.setOutput('payload', JSON.stringify({ + channel: 'C0ARV62K59C', // #devtools-gardener-backlog + text: `Maintenance PR (${process.env.MAINTENANCE_TASK}): ${pullRequest.html_url}`, + unfurl_links: false, + unfurl_media: false, + })); + + - name: Post PR to gardener backlog + if: ${{ !cancelled() && steps.pull-request.outputs.payload != '' }} + uses: slackapi/slack-github-action@b0fa283ad8fea605de13dc3f449259339835fc52 # v2.1.0 + with: + method: chat.postMessage + token: ${{ secrets.SLACK_GARDENER_BOT_TOKEN }} + payload: ${{ steps.pull-request.outputs.payload }} From 6dc81fd541f229c4bcd1893b730b136518150da1 Mon Sep 17 00:00:00 2001 From: Gonzalo Riestra Date: Wed, 9 Sep 2026 14:06:48 +0200 Subject: [PATCH 2/4] Allow manual maintenance PR runs --- .github/workflows/maintenance-prs.yml | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/.github/workflows/maintenance-prs.yml b/.github/workflows/maintenance-prs.yml index 7d7c3056fbe..72bc1071382 100644 --- a/.github/workflows/maintenance-prs.yml +++ b/.github/workflows/maintenance-prs.yml @@ -1,6 +1,12 @@ name: maintenance-prs on: + workflow_dispatch: + inputs: + include_weekly: + description: 'Also run performance and security tasks' + type: boolean + default: false schedule: # GitHub schedules use UTC: midnight is 02:00 at a fixed GMT+2 offset. - cron: '0 0 * * 0,2-6' @@ -25,7 +31,7 @@ jobs: strategy: fail-fast: false matrix: - task: ${{ fromJSON(github.event.schedule == '0 0 * * 1' && '["refactor","tests","performance","security"]' || '["refactor","tests"]') }} + task: ${{ fromJSON((inputs.include_weekly || github.event.schedule == '0 0 * * 1') && '["refactor","tests","performance","security"]' || '["refactor","tests"]') }} concurrency: group: maintenance-prs-${{ matrix.task }} cancel-in-progress: false From c3598dec9e0e714e84cbe287a8fec6fbfdb5f7da Mon Sep 17 00:00:00 2001 From: Gonzalo Riestra Date: Wed, 9 Sep 2026 14:07:20 +0200 Subject: [PATCH 3/4] Run all maintenance tasks on manual dispatch --- .github/workflows/maintenance-prs.yml | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/.github/workflows/maintenance-prs.yml b/.github/workflows/maintenance-prs.yml index 72bc1071382..e9e6dbb5f60 100644 --- a/.github/workflows/maintenance-prs.yml +++ b/.github/workflows/maintenance-prs.yml @@ -2,11 +2,6 @@ name: maintenance-prs on: workflow_dispatch: - inputs: - include_weekly: - description: 'Also run performance and security tasks' - type: boolean - default: false schedule: # GitHub schedules use UTC: midnight is 02:00 at a fixed GMT+2 offset. - cron: '0 0 * * 0,2-6' @@ -31,7 +26,7 @@ jobs: strategy: fail-fast: false matrix: - task: ${{ fromJSON((inputs.include_weekly || github.event.schedule == '0 0 * * 1') && '["refactor","tests","performance","security"]' || '["refactor","tests"]') }} + task: ${{ fromJSON((github.event_name == 'workflow_dispatch' || github.event.schedule == '0 0 * * 1') && '["refactor","tests","performance","security"]' || '["refactor","tests"]') }} concurrency: group: maintenance-prs-${{ matrix.task }} cancel-in-progress: false From 43b6be7d4b70e6eb871307ffa93f9066cdccca2a Mon Sep 17 00:00:00 2001 From: Gonzalo Riestra Date: Wed, 9 Sep 2026 14:14:44 +0200 Subject: [PATCH 4/4] Simplify maintenance scheduling with a weekday check --- .agents/automated-tasks/performance.md | 3 ++- .agents/automated-tasks/refactor.md | 2 +- .agents/automated-tasks/security.md | 2 +- .agents/automated-tasks/tests.md | 2 +- .github/workflows/maintenance-prs.yml | 34 ++++++++++++++++++++------ 5 files changed, 32 insertions(+), 11 deletions(-) diff --git a/.agents/automated-tasks/performance.md b/.agents/automated-tasks/performance.md index f7fd95efe0b..012449b1306 100644 --- a/.agents/automated-tasks/performance.md +++ b/.agents/automated-tasks/performance.md @@ -10,7 +10,7 @@ Every branch you create MUST start with `performance-` (e.g. `performance-memoiz ✅ **Always do:** - Do exactly ONE thing per PR. -- Run `pnpm lint`, `pnpm knip`, `pnpm type-check`, and `pnpm test:unit` (or the project's equivalents) before opening the PR. +- Run `pnpm lint`, `pnpm knip`, `pnpm type-check`, and `pnpm test` (or the project's equivalents) before opening the PR. - Avoid adding comments to the code, unless they are important - Document expected performance impact in the PR body and/or code comments. - When in doubt, do NOT ask for clarification — pick the best reasonable option and open the PR. @@ -24,6 +24,7 @@ Every branch you create MUST start with `performance-` (e.g. `performance-memoiz - Add any markdown file (e.g. notes, descriptions, design docs) as part of the PR. - Add a "Duplicate check" section — or ANY section that is not already in `.github/PULL_REQUEST_TEMPLATE.md` — to the PR body. - Modify the PR template checklist. Leave every checkbox UNCHECKED. +- Do not memoize variables unless you are sure they are used many times and it really saves time for a CLI command. ## Philosophy diff --git a/.agents/automated-tasks/refactor.md b/.agents/automated-tasks/refactor.md index 44d1835f1f6..9ebc5c8da3d 100644 --- a/.agents/automated-tasks/refactor.md +++ b/.agents/automated-tasks/refactor.md @@ -11,7 +11,7 @@ Every branch you create MUST start with `refactor-` (e.g. `refactor-extract-load ✅ **Always do:** - Do exactly ONE thing per PR. - Preserve observable behavior exactly. No semantic changes. -- Run `pnpm lint`, `pnpm knip`, `pnpm type-check`, and `pnpm test:unit` (or the project's equivalents) before opening the PR. +- Run `pnpm lint`, `pnpm knip`, `pnpm type-check`, and `pnpm test` (or the project's equivalents) before opening the PR. - Follow existing patterns and conventions in the surrounding code. - Avoid adding comments to the code, unless they are important - When in doubt, do NOT ask for clarification — pick the best reasonable option and open the PR. diff --git a/.agents/automated-tasks/security.md b/.agents/automated-tasks/security.md index b8723e04c21..727bbdfabc1 100644 --- a/.agents/automated-tasks/security.md +++ b/.agents/automated-tasks/security.md @@ -11,7 +11,7 @@ Every branch you create MUST start with `security-` (e.g. `security-sanitize-inp ✅ **Always do:** - Do exactly ONE thing per PR. - Verify the fix actually closes the vector (don't just rename the symptom). -- Run `pnpm lint`, `pnpm knip`, `pnpm type-check`, and `pnpm test:unit` (or the project's equivalents) before opening the PR. +- Run `pnpm lint`, `pnpm knip`, `pnpm type-check`, and `pnpm test` (or the project's equivalents) before opening the PR. - Prefer well-vetted standard libraries over hand-rolled crypto/validation. - Avoid adding comments to the code, unless they are important - When in doubt, do NOT ask for clarification — pick the best reasonable option and open the PR. diff --git a/.agents/automated-tasks/tests.md b/.agents/automated-tasks/tests.md index bb064d0a3cf..eb562908002 100644 --- a/.agents/automated-tasks/tests.md +++ b/.agents/automated-tasks/tests.md @@ -13,7 +13,7 @@ Every branch you create MUST start with `tests-` (e.g. `tests-cover-loader`). - Test behavior, not implementation details. - Use real files and directories in temporary directories — NEVER mock the filesystem. - Keep tests isolated: avoid `beforeAll` / `afterAll` and minimize shared state. -- Run `pnpm lint`, `pnpm knip`, `pnpm type-check`, and `pnpm test:unit` before opening the PR. +- Run `pnpm lint`, `pnpm knip`, `pnpm type-check`, and `pnpm test` before opening the PR. - Avoid adding comments to the code, unless they are important - When in doubt, do NOT ask for clarification — pick the best reasonable option and open the PR. diff --git a/.github/workflows/maintenance-prs.yml b/.github/workflows/maintenance-prs.yml index e9e6dbb5f60..1adc0ad513e 100644 --- a/.github/workflows/maintenance-prs.yml +++ b/.github/workflows/maintenance-prs.yml @@ -3,9 +3,8 @@ name: maintenance-prs on: workflow_dispatch: schedule: - # GitHub schedules use UTC: midnight is 02:00 at a fixed GMT+2 offset. - - cron: '0 0 * * 0,2-6' - - cron: '0 0 * * 1' + # Everyday at 00:00 UTC + - cron: '0 0 * * *' permissions: contents: write @@ -26,7 +25,7 @@ jobs: strategy: fail-fast: false matrix: - task: ${{ fromJSON((github.event_name == 'workflow_dispatch' || github.event.schedule == '0 0 * * 1') && '["refactor","tests","performance","security"]' || '["refactor","tests"]') }} + task: [refactor, tests, performance, security] concurrency: group: maintenance-prs-${{ matrix.task }} cancel-in-progress: false @@ -34,17 +33,33 @@ jobs: MAINTENANCE_TASK: ${{ matrix.task }} MAINTENANCE_BRANCH: ${{ matrix.task }}-maintenance-${{ github.run_id }} steps: + - name: Check task schedule + id: schedule + run: | + if [[ "$GITHUB_EVENT_NAME" == 'workflow_dispatch' || + "$MAINTENANCE_TASK" == 'refactor' || + "$MAINTENANCE_TASK" == 'tests' || + "$(date -u +%u)" == '1' ]]; then + echo 'run=true' >> "$GITHUB_OUTPUT" + else + echo 'run=false' >> "$GITHUB_OUTPUT" + echo "Skipping $MAINTENANCE_TASK until Monday." + fi + - uses: actions/checkout@v6 + if: steps.schedule.outputs.run == 'true' with: # Include remote branches and history for the prompts' duplicate checks. fetch-depth: 0 - name: Setup deps + if: steps.schedule.outputs.run == 'true' uses: ./.github/actions/setup-cli-deps with: node-version: '26.1.0' - name: Create maintenance branch + if: steps.schedule.outputs.run == 'true' run: | git config user.name 'github-actions[bot]' git config user.email '41898282+github-actions[bot]@users.noreply.github.com' @@ -52,6 +67,7 @@ jobs: - name: Run maintenance task id: maintenance + if: steps.schedule.outputs.run == 'true' uses: anthropics/claude-code-action@36a69b6a90b850823f86de06fdfd56264772ad98 # v1 env: ANTHROPIC_BASE_URL: ${{ secrets.ANTHROPIC_BASE_URL }} @@ -79,8 +95,7 @@ jobs: stop successfully without opening a PR. Dependencies are installed. Run all verification required by the task before - opening the PR. This repo uses `pnpm test` for its unit suite; use it wherever - the task says `pnpm test:unit`. Do not open a PR if the required checks fail. + opening the PR. Do not open a PR if the required checks fail. Keep duplicate-check notes out of the PR body and all template checkboxes unchecked. Do not post to Slack; the workflow will announce the PR after it exists. claude_args: | @@ -105,9 +120,14 @@ jobs: return; } + const pullRequestTitle = pullRequest.title + .replaceAll('&', '&') + .replaceAll('<', '<') + .replaceAll('>', '>'); + core.setOutput('payload', JSON.stringify({ channel: 'C0ARV62K59C', // #devtools-gardener-backlog - text: `Maintenance PR (${process.env.MAINTENANCE_TASK}): ${pullRequest.html_url}`, + text: `Maintenance PR (${process.env.MAINTENANCE_TASK}): <${pullRequest.html_url}|${pullRequestTitle}>`, unfurl_links: false, unfurl_media: false, }));