Skip to content

Commit 48bb60d

Browse files
cursoragentechobt
andcommitted
ci(codebuild): add oidc linux x64/arm64 status checks
Wire GitHub Actions to dedicated CodeBuild projects via OIDC so PR/main CI can run the heavy Linux suite on x64 and arm64 without long-lived keys. Existing workflows stay required; Windows CodeBuild is documented as a follow-up only. Co-authored-by: Mathis <echobt@users.noreply.github.com>
1 parent fe94a48 commit 48bb60d

17 files changed

Lines changed: 1066 additions & 0 deletions

.github/workflows/ci.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
name: CI
22

3+
# Linux clippy/test/coverage/TUI also run on AWS CodeBuild when
4+
# vars.AWS_CODEBUILD_ROLE_ARN is set. Status checks: cortex-cli-gha-x64
5+
# and cortex-cli-gha-arm64. See deploy/aws/codebuild/README.md.
6+
# Jobs in this workflow stay required and unchanged.
7+
38
on:
49
push:
510
branches: [main]

.github/workflows/codebuild.yml

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
# AWS CodeBuild CI for CortexLM/cli (Linux x64 + arm64).
2+
# Marker: CLI_CODEBUILD_CI_READY
3+
#
4+
# Assumes a dedicated IAM role via GitHub OIDC. No long-lived AWS keys.
5+
# Role ARN and region come from repository *variables*, not secrets.
6+
# See deploy/aws/codebuild/README.md for the one-time admin steps.
7+
#
8+
# Does not replace ci.yml / release.yml / publish-r2.yml / homebrew.yml /
9+
# winget.yml / version-bump.yml / test-stability.yml.
10+
11+
name: CodeBuild CI
12+
13+
on:
14+
push:
15+
branches: [main]
16+
pull_request:
17+
branches: [main]
18+
workflow_dispatch:
19+
20+
concurrency:
21+
group: codebuild-${{ github.workflow }}-${{ github.ref }}
22+
cancel-in-progress: true
23+
24+
permissions:
25+
contents: read
26+
id-token: write
27+
statuses: write
28+
29+
jobs:
30+
wiring:
31+
name: CodeBuild wiring
32+
runs-on: ubuntu-latest
33+
timeout-minutes: 10
34+
outputs:
35+
enabled: ${{ steps.gate.outputs.enabled }}
36+
same_repo: ${{ steps.gate.outputs.same_repo }}
37+
steps:
38+
- uses: actions/checkout@v7
39+
- uses: actions/setup-python@v5
40+
with:
41+
python-version: '3.12'
42+
- run: python -m pip install -r scripts/readiness/requirements.txt
43+
- name: Validate CodeBuild assets
44+
run: python -B -m unittest discover -s scripts/readiness -p test_codebuild.py -v
45+
- name: Decide whether StartBuild is configured
46+
id: gate
47+
env:
48+
ROLE_ARN: ${{ vars.AWS_CODEBUILD_ROLE_ARN }}
49+
PR_HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name || github.repository }}
50+
run: |
51+
same_repo=false
52+
if [ "$PR_HEAD_REPO" = "${{ github.repository }}" ]; then
53+
same_repo=true
54+
fi
55+
echo "same_repo=$same_repo" >> "$GITHUB_OUTPUT"
56+
if [ -z "$ROLE_ARN" ]; then
57+
echo "enabled=false" >> "$GITHUB_OUTPUT"
58+
echo "AWS_CODEBUILD_ROLE_ARN is unset. Skipping StartBuild. One-time IAM is in deploy/aws/codebuild/README.md"
59+
exit 0
60+
fi
61+
case "$ROLE_ARN" in
62+
arn:aws:iam::*:role/*) ;;
63+
*)
64+
echo "::error::AWS_CODEBUILD_ROLE_ARN must be an IAM role ARN (set a variable, do not commit it)"
65+
exit 1
66+
;;
67+
esac
68+
echo "enabled=true" >> "$GITHUB_OUTPUT"
69+
70+
codebuild:
71+
name: ${{ matrix.context }}
72+
needs: wiring
73+
if: needs.wiring.outputs.enabled == 'true' && needs.wiring.outputs.same_repo == 'true'
74+
runs-on: ubuntu-latest
75+
timeout-minutes: 120
76+
strategy:
77+
fail-fast: false
78+
matrix:
79+
include:
80+
- context: cortex-cli-gha-x64
81+
project_var: AWS_CODEBUILD_PROJECT_X64
82+
project_default: cortex-cli-gha-x64
83+
- context: cortex-cli-gha-arm64
84+
project_var: AWS_CODEBUILD_PROJECT_ARM64
85+
project_default: cortex-cli-gha-arm64
86+
env:
87+
STATUS_CONTEXT: ${{ matrix.context }}
88+
QUALITY_BASE: ${{ github.event.pull_request.base.sha || github.event.before }}
89+
CORTEX_GITHUB_REPOSITORY: ${{ github.repository }}
90+
steps:
91+
- uses: actions/checkout@v7
92+
- name: Resolve head SHA
93+
id: rev
94+
run: echo "sha=${{ github.event.pull_request.head.sha || github.sha }}" >> "$GITHUB_OUTPUT"
95+
- name: Post pending status
96+
env:
97+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
98+
SHA: ${{ steps.rev.outputs.sha }}
99+
run: |
100+
gh api "repos/${{ github.repository }}/statuses/${SHA}" \
101+
--field state=pending \
102+
--field context="${STATUS_CONTEXT}" \
103+
--field description="AWS CodeBuild starting" \
104+
--field target_url="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"
105+
- name: Configure AWS credentials (OIDC)
106+
uses: aws-actions/configure-aws-credentials@v4
107+
with:
108+
role-to-assume: ${{ vars.AWS_CODEBUILD_ROLE_ARN }}
109+
aws-region: ${{ vars.AWS_REGION || 'us-east-1' }}
110+
role-session-name: ${{ matrix.context }}
111+
- name: Read buildspec
112+
id: spec
113+
run: |
114+
{
115+
echo "yaml<<ENDOFFILE"
116+
cat deploy/aws/codebuild/buildspec-ci.yml
117+
echo "ENDOFFILE"
118+
} >> "$GITHUB_OUTPUT"
119+
- name: Run CodeBuild
120+
uses: aws-actions/aws-codebuild-run-build@v1
121+
with:
122+
project-name: ${{ vars[matrix.project_var] || matrix.project_default }}
123+
disable-source-override: true
124+
buildspec-override: ${{ steps.spec.outputs.yaml }}
125+
env-vars-for-codebuild: |
126+
QUALITY_BASE,
127+
CORTEX_SOURCE_SHA,
128+
CORTEX_GITHUB_REPOSITORY
129+
env:
130+
CORTEX_SOURCE_SHA: ${{ steps.rev.outputs.sha }}
131+
- name: Post final status
132+
if: always()
133+
env:
134+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
135+
SHA: ${{ steps.rev.outputs.sha }}
136+
OUTCOME: ${{ job.status }}
137+
run: |
138+
if [ "$OUTCOME" = "success" ]; then
139+
state=success
140+
desc="AWS CodeBuild passed"
141+
else
142+
state=failure
143+
desc="AWS CodeBuild failed"
144+
fi
145+
gh api "repos/${{ github.repository }}/statuses/${SHA}" \
146+
--field state="$state" \
147+
--field context="${STATUS_CONTEXT}" \
148+
--field description="$desc" \
149+
--field target_url="${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}"

AGENTS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ Working branch: **`main`**. Version bumps land on `main` via PR; `.github/workfl
2323
| `docs/` | User and plugin docs |
2424
| `.rules/` | Engineering rules (security, errors, TUI, tests, …) |
2525
| `scripts/` | Version bump / consistency / release helpers |
26+
| `deploy/aws/codebuild/` | Public-safe CodeBuild buildspecs and OIDC IAM ([README](deploy/aws/codebuild/README.md)) |
2627

2728
## Non-negotiables
2829

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -198,6 +198,8 @@ via [`publish-r2.yml`](.github/workflows/publish-r2.yml).
198198

199199
This repository does not invent cloud accounts. The secret *names* CI expects are
200200
listed in [docs/CI_SECRETS.md](./docs/CI_SECRETS.md). Values never go in git.
201+
Linux CI can additionally run on AWS CodeBuild via GitHub OIDC; the one-time
202+
IAM steps are in [deploy/aws/codebuild/README.md](./deploy/aws/codebuild/README.md).
201203

202204
## Contributing
203205

deploy/aws/codebuild/README.md

Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
# AWS CodeBuild CI for CortexLM/cli
2+
3+
Public-repo CodeBuild integration for Linux **x64** and **arm64**. GitHub
4+
Actions assumes a dedicated IAM role with **OIDC** (no long-lived AWS keys)
5+
and starts the projects. Each project posts a commit status comparable to
6+
the CortexLM/backend checks `cortex-gha-x64` / `cortex-gha-arm64`.
7+
8+
Marker: `CLI_CODEBUILD_CI_READY`
9+
10+
Existing workflows stay in place: `ci.yml`, `release.yml`, `publish-r2.yml`,
11+
`homebrew.yml`, `winget.yml`, `version-bump.yml`, `test-stability.yml`.
12+
CodeBuild **extends** them. It does not replace R2 publishing, version
13+
bumps, or macOS/Windows release jobs. Staging/prod app deploy remains
14+
unchanged (prod HOLD).
15+
16+
Windows CodeBuild is **out of scope**. Compliance treats Windows CI as
17+
outside the production gate; keep `windows-latest` on GitHub-hosted runners
18+
in `ci.yml` / `release.yml` until a separate follow-up.
19+
20+
Do not commit AWS account IDs, access keys, PATs, or internal hostnames.
21+
22+
## Status checks (branch protection)
23+
24+
After the one-time AWS setup below, add these **required** checks on `main`:
25+
26+
| Context | Project | Arch |
27+
|---------|---------|------|
28+
| `cortex-cli-gha-x64` | `cortex-cli-gha-x64` | Linux x86_64 |
29+
| `cortex-cli-gha-arm64` | `cortex-cli-gha-arm64` | Linux aarch64 |
30+
31+
Keep the existing `ci.yml` checks (`Format`, `Clippy`, `Test`, `TUI checks`,
32+
`Security Audit`, `Source and dependency policy`, `Changed-line coverage`,
33+
`CLI Version and Distribution`, `CI Success`). Do not remove them in this
34+
change. After CodeBuild is required and stable, a later PR can slim the
35+
duplicate GitHub-hosted Linux cargo jobs.
36+
37+
Same-repo PRs and pushes to `main` start CodeBuild. Fork PRs keep using
38+
GitHub-hosted `ci.yml` only (OIDC is not granted to forks).
39+
40+
## Prefer existing org projects?
41+
42+
If this AWS account already hosts backend projects `cortex-gha-x64` /
43+
`cortex-gha-arm64`, **reuse the GitHub OIDC provider** and the account, not
44+
the projects. A CodeBuild project has one source/buildspec; do not point
45+
backend projects at this public CLI repo. Create dedicated
46+
`cortex-cli-gha-*` projects. Override names only via GitHub **variables**
47+
if an admin already created equivalent CLI projects.
48+
49+
## One-time admin setup
50+
51+
### 1. Reuse or create the GitHub OIDC provider
52+
53+
In the AWS account that already runs CortexLM/backend CodeBuild (or a new
54+
account dedicated to public CLI CI):
55+
56+
1. IAM → Identity providers → `token.actions.githubusercontent.com`.
57+
2. If it exists, **do not recreate it**. Continue to the role.
58+
3. If it does not exist, create it:
59+
- Provider URL: `https://token.actions.githubusercontent.com`
60+
- Audience: `sts.amazonaws.com`
61+
- Or pass `CreateGithubOidcProvider=true` to the stack below.
62+
63+
### 2. Deploy the stack (recommended)
64+
65+
From a workstation that can assume an admin role (never from this repo's
66+
CI, and never with keys committed here):
67+
68+
```bash
69+
aws cloudformation deploy \
70+
--stack-name cortex-cli-codebuild \
71+
--template-file deploy/aws/codebuild/cloudformation.yaml \
72+
--capabilities CAPABILITY_NAMED_IAM \
73+
--parameter-overrides \
74+
GitHubOrgRepo=CortexLM/cli \
75+
ProjectNameX64=cortex-cli-gha-x64 \
76+
ProjectNameArm64=cortex-cli-gha-arm64 \
77+
GhaRoleName=cortex-cli-codebuild-gha \
78+
CreateGithubOidcProvider=false
79+
```
80+
81+
Copy the `GithubActionsRoleArn` output. It contains the account ID; store
82+
it as a GitHub **variable**, not in git.
83+
84+
### 3. Manual IAM if you do not use CloudFormation
85+
86+
1. Create role `cortex-cli-codebuild-gha`.
87+
2. Trust policy: `iam-trust-policy.json` with `ACCOUNT_ID` replaced at
88+
deploy time. Subject must be `repo:CortexLM/cli:*` only.
89+
3. Permissions: `iam-gha-permissions.json` with `ACCOUNT_ID` and `REGION`
90+
replaced. Actions are only `codebuild:StartBuild`,
91+
`codebuild:BatchGetBuilds`, and `logs:GetLogEvents` on the two CLI
92+
projects.
93+
4. Create CodeBuild projects `cortex-cli-gha-x64` (Linux x86,
94+
`aws/codebuild/standard:7.0`, `BUILD_GENERAL1_LARGE`) and
95+
`cortex-cli-gha-arm64` (Linux ARM,
96+
`aws/codebuild/amazonlinux-aarch64-standard:3.0`,
97+
`BUILD_GENERAL1_LARGE`). Source type **NO_SOURCE**. S3 cache on a
98+
private bucket. Build timeout 90 minutes.
99+
5. CodeBuild service role: CloudWatch Logs for those projects plus
100+
read/write on the cache bucket. No deploy, no R2, no production secrets.
101+
102+
### 4. GitHub repository variables (not secrets)
103+
104+
On `CortexLM/cli` → Settings → Secrets and variables → Actions → Variables:
105+
106+
| Variable | Value |
107+
|----------|--------|
108+
| `AWS_CODEBUILD_ROLE_ARN` | `GithubActionsRoleArn` stack output |
109+
| `AWS_REGION` | Region of the stack (default in the workflow is `us-east-1`) |
110+
| `AWS_CODEBUILD_PROJECT_X64` | Optional override; default `cortex-cli-gha-x64` |
111+
| `AWS_CODEBUILD_PROJECT_ARM64` | Optional override; default `cortex-cli-gha-arm64` |
112+
113+
Do **not** add `AWS_ACCESS_KEY_ID` / `AWS_SECRET_ACCESS_KEY`. Do not put
114+
staging or production app secrets on these projects.
115+
116+
Until `AWS_CODEBUILD_ROLE_ARN` is set, `.github/workflows/codebuild.yml`
117+
validates the in-repo assets and **skips** StartBuild. It does not post a
118+
green `cortex-cli-gha-*` status for that skip (no mock-success).
119+
120+
### 5. Require the checks
121+
122+
Branch protection / ruleset on `main`: require
123+
`cortex-cli-gha-x64` and `cortex-cli-gha-arm64` in addition to the
124+
existing `ci.yml` jobs. Require these only after a successful StartBuild
125+
has been observed on a test PR.
126+
127+
## What CodeBuild runs
128+
129+
`buildspec-ci.yml` clones the public `CortexLM/cli` commit over HTTPS
130+
(no PAT) and runs `run-ci.sh`:
131+
132+
- `cargo fmt --all -- --check`
133+
- `./scripts/clippy.sh`
134+
- `./scripts/check-cli-version.sh`
135+
- `python3 scripts/readiness/tests.py`
136+
- `cargo test --locked --workspace --doc`
137+
- `python3 scripts/readiness/schema.py`
138+
- `cargo build --locked -p cortex-cli -p cortex-app-server`
139+
- `python3 scripts/readiness/qa.py`
140+
- headless TUI / snapshot packages (same set as `ci.yml`)
141+
- changed-line coverage against the real PR base SHA
142+
143+
Cargo registry, git, rustup, and `target/` are cached in S3.
144+
145+
## Follow-up (Windows)
146+
147+
Not in this change. If Windows CodeBuild is added later, use a separate
148+
project and a non-required check. Do not block production on it.
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# Linux CI for CortexLM/cli on AWS CodeBuild (x64 and arm64).
2+
# Marker: CLI_CODEBUILD_CI_READY
3+
#
4+
# This file is the source of truth. GitHub Actions passes it as an inline
5+
# buildspec override so the project can use NO_SOURCE (no GitHub token).
6+
# The install phase clones the public repo over HTTPS when the workspace
7+
# is empty, then runs scripts from that commit.
8+
version: 0.2
9+
10+
env:
11+
variables:
12+
CARGO_TERM_COLOR: always
13+
CARGO_INCREMENTAL: "0"
14+
CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
15+
RUST_BACKTRACE: "1"
16+
GIT_TERMINAL_PROMPT: "0"
17+
18+
phases:
19+
install:
20+
runtime-versions:
21+
python: 3.12
22+
nodejs: 22
23+
commands:
24+
- |
25+
set -euo pipefail
26+
REPO="${CORTEX_GITHUB_REPOSITORY:-${GITHUB_REPOSITORY:-CortexLM/cli}}"
27+
SHA="${CORTEX_SOURCE_SHA:-${GITHUB_SHA:-}}"
28+
if [ -z "$SHA" ]; then
29+
echo "Missing CORTEX_SOURCE_SHA / GITHUB_SHA" >&2
30+
exit 1
31+
fi
32+
if [ -f "${CODEBUILD_SRC_DIR:-}/Cargo.toml" ]; then
33+
SRC="$CODEBUILD_SRC_DIR"
34+
else
35+
SRC=/tmp/cortex-cli-src
36+
rm -rf "$SRC"
37+
git clone --no-tags "https://github.com/${REPO}.git" "$SRC"
38+
git -C "$SRC" checkout --detach "$SHA"
39+
fi
40+
printf 'export CORTEX_CLI_SRC=%q\n' "$SRC" > /tmp/cortex-cli-env.sh
41+
# shellcheck disable=SC1091
42+
. /tmp/cortex-cli-env.sh
43+
bash "$CORTEX_CLI_SRC/deploy/aws/codebuild/install-deps.sh"
44+
45+
build:
46+
commands:
47+
- |
48+
set -euo pipefail
49+
# shellcheck disable=SC1091
50+
. /tmp/cortex-cli-env.sh
51+
bash "$CORTEX_CLI_SRC/deploy/aws/codebuild/run-ci.sh"
52+
53+
cache:
54+
paths:
55+
- /root/.cargo/registry/**/*
56+
- /root/.cargo/git/**/*
57+
- /root/.rustup/toolchains/**/*
58+
- /tmp/cortex-cli-src/target/**/*
59+
- target/**/*

0 commit comments

Comments
 (0)