Skip to content

Commit 8f9507a

Browse files
authored
Merge pull request #93 from LAA-Software-Engineering/implement/phase-a-github-read
feat: GitHub provider (REST read/write), PR review examples, and Actions workflow
2 parents a4c463a + 31379f1 commit 8f9507a

28 files changed

Lines changed: 1658 additions & 9 deletions
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
# Manual publish: run agentctl with --approve so a real PR comment is posted.
2+
# Separate from agentctl-pr-review.yml so pull_request runs do not list a skipped publish job.
3+
# See docs/GITHUB_ACTIONS.md
4+
5+
name: Agentic PR review (publish)
6+
7+
on:
8+
workflow_dispatch:
9+
inputs:
10+
owner:
11+
description: Repository owner (org or user)
12+
required: true
13+
type: string
14+
repo:
15+
description: Repository name
16+
required: true
17+
type: string
18+
number:
19+
description: Pull request number
20+
required: true
21+
type: string
22+
23+
concurrency:
24+
group: agentctl-pr-publish-${{ github.repository }}-${{ github.run_id }}
25+
cancel-in-progress: false
26+
27+
defaults:
28+
run:
29+
shell: bash
30+
31+
env:
32+
AGENTCTL_INSTALL: go-build
33+
AGENTCTL_VERSION: v0.1.9
34+
AGENTIC_PROJECT: examples/pr-review-github-actions
35+
AGENTIC_STATE: ${{ github.workspace }}/.agentic/ci-pr-publish.db
36+
AGENTIC_CACHE_STATE: "false"
37+
38+
jobs:
39+
publish-comment:
40+
runs-on: ubuntu-latest
41+
env:
42+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
43+
permissions:
44+
contents: read
45+
pull-requests: write
46+
47+
steps:
48+
- name: Checkout
49+
uses: actions/checkout@v4
50+
51+
- name: Cache SQLite state (optional)
52+
if: env.AGENTIC_CACHE_STATE == 'true'
53+
uses: actions/cache@v4
54+
with:
55+
path: .agentic/ci-pr-publish.db
56+
key: ${{ runner.os }}-agentic-pr-publish-${{ hashFiles('examples/pr-review-github-actions/**/*.yaml', 'examples/pr-review-github-actions/project.yaml') }}
57+
58+
- name: Set up Go (build agentctl from checkout)
59+
if: env.AGENTCTL_INSTALL == 'go-build'
60+
uses: actions/setup-go@v5
61+
with:
62+
go-version-file: go.mod
63+
cache: true
64+
65+
- name: Install agentctl (go build from checkout)
66+
if: env.AGENTCTL_INSTALL == 'go-build'
67+
run: |
68+
set -euo pipefail
69+
go build -o /tmp/agentctl ./cmd/agentctl
70+
sudo install -m 0755 /tmp/agentctl /usr/local/bin/agentctl
71+
agentctl version
72+
73+
- name: Install agentctl (release tarball)
74+
if: env.AGENTCTL_INSTALL != 'go-build'
75+
run: |
76+
set -euo pipefail
77+
version="${AGENTCTL_VERSION}"
78+
asset="agentctl-${version}-linux-amd64.tar.gz"
79+
url="https://github.com/LAA-Software-Engineering/agentic-control-plane/releases/download/${version}/${asset}"
80+
curl -fsSL "$url" -o /tmp/agentctl.tgz
81+
tar -xzf /tmp/agentctl.tgz -C /tmp
82+
if [[ ! -x /tmp/agentctl ]]; then
83+
echo "Release tarball did not extract ./agentctl to /tmp (layout may have changed). Contents:" >&2
84+
tar -tzf /tmp/agentctl.tgz | head -n 50 >&2 || true
85+
exit 1
86+
fi
87+
sudo install -m 0755 /tmp/agentctl /usr/local/bin/agentctl
88+
agentctl version
89+
90+
- name: Build workflow input
91+
run: |
92+
set -euo pipefail
93+
jq -n \
94+
--arg owner "${{ inputs.owner }}" \
95+
--arg repo "${{ inputs.repo }}" \
96+
--arg num "${{ inputs.number }}" \
97+
'{owner: $owner, repo: $repo, number: ($num | tonumber)}' > /tmp/pr-input.json
98+
99+
- name: Validate / plan / apply
100+
env:
101+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
102+
AGENTCTL_AUTO_APPROVE: "1"
103+
run: |
104+
set -euo pipefail
105+
agentctl validate --project "$AGENTIC_PROJECT" --no-color
106+
agentctl plan --project "$AGENTIC_PROJECT" --state "$AGENTIC_STATE"
107+
agentctl apply --project "$AGENTIC_PROJECT" --state "$AGENTIC_STATE"
108+
109+
- name: Run with approved comment post
110+
id: publish_run
111+
env:
112+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
113+
run: |
114+
set -euo pipefail
115+
set +e
116+
agentctl run workflow/pr-review-github \
117+
--project "$AGENTIC_PROJECT" \
118+
--state "$AGENTIC_STATE" \
119+
--input-file /tmp/pr-input.json \
120+
--approve tool.github.pull_request.post_comment \
121+
-o json > /tmp/publish-run-meta.json
122+
ec=$?
123+
set -e
124+
exit "$ec"
125+
126+
- name: Job summary (publish run)
127+
if: always()
128+
env:
129+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
130+
run: |
131+
set -euo pipefail
132+
rid=""
133+
if [[ -f /tmp/publish-run-meta.json ]]; then
134+
rid="$(jq -r '.runId // empty' /tmp/publish-run-meta.json)"
135+
fi
136+
{
137+
echo "## Agentic PR review (publish)"
138+
echo ""
139+
echo "| Field | Value |"
140+
echo "|------|--------|"
141+
echo "| Step outcome | \`${{ steps.publish_run.outcome }}\` |"
142+
echo "| Run ID | \`$rid\` |"
143+
echo ""
144+
if [[ -n "$rid" ]]; then
145+
echo "### Trace (truncated)"
146+
echo ""
147+
echo '```text'
148+
agentctl logs --project "$AGENTIC_PROJECT" --state "$AGENTIC_STATE" --run "$rid" 2>/dev/null | head -n 200 || true
149+
echo '```'
150+
fi
151+
} >> "$GITHUB_STEP_SUMMARY"
Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
# Runs on pull requests in this repository (declarative PR review with OpenAI gpt-4o-mini).
2+
# Posts an issue comment on the PR (--approve satisfies policy on pull_request.post_comment).
3+
# Requires repository secret OPENAI_API_KEY. Same-repo PRs only (fork PRs are skipped — no secrets).
4+
# Optional post-pointer job is skipped unless AGENTIC_GH_PR_COMMENT=true (expected default).
5+
# Manual publish for arbitrary owner/repo/number: agentctl-pr-review-publish.yml.
6+
# See docs/GITHUB_ACTIONS.md and examples/pr-review-github-actions/README.md
7+
#
8+
# Downstream repos: copy to .github/workflows/, set AGENTIC_PROJECT, AGENTCTL_INSTALL=release, AGENTCTL_VERSION.
9+
10+
name: Agentic PR review
11+
12+
on:
13+
pull_request:
14+
types: [opened, synchronize, reopened]
15+
paths-ignore:
16+
- "Makefile"
17+
- "**/*.md"
18+
19+
# Must not reference github.event.pull_request.* unless pull_request is the active event — GitHub
20+
# validates workflow files on push; a bare pull_request.number breaks that pass and can block PR runs.
21+
concurrency:
22+
group: >-
23+
agentctl-pr-${{ github.repository }}-${{
24+
github.event_name == 'pull_request' && github.event.pull_request.number || github.run_id
25+
}}
26+
cancel-in-progress: true
27+
28+
defaults:
29+
run:
30+
shell: bash
31+
32+
env:
33+
# Inside this monorepo, build agentctl from the checkout so PRs always match native tools.
34+
# In a downstream repo, set AGENTCTL_INSTALL to "release" and pin AGENTCTL_VERSION to a published tag.
35+
AGENTCTL_INSTALL: go-build
36+
AGENTCTL_VERSION: v0.1.9
37+
AGENTIC_PROJECT: examples/pr-review-github-actions
38+
AGENTIC_STATE: ${{ github.workspace }}/.agentic/ci-pr-review.db
39+
AGENTIC_CACHE_STATE: "false"
40+
AGENTIC_GH_PR_COMMENT: "false"
41+
42+
jobs:
43+
review:
44+
if: >-
45+
github.event_name == 'pull_request' &&
46+
github.event.pull_request.head.repo.full_name == github.repository
47+
runs-on: ubuntu-latest
48+
env:
49+
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
50+
permissions:
51+
contents: read
52+
pull-requests: write
53+
outputs:
54+
run_id: ${{ steps.run_review.outputs.run_id }}
55+
exit_code: ${{ steps.run_review.outputs.exit_code }}
56+
gh_pr_comment: ${{ steps.export_flags.outputs.gh_pr_comment }}
57+
58+
steps:
59+
- name: Checkout
60+
uses: actions/checkout@v4
61+
62+
- name: Export workflow flags for downstream jobs
63+
id: export_flags
64+
run: |
65+
set -euo pipefail
66+
echo "gh_pr_comment=${AGENTIC_GH_PR_COMMENT:-false}" >> "$GITHUB_OUTPUT"
67+
68+
- name: Cache SQLite state (optional)
69+
if: env.AGENTIC_CACHE_STATE == 'true'
70+
uses: actions/cache@v4
71+
with:
72+
path: .agentic/ci-pr-review.db
73+
key: ${{ runner.os }}-agentic-pr-review-${{ hashFiles('examples/pr-review-github-actions/**/*.yaml', 'examples/pr-review-github-actions/project.yaml') }}
74+
75+
- name: Set up Go (build agentctl from checkout)
76+
if: env.AGENTCTL_INSTALL == 'go-build'
77+
uses: actions/setup-go@v5
78+
with:
79+
go-version-file: go.mod
80+
cache: true
81+
82+
- name: Install agentctl (go build from checkout)
83+
if: env.AGENTCTL_INSTALL == 'go-build'
84+
run: |
85+
set -euo pipefail
86+
go build -o /tmp/agentctl ./cmd/agentctl
87+
sudo install -m 0755 /tmp/agentctl /usr/local/bin/agentctl
88+
agentctl version
89+
90+
- name: Install agentctl (release tarball)
91+
if: env.AGENTCTL_INSTALL != 'go-build'
92+
run: |
93+
set -euo pipefail
94+
version="${AGENTCTL_VERSION}"
95+
asset="agentctl-${version}-linux-amd64.tar.gz"
96+
url="https://github.com/LAA-Software-Engineering/agentic-control-plane/releases/download/${version}/${asset}"
97+
curl -fsSL "$url" -o /tmp/agentctl.tgz
98+
tar -xzf /tmp/agentctl.tgz -C /tmp
99+
if [[ ! -x /tmp/agentctl ]]; then
100+
echo "Release tarball did not extract ./agentctl to /tmp (layout may have changed). Contents:" >&2
101+
tar -tzf /tmp/agentctl.tgz | head -n 50 >&2 || true
102+
exit 1
103+
fi
104+
sudo install -m 0755 /tmp/agentctl /usr/local/bin/agentctl
105+
agentctl version
106+
107+
- name: Build workflow input (target repository)
108+
run: |
109+
set -euo pipefail
110+
owner="${GITHUB_REPOSITORY%%/*}"
111+
repo="${GITHUB_REPOSITORY#*/}"
112+
number="${{ github.event.pull_request.number }}"
113+
jq -n \
114+
--arg owner "$owner" \
115+
--arg repo "$repo" \
116+
--argjson number "$number" \
117+
'{owner: $owner, repo: $repo, number: $number}' > /tmp/pr-input.json
118+
test -s /tmp/pr-input.json
119+
120+
- name: Validate project
121+
env:
122+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
123+
run: agentctl validate --project "$AGENTIC_PROJECT" --no-color
124+
125+
- name: Plan
126+
env:
127+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
128+
run: agentctl plan --project "$AGENTIC_PROJECT" --state "$AGENTIC_STATE"
129+
130+
- name: Apply
131+
env:
132+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
133+
AGENTCTL_AUTO_APPROVE: "1"
134+
run: agentctl apply --project "$AGENTIC_PROJECT" --state "$AGENTIC_STATE"
135+
136+
- name: Run PR review (post comment; exit 5 = policy denial — OK)
137+
id: run_review
138+
env:
139+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
140+
run: |
141+
set -euo pipefail
142+
set +e
143+
agentctl run workflow/pr-review-github \
144+
--project "$AGENTIC_PROJECT" \
145+
--state "$AGENTIC_STATE" \
146+
--input-file /tmp/pr-input.json \
147+
--approve tool.github.pull_request.post_comment \
148+
-o json > /tmp/run-meta.json
149+
ec=$?
150+
set -e
151+
echo "exit_code=$ec" >> "$GITHUB_OUTPUT"
152+
rid="$(jq -r '.runId // empty' /tmp/run-meta.json)"
153+
echo "run_id=$rid" >> "$GITHUB_OUTPUT"
154+
if [[ "$ec" -eq 0 || "$ec" -eq 5 ]]; then
155+
exit 0
156+
fi
157+
exit "$ec"
158+
159+
- name: Job summary (trace excerpt)
160+
if: always()
161+
env:
162+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
163+
run: |
164+
set -euo pipefail
165+
ec="${{ steps.run_review.outputs.exit_code }}"
166+
mapped="no"
167+
if [[ "$ec" == "0" || "$ec" == "5" ]]; then mapped="yes"; fi
168+
{
169+
echo "## Agentic PR review"
170+
echo ""
171+
echo "| Field | Value |"
172+
echo "|------|--------|"
173+
echo "| Raw \`agentctl run\` exit | \`$ec\` |"
174+
echo "| Run ID | \`${{ steps.run_review.outputs.run_id }}\` |"
175+
echo "| Treat as success (0 or 5) | $mapped |"
176+
echo ""
177+
echo "See **section 11.2** in DESIGN_DOC (\`5\` = policy denial)."
178+
echo ""
179+
rid="${{ steps.run_review.outputs.run_id }}"
180+
if [[ -n "$rid" ]]; then
181+
echo "### Trace (latest run, truncated)"
182+
echo ""
183+
echo '```text'
184+
agentctl logs --project "$AGENTIC_PROJECT" --state "$AGENTIC_STATE" --run "$rid" 2>/dev/null | head -n 200 || true
185+
echo '```'
186+
fi
187+
} >> "$GITHUB_STEP_SUMMARY"
188+
189+
- name: Console trace tail
190+
if: always() && steps.run_review.outputs.run_id != ''
191+
env:
192+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
193+
run: |
194+
set -euo pipefail
195+
agentctl logs --project "$AGENTIC_PROJECT" --state "$AGENTIC_STATE" --run "${{ steps.run_review.outputs.run_id }}" 2>/dev/null | head -n 120 || true
196+
197+
# Optional: set workflow env AGENTIC_GH_PR_COMMENT to "true" to post a short gh pr comment (needs write).
198+
post-pointer:
199+
needs: review
200+
if: >
201+
always() &&
202+
github.event_name == 'pull_request' &&
203+
needs.review.outputs.gh_pr_comment == 'true' &&
204+
needs.review.outputs.run_id != ''
205+
runs-on: ubuntu-latest
206+
permissions:
207+
contents: read
208+
pull-requests: write
209+
steps:
210+
- name: Post pointer comment (gh)
211+
env:
212+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
213+
run: |
214+
set -euo pipefail
215+
printf '%s\n\n%s\n%s\n' \
216+
"**agentctl** finished — [workflow run](${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }})." \
217+
"- Raw exit: \`${{ needs.review.outputs.exit_code }}\` (0 = success, 5 = policy blocked comment)" \
218+
"- Run ID: \`${{ needs.review.outputs.run_id }}\`" > /tmp/agentic-gh.md
219+
gh pr comment "${{ github.event.pull_request.number }}" --body-file /tmp/agentic-gh.md

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ Most agent stacks today bury prompts, tool wiring, and permissions in applicatio
2525

