diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..5ace460 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/.github/workflows/reviewer.yml b/.github/workflows/reviewer.yml new file mode 100644 index 0000000..f7227bc --- /dev/null +++ b/.github/workflows/reviewer.yml @@ -0,0 +1,70 @@ +name: Reviewer + +# Advisory PR review (umbra-reviewer): surfaces architecture + security issues +# and posts one recommendation comment. Advisory only — it never merges and never +# fails the PR. Changes to security-sensitive surfaces (workflows, packaging) are +# escalated to a human. See https://github.com/bkd-dotcom/umbra-reviewer +on: + pull_request: + +permissions: + contents: read + pull-requests: write + checks: read + +jobs: + review: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - uses: actions/setup-python@v5 + with: + python-version: "3.12" + - name: Install umbra-reviewer + run: pip install "umbra-reviewer>=0.1.0" + - name: Compute the PR diff + env: + BASE_SHA: ${{ github.event.pull_request.base.sha }} + HEAD_SHA: ${{ github.event.pull_request.head.sha }} + run: | + set -euo pipefail + git fetch --no-tags origin "$BASE_SHA" "$HEAD_SHA" 2>/dev/null || true + echo "OUT=${RUNNER_TEMP:-/tmp}/rev" >> "$GITHUB_ENV" + mkdir -p "${RUNNER_TEMP:-/tmp}/rev" + git diff "$BASE_SHA" "$HEAD_SHA" > "${RUNNER_TEMP:-/tmp}/rev/pr.diff" + - name: Run the advisory review + env: + PR: ${{ github.event.pull_request.number }} + run: | + set -uo pipefail + umbra-reviewer review \ + --diff "$OUT/pr.diff" \ + --repo "$GITHUB_REPOSITORY" \ + --pr "$PR" \ + --required-check unknown \ + --protected ".github/workflows/*,.github/actions/**,action.yml,pyproject.toml" \ + --comment-out "$OUT/comment.md" \ + --json-out "$OUT/review.json" + exit 0 + - name: Post / update the review comment + if: always() + uses: actions/github-script@v7 + with: + script: | + const fs = require('fs'); const path = require('path'); + const out = process.env.OUT || ''; + let body = 'Umbra Reviewer: no review was produced.'; + try { body = fs.readFileSync(path.join(out, 'comment.md'), 'utf8'); } catch (e) {} + const marker = ''; + body = marker + '\n' + body; + const { owner, repo } = context.repo; + const issue_number = context.issue.number; + const comments = await github.rest.issues.listComments({ owner, repo, issue_number }); + const mine = comments.data.find(c => c.body && c.body.includes(marker)); + if (mine) { + await github.rest.issues.updateComment({ owner, repo, comment_id: mine.id, body }); + } else { + await github.rest.issues.createComment({ owner, repo, issue_number, body }); + }