Skip to content
Closed
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
19 changes: 6 additions & 13 deletions .claude/skills/ambient-pr-test/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ with .claude/skills/ambient-pr-test https://github.com/ambient-code/platform/pu
```

Optional modifiers the user may specify:
- **`--force-build`** — rebuild and push images even if CI already pushed them
- **`--keep-alive`** — do not tear down after the workflow; leave the instance online for human access
- **`provision-only`** / **`deploy-only`** / **`teardown-only`** — run a single phase instead of the full workflow

Expand Down Expand Up @@ -56,7 +55,7 @@ This cluster's tenant operator does not emit `Ready` conditions on `TenantNamesp
## Full Workflow

```
0. Build: skip if CI pushed images (or --force-build to always rebuild)
0. Build: always run build.sh to build and push images tagged pr-<PR_NUMBER>
1. Derive instance-id from PR number
2. Provision: bash components/pr-test/provision.sh create <instance-id>
3. Deploy: bash components/pr-test/install.sh <namespace> <image-tag>
Expand All @@ -70,21 +69,15 @@ Phases can be run individually — see **Individual Phases** below.

## Step 0: Build and Push Images

Check CI first:
```bash
gh run list --repo ambient-code/platform \
--workflow "Build and Push Component Docker Images" \
--branch <head-branch> --limit 1
```

**Skip** if the latest run shows `completed / success`. Otherwise build:
Always run `build.sh` — CI may skip builds when no component source files changed (e.g. sync/merge branches), so never rely on CI to have pushed images:
```bash
bash components/pr-test/build.sh https://github.com/ambient-code/platform/pull/1005
```

**`--force-build`**: skip the CI check and always run `build.sh` regardless. Use when:
- Images exist but were built from a different commit (e.g. after a force-push)
- CI built images but from a stale SHA
This builds and pushes 3 images tagged `pr-<PR_NUMBER>`:
- `quay.io/ambient_code/vteam_api_server:pr-<PR_NUMBER>`
- `quay.io/ambient_code/vteam_control_plane:pr-<PR_NUMBER>`
- `quay.io/ambient_code/vteam_claude_runner:pr-<PR_NUMBER>`

Builds 3 images: `vteam_api_server`, `vteam_control_plane`, `vteam_claude_runner`.

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/ci-failure-resolver-with-agent.yml
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ jobs:
- name: Checkout & label
if: steps.analyze.outputs.action == 'fix'
uses: actions/checkout@v4
uses: actions/checkout@v6
with:
ref: refs/pull/${{ matrix.pr }}/head
fetch-depth: 0
Expand Down
58 changes: 26 additions & 32 deletions .github/workflows/components-build-deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ name: Build and Push Component Docker Images

on:
push:
branches: [main]
branches: [main, alpha]
paths:
- '.github/workflows/components-build-deploy.yml'
- 'components/manifests/**'
Expand All @@ -15,7 +15,7 @@ on:
- 'components/ambient-control-plane/**'
- 'components/ambient-mcp/**'
pull_request:
branches: [main]
branches: [main, alpha]
paths:
- '.github/workflows/components-build-deploy.yml'
- 'components/manifests/**'
Expand All @@ -29,11 +29,6 @@ on:
- 'components/ambient-mcp/**'
workflow_dispatch:
inputs:
force_build_all:
description: 'Force rebuild all components'
required: false
type: boolean
default: false
components:
description: 'Components to build (comma-separated: frontend,backend,operator,ambient-runner,state-sync,public-api,ambient-api-server,ambient-control-plane,ambient-mcp) - leave empty for all'
required: false
Expand Down Expand Up @@ -107,29 +102,9 @@ jobs:
{"name":"ambient-mcp","context":"./components/ambient-mcp","image":"quay.io/ambient_code/vteam_mcp","dockerfile":"./components/ambient-mcp/Dockerfile"}
]'

FORCE_ALL="${{ github.event.inputs.force_build_all }}"
SELECTED="${{ github.event.inputs.components }}"
EVENT="${{ github.event_name }}"

# Map component names to paths-filter output names
# (ambient-runner uses claude-runner filter)
declare -A FILTER_MAP=(
[frontend]="${{ steps.filter.outputs.frontend }}"
[backend]="${{ steps.filter.outputs.backend }}"
[operator]="${{ steps.filter.outputs.operator }}"
[ambient-runner]="${{ steps.filter.outputs.claude-runner }}"
[state-sync]="${{ steps.filter.outputs.state-sync }}"
[public-api]="${{ steps.filter.outputs.public-api }}"
[ambient-api-server]="${{ steps.filter.outputs.ambient-api-server }}"
)

if [ "$FORCE_ALL" == "true" ]; then
# Force build all
FILTERED="$ALL_COMPONENTS"
elif [ "$EVENT" == "workflow_dispatch" ] && [ -z "$SELECTED" ] && [ "$FORCE_ALL" != "true" ]; then
# Dispatch with no selection and no force — build all
FILTERED="$ALL_COMPONENTS"
elif [ -n "$SELECTED" ]; then

if [ -n "$SELECTED" ]; then
# Dispatch with specific components
FILTERED=$(echo "$ALL_COMPONENTS" | jq -c --arg sel "$SELECTED" '[.[] | select(.name as $n | $sel | split(",") | map(gsub("^\\s+|\\s+$";"")) | index($n))]')
elif [ "$EVENT" == "pull_request" ]; then
Expand Down Expand Up @@ -215,7 +190,7 @@ jobs:
cache-from: type=gha,scope=${{ matrix.component.name }}-${{ matrix.arch.suffix }}
cache-to: type=gha,mode=max,scope=${{ matrix.component.name }}-${{ matrix.arch.suffix }}

- name: Build ${{ matrix.component.name }} (${{ matrix.arch.suffix }}) for pull request
- name: Build and push ${{ matrix.component.name }} (${{ matrix.arch.suffix }}) for pull request
if: github.event_name == 'pull_request'
uses: docker/build-push-action@v7
with:
Expand All @@ -228,10 +203,11 @@ jobs:
${{ matrix.component.image }}:pr-${{ github.event.pull_request.number }}-${{ github.sha }}-${{ matrix.arch.suffix }}
build-args: AMBIENT_VERSION=${{ github.sha }}
cache-from: type=gha,scope=${{ matrix.component.name }}-${{ matrix.arch.suffix }}
cache-to: type=gha,mode=max,scope=${{ matrix.component.name }}-${{ matrix.arch.suffix }}

merge-manifests:
needs: [detect-changes, build]
if: github.event_name != 'pull_request' && needs.detect-changes.outputs.has-builds == 'true'
if: needs.detect-changes.outputs.has-builds == 'true'
runs-on: ubuntu-latest
permissions:
contents: read
Expand All @@ -251,7 +227,8 @@ jobs:
username: ${{ secrets.QUAY_USERNAME }}
password: ${{ secrets.QUAY_PASSWORD }}

- name: Create multi-arch manifest for ${{ matrix.component.name }}
- name: Create multi-arch manifest for ${{ matrix.component.name }} (main)
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
# Suffixes (-amd64, -arm64) must match the arch matrix in the build job above.
# Arch-suffixed tags remain in the registry after merging. Clean these up
# via Quay tag expiration policies or a periodic job.
Expand All @@ -263,6 +240,23 @@ jobs:
${{ matrix.component.image }}:${{ github.sha }}-amd64 \
${{ matrix.component.image }}:${{ github.sha }}-arm64

- name: Create multi-arch manifest for ${{ matrix.component.name }} (alpha)
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/alpha'
run: |
docker buildx imagetools create \
-t ${{ matrix.component.image }}:alpha \
-t ${{ matrix.component.image }}:${{ github.sha }} \
${{ matrix.component.image }}:${{ github.sha }}-amd64 \
${{ matrix.component.image }}:${{ github.sha }}-arm64

- name: Create multi-arch manifest for ${{ matrix.component.name }} (pull request)
if: github.event_name == 'pull_request'
run: |
docker buildx imagetools create \
-t ${{ matrix.component.image }}:pr-${{ github.event.pull_request.number }} \
${{ matrix.component.image }}:pr-${{ github.event.pull_request.number }}-amd64 \
${{ matrix.component.image }}:pr-${{ github.event.pull_request.number }}-arm64

update-rbac-and-crd:
runs-on: ubuntu-latest
needs: [detect-changes, merge-manifests]
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/feedback-loop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v4
uses: actions/checkout@v6

- name: Set up Python
uses: actions/setup-python@v6
Expand Down
199 changes: 199 additions & 0 deletions .github/workflows/sync-alpha-from-main.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,199 @@
name: Sync Alpha from Main

on:
push:
branches: [main]
workflow_dispatch:

permissions:
contents: write
pull-requests: write

concurrency:
group: sync-alpha-from-main
cancel-in-progress: false

jobs:
sync:
name: Rebase main into alpha
runs-on: ubuntu-latest
timeout-minutes: 15

steps:
- name: Checkout repository
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
fetch-depth: 0
token: ${{ secrets.GITHUB_TOKEN }}

- name: Configure git
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"

- name: Check if alpha is already up to date
id: check
run: |
MAIN_SHA="$(git rev-parse origin/main)"
ALPHA_SHA="$(git rev-parse origin/alpha)"
COMMIT_COUNT="$(git rev-list --count "${ALPHA_SHA}..${MAIN_SHA}")"

echo "main_sha=${MAIN_SHA}" >> "$GITHUB_OUTPUT"
echo "alpha_sha=${ALPHA_SHA}" >> "$GITHUB_OUTPUT"
echo "commit_count=${COMMIT_COUNT}" >> "$GITHUB_OUTPUT"

if [ "${COMMIT_COUNT}" -eq 0 ]; then
echo "needs_sync=false" >> "$GITHUB_OUTPUT"
echo "alpha is already up to date with main"
else
echo "needs_sync=true" >> "$GITHUB_OUTPUT"
echo "Commits in main not in alpha: ${COMMIT_COUNT}"
fi

- name: Check for existing open sync PR
if: steps.check.outputs.needs_sync == 'true'
id: existing_pr
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
EXISTING=$(gh pr list \
--base alpha \
--state open \
--json headRefName \
--jq '[.[] | select(.headRefName | startswith("chore/sync-alpha-from-main-"))] | length')

if [ "${EXISTING}" -gt 0 ]; then
echo "Open sync PR already exists — skipping"
echo "pr_exists=true" >> "$GITHUB_OUTPUT"
else
echo "pr_exists=false" >> "$GITHUB_OUTPUT"
fi

- name: Create work branch off alpha
if: steps.check.outputs.needs_sync == 'true' && steps.existing_pr.outputs.pr_exists == 'false'
id: branch
run: |
TIMESTAMP="$(date +%Y%m%d-%H%M%S)"
WORK_BRANCH="chore/sync-alpha-from-main-${TIMESTAMP}"
echo "work_branch=${WORK_BRANCH}" >> "$GITHUB_OUTPUT"

git checkout -b "${WORK_BRANCH}" origin/alpha
echo "Created ${WORK_BRANCH} from origin/alpha"

- name: Attempt rebase of main onto work branch
if: steps.check.outputs.needs_sync == 'true' && steps.existing_pr.outputs.pr_exists == 'false'
id: rebase
env:
WORK_BRANCH: ${{ steps.branch.outputs.work_branch }}
MAIN_SHA: ${{ steps.check.outputs.main_sha }}
ALPHA_SHA: ${{ steps.check.outputs.alpha_sha }}
run: |
MERGE_BASE="$(git merge-base "${ALPHA_SHA}" "${MAIN_SHA}")"

git rebase --onto "${WORK_BRANCH}" "${MERGE_BASE}" origin/main && {
echo "rebase_clean=true" >> "$GITHUB_OUTPUT"
git checkout -B "${WORK_BRANCH}"
echo "Rebase completed cleanly"
} || {
echo "rebase_clean=false" >> "$GITHUB_OUTPUT"
git rebase --abort 2>/dev/null || true

echo "Rebase had conflicts — falling back to merge"
MERGE_MSG=$(cat <<'MSG'
chore: merge main into alpha (conflict resolution required)

Automated merge of origin/main into origin/alpha.
Rebase encountered conflicts; falling back to merge.
A human must resolve conflict markers before merging this PR.
MSG
)
git merge --no-ff --allow-unrelated-histories origin/main -m "${MERGE_MSG}" || {
git add -A
CONFLICT_MSG=$(cat <<'MSG'
chore: best-effort merge main into alpha (conflicts present)

Automated merge of origin/main into origin/alpha.
Both rebase and merge encountered conflicts. Conflict markers
are present and must be resolved before this PR can be merged.
MSG
)
git commit --no-verify -m "${CONFLICT_MSG}"
}
}

- name: Push work branch
if: steps.check.outputs.needs_sync == 'true' && steps.existing_pr.outputs.pr_exists == 'false'
env:
WORK_BRANCH: ${{ steps.branch.outputs.work_branch }}
run: |
git push origin "${WORK_BRANCH}"

- name: Open PR against alpha
if: steps.check.outputs.needs_sync == 'true' && steps.existing_pr.outputs.pr_exists == 'false'
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
WORK_BRANCH: ${{ steps.branch.outputs.work_branch }}
COMMIT_COUNT: ${{ steps.check.outputs.commit_count }}
REBASE_CLEAN: ${{ steps.rebase.outputs.rebase_clean }}
MAIN_SHA: ${{ steps.check.outputs.main_sha }}
ALPHA_SHA: ${{ steps.check.outputs.alpha_sha }}
run: |
if [ "${REBASE_CLEAN}" = "true" ]; then
CONFLICT_NOTE="Rebase completed cleanly — no conflicts detected. This PR can be merged directly."
else
CONFLICT_NOTE="⚠️ **Conflicts detected.** Rebase fell back to merge. Search for \`<<<<<<<\` conflict markers and resolve before merging."
fi

gh pr create \
--base alpha \
--head "${WORK_BRANCH}" \
--title "chore: sync alpha from main ($(date +%Y-%m-%d))" \
--body "## Summary

Automated sync of \`main\` into \`alpha\` triggered by push to \`main\`.

| | |
|---|---|
| Commits synced | ${COMMIT_COUNT} |
| origin/main | \`${MAIN_SHA:0:8}\` |
| origin/alpha | \`${ALPHA_SHA:0:8}\` |

## Status

${CONFLICT_NOTE}

## Review Instructions

1. Check for conflict markers (\`<<<<<<<\`) in changed files.
2. Resolve any conflicts and push to this branch.
3. Verify the build passes.
4. Merge into \`alpha\`.

---
*Auto-generated by \`.github/workflows/sync-alpha-from-main.yml\`*"

- name: Summary
if: always()
env:
NEEDS_SYNC: ${{ steps.check.outputs.needs_sync }}
PR_EXISTS: ${{ steps.existing_pr.outputs.pr_exists || 'false' }}
COMMIT_COUNT: ${{ steps.check.outputs.commit_count || '0' }}
REBASE_CLEAN: ${{ steps.rebase.outputs.rebase_clean || 'n/a' }}
JOB_STATUS: ${{ job.status }}
run: |
if [ "${NEEDS_SYNC}" = "false" ]; then
echo "## ✅ Already in sync" >> "$GITHUB_STEP_SUMMARY"
echo "alpha is up to date with main — nothing to do." >> "$GITHUB_STEP_SUMMARY"
elif [ "${PR_EXISTS}" = "true" ]; then
echo "## ℹ️ Sync PR already open" >> "$GITHUB_STEP_SUMMARY"
echo "An open sync PR already exists against alpha — skipped." >> "$GITHUB_STEP_SUMMARY"
elif [ "${JOB_STATUS}" = "failure" ]; then
echo "## ❌ Sync failed" >> "$GITHUB_STEP_SUMMARY"
echo "Check the logs above for details." >> "$GITHUB_STEP_SUMMARY"
elif [ "${REBASE_CLEAN}" = "true" ]; then
echo "## ✅ PR opened — clean rebase" >> "$GITHUB_STEP_SUMMARY"
echo "${COMMIT_COUNT} commits synced from main to alpha with no conflicts." >> "$GITHUB_STEP_SUMMARY"
else
echo "## ⚠️ PR opened — conflicts require resolution" >> "$GITHUB_STEP_SUMMARY"
echo "${COMMIT_COUNT} commits from main; rebase had conflicts. PR opened for human resolution." >> "$GITHUB_STEP_SUMMARY"
fi
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,8 @@ Thumbs.db
# IDE / AI assistant configuration
.cursor/
.tessl/
.idea/


# mypy
.mypy_cache/
Expand Down
Loading
Loading