2626
The full product vision, YAML spec v0, and architecture are documented in [**`docs/DESIGN_DOC.md`**](docs/DESIGN_DOC.md).
2727

28-
**Featured walkthrough:** declarative PR review with a **policy-blocked** (simulated) GitHub comment — no API keys required — in [**`examples/pr-review-demo/README.md`**](examples/pr-review-demo/README.md).
28+
**Featured walkthrough:** declarative PR review with a **policy-blocked** (simulated) GitHub comment — no API keys required — in [**`examples/pr-review-demo/README.md`**](examples/pr-review-demo/README.md). For the **live GitHub read/write path** with a **mock** reviewer (CI-friendly, no API keys), see [**`examples/pr-review-github/README.md`**](examples/pr-review-github/README.md). For the **same flow with OpenAI `gpt-4o-mini`** plus **GitHub Actions** (PR workflow posts a review comment), see [**`examples/pr-review-github-actions/README.md`**](examples/pr-review-github-actions/README.md) ( **[`.github/workflows/agentctl-pr-review.yml`](.github/workflows/agentctl-pr-review.yml)**; optional manual **`owner`/`repo`/`number`**: **[`.github/workflows/agentctl-pr-review-publish.yml`](.github/workflows/agentctl-pr-review-publish.yml)**).
2929

3030
---
3131

@@ -172,6 +172,8 @@ Exit codes are summarized in **section 11.2** of [`docs/DESIGN_DOC.md`](docs/DES
172172
| `internal/state/sqlite` | SQLite deployment + runtime/trace tables |
173173
| `test/integration` | End-to-end CLI flow tests |
174174
| `docs/DESIGN_DOC.md` | Spec, CLI UX, architecture, roadmap |
175+
| `docs/GITHUB_ACTIONS.md` | Running **`agentctl`** from GitHub Actions (tokens, exit code **5**, template path) |
176+
| `examples/pr-review-github-actions/` | Full **`gpt-4o-mini`** project; PR workflow **`.github/workflows/agentctl-pr-review.yml`**; optional publish **`.github/workflows/agentctl-pr-review-publish.yml`** |
175177

176178
---
177179

0 commit comments

Comments
 (0)