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
172 changes: 135 additions & 37 deletions .github/workflows/build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,9 @@ jobs:
exit 1
fi

- name: Set up Helm
uses: azure/setup-helm@v5.0.1

- name: Check formatting
run: cargo fmt --all -- --check

Expand Down Expand Up @@ -140,9 +143,6 @@ jobs:
arguments: --all-features
command: check

- name: Set up Helm
uses: azure/setup-helm@v5.0.1

- name: Lint and render Helm chart
run: |
helm lint charts/operator
Expand All @@ -153,6 +153,25 @@ jobs:
--set networkPolicy.enabled=true \
--set bootstrap.existingSecret=operator-bootstrap \
> operator-chart.yaml
helm template operator charts/operator \
--set-json 'extraVolumes=[{"name":"custom-ca","configMap":{"name":"custom-ca"}}]' \
--set-json 'extraVolumeMounts=[{"name":"custom-ca","mountPath":"/etc/custom-ca","readOnly":true}]' \
--set-json 'lifecycle={"preStop":{"exec":{"command":["/bin/sh","-c","true"]}}}' \
> operator-chart-extensions.yaml
if helm template operator charts/operator --set terminationGracePeriodSeconds=75; then
echo "::error::invalid shutdown budget rendered successfully"
exit 1
fi
if helm template operator charts/operator \
--set-json 'extraVolumes=[{"name":"workspace","emptyDir":{}}]'; then
echo "::error::chart-owned volume collision rendered successfully"
exit 1
fi
if helm template operator charts/operator \
--set-json 'extraVolumeMounts=[{"name":"one","mountPath":"/shared"},{"name":"two","mountPath":"/shared"}]'; then
echo "::error::duplicate mount path rendered successfully"
exit 1
fi

- name: Scan rendered Helm chart
uses: aquasecurity/trivy-action@v0.36.0
Expand All @@ -164,7 +183,6 @@ jobs:

# Compute the next version once, before anything is built, so the compiled
# binaries embed the same version that the release tag + Docker tag will use.
# The actual file edits + commit + tag still happen in the release job.
version:
needs: lint-test
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
Expand Down Expand Up @@ -585,7 +603,7 @@ jobs:

- name: Update TypeScript VERSION constant
run: |
sed -i "s/const VERSION = '[^']*'/const VERSION = '${{ needs.version.outputs.version }}'/" vscode-extension/src/webhook-server.ts
sed -i "s/const VERSION = [\"'][^\"']*[\"']/const VERSION = \\"${{ needs.version.outputs.version }}\\"/" vscode-extension/src/webhook-server.ts

# Pinned by tests/version_parity.rs; the range keeps the sed inside the
# install_version block so sibling variable defaults are untouched.
Expand Down Expand Up @@ -672,67 +690,135 @@ jobs:

docker:
needs: [build, build-opr8r, release]
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
include:
- arch: amd64
runner: ubuntu-latest
operator_artifact: operator-linux-x86_64
opr8r_artifact: opr8r-linux-x86_64
- arch: arm64
runner: ubuntu-24.04-arm
operator_artifact: operator-linux-arm64
opr8r_artifact: opr8r-linux-arm64
runs-on: ${{ matrix.runner }}
steps:
- uses: actions/checkout@v7
with:
ref: ${{ needs.release.outputs.commit }}

- name: Download linux operator binaries
- name: Download operator binary
uses: actions/download-artifact@v8
with:
pattern: operator-linux-*
name: ${{ matrix.operator_artifact }}
path: bins
merge-multiple: true

# The image ships both halves of the server-client pair, so agent
# sessions inside the container can report step completion via opr8r.
- name: Download linux opr8r binaries
- name: Download opr8r binary
uses: actions/download-artifact@v8
with:
pattern: opr8r-linux-*
name: ${{ matrix.opr8r_artifact }}
path: bins
merge-multiple: true

