Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 48 additions & 0 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -312,6 +312,54 @@ jobs:
gh release edit "$VERSION" --repo docker/docker-agent-action --notes-file /tmp/release-notes-filtered.md
echo "✅ Release notes filtered and updated."

- name: Flag caller-facing permission increases as breaking
if: ${{ !inputs.pre_release }}
env:
VERSION: ${{ steps.version.outputs.version }}
PREVIOUS: ${{ steps.version.outputs.previous }}
GH_TOKEN: ${{ env.GITHUB_APP_TOKEN }}
run: |
# A reusable-workflow caller cannot elevate permissions: when a release
# raises what review-pr.yml requests from its caller, existing callers
# fail GitHub's startup validation until their permissions: block is
# updated (issue #72 — v2.0.3 raised actions: read → write and broke
# callers granting actions: read). Compare against the previous stable
# release and prepend a breaking-change migration warning to the
# generated notes when the requirement increased; the helper prints
# nothing when it is unchanged or reduced. The workspace copy of
# review-pr.yml is authoritative here: the release-commit passes only
# rewrote `uses:` pins, never permissions.
if [ -z "$PREVIOUS" ]; then
echo "ℹ️ First release — no previous requirement to compare against."
exit 0
fi
PREV_FILE=/tmp/review-pr-previous.yml

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion (non-blocking): PREV_FILE is hardcoded to /tmp/review-pr-previous.yml. if two release jobs overlap on a shared or self-hosted runner (e.g. a stable and a beta release triggered close together), they race to overwrite each other's snapshot, which could produce a missed or spurious breaking-change warning. consider PREV_FILE=/tmp/review-pr-previous-${VERSION}.yml (and update the matching trap cleanup in tests/test-release-caller-permissions.sh).

if ! git show "${PREVIOUS}:.github/workflows/review-pr.yml" > "$PREV_FILE" 2>/dev/null; then
echo "ℹ️ ${PREVIOUS} has no .github/workflows/review-pr.yml — nothing to compare."
exit 0
fi
# The tag and GitHub release already exist here, so a helper failure
# must not strand the rest of the release pipeline: annotate and skip
# the safeguard instead. stdout carries only the warning markdown
# (stderr streams to the log), and on a non-zero exit any partial
# stdout is discarded so an error can never be prepended to the notes
# as markdown. The gh calls below stay fatal: once a valid breaking
# warning exists, dropping it would ship misleading release notes.
HELPER_STATUS=0
WARNING=$(node "$GITHUB_WORKSPACE/dist/caller-permissions.js" "$PREV_FILE" ".github/workflows/review-pr.yml") || HELPER_STATUS=$?
if [ "$HELPER_STATUS" -ne 0 ]; then
echo "::warning title=Caller-permissions safeguard skipped::dist/caller-permissions.js exited ${HELPER_STATUS} comparing review-pr.yml against ${PREVIOUS} (see step log for details). Diff the caller-facing permissions manually and prepend a breaking-change warning to the ${VERSION} release notes if any level increased."
exit 0
fi
if [ -z "$WARNING" ]; then
echo "✅ Caller permissions unchanged since ${PREVIOUS} — release notes left as generated."
exit 0
fi
NOTES=$(gh release view "$VERSION" --repo docker/docker-agent-action --json body --jq '.body')
{ printf '%s\n\n' "$WARNING"; printf '%s' "$NOTES"; } > /tmp/release-notes-with-warning.md
gh release edit "$VERSION" --repo docker/docker-agent-action --notes-file /tmp/release-notes-with-warning.md
echo "⚠️ Prepended caller-permissions breaking-change warning to ${VERSION} release notes."

publish-agent:
name: Push review-pr agent to Docker Hub
needs: release
Expand Down
21 changes: 21 additions & 0 deletions .github/workflows/test-e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,27 @@
chmod +x test-job-summary.sh
./test-job-summary.sh

test-release-caller-permissions:
name: Release Caller Permissions Tests
runs-on: ubuntu-latest
if: |
github.event_name == 'push' ||
github.event_name == 'workflow_dispatch' ||
github.event.workflow_run.conclusion == 'success'
permissions:
contents: read
steps:
- name: Checkout code
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
ref: ${{ github.event_name == 'workflow_run' && github.event.workflow_run.head_sha || '' }}

- name: Run release caller-permissions tests
Comment on lines +76 to +81
run: |
cd tests
chmod +x test-release-caller-permissions.sh
./test-release-caller-permissions.sh

