diff --git a/README.md b/README.md index 95ccca6..4144987 100644 --- a/README.md +++ b/README.md @@ -116,6 +116,44 @@ jobs: > Already on `@v1`? It keeps working but is frozen — switch the tag to `@v2`. The interface is identical. +### Or as a Marketplace action + +Prefer the familiar `- uses:` step syntax, or want Upkeep to show up in the +[GitHub Marketplace](https://github.com/marketplace)? Use the **Upkeep Audit** +action instead — same engine, same inputs, but the reviewers run one after +another instead of in parallel (see +[`docs/why-reusable-workflow.md`](docs/en/why-reusable-workflow.md) for why +the reusable workflow above is the primary path): + +```yaml +name: repo audit +on: + schedule: + - cron: '0 3 * * 1' # weekly, Monday 03:00 UTC + workflow_dispatch: + +permissions: + contents: read + issues: write + id-token: write + +jobs: + audit: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v7 + - uses: wei18/upkeep@v2 + with: + model: claude-opus-4-8 # optional + issue_label: audit # optional; default: audit + rubric_lang: en # optional; reviewer language: en | zh-TW + claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} +``` + +Same requirements as above: a repo secret named `CLAUDE_CODE_OAUTH_TOKEN` +(from `claude setup-token`) and the `permissions` block, now on the job +itself since a step action can't declare its own permissions. + ## Reviewers | Name | Default | Checks | diff --git a/action.yml b/action.yml new file mode 100644 index 0000000..235d500 --- /dev/null +++ b/action.yml @@ -0,0 +1,137 @@ +name: Upkeep Audit +description: >- + AI audit crew for repo drift — stale docs, specs that no longer match the + code, orphaned assets, broken conventions. Reports findings with evidence; + never edits or deletes your files. +author: Wei18 +branding: + icon: search + color: purple + +# NOTE: this is a convenience wrapper around the reviewer/synthesis/report +# steps in .github/workflows/audit.yml. A composite action runs as a single +# job with no strategy.matrix, so reviewers execute sequentially here instead +# of in parallel. See docs/en/why-reusable-workflow.md for why the primary +# integration path is the reusable workflow (`uses: wei18/upkeep/.github/workflows/audit.yml@v2`) +# rather than this action. Prefer the reusable workflow when reviewer +# parallelism matters (large repos, many reviewers enabled). +inputs: + model: + description: Claude model to run reviewers/synthesis with + default: claude-opus-4-8 + max_turns: + description: Max turns per Claude Code Action call + default: '30' + issue_label: + description: Label for the upserted tracking issue + default: audit + rubric_lang: + description: 'Reviewer rubric / prompt language: en | zh-TW | ...' + default: en + claude_code_oauth_token: + description: >- + Claude Code OAuth token, generated locally with `claude setup-token` + (Claude Pro/Max subscription; usage counts against your subscription). + required: true + github_token: + description: Passed to claude-code-action so it skips the Claude GitHub App requirement, and used to upsert the tracking issue. + default: ${{ github.token }} + +runs: + using: composite + steps: + - id: discovery + uses: wei18/upkeep/.github/actions/discovery@v2 + + - if: contains(fromJSON(steps.discovery.outputs.reviewers), 'docs_staleness') + continue-on-error: true + uses: wei18/upkeep/.github/actions/reviewer@v2 + with: + reviewer: docs_staleness + model: ${{ inputs.model }} + max_turns: ${{ inputs.max_turns }} + rubric_lang: ${{ inputs.rubric_lang }} + claude_code_oauth_token: ${{ inputs.claude_code_oauth_token }} + github_token: ${{ inputs.github_token }} + + - if: always() && contains(fromJSON(steps.discovery.outputs.reviewers), 'code_hygiene') + continue-on-error: true + uses: wei18/upkeep/.github/actions/reviewer@v2 + with: + reviewer: code_hygiene + model: ${{ inputs.model }} + max_turns: ${{ inputs.max_turns }} + rubric_lang: ${{ inputs.rubric_lang }} + claude_code_oauth_token: ${{ inputs.claude_code_oauth_token }} + github_token: ${{ inputs.github_token }} + + - if: always() && contains(fromJSON(steps.discovery.outputs.reviewers), 'spec_flow') + continue-on-error: true + uses: wei18/upkeep/.github/actions/reviewer@v2 + with: + reviewer: spec_flow + model: ${{ inputs.model }} + max_turns: ${{ inputs.max_turns }} + rubric_lang: ${{ inputs.rubric_lang }} + claude_code_oauth_token: ${{ inputs.claude_code_oauth_token }} + github_token: ${{ inputs.github_token }} + + - if: always() && contains(fromJSON(steps.discovery.outputs.reviewers), 'visual_icon') + continue-on-error: true + uses: wei18/upkeep/.github/actions/reviewer@v2 + with: + reviewer: visual_icon + model: ${{ inputs.model }} + max_turns: ${{ inputs.max_turns }} + rubric_lang: ${{ inputs.rubric_lang }} + claude_code_oauth_token: ${{ inputs.claude_code_oauth_token }} + github_token: ${{ inputs.github_token }} + + - if: always() && contains(fromJSON(steps.discovery.outputs.reviewers), 'duplicate_orphan') + continue-on-error: true + uses: wei18/upkeep/.github/actions/reviewer@v2 + with: + reviewer: duplicate_orphan + model: ${{ inputs.model }} + max_turns: ${{ inputs.max_turns }} + rubric_lang: ${{ inputs.rubric_lang }} + claude_code_oauth_token: ${{ inputs.claude_code_oauth_token }} + github_token: ${{ inputs.github_token }} + + - if: always() && contains(fromJSON(steps.discovery.outputs.reviewers), 'convention') + continue-on-error: true + uses: wei18/upkeep/.github/actions/reviewer@v2 + with: + reviewer: convention + model: ${{ inputs.model }} + max_turns: ${{ inputs.max_turns }} + rubric_lang: ${{ inputs.rubric_lang }} + claude_code_oauth_token: ${{ inputs.claude_code_oauth_token }} + github_token: ${{ inputs.github_token }} + + - if: always() && contains(fromJSON(steps.discovery.outputs.reviewers), 'i18n') + continue-on-error: true + uses: wei18/upkeep/.github/actions/reviewer@v2 + with: + reviewer: i18n + model: ${{ inputs.model }} + max_turns: ${{ inputs.max_turns }} + rubric_lang: ${{ inputs.rubric_lang }} + claude_code_oauth_token: ${{ inputs.claude_code_oauth_token }} + github_token: ${{ inputs.github_token }} + + - if: always() + continue-on-error: true + uses: wei18/upkeep/.github/actions/synthesis@v2 + with: + model: ${{ inputs.model }} + max_turns: ${{ inputs.max_turns }} + rubric_lang: ${{ inputs.rubric_lang }} + claude_code_oauth_token: ${{ inputs.claude_code_oauth_token }} + github_token: ${{ inputs.github_token }} + + - if: always() + uses: wei18/upkeep/.github/actions/report@v2 + with: + issue_label: ${{ inputs.issue_label }} + github_token: ${{ inputs.github_token }} diff --git a/docs/en/why-reusable-workflow.md b/docs/en/why-reusable-workflow.md index fecc97f..f1fec8d 100644 --- a/docs/en/why-reusable-workflow.md +++ b/docs/en/why-reusable-workflow.md @@ -35,6 +35,8 @@ There are two kinds of action, and neither can express that fan-out: So a composite action (`- uses: wei18/upkeep@v2`) *is* possible — at the cost of sequential reviewers. Upkeep deliberately chose the reusable-workflow form to keep reviewers parallel and independently isolated. For a scheduled audit, the slower sequential path would be acceptable; we preferred parallelism and clean failure isolation. +As of v2.1, the repo root ships exactly that trade-off as `action.yml` (`- uses: wei18/upkeep@v2`) — a sequential alternative that gets Upkeep listed on the GitHub Marketplace. The reusable workflow above stays the recommended integration path whenever reviewer parallelism matters. + ## What you actually give up Only the call-site syntax. `jobs..uses: owner/repo/.github/workflows/file.yml@ref` instead of `- uses: owner/action@ref`. Everything else behaves like an action: inputs via `with:`, secrets via `secrets:`, version pinning with `@v1`. diff --git a/docs/ja/why-reusable-workflow.md b/docs/ja/why-reusable-workflow.md index 3515e2f..d492f39 100644 --- a/docs/ja/why-reusable-workflow.md +++ b/docs/ja/why-reusable-workflow.md @@ -35,6 +35,8 @@ action には 2 種類あり、どちらもこのファンアウトを表現で つまり composite action(`- uses: wei18/upkeep@v2`)も *可能* です——reviewer が逐次になる代償付きで。Upkeep は reviewer を並列かつ各自隔離に保つため、あえて再利用可能ワークフローの形を選びました。スケジュール監査なら遅い逐次パスでも許容できますが、私たちは並列性とクリーンな障害隔離を優先しました。 +v2.1 以降、リポジトリルートはまさにこのトレードオフを体現した `action.yml`(`- uses: wei18/upkeep@v2`)を提供しています——逐次実行と引き換えに GitHub Marketplace に掲載できる代替パスです。reviewer の並列性が重要な場合は、上記の再利用可能ワークフローが引き続き推奨される統合パスです。 + ## 実際に手放すもの 呼び出し側の構文だけです。`- uses: owner/action@ref` の代わりに `jobs..uses: owner/repo/.github/workflows/file.yml@ref` を使います。それ以外はすべて action と同じです:`with:` で inputs、`secrets:` で secrets、`@v1` でバージョン固定。 diff --git a/docs/ko/why-reusable-workflow.md b/docs/ko/why-reusable-workflow.md index 04eb7d3..092ec16 100644 --- a/docs/ko/why-reusable-workflow.md +++ b/docs/ko/why-reusable-workflow.md @@ -35,6 +35,8 @@ action에는 두 종류가 있는데, 둘 다 이 팬아웃을 표현할 수 없 따라서 composite action(`- uses: wei18/upkeep@v2`)도 *가능*합니다 — reviewer가 순차가 되는 대가로. Upkeep은 reviewer를 병렬·각자 격리로 유지하기 위해 의도적으로 재사용 가능한 workflow 형태를 선택했습니다. 예약 감사라면 느린 순차 경로도 허용 가능하지만, 우리는 병렬성과 깔끔한 장애 격리를 우선했습니다. +v2.1부터 저장소 루트는 바로 이 트레이드오프를 담은 `action.yml`(`- uses: wei18/upkeep@v2`)을 제공합니다 — 순차 실행과 맞바꿔 GitHub Marketplace에 등록할 수 있는 대안 경로입니다. reviewer 병렬성이 중요할 때는 위의 재사용 가능한 workflow가 여전히 권장되는 통합 경로입니다. + ## 실제로 포기하는 것 호출부 구문뿐입니다. `- uses: owner/action@ref` 대신 `jobs..uses: owner/repo/.github/workflows/file.yml@ref`를 사용합니다. 그 외에는 모두 action과 동일합니다: `with:`로 inputs, `secrets:`로 secrets, `@v1`로 버전 고정. diff --git a/docs/zh-CN/why-reusable-workflow.md b/docs/zh-CN/why-reusable-workflow.md index d0ec817..b6600af 100644 --- a/docs/zh-CN/why-reusable-workflow.md +++ b/docs/zh-CN/why-reusable-workflow.md @@ -35,6 +35,8 @@ action 有两种,两种都无法表达这种扇出: 所以 composite action(`- uses: wei18/upkeep@v2`)*是*做得到的——代价是 reviewer 变串行。Upkeep 刻意选择 reusable workflow 形式,以保持 reviewer 并行且各自隔离。对排程审计而言,较慢的串行路径其实可接受;但我们偏好并行与干净的故障隔离。 +v2.1 起,repo root 已提供对应这个取舍的 `action.yml`(`- uses: wei18/upkeep@v2`)——一个串行执行、可在 GitHub Marketplace 曝光的替代路径。当 reviewer 并行性重要时,上面的 reusable workflow 仍是推荐的集成路径。 + ## 你实际放弃的是什么 只有调用端语法。用 `jobs..uses: owner/repo/.github/workflows/file.yml@ref` 取代 `- uses: owner/action@ref`。其余一切都跟 action 一样:用 `with:` 传 inputs、用 `secrets:` 传 secrets、用 `@v1` 钉版本。 diff --git a/docs/zh-TW/why-reusable-workflow.md b/docs/zh-TW/why-reusable-workflow.md index 6a7e931..655d0b6 100644 --- a/docs/zh-TW/why-reusable-workflow.md +++ b/docs/zh-TW/why-reusable-workflow.md @@ -35,6 +35,8 @@ action 有兩種,兩種都無法表達這種扇出: 所以 composite action(`- uses: wei18/upkeep@v2`)*是*做得到的——代價是 reviewer 變循序。Upkeep 刻意選擇 reusable workflow 形式,以保持 reviewer 平行且各自隔離。對排程稽核而言,較慢的循序路徑其實可接受;但我們偏好平行與乾淨的失敗隔離。 +v2.1 起,repo root 已提供對應這個取捨的 `action.yml`(`- uses: wei18/upkeep@v2`)——一個循序執行、可在 GitHub Marketplace 曝光的替代路徑。當 reviewer 平行性重要時,上面的 reusable workflow 仍是建議的整合路徑。 + ## 你實際放棄的是什麼 只有呼叫端語法。用 `jobs..uses: owner/repo/.github/workflows/file.yml@ref` 取代 `- uses: owner/action@ref`。其餘一切都跟 action 一樣:用 `with:` 傳 inputs、用 `secrets:` 傳 secrets、用 `@v1` 釘版本。