# buildx exposes TARGETARCH as amd64/arm64; map the x86_64 artifact name.
- name: Stage binaries for build context
run: |
cp bins/operator-linux-x86_64 ./operator-linux-amd64
cp bins/operator-linux-arm64 ./operator-linux-arm64
cp bins/opr8r-linux-x86_64 ./opr8r-linux-amd64
cp bins/opr8r-linux-arm64 ./opr8r-linux-arm64
chmod +x operator-linux-amd64 operator-linux-arm64 \
opr8r-linux-amd64 opr8r-linux-arm64
cp "bins/${{ matrix.operator_artifact }}" "operator-linux-${{ matrix.arch }}"
cp "bins/${{ matrix.opr8r_artifact }}" "opr8r-linux-${{ matrix.arch }}"
chmod +x "operator-linux-${{ matrix.arch }}" "opr8r-linux-${{ matrix.arch }}"

- uses: docker/setup-qemu-action@v4
- uses: docker/setup-buildx-action@v4

- name: Log in to Docker Hub
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Build and push multi-arch image
- name: Build image for scanning
uses: docker/build-push-action@v7
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: |
untra/operator:${{ needs.release.outputs.version }}
untra/operator:latest
platforms: linux/${{ matrix.arch }}
load: true
tags: operator-ci:${{ needs.release.outputs.version }}-${{ matrix.arch }}

- name: Scan container image
uses: aquasecurity/trivy-action@v0.36.0
with:
image-ref: untra/operator:${{ needs.release.outputs.version }}
image-ref: operator-ci:${{ needs.release.outputs.version }}-${{ matrix.arch }}
scanners: vuln
severity: HIGH,CRITICAL
ignore-unfixed: true
format: sarif
output: trivy-${{ matrix.arch }}.sarif
exit-code: '1'

chart:
- name: Upload scan report
if: always()
uses: actions/upload-artifact@v7
with:
name: trivy-${{ matrix.arch }}
path: trivy-${{ matrix.arch }}.sarif
if-no-files-found: ignore
retention-days: 14

- name: Export scanned image
run: docker save "operator-ci:${{ needs.release.outputs.version }}-${{ matrix.arch }}" -o "operator-${{ matrix.arch }}.tar"

- name: Upload scanned image
uses: actions/upload-artifact@v7
with:
name: operator-image-${{ matrix.arch }}
path: operator-${{ matrix.arch }}.tar
compression-level: 0
retention-days: 1

- name: Report scan result
if: always()
run: echo "### linux/${{ matrix.arch }} blocking Trivy scan — ${{ job.status }}" >> "$GITHUB_STEP_SUMMARY"

docker-publish:
needs: [release, docker]
runs-on: ubuntu-latest
outputs:
digest: ${{ steps.manifest.outputs.digest }}
steps:
- uses: actions/download-artifact@v8
with:
pattern: operator-image-*
path: images
merge-multiple: true

- name: Log in to Docker Hub
uses: docker/login-action@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Push scanned platform images
id: platforms
run: |
for arch in amd64 arm64; do
docker load -i "images/operator-${arch}.tar"
source="operator-ci:${{ needs.release.outputs.version }}-${arch}"
candidate="untra/operator:${{ needs.release.outputs.version }}-${arch}-${{ github.run_id }}"
docker tag "$source" "$candidate"
docker push "$candidate"
digest=$(docker buildx imagetools inspect "$candidate" --format '{{json .Manifest.Digest}}' | tr -d '"')
echo "${arch}=untra/operator@${digest}" >> "$GITHUB_OUTPUT"
done

- name: Publish multi-platform manifests
id: manifest
run: |
version="${{ needs.release.outputs.version }}"
amd64="${{ steps.platforms.outputs.amd64 }}"
arm64="${{ steps.platforms.outputs.arm64 }}"
docker buildx imagetools create -t "untra/operator:${version}" -t untra/operator:latest "$amd64" "$arm64"
digest=$(docker buildx imagetools inspect "untra/operator:${version}" --format '{{json .Manifest.Digest}}' | tr -d '"')
echo "digest=$digest" >> "$GITHUB_OUTPUT"
docker buildx imagetools inspect "untra/operator:${version}"
docker buildx imagetools inspect "untra/operator:${version}" --raw \
| jq -e '[.manifests[].platform | select(.os == "linux") | .architecture] | sort == ["amd64", "arm64"]'
echo "### Published untra/operator:${version} at ${digest}" >> "$GITHUB_STEP_SUMMARY"

chart:
needs: [release, docker-publish]
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
Expand All @@ -752,8 +838,20 @@ jobs:
helm package charts/operator --destination dist
helm push "dist/operator-${{ needs.release.outputs.version }}.tgz" oci://ghcr.io/untra/charts

