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 new file mode 100644 index 00000000000..1adc0ad513e --- /dev/null +++ b/.github/workflows/maintenance-prs.yml @@ -0,0 +1,141 @@ +name: maintenance-prs + +on: + workflow_dispatch: + schedule: + # Everyday at 00:00 UTC + - cron: '0 0 * * *' + +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: [refactor, tests, performance, security] + concurrency: + group: maintenance-prs-${{ matrix.task }} + cancel-in-progress: false + env: + 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' + git checkout -b "$MAINTENANCE_BRANCH" + + - 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 }} + 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. 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; + } + + 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}|${pullRequestTitle}>`, + 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 }}