resolve-context:
name: Resolve PR Context
runs-on: ubuntu-latest
Expand Down
13 changes: 10 additions & 3 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ Anything else here (workflows under `.github/workflows/`, scripts, tests) exists
│ ├── add-reaction/ # Adds emoji reactions to issue/PR comments.
│ │ ├── index.ts # Entry → bundled to dist/add-reaction.js
│ │ └── __tests__/
│ ├── caller-permissions/ # Release safeguard: diffs the caller-facing permission requirement of review-pr.yml between releases (issue #72).
│ │ ├── index.ts # CLI entry → bundled to dist/caller-permissions.js (used by release.yml to prepend a breaking-change warning to release notes).
│ │ ├── caller-permissions.ts # Permissions extractor + requirement diff (none < read < write) + warning renderer.
│ │ └── __tests__/
│ ├── check-org-membership/ # Authorizes a review: auto-run on PR-author membership, review_requested on the (trusted, timeline-derived) requester. Resolves PR author via pulls.get.
│ │ ├── index.ts # Entry → bundled to dist/check-org-membership.js (standalone CLI + library).
│ │ └── __tests__/
Expand Down Expand Up @@ -118,9 +122,10 @@ Anything else here (workflows under `.github/workflows/`, scripts, tests) exists
│ └── add-pr-reviewer-to-repo/
│ └── SKILL.md # Skill: set up or upgrade a repo to use the PR reviewer reusable workflow.
└── tests/ # Shell-based integration tests for action.yml bash logic.
└── tests/ # Shell-based integration tests for action.yml / release.yml bash logic.
├── test-job-summary.sh
├── test-output-extraction.sh
├── test-release-caller-permissions.sh # Exercises the release.yml caller-permissions safeguard step (helper failure must be non-fatal).
├── out.diff # Fixture used by test-output-extraction.sh
└── test.diff # Fixture used by test-output-extraction.sh
```
Expand Down Expand Up @@ -154,7 +159,7 @@ Anything else here (workflows under `.github/workflows/`, scripts, tests) exists

- `pnpm test` — Vitest "unit" project (`src/**/__tests__/**/*.test.ts`).
- `pnpm test:integration` — Vitest "integration" project (`*.integration.test.ts`).
- `tests/*.sh` are integration tests for the **shell logic** inside `action.yml` (output extraction, job summary, etc.). Run them when changing the bash blocks of `action.yml`.
- `tests/*.sh` are integration tests for **shell logic** embedded in YAML (output extraction and job summary in `action.yml`, the release-notes caller-permissions safeguard in `release.yml`). Run them when changing the corresponding bash blocks.
- Security unit tests live in `src/security/__tests__/security.test.ts` (Vitest) and run as part of `pnpm test`. Run them when changing anything under `src/security/`.
- The PR review agent has a separate eval suite under `review-pr/agents/evals/`. Run with `docker agent eval review-pr/agents/pr-review.yaml review-pr/agents/evals/`.

Expand Down Expand Up @@ -219,9 +224,10 @@ pnpm test
# Integration tests (Vitest)
pnpm test:integration

# Shell-based integration tests for action.yml bash logic
# Shell-based integration tests for shell logic embedded in YAML (action.yml, release.yml)
bash tests/test-job-summary.sh
bash tests/test-output-extraction.sh
bash tests/test-release-caller-permissions.sh

# Format + lint (write fixes)
pnpm format
Expand All @@ -244,6 +250,7 @@ When you change something, verify:
- [ ] Did you change anything under `src/security/`? Re-run `pnpm test` (covers `src/security/__tests__/security.test.ts`) and confirm the threat model above is still covered.
- [ ] Did you bump a pinned `uses:` SHA? Update the trailing version comment too.
- [ ] Did you change a `<!-- docker-agent-* -->` marker, an output name, or an env var name? Search the repo (and consumer documentation) for references first — these are public contracts.
- [ ] Did you increase any caller-facing permission requested by `.github/workflows/review-pr.yml` (workflow-level or job-level `permissions:`)? That is a **breaking change** for existing callers of the reusable workflow — update the documented `permissions:` blocks in `README.md` and `review-pr/README.md`. The release workflow's caller-permissions safeguard adds the release-notes warning automatically.

## Things to avoid

Expand Down
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,6 +223,24 @@ jobs:

For comprehensive documentation on setting up AI-powered PR reviews, including features like automatic reviews, requesting a review from `docker-agent`, feedback learning, and customization options, see the **[PR Review documentation](review-pr/README.md)**.

The job that calls the reusable workflow must grant exactly these permissions:

```yaml
jobs:
review:
uses: docker/docker-agent-action/.github/workflows/review-pr.yml@VERSION
permissions:
contents: read # Read repository files and PR diffs
pull-requests: write # Post review comments
issues: write # Create security incident issues if secrets detected
checks: write # Show review progress as a check run
id-token: write # Required for OIDC authentication to AWS Secrets Manager
actions: write # Required since v2.0.3 — review-lock cache cleanup and feedback artifacts
```

> [!IMPORTANT]
> **`actions: write` is required since v2.0.3** (earlier releases needed only `actions: read`). A called workflow cannot elevate its caller's permissions, so a caller job granting only `actions: read` fails GitHub's workflow validation at startup — no job even runs. This applies only to callers of the reusable PR-review workflow shown above; workflows using the root `docker/docker-agent-action` action directly need only the [permissions listed earlier](#permissions). See the [PR Review documentation](review-pr/README.md#quick-start) for complete setup, including the two-workflow pattern for fork PRs.

For external or fork contributor PRs, an org member approves the workflow run and then requests a review from `docker-agent` via GitHub's native review request UI (no special commands or workflow inputs required). See [External and fork contributor PRs](review-pr/README.md#external-and-fork-contributor-prs).

### Manual Trigger with Inputs
Expand Down
3 changes: 3 additions & 0 deletions review-pr/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ AI-powered pull request review using a multi-agent system. Analyzes code changes

## Quick Start

> [!IMPORTANT]
> The calling job must grant every permission shown in the examples below. Since **v2.0.3** that includes **`actions: write`** (earlier releases needed only `actions: read`). A called workflow cannot elevate its caller's permissions, so a caller still granting `actions: read` fails GitHub's workflow validation at startup — before any job runs. Update the `permissions:` block when upgrading.

### Same-repo PRs (1 workflow)

If your repo only accepts PRs from branches within the same repo (no forks), you need a single workflow file:
Expand Down
Loading