From 49334f72b9253766ff648221ccffaa69a41df7e0 Mon Sep 17 00:00:00 2001 From: "monitoring-forge-files-sync-action[bot]" <312405152+monitoring-forge-files-sync-action[bot]@users.noreply.github.com> Date: Thu, 8 Oct 2026 14:43:01 +0000 Subject: [PATCH] chore: sync files with `monitoring-forge/github-common` --- .github/dependabot.yml | 6 + .github/workflows/dependabot-auto-merge.yml | 147 ++++++++++++++++++++ .github/workflows/pr-agent.yml | 6 +- 3 files changed, 156 insertions(+), 3 deletions(-) create mode 100644 .github/workflows/dependabot-auto-merge.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml index f090c50..09a6543 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -11,10 +11,16 @@ updates: default-days: 7 ignore: - dependency-name: "the-pr-agent/pr-agent" + - dependency-name: "actions/github-script" groups: + tagpr: + patterns: + - "Songmu/tagpr" dependencies: patterns: - "*" + exclude-patterns: + - "Songmu/tagpr" open-pull-requests-limit: 20 - package-ecosystem: gomod diff --git a/.github/workflows/dependabot-auto-merge.yml b/.github/workflows/dependabot-auto-merge.yml new file mode 100644 index 0000000..32a087e --- /dev/null +++ b/.github/workflows/dependabot-auto-merge.yml @@ -0,0 +1,147 @@ +name: Auto-merge tagpr updates + +on: + workflow_run: + workflows: [test, Tests, CI] + types: [completed] + +permissions: {} + +concurrency: + group: tagpr-auto-merge-${{ github.event.workflow_run.head_branch }} + cancel-in-progress: false + +jobs: + merge: + if: >- + github.repository_owner == 'monitoring-forge' && + github.event.workflow_run.conclusion == 'success' && + github.event.workflow_run.head_repository.full_name == github.repository + runs-on: ubuntu-latest + timeout-minutes: 5 + permissions: + actions: read + contents: read + pull-requests: read + steps: + - name: Verify tests and tagpr-only changes + id: verify + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const {owner, repo} = context.repo; + const fullName = `${owner}/${repo}`; + const eventRun = context.payload.workflow_run; + const {data: run} = await github.rest.actions.getWorkflowRun({owner, repo, run_id: eventRun.id}); + if (run.status !== 'completed' || run.conclusion !== 'success' || + run.run_attempt !== eventRun.run_attempt || + !['push', 'pull_request'].includes(run.event) || + run.head_repository?.full_name !== fullName || + !['.github/workflows/test.yml', '.github/workflows/ci.yml'].includes(run.path?.split('@')[0])) { + core.info('Skip: no current successful test run in this repository.'); + return; + } + const prs = await github.paginate(github.rest.pulls.list, { + owner, repo, state: 'open', head: `${owner}:${run.head_branch}`, per_page: 100, + }); + for (const candidate of prs) { + const {data: pr} = await github.rest.pulls.get({owner, repo, pull_number: candidate.number}); + if (pr.user.login !== 'dependabot[bot]' || pr.draft || + pr.head.repo?.full_name !== fullName || pr.head.sha !== run.head_sha || + pr.base.ref !== context.payload.repository.default_branch) continue; + + // Inspect data only: never check out or execute code from the PR. + const files = await github.paginate(github.rest.pulls.listFiles, { + owner, repo, pull_number: pr.number, per_page: 100, + }); + if (!files.length || files.length !== pr.changed_files) continue; + let tagprOnly = true; + const normalize = text => text.replace( + /^(\s*(?:-\s+)?uses:\s*Songmu\/tagpr@)[a-zA-Z0-9._/-]+(?:[ \t]+#[^\r\n]*)?[ \t]*$/gm, + '$1', + ); + for (const file of files) { + if (file.status !== 'modified' || !/^\.github\/workflows\/[^/]+\.ya?ml$/.test(file.filename)) { + tagprOnly = false; + break; + } + const read = async ref => { + const {data} = await github.rest.repos.getContent({owner, repo, path: file.filename, ref}); + if (data.type !== 'file' || data.encoding !== 'base64') throw new Error('Unexpected file response'); + return Buffer.from(data.content, 'base64').toString('utf8'); + }; + const before = await read(pr.base.sha); + const after = await read(pr.head.sha); + if (before === after || normalize(before) !== normalize(after)) { + tagprOnly = false; + break; + } + } + if (!tagprOnly) { + core.info(`Skip #${pr.number}: changes are not limited to Songmu/tagpr references.`); + continue; + } + + // Reject a superseded success or a pending/failed rerun for this exact head. + const runs = await github.paginate(github.rest.actions.listWorkflowRuns, { + owner, repo, workflow_id: run.workflow_id, head_sha: pr.head.sha, + branch: pr.head.ref, event: run.event, per_page: 100, + }); + const latest = runs.sort((a, b) => b.id - a.id)[0]; + if (!latest || latest.id !== run.id || latest.status !== 'completed' || + latest.conclusion !== 'success' || latest.run_attempt !== run.run_attempt) continue; + + // REST omits allow_* settings for read-only tokens. GraphQL exposes them explicitly. + const {repository} = await github.graphql(` + query MergeMethods($owner: String!, $repo: String!) { + repository(owner: $owner, name: $repo) { + mergeCommitAllowed + squashMergeAllowed + rebaseMergeAllowed + } + } + `, {owner, repo}); + if (!repository || !['mergeCommitAllowed', 'squashMergeAllowed', 'rebaseMergeAllowed'] + .every(key => typeof repository[key] === 'boolean')) { + throw new Error('Could not determine enabled merge methods'); + } + const method = repository.mergeCommitAllowed ? 'merge' : + repository.squashMergeAllowed ? 'squash' : repository.rebaseMergeAllowed ? 'rebase' : null; + if (!method) throw new Error('No merge method is enabled'); + core.setOutput('pull-number', pr.number); + core.setOutput('head-sha', pr.head.sha); + core.setOutput('merge-method', method); + return; + } + + - name: Generate repository-scoped merge token + if: steps.verify.outputs.pull-number != '' + id: app-token + uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3 + with: + client-id: ${{ secrets.CLIENT_ID }} + private-key: ${{ secrets.APP_PRIVATE_KEY }} + owner: ${{ github.repository_owner }} + repositories: ${{ github.event.repository.name }} + permission-contents: write + permission-pull-requests: write + + - name: Merge with GitHub App to trigger push workflows + if: steps.verify.outputs.pull-number != '' + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + PR_NUMBER: ${{ steps.verify.outputs.pull-number }} + HEAD_SHA: ${{ steps.verify.outputs.head-sha }} + MERGE_METHOD: ${{ steps.verify.outputs.merge-method }} + with: + github-token: ${{ steps.app-token.outputs.token }} + script: | + const {owner, repo} = context.repo; + const pull_number = Number(process.env.PR_NUMBER); + // The App token triggers push workflows; the SHA rejects a newer push. + const {data: result} = await github.rest.pulls.merge({ + owner, repo, pull_number, sha: process.env.HEAD_SHA, + merge_method: process.env.MERGE_METHOD, + }); + if (!result.merged) throw new Error(result.message); + core.info(`Merged #${pull_number}: ${result.sha}`); diff --git a/.github/workflows/pr-agent.yml b/.github/workflows/pr-agent.yml index 77f2006..0ffb1e8 100644 --- a/.github/workflows/pr-agent.yml +++ b/.github/workflows/pr-agent.yml @@ -15,7 +15,7 @@ jobs: if: ${{ github.event.sender.type != 'Bot' }} steps: - name: PR Agent action step - uses: the-pr-agent/pr-agent@4ebd5c5333c6ef21509e7304d27969eb825e6f22 # v0.43.0 + uses: the-pr-agent/pr-agent@1d01f24f455bb879c1d9c557ad7de3d72dcc7975 # v0.46.0 env: OPENAI_KEY: ${{ secrets.OPENAI_KEY }} OPENAI_API_BASE: ${{ secrets.OPENAI_API_BASE }} @@ -25,8 +25,8 @@ jobs: github_action_config.auto_describe: "true" github_action_config.auto_improve: "true" github_action_config.pr_actions: '["opened", "reopened", "ready_for_review", "synchronize"]' - config.model: "openrouter/openai/gpt-5.6-terra" - config.fallback_models: '["openrouter/openai/gpt-5.6-luna"]' + config.model: "openrouter/openai/gpt-6-sol" + config.fallback_models: '["openrouter/openai/gpt-5.6-terra"]' config.custom_model_max_tokens: 256000 config.max_model_tokens: 120000 config.temperature: "0"