- name: Verify public chart
run: |
mkdir -p /tmp/helm-public
export HELM_REGISTRY_CONFIG=/tmp/helm-public/config.json
version="${{ needs.release.outputs.version }}"
helm pull oci://ghcr.io/untra/charts/operator --version "$version" --destination /tmp/helm-public
chart_version=$(helm show chart "/tmp/helm-public/operator-${version}.tgz" | awk '/^version:/ {print $2}')
image=$(helm template verify "/tmp/helm-public/operator-${version}.tgz" | awk '/image: "untra\/operator:/ {print $2; exit}' | tr -d '"')
test "$chart_version" = "$version"
test "$image" = "untra/operator:$version"
echo "### Published and anonymously verified operator chart ${version}" >> "$GITHUB_STEP_SUMMARY"

deploy-docs:
needs: release
needs: [release, chart]
runs-on: ubuntu-latest
steps:
- name: Trigger docs workflow
Expand Down
3 changes: 1 addition & 2 deletions .oxlintrc.jsonc
Original file line number Diff line number Diff line change
Expand Up @@ -176,10 +176,9 @@
"typescript/no-unsafe-member-access": "off",
"typescript/no-unsafe-return": "off",

// ---- react-perf: new class of finding, land as warn ----
"react-perf/jsx-no-new-object-as-prop": "warn",
"react-perf/jsx-no-new-array-as-prop": "warn",
"react-perf/jsx-no-new-function-as-prop": "warn",
"react-perf/jsx-no-new-function-as-prop": ["error", { "nativeAllowList": "all" }],
"react-perf/jsx-no-jsx-as-prop": "warn"
},

Expand Down
16 changes: 8 additions & 8 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ ARG TARGETARCH
# ssh and coder target launch, and git over SSH remotes), curl (in-pod reachability checks).
# The LLM CLI (claude / codex / gemini) and its auth are supplied by the user via a derived image or env vars
RUN apt-get update \
&& apt-get upgrade -y --no-install-recommends \
&& apt-get install -y --no-install-recommends ca-certificates curl git openssh-client tmux \
&& rm -rf /var/lib/apt/lists/*

Expand Down
10 changes: 7 additions & 3 deletions bindings/AgentState.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
import type { GitExecutionConfig } from "./GitExecutionConfig";
import type { ShutdownRecovery } from "./ShutdownRecovery";
import type { StepLaunchContext } from "./StepLaunchContext";

export type AgentState = {
Expand Down Expand Up @@ -73,7 +74,6 @@ llm_tool: string | null,
llm_model: string | null,
/**
* Launch mode: `default|yolo|docker[-yolo]|coder[-yolo]|ssh[-yolo]`
* (derived from the resolved execution target; parse with `agents::parse_launch_mode`, never substring-match)
*/
launch_mode: string | null,
/**
Expand All @@ -98,6 +98,10 @@ remote_host: string | null,
*/
step_launch_context: StepLaunchContext | null,
/**
* Name of the resolved execution target this agent launched on
* Name of the resolved execution target this agent launched on.
*/
target_name: string | null, };
target_name: string | null,
/**
* Shutdown recovery strategy.
*/
shutdown_recovery?: ShutdownRecovery | null, };
10 changes: 9 additions & 1 deletion bindings/RestApiConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,12 @@ cors_origins: Array<string>,
/**
* Externally reachable base URL (e.g. `https://operator.example.com`). Defaults to request host.
*/
public_url: string | null, };
public_url: string | null,
/**
* Maximum time to wait for active agents before shutdown cleanup begins.
*/
shutdown_drain_seconds: number,
/**
* Maximum time reserved for final callbacks and persistent cleanup.
*/
shutdown_cleanup_seconds: number, };
2 changes: 1 addition & 1 deletion bindings/SetupExecutionTarget.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.

export type SetupExecutionTarget = { "kind": "local" } | { "kind": "coder", name: string, template: string, };
export type SetupExecutionTarget = { "kind": "local" } | { "kind": "coder", name: string, template: string, parameters: { [key in string]: string }, };
3 changes: 3 additions & 0 deletions bindings/ShutdownRecovery.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.

export type ShutdownRecovery = "interrupted_local" | "remote_awaiting_reconciliation";
Loading
Loading