Files
onyx/.github/workflows/deployment.yml
T

2166 lines
96 KiB
YAML

name: Build and Push Docker Images on Tag
on:
push:
tags:
- "*"
workflow_dispatch:
# Set restrictive default permissions for all jobs. Jobs that need more permissions
# should explicitly declare them.
permissions:
# Required to checkout the repo on private repos (no-op on public)
contents: read
# Required for OIDC authentication with AWS
id-token: write # zizmor: ignore[excessive-permissions]
env:
EDGE_TAG: ${{ startsWith(github.ref_name, 'nightly-latest') || github.ref_name == 'edge' }}
jobs:
# Determine which components to build based on the tag
determine-builds:
# NOTE: Github-hosted runners have about 20s faster queue times and are preferred here.
runs-on: ubuntu-slim
timeout-minutes: 90
outputs:
build-desktop: ${{ steps.check.outputs.build-desktop }}
build-web: ${{ steps.check.outputs.build-web }}
build-web-cloud: ${{ steps.check.outputs.build-web-cloud }}
build-backend: ${{ steps.check.outputs.build-backend }}
build-model-server: ${{ steps.check.outputs.build-model-server }}
is-cloud-tag: ${{ steps.check.outputs.is-cloud-tag }}
is-beta: ${{ steps.check.outputs.is-beta }}
is-beta-standalone: ${{ steps.check.outputs.is-beta-standalone }}
is-stable: ${{ steps.check.outputs.is-stable }}
is-latest: ${{ steps.check.outputs.is-latest }}
is-test-run: ${{ steps.check.outputs.is-test-run }}
sanitized-tag: ${{ steps.check.outputs.sanitized-tag }}
short-sha: ${{ steps.check.outputs.short-sha }}
steps:
- name: Checkout (for git tags)
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # ratchet:actions/checkout@v6
with:
persist-credentials: false
fetch-depth: 0
fetch-tags: true
- name: Setup uv
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # ratchet:astral-sh/setup-uv@v10.0.1
with:
version: "0.11.25"
enable-cache: false
- name: Install ods from uv.lock
run: uv sync --frozen --no-install-project --only-group ods
- name: Check which components to build and version info
id: check
env:
EVENT_NAME: ${{ github.event_name }}
run: |
set -eo pipefail
# `ods release classify` owns the routing rules; see
# tools/ods/internal/release/classify.go. The checkout above fetches
# all tags, which the "latest" decision needs.
uv run --no-sync ods release classify \
--ref "${GITHUB_REF_NAME}" \
--sha "${GITHUB_SHA}" \
--event "${EVENT_NAME}" >> "$GITHUB_OUTPUT"
# Block the release when dependencies carry unignored critical vulnerabilities.
# Runs the same `ods audit` gate used on PRs and nightly. Always runs (no skip
# condition) so the build jobs that depend on it aren't skipped on test runs;
# the audit itself degrades gracefully when the S3 allowlist or Dependabot API
# is unavailable.
audit-gate:
needs: [determine-builds]
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
contents: read # checkout + read lockfiles
id-token: write # OIDC for fetching the S3 allowlist and the registry credentials
vulnerability-alerts: read # read Dependabot alerts
security-events: write # SARIF uploads of the image audits
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # ratchet:actions/checkout@v6
with:
persist-credentials: false
- name: Setup uv
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # ratchet:astral-sh/setup-uv@v10.0.1
with:
version: "0.11.25"
enable-cache: false
- name: Install ods from uv.lock
run: uv sync --frozen --no-install-project --only-group ods
- name: Configure AWS credentials
continue-on-error: true # the audit still runs (with no suppressions) if creds are unavailable
uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c # ratchet:aws-actions/configure-aws-credentials@v6.2.3
with:
role-to-assume: ${{ secrets.AWS_OIDC_ROLE_ARN }}
aws-region: us-east-2
- name: Run dependency audit
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: uv run --no-sync ods audit --fail-on=critical
# The same registry credentials the builds use, fetched the same way, so
# the bases scanned are the bases shipped. Missing credentials fail the
# gate, since the builds would ship hardened bases the gate never saw.
- name: Get AWS Secrets
uses: aws-actions/aws-secretsmanager-get-secrets@2cb1a461cbd4865ac4299648312e4704c646cd53
with:
secret-ids: |
DOCKER_USERNAME, deploy/docker-username
DOCKER_TOKEN, deploy/docker-token
parse-json-secrets: true
- name: Login to Docker Hub
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4
with:
username: ${{ env.DOCKER_USERNAME }}
password: ${{ env.DOCKER_TOKEN }}
- name: Login to the hardened base images
uses: ./.github/actions/dhi-base-images
with:
docker-username: ${{ env.DOCKER_USERNAME }}
docker-token: ${{ env.DOCKER_TOKEN }}
# Everything a shipped image holds outside its lockfiles: the pinned
# runtime base for web and model-server, the backend's and the sandbox's
# os stage built here. All four are scanned before the step fails, so one run shows
# every blocker, and --strict keeps an empty scan from passing.
- name: Run base image audit
run: |
status=0
for component in web model-server backend sandbox; do
image="$(.github/scripts/base-image-ref.sh "${component}")"
uv run --no-sync ods audit image "${image}" --fail-on=critical --strict --format=sarif,text > "image-audit-${component}.sarif" || status=1
if [ ! -s "image-audit-${component}.sarif" ]; then
# $schema is a literal SARIF key, not a shell variable.
# shellcheck disable=SC2016
echo '{"$schema":"https://json.schemastore.org/sarif-2.1.0.json","version":"2.1.0","runs":[]}' > "image-audit-${component}.sarif"
fi
# A stage built here is named "name:tag" with no registry path, which
# the Security tab reads as a URI scheme and rejects; point its
# findings at the Dockerfile the stage comes from.
case "${component}" in
backend) dockerfile=backend/Dockerfile ;;
sandbox) dockerfile=backend/onyx/server/features/build/sandbox/image/Dockerfile ;;
*) dockerfile="" ;;
esac
if [ -n "${dockerfile}" ]; then
jq --arg uri "${dockerfile}" '(.runs[]?.results[]?.locations[]?.physicalLocation.artifactLocation.uri) |= $uri' \
"image-audit-${component}.sarif" > "image-audit-${component}.sarif.tmp"
mv "image-audit-${component}.sarif.tmp" "image-audit-${component}.sarif"
fi
done
exit "${status}"
# Rolling main-HEAD builds map to refs/heads/main so the Security tab stays
# authoritative for main; test runs upload against their own ref. Release
# tags sit on older commits, so they gate without uploading.
- name: Upload the web image audit to the GitHub Security tab
id: upload-web
continue-on-error: true # reporting never blocks a release; the gate's verdict is the step above
if: >-
always() &&
((env.EDGE_TAG == 'true' && needs.determine-builds.outputs.is-test-run != 'true') ||
needs.determine-builds.outputs.is-test-run == 'true')
uses: github/codeql-action/upload-sarif@ba454b8ab46733eb6145342877cd148270bb77ab
with:
sarif_file: image-audit-web.sarif
category: image-audit-web
ref: ${{ needs.determine-builds.outputs.is-test-run == 'true' && github.ref || 'refs/heads/main' }}
sha: ${{ github.sha }}
- name: Upload the model-server image audit to the GitHub Security tab
id: upload-model-server
continue-on-error: true # reporting never blocks a release; the gate's verdict is the step above
if: >-
always() &&
((env.EDGE_TAG == 'true' && needs.determine-builds.outputs.is-test-run != 'true') ||
needs.determine-builds.outputs.is-test-run == 'true')
uses: github/codeql-action/upload-sarif@ba454b8ab46733eb6145342877cd148270bb77ab
with:
sarif_file: image-audit-model-server.sarif
category: image-audit-model-server
ref: ${{ needs.determine-builds.outputs.is-test-run == 'true' && github.ref || 'refs/heads/main' }}
sha: ${{ github.sha }}
- name: Upload the backend image audit to the GitHub Security tab
id: upload-backend
continue-on-error: true # reporting never blocks a release; the gate's verdict is the step above
if: >-
always() &&
((env.EDGE_TAG == 'true' && needs.determine-builds.outputs.is-test-run != 'true') ||
needs.determine-builds.outputs.is-test-run == 'true')
uses: github/codeql-action/upload-sarif@ba454b8ab46733eb6145342877cd148270bb77ab
with:
sarif_file: image-audit-backend.sarif
category: image-audit-backend
ref: ${{ needs.determine-builds.outputs.is-test-run == 'true' && github.ref || 'refs/heads/main' }}
sha: ${{ github.sha }}
- name: Upload the sandbox image audit to the GitHub Security tab
id: upload-sandbox
continue-on-error: true # reporting never blocks a release; the gate's verdict is the step above
if: >-
always() &&
((env.EDGE_TAG == 'true' && needs.determine-builds.outputs.is-test-run != 'true') ||
needs.determine-builds.outputs.is-test-run == 'true')
uses: github/codeql-action/upload-sarif@ba454b8ab46733eb6145342877cd148270bb77ab
with:
sarif_file: image-audit-sandbox.sarif
category: image-audit-sandbox
ref: ${{ needs.determine-builds.outputs.is-test-run == 'true' && github.ref || 'refs/heads/main' }}
sha: ${{ github.sha }}
# A rejected upload leaves the Security tab stale without failing the
# job, so it is called out where the run is read.
- name: Report failed uploads
if: always() && (steps.upload-web.outcome == 'failure' || steps.upload-model-server.outcome == 'failure' || steps.upload-backend.outcome == 'failure' || steps.upload-sandbox.outcome == 'failure')
env:
WEB: ${{ steps.upload-web.outcome }}
MODEL_SERVER: ${{ steps.upload-model-server.outcome }}
BACKEND: ${{ steps.upload-backend.outcome }}
SANDBOX: ${{ steps.upload-sandbox.outcome }}
run: |
failed=""
[ "${WEB}" = failure ] && failed="${failed} web"
[ "${MODEL_SERVER}" = failure ] && failed="${failed} model-server"
[ "${BACKEND}" = failure ] && failed="${failed} backend"
[ "${SANDBOX}" = failure ] && failed="${failed} sandbox"
echo "::warning::The Security tab upload failed for:${failed}. The tab is stale until the next nightly upload succeeds."
echo "Security tab upload failed for:${failed}" >> "${GITHUB_STEP_SUMMARY}"
# Validate that a pushed release tag is versioned correctly, via
# `ods release --check`. A cloud tag must be the tag `ods release cloud`
# would have cut: its commit on origin/main, its base matching the release
# branches, its counter in sequence. Stable (vX.Y.Z) and beta
# (vX.Y.Z-beta.N) tags must sit on their release/vX.Y branch with the
# patch or counter in sequence; a beta must precede its stable release.
#
# Deliberately gated on the raw ref name, not on computed tag-type outputs:
# a tag that is neither a valid cloud nor stable tag fails loudly here
# instead of being skipped by a wrongly computed output.
check-version-tag:
runs-on: ubuntu-slim
timeout-minutes: 10
if: ${{ !startsWith(github.ref_name, 'nightly-latest') && github.ref_name != 'edge' && github.event_name != 'workflow_dispatch' }}
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # ratchet:actions/checkout@v6
with:
persist-credentials: false
fetch-depth: 0
- name: Setup uv
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # ratchet:astral-sh/setup-uv@v10.0.1
with:
version: "0.11.25"
# NOTE: This isn't caching much and zizmor suggests this could be poisoned, so disable.
enable-cache: false
- name: Install ods from uv.lock
run: uv sync --frozen --no-install-project --only-group ods
- name: Validate the tag is versioned correctly
run: |
uv run --no-sync ods release --check --ref "$GITHUB_REF_NAME"
notify-slack-on-tag-check-failure:
needs:
- check-version-tag
if: always() && needs.check-version-tag.result == 'failure' && github.event_name != 'workflow_dispatch'
runs-on: ubuntu-slim
timeout-minutes: 10
environment: release
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # ratchet:actions/checkout@v6
with:
persist-credentials: false
- name: Send Slack notification
uses: ./.github/actions/slack-notify
with:
webhook-url: ${{ secrets.MONITOR_DEPLOYMENTS_WEBHOOK }}
failed-jobs: "• check-version-tag"
title: "🚨 Version Tag Check Failed"
ref-name: ${{ github.ref_name }}
# Create GitHub release first, before desktop builds start.
# This ensures all desktop matrix jobs upload to the same release instead of
# racing to create duplicate releases.
create-release:
needs:
- determine-builds
- audit-gate
if: needs.determine-builds.outputs.build-desktop == 'true'
runs-on: ubuntu-slim
timeout-minutes: 10
permissions:
contents: write
outputs:
release-id: ${{ steps.create-release.outputs.id }}
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # ratchet:actions/checkout@v6
with:
persist-credentials: false
- name: Determine release tag
id: release-tag
env:
IS_TEST_RUN: ${{ needs.determine-builds.outputs.is-test-run }}
SHORT_SHA: ${{ needs.determine-builds.outputs.short-sha }}
run: |
if [ "${IS_TEST_RUN}" == "true" ]; then
echo "tag=v0.0.0-dev+${SHORT_SHA}" >> "$GITHUB_OUTPUT"
else
echo "tag=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT"
fi
- name: Create GitHub Release
id: create-release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ steps.release-tag.outputs.tag }}
run: |
# Look up an existing release (including drafts) by tag name.
#
# NOTE: a draft release does not create the underlying git tag until
# it is published, so `GET /releases/tags/{tag}` (and
# `gh release view <tag>`) returns 404 for drafts. We must list
# releases instead — that endpoint DOES include drafts — and match
# on tag_name.
get_release_id() {
gh api --paginate "repos/${GITHUB_REPOSITORY}/releases" \
--jq ".[] | select(.tag_name == \"${TAG}\") | .id" | head -n1
}
release_id=$(get_release_id)
if [ -z "$release_id" ]; then
release_id=$(gh api --method POST "repos/${GITHUB_REPOSITORY}/releases" \
-f tag_name="$TAG" \
-f name="$TAG" \
-f body="See the assets to download this version and install." \
-F draft=true \
--jq '.id')
fi
if [ -z "$release_id" ]; then
echo "::error::Failed to resolve release id for tag ${TAG}"
exit 1
fi
echo "id=${release_id}" >> "$GITHUB_OUTPUT"
build-desktop:
needs:
- determine-builds
- create-release
if: needs.determine-builds.outputs.build-desktop == 'true'
permissions:
id-token: write
contents: write
actions: read
strategy:
fail-fast: false
matrix:
include:
- platform: "macos-latest" # Build a universal image for macOS.
args: "--target universal-apple-darwin"
rust-targets: "aarch64-apple-darwin,x86_64-apple-darwin"
- platform: "ubuntu-24.04"
args: "--bundles deb,rpm"
- platform: "ubuntu-24.04-arm" # Only available in public repos.
args: "--bundles deb,rpm"
- platform: "windows-latest"
args: ""
# Cross-compiled Windows ARM64 build. Only the NSIS bundle supports
# ARM64 (WiX/MSI does not), hence --bundles nsis.
- platform: "windows-latest"
args: "--target aarch64-pc-windows-msvc --bundles nsis"
rust-targets: "aarch64-pc-windows-msvc"
runs-on: ${{ matrix.platform }}
timeout-minutes: 90
environment: release
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # ratchet:actions/checkout@v7.0.1
with:
# NOTE: persist-credentials is needed for tauri-action to upload assets to GitHub releases.
persist-credentials: true # zizmor: ignore[artipacked]
- name: Configure AWS credentials
if: startsWith(matrix.platform, 'macos-')
uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c
with:
role-to-assume: ${{ secrets.AWS_OIDC_ROLE_ARN }}
aws-region: us-east-2
- name: Get AWS Secrets
if: startsWith(matrix.platform, 'macos-')
uses: aws-actions/aws-secretsmanager-get-secrets@2cb1a461cbd4865ac4299648312e4704c646cd53
with:
secret-ids: |
APPLE_ID, deploy/apple-id
APPLE_PASSWORD, deploy/apple-password
APPLE_CERTIFICATE, deploy/apple-certificate
APPLE_CERTIFICATE_PASSWORD, deploy/apple-certificate-password
KEYCHAIN_PASSWORD, deploy/keychain-password
APPLE_TEAM_ID, deploy/apple-team-id
parse-json-secrets: true
- name: install dependencies (ubuntu only)
if: startsWith(matrix.platform, 'ubuntu-')
run: |
sudo apt-get update
sudo apt-get install -y \
build-essential \
libglib2.0-dev \
libgirepository1.0-dev \
libgtk-3-dev \
libjavascriptcoregtk-4.1-dev \
libwebkit2gtk-4.1-dev \
libayatana-appindicator3-dev \
gobject-introspection \
pkg-config \
curl \
xdg-utils
- name: Setup Bun
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # ratchet:oven-sh/setup-bun@v2 # zizmor: ignore[cache-poisoning]
with:
bun-version: "1.3.13"
- name: install Rust stable
uses: dtolnay/rust-toolchain@6d9817901c499d6b02debbb57edb38d33daa680b # zizmor: ignore[impostor-commit]
with:
# Extra targets for jobs that cross-compile (macOS universal, Windows ARM64).
# Jobs building only for the host architecture leave this empty.
targets: ${{ matrix.rust-targets || '' }}
- name: install frontend dependencies
run: bun install --frozen-lockfile
- name: Inject version (Unix)
if: runner.os != 'Windows'
working-directory: ./desktop
env:
SHORT_SHA: ${{ needs.determine-builds.outputs.short-sha }}
IS_TEST_RUN: ${{ needs.determine-builds.outputs.is-test-run }}
run: |
if [ "${IS_TEST_RUN}" == "true" ]; then
VERSION="0.0.0-dev+${SHORT_SHA}"
else
VERSION="${GITHUB_REF_NAME#v}"
fi
echo "Injecting version: $VERSION"
# Update Cargo.toml
sed "s/^version = .*/version = \"$VERSION\"/" src-tauri/Cargo.toml > src-tauri/Cargo.toml.tmp
mv src-tauri/Cargo.toml.tmp src-tauri/Cargo.toml
# Update tauri.conf.json
jq --arg v "$VERSION" '.version = $v' src-tauri/tauri.conf.json > src-tauri/tauri.conf.json.tmp
mv src-tauri/tauri.conf.json.tmp src-tauri/tauri.conf.json
# Update package.json
jq --arg v "$VERSION" '.version = $v' package.json > package.json.tmp
mv package.json.tmp package.json
echo "Versions set to: $VERSION"
- name: Inject version (Windows)
if: runner.os == 'Windows'
working-directory: ./desktop
shell: pwsh
env:
IS_TEST_RUN: ${{ needs.determine-builds.outputs.is-test-run }}
run: |
# Windows MSI requires numeric-only build metadata, so we skip the SHA suffix
if ($env:IS_TEST_RUN -eq "true") {
$VERSION = "0.0.0"
} else {
# Strip 'v' prefix and any pre-release suffix (e.g., -beta.13) for MSI compatibility
$VERSION = "$env:GITHUB_REF_NAME" -replace '^v', '' -replace '-.*$', ''
}
Write-Host "Injecting version: $VERSION"
# Update Cargo.toml
$cargo = Get-Content src-tauri/Cargo.toml -Raw
$cargo = $cargo -replace '(?m)^version = .*', "version = `"$VERSION`""
Set-Content src-tauri/Cargo.toml $cargo -NoNewline
# Update tauri.conf.json
$json = Get-Content src-tauri/tauri.conf.json | ConvertFrom-Json
$json.version = $VERSION
$json | ConvertTo-Json -Depth 100 | Set-Content src-tauri/tauri.conf.json
# Update package.json
$pkg = Get-Content package.json | ConvertFrom-Json
$pkg.version = $VERSION
$pkg | ConvertTo-Json -Depth 100 | Set-Content package.json
Write-Host "Versions set to: $VERSION"
- name: Import Apple Developer Certificate
if: startsWith(matrix.platform, 'macos-')
run: |
echo $APPLE_CERTIFICATE | base64 --decode > certificate.p12
security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security default-keychain -s build.keychain
security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain
security set-keychain-settings -t 3600 -u build.keychain
security import certificate.p12 -k build.keychain -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign
security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" build.keychain
security find-identity -v -p codesigning build.keychain
- name: Verify Certificate
if: startsWith(matrix.platform, 'macos-')
run: |
CERT_INFO=$(security find-identity -v -p codesigning build.keychain | grep -E "(Developer ID Application|Apple Distribution|Apple Development)" | head -n 1)
CERT_ID=$(echo "$CERT_INFO" | awk -F'"' '{print $2}')
echo "CERT_ID=$CERT_ID" >> $GITHUB_ENV
echo "Certificate imported."
- uses: tauri-apps/tauri-action@1deb371b0cd8bd54025b384f1cd735e725c4060f # ratchet:tauri-apps/tauri-action@action-v1.0.0
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
APPLE_ID: ${{ env.APPLE_ID }}
APPLE_PASSWORD: ${{ env.APPLE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ env.CERT_ID }}
APPLE_TEAM_ID: ${{ env.APPLE_TEAM_ID }}
with:
projectPath: desktop
# Use the release created by the create-release job to avoid race conditions
# when multiple matrix jobs try to create/update the same release simultaneously
releaseId: ${{ needs.create-release.outputs.release-id }}
releaseAssetNamePattern: "[name]_[arch][ext]"
args: ${{ matrix.args }}
build-web-amd64:
needs:
- determine-builds
- audit-gate
if: needs.determine-builds.outputs.build-web == 'true'
runs-on:
- runs-on
- runner=4cpu-linux-x64
- run-id=${{ github.run_id }}-web-amd64
- extras=ecr-cache
timeout-minutes: 90
environment: release
outputs:
digest: ${{ steps.build.outputs.digest }}
env:
REGISTRY_IMAGE: onyxdotapp/onyx-web-server
steps:
- uses: runs-on/action@4e5f72399b6b17f2e79c511c1b38a315a64d22dc
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # ratchet:actions/checkout@v6
with:
persist-credentials: false
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c
with:
role-to-assume: ${{ secrets.AWS_OIDC_ROLE_ARN }}
aws-region: us-east-2
- name: Get AWS Secrets
uses: aws-actions/aws-secretsmanager-get-secrets@2cb1a461cbd4865ac4299648312e4704c646cd53
with:
secret-ids: |
DOCKER_USERNAME, deploy/docker-username
DOCKER_TOKEN, deploy/docker-token
parse-json-secrets: true
- name: Docker meta
id: meta
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302
with:
images: ${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }}
flavor: |
latest=false
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # ratchet:docker/setup-buildx-action@v4
- name: Login to Docker Hub
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4
with:
username: ${{ env.DOCKER_USERNAME }}
password: ${{ env.DOCKER_TOKEN }}
# web/Dockerfile defaults to the public Node bases. Release builds ship on the
# hardened DHI equivalents, passed as build args below.
- name: Resolve Docker Hardened Image bases
uses: ./.github/actions/dhi-base-images
with:
docker-username: ${{ env.DOCKER_USERNAME }}
docker-token: ${{ env.DOCKER_TOKEN }}
- name: Build and push AMD64
id: build
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a
with:
context: ./web
file: ./web/Dockerfile
platforms: linux/amd64
labels: ${{ steps.meta.outputs.labels }}
build-args: |
ONYX_VERSION=${{ github.ref_name }}
${{ env.DHI_NODE_BUILD_ARGS }}
NODE_OPTIONS=--max-old-space-size=8192
cache-from: |
type=registry,ref=${{ env.REGISTRY_IMAGE }}:edge
type=registry,ref=${{ env.REGISTRY_IMAGE }}:latest
cache-to: type=inline
outputs: type=image,name=${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true
no-cache: ${{ vars.DOCKER_NO_CACHE == 'true' }}
build-web-arm64:
needs:
- determine-builds
- audit-gate
if: needs.determine-builds.outputs.build-web == 'true'
runs-on:
- runs-on
- runner=4cpu-linux-arm64
- run-id=${{ github.run_id }}-web-arm64
- extras=ecr-cache
timeout-minutes: 90
environment: release
outputs:
digest: ${{ steps.build.outputs.digest }}
env:
REGISTRY_IMAGE: onyxdotapp/onyx-web-server
steps:
- uses: runs-on/action@4e5f72399b6b17f2e79c511c1b38a315a64d22dc
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # ratchet:actions/checkout@v6
with:
persist-credentials: false
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c
with:
role-to-assume: ${{ secrets.AWS_OIDC_ROLE_ARN }}
aws-region: us-east-2
- name: Get AWS Secrets
uses: aws-actions/aws-secretsmanager-get-secrets@2cb1a461cbd4865ac4299648312e4704c646cd53
with:
secret-ids: |
DOCKER_USERNAME, deploy/docker-username
DOCKER_TOKEN, deploy/docker-token
parse-json-secrets: true
- name: Docker meta
id: meta
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302
with:
images: ${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }}
flavor: |
latest=false
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # ratchet:docker/setup-buildx-action@v4
- name: Login to Docker Hub
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4
with:
username: ${{ env.DOCKER_USERNAME }}
password: ${{ env.DOCKER_TOKEN }}
# web/Dockerfile defaults to the public Node bases. Release builds ship on the
# hardened DHI equivalents, passed as build args below.
- name: Resolve Docker Hardened Image bases
uses: ./.github/actions/dhi-base-images
with:
docker-username: ${{ env.DOCKER_USERNAME }}
docker-token: ${{ env.DOCKER_TOKEN }}
- name: Build and push ARM64
id: build
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a
with:
context: ./web
file: ./web/Dockerfile
platforms: linux/arm64
labels: ${{ steps.meta.outputs.labels }}
build-args: |
ONYX_VERSION=${{ github.ref_name }}
${{ env.DHI_NODE_BUILD_ARGS }}
NODE_OPTIONS=--max-old-space-size=8192
cache-from: |
type=registry,ref=${{ env.REGISTRY_IMAGE }}:edge
type=registry,ref=${{ env.REGISTRY_IMAGE }}:latest
cache-to: type=inline
outputs: type=image,name=${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true
no-cache: ${{ vars.DOCKER_NO_CACHE == 'true' }}
merge-web:
needs:
- determine-builds
- build-web-amd64
- build-web-arm64
if: always() && needs.build-web-amd64.result == 'success' && needs.build-web-arm64.result == 'success'
runs-on:
- runs-on
- runner=2cpu-linux-x64
- run-id=${{ github.run_id }}-merge-web
- extras=ecr-cache
timeout-minutes: 90
environment: release
env:
REGISTRY_IMAGE: onyxdotapp/onyx-web-server
steps:
- uses: runs-on/action@4e5f72399b6b17f2e79c511c1b38a315a64d22dc
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c
with:
role-to-assume: ${{ secrets.AWS_OIDC_ROLE_ARN }}
aws-region: us-east-2
- name: Get AWS Secrets
uses: aws-actions/aws-secretsmanager-get-secrets@2cb1a461cbd4865ac4299648312e4704c646cd53
with:
secret-ids: |
DOCKER_USERNAME, deploy/docker-username
DOCKER_TOKEN, deploy/docker-token
parse-json-secrets: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # ratchet:docker/setup-buildx-action@v4
- name: Login to Docker Hub
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4
with:
username: ${{ env.DOCKER_USERNAME }}
password: ${{ env.DOCKER_TOKEN }}
- name: Docker meta
id: meta
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302
with:
images: ${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }}
flavor: |
latest=false
tags: |
type=raw,value=${{ needs.determine-builds.outputs.is-test-run == 'true' && format('web-{0}', needs.determine-builds.outputs.sanitized-tag) || github.ref_name }}
type=raw,value=${{ needs.determine-builds.outputs.is-test-run != 'true' && needs.determine-builds.outputs.is-latest == 'true' && 'latest' || '' }}
type=raw,value=${{ needs.determine-builds.outputs.is-test-run != 'true' && env.EDGE_TAG == 'true' && 'edge' || '' }}
type=raw,value=${{ needs.determine-builds.outputs.is-test-run != 'true' && needs.determine-builds.outputs.is-beta == 'true' && 'beta' || '' }}
type=semver,pattern={{version}},enable=${{ needs.determine-builds.outputs.is-stable == 'true' }}
type=semver,pattern={{major}}.{{minor}},enable=${{ needs.determine-builds.outputs.is-stable == 'true' }}
type=semver,pattern={{major}},enable=${{ needs.determine-builds.outputs.is-stable == 'true' }}
# Same tag set with a -dev suffix. Only the backend has a genuinely different dev image
# (runtime + debugging tools); the web -dev tags point at the same manifest as their
# plain counterparts so that one version string (IMAGE_TAG / helm global.version) selects
# a whole deployment. The suffix is spelled out per tag rather than via flavor `suffix=`
# so that disabled (empty) raw tags stay empty instead of degenerating to a bare "-dev".
- name: Docker meta (dev)
id: meta-dev
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302
with:
images: ${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }}
flavor: |
latest=false
tags: |
type=raw,value=${{ needs.determine-builds.outputs.is-test-run == 'true' && format('web-{0}-dev', needs.determine-builds.outputs.sanitized-tag) || format('{0}-dev', github.ref_name) }}
type=raw,value=${{ needs.determine-builds.outputs.is-test-run != 'true' && needs.determine-builds.outputs.is-latest == 'true' && 'latest-dev' || '' }}
type=raw,value=${{ needs.determine-builds.outputs.is-test-run != 'true' && env.EDGE_TAG == 'true' && 'edge-dev' || '' }}
type=raw,value=${{ needs.determine-builds.outputs.is-test-run != 'true' && needs.determine-builds.outputs.is-beta == 'true' && 'beta-dev' || '' }}
type=semver,pattern={{version}}-dev,enable=${{ needs.determine-builds.outputs.is-stable == 'true' }}
type=semver,pattern={{major}}.{{minor}}-dev,enable=${{ needs.determine-builds.outputs.is-stable == 'true' }}
type=semver,pattern={{major}}-dev,enable=${{ needs.determine-builds.outputs.is-stable == 'true' }}
- name: Create and push manifest
env:
IMAGE_REPO: ${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }}
AMD64_DIGEST: ${{ needs.build-web-amd64.outputs.digest }}
ARM64_DIGEST: ${{ needs.build-web-arm64.outputs.digest }}
META_TAGS: ${{ steps.meta.outputs.tags }}
META_DEV_TAGS: ${{ steps.meta-dev.outputs.tags }}
run: |
IMAGES="${IMAGE_REPO}@${AMD64_DIGEST} ${IMAGE_REPO}@${ARM64_DIGEST}"
docker buildx imagetools create \
$(printf '%s\n%s\n' "${META_TAGS}" "${META_DEV_TAGS}" | sed '/^$/d' | xargs -I {} echo -t {}) \
$IMAGES
build-web-cloud-amd64:
needs:
- determine-builds
- audit-gate
if: needs.determine-builds.outputs.build-web-cloud == 'true'
runs-on:
- runs-on
- runner=4cpu-linux-x64
- run-id=${{ github.run_id }}-web-cloud-amd64
- extras=ecr-cache
timeout-minutes: 90
environment: release
outputs:
digest: ${{ steps.build.outputs.digest }}
env:
REGISTRY_IMAGE: onyxdotapp/onyx-web-server
steps:
- uses: runs-on/action@4e5f72399b6b17f2e79c511c1b38a315a64d22dc
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # ratchet:actions/checkout@v6
with:
persist-credentials: false
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c
with:
role-to-assume: ${{ secrets.AWS_OIDC_ROLE_ARN }}
aws-region: us-east-2
- name: Get AWS Secrets
uses: aws-actions/aws-secretsmanager-get-secrets@2cb1a461cbd4865ac4299648312e4704c646cd53
with:
secret-ids: |
DOCKER_USERNAME, deploy/docker-username
DOCKER_TOKEN, deploy/docker-token
parse-json-secrets: true
- name: Docker meta
id: meta
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302
with:
images: ${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }}
flavor: |
latest=false
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # ratchet:docker/setup-buildx-action@v4
- name: Login to Docker Hub
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4
with:
username: ${{ env.DOCKER_USERNAME }}
password: ${{ env.DOCKER_TOKEN }}
# web/Dockerfile defaults to the public Node bases. Release builds ship on the
# hardened DHI equivalents, passed as build args below.
- name: Resolve Docker Hardened Image bases
uses: ./.github/actions/dhi-base-images
with:
docker-username: ${{ env.DOCKER_USERNAME }}
docker-token: ${{ env.DOCKER_TOKEN }}
- name: Build and push AMD64
id: build
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a
with:
context: ./web
file: ./web/Dockerfile
platforms: linux/amd64
labels: ${{ steps.meta.outputs.labels }}
build-args: |
ONYX_VERSION=${{ github.ref_name }}
${{ env.DHI_NODE_BUILD_ARGS }}
NEXT_PUBLIC_CLOUD_ENABLED=true
WEB_FRAME_PROTECTION_ENABLED=false
NEXT_PUBLIC_POSTHOG_KEY=${{ secrets.POSTHOG_KEY }}
NEXT_PUBLIC_POSTHOG_HOST=${{ secrets.POSTHOG_HOST }}
NEXT_PUBLIC_SENTRY_DSN=${{ secrets.SENTRY_DSN }}
NEXT_PUBLIC_RECAPTCHA_SITE_KEY=${{ vars.NEXT_PUBLIC_RECAPTCHA_SITE_KEY }}
NEXT_PUBLIC_GTM_ENABLED=true
NEXT_PUBLIC_FORGOT_PASSWORD_ENABLED=true
NEXT_PUBLIC_INCLUDE_ERROR_POPUP_SUPPORT_LINK=true
NODE_OPTIONS=--max-old-space-size=8192
SENTRY_RELEASE=${{ github.sha }}
secrets: |
sentry_auth_token=${{ secrets.SENTRY_AUTH_TOKEN }}
cache-from: type=registry,ref=${{ env.REGISTRY_IMAGE }}:latest
cache-to: type=inline
outputs: type=image,name=${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true
no-cache: ${{ vars.DOCKER_NO_CACHE == 'true' }}
build-web-cloud-arm64:
needs:
- determine-builds
- audit-gate
if: needs.determine-builds.outputs.build-web-cloud == 'true'
runs-on:
- runs-on
- runner=4cpu-linux-arm64
- run-id=${{ github.run_id }}-web-cloud-arm64
- extras=ecr-cache
timeout-minutes: 90
environment: release
outputs:
digest: ${{ steps.build.outputs.digest }}
env:
REGISTRY_IMAGE: onyxdotapp/onyx-web-server
steps:
- uses: runs-on/action@4e5f72399b6b17f2e79c511c1b38a315a64d22dc
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # ratchet:actions/checkout@v6
with:
persist-credentials: false
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c
with:
role-to-assume: ${{ secrets.AWS_OIDC_ROLE_ARN }}
aws-region: us-east-2
- name: Get AWS Secrets
uses: aws-actions/aws-secretsmanager-get-secrets@2cb1a461cbd4865ac4299648312e4704c646cd53
with:
secret-ids: |
DOCKER_USERNAME, deploy/docker-username
DOCKER_TOKEN, deploy/docker-token
parse-json-secrets: true
- name: Docker meta
id: meta
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302
with:
images: ${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }}
flavor: |
latest=false
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # ratchet:docker/setup-buildx-action@v4
- name: Login to Docker Hub
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4
with:
username: ${{ env.DOCKER_USERNAME }}
password: ${{ env.DOCKER_TOKEN }}
# web/Dockerfile defaults to the public Node bases. Release builds ship on the
# hardened DHI equivalents, passed as build args below.
- name: Resolve Docker Hardened Image bases
uses: ./.github/actions/dhi-base-images
with:
docker-username: ${{ env.DOCKER_USERNAME }}
docker-token: ${{ env.DOCKER_TOKEN }}
- name: Build and push ARM64
id: build
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a
with:
context: ./web
file: ./web/Dockerfile
platforms: linux/arm64
labels: ${{ steps.meta.outputs.labels }}
build-args: |
ONYX_VERSION=${{ github.ref_name }}
${{ env.DHI_NODE_BUILD_ARGS }}
NEXT_PUBLIC_CLOUD_ENABLED=true
WEB_FRAME_PROTECTION_ENABLED=false
NEXT_PUBLIC_POSTHOG_KEY=${{ secrets.POSTHOG_KEY }}
NEXT_PUBLIC_POSTHOG_HOST=${{ secrets.POSTHOG_HOST }}
NEXT_PUBLIC_SENTRY_DSN=${{ secrets.SENTRY_DSN }}
NEXT_PUBLIC_RECAPTCHA_SITE_KEY=${{ vars.NEXT_PUBLIC_RECAPTCHA_SITE_KEY }}
NEXT_PUBLIC_GTM_ENABLED=true
NEXT_PUBLIC_FORGOT_PASSWORD_ENABLED=true
NEXT_PUBLIC_INCLUDE_ERROR_POPUP_SUPPORT_LINK=true
NODE_OPTIONS=--max-old-space-size=8192
SENTRY_RELEASE=${{ github.sha }}
secrets: |
sentry_auth_token=${{ secrets.SENTRY_AUTH_TOKEN }}
cache-from: type=registry,ref=${{ env.REGISTRY_IMAGE }}:latest
cache-to: type=inline
outputs: type=image,name=${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true
no-cache: ${{ vars.DOCKER_NO_CACHE == 'true' }}
merge-web-cloud:
needs:
- determine-builds
- build-web-cloud-amd64
- build-web-cloud-arm64
if: always() && needs.build-web-cloud-amd64.result == 'success' && needs.build-web-cloud-arm64.result == 'success'
runs-on:
- runs-on
- runner=2cpu-linux-x64
- run-id=${{ github.run_id }}-merge-web-cloud
- extras=ecr-cache
timeout-minutes: 90
environment: release
env:
REGISTRY_IMAGE: onyxdotapp/onyx-web-server
steps:
- uses: runs-on/action@4e5f72399b6b17f2e79c511c1b38a315a64d22dc
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c
with:
role-to-assume: ${{ secrets.AWS_OIDC_ROLE_ARN }}
aws-region: us-east-2
- name: Get AWS Secrets
uses: aws-actions/aws-secretsmanager-get-secrets@2cb1a461cbd4865ac4299648312e4704c646cd53
with:
secret-ids: |
DOCKER_USERNAME, deploy/docker-username
DOCKER_TOKEN, deploy/docker-token
parse-json-secrets: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # ratchet:docker/setup-buildx-action@v4
- name: Login to Docker Hub
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4
with:
username: ${{ env.DOCKER_USERNAME }}
password: ${{ env.DOCKER_TOKEN }}
- name: Docker meta
id: meta
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302
with:
images: ${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }}
flavor: |
latest=false
tags: |
type=raw,value=${{ needs.determine-builds.outputs.is-test-run == 'true' && format('web-cloud-{0}', needs.determine-builds.outputs.sanitized-tag) || github.ref_name }}
# -dev twin of the tag above, pointing at the same manifest — see the merge-web dev note.
# Cloud tags build the web image here rather than in merge-web, so without this a cloud
# deployment couldn't set a single -dev version across all of its images.
- name: Docker meta (dev)
id: meta-dev
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302
with:
images: ${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }}
flavor: |
latest=false
tags: |
type=raw,value=${{ needs.determine-builds.outputs.is-test-run == 'true' && format('web-cloud-{0}-dev', needs.determine-builds.outputs.sanitized-tag) || format('{0}-dev', github.ref_name) }}
- name: Create and push manifest
env:
IMAGE_REPO: ${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }}
AMD64_DIGEST: ${{ needs.build-web-cloud-amd64.outputs.digest }}
ARM64_DIGEST: ${{ needs.build-web-cloud-arm64.outputs.digest }}
META_TAGS: ${{ steps.meta.outputs.tags }}
META_DEV_TAGS: ${{ steps.meta-dev.outputs.tags }}
run: |
IMAGES="${IMAGE_REPO}@${AMD64_DIGEST} ${IMAGE_REPO}@${ARM64_DIGEST}"
docker buildx imagetools create \
$(printf '%s\n%s\n' "${META_TAGS}" "${META_DEV_TAGS}" | sed '/^$/d' | xargs -I {} echo -t {}) \
$IMAGES
build-backend-amd64:
needs:
- determine-builds
- audit-gate
if: needs.determine-builds.outputs.build-backend == 'true'
runs-on:
- runs-on
- runner=2cpu-linux-x64
- run-id=${{ github.run_id }}-backend-amd64
- extras=ecr-cache
timeout-minutes: 90
environment: release
outputs:
digest: ${{ steps.build.outputs.digest }}
digest-dev: ${{ steps.build-dev.outputs.digest }}
env:
REGISTRY_IMAGE: onyxdotapp/onyx-backend
steps:
- uses: runs-on/action@4e5f72399b6b17f2e79c511c1b38a315a64d22dc
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # ratchet:actions/checkout@v6
with:
persist-credentials: false
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c
with:
role-to-assume: ${{ secrets.AWS_OIDC_ROLE_ARN }}
aws-region: us-east-2
- name: Get AWS Secrets
uses: aws-actions/aws-secretsmanager-get-secrets@2cb1a461cbd4865ac4299648312e4704c646cd53
with:
secret-ids: |
DOCKER_USERNAME, deploy/docker-username
DOCKER_TOKEN, deploy/docker-token
parse-json-secrets: true
- name: Docker meta
id: meta
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302
with:
images: ${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }}
flavor: |
latest=false
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # ratchet:docker/setup-buildx-action@v4
- name: Login to Docker Hub
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4
with:
username: ${{ env.DOCKER_USERNAME }}
password: ${{ env.DOCKER_TOKEN }}
- name: Build and push AMD64
id: build
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a
with:
context: ./backend
file: ./backend/Dockerfile
target: runtime
platforms: linux/amd64
labels: ${{ steps.meta.outputs.labels }}
build-args: |
ONYX_VERSION=${{ github.ref_name }}
cache-from: |
type=registry,ref=${{ env.REGISTRY_IMAGE }}:edge
type=registry,ref=${{ env.REGISTRY_IMAGE }}:latest
cache-to: type=inline
outputs: type=image,name=${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true
no-cache: ${{ vars.DOCKER_NO_CACHE == 'true' }}
# Dev variant: runtime + interactive debugging tools, published with a -dev tag suffix by
# merge-backend. Reuses the runtime stage from the build above via the local BuildKit
# cache, so this only builds and pushes the extra apt layer.
- name: Build and push AMD64 dev
id: build-dev
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a
with:
context: ./backend
file: ./backend/Dockerfile
target: dev
platforms: linux/amd64
labels: ${{ steps.meta.outputs.labels }}
build-args: |
ONYX_VERSION=${{ github.ref_name }}
cache-from: |
type=registry,ref=${{ env.REGISTRY_IMAGE }}:edge-dev
type=registry,ref=${{ env.REGISTRY_IMAGE }}:edge
type=registry,ref=${{ env.REGISTRY_IMAGE }}:latest
cache-to: type=inline
outputs: type=image,name=${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true
no-cache: ${{ vars.DOCKER_NO_CACHE == 'true' }}
build-backend-arm64:
needs:
- determine-builds
- audit-gate
if: needs.determine-builds.outputs.build-backend == 'true'
runs-on:
- runs-on
- runner=2cpu-linux-arm64
- run-id=${{ github.run_id }}-backend-arm64
- extras=ecr-cache
timeout-minutes: 90
environment: release
outputs:
digest: ${{ steps.build.outputs.digest }}
digest-dev: ${{ steps.build-dev.outputs.digest }}
env:
REGISTRY_IMAGE: onyxdotapp/onyx-backend
steps:
- uses: runs-on/action@4e5f72399b6b17f2e79c511c1b38a315a64d22dc
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # ratchet:actions/checkout@v6
with:
persist-credentials: false
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c
with:
role-to-assume: ${{ secrets.AWS_OIDC_ROLE_ARN }}
aws-region: us-east-2
- name: Get AWS Secrets
uses: aws-actions/aws-secretsmanager-get-secrets@2cb1a461cbd4865ac4299648312e4704c646cd53
with:
secret-ids: |
DOCKER_USERNAME, deploy/docker-username
DOCKER_TOKEN, deploy/docker-token
parse-json-secrets: true
- name: Docker meta
id: meta
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302
with:
images: ${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }}
flavor: |
latest=false
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # ratchet:docker/setup-buildx-action@v4
- name: Login to Docker Hub
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4
with:
username: ${{ env.DOCKER_USERNAME }}
password: ${{ env.DOCKER_TOKEN }}
- name: Build and push ARM64
id: build
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a
with:
context: ./backend
file: ./backend/Dockerfile
target: runtime
platforms: linux/arm64
labels: ${{ steps.meta.outputs.labels }}
build-args: |
ONYX_VERSION=${{ github.ref_name }}
cache-from: |
type=registry,ref=${{ env.REGISTRY_IMAGE }}:edge
type=registry,ref=${{ env.REGISTRY_IMAGE }}:latest
cache-to: type=inline
outputs: type=image,name=${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true
no-cache: ${{ vars.DOCKER_NO_CACHE == 'true' }}
# Dev variant: runtime + interactive debugging tools, published with a -dev tag suffix by
# merge-backend. Reuses the runtime stage from the build above via the local BuildKit
# cache, so this only builds and pushes the extra apt layer.
- name: Build and push ARM64 dev
id: build-dev
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a
with:
context: ./backend
file: ./backend/Dockerfile
target: dev
platforms: linux/arm64
labels: ${{ steps.meta.outputs.labels }}
build-args: |
ONYX_VERSION=${{ github.ref_name }}
cache-from: |
type=registry,ref=${{ env.REGISTRY_IMAGE }}:edge-dev
type=registry,ref=${{ env.REGISTRY_IMAGE }}:edge
type=registry,ref=${{ env.REGISTRY_IMAGE }}:latest
cache-to: type=inline
outputs: type=image,name=${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true
no-cache: ${{ vars.DOCKER_NO_CACHE == 'true' }}
merge-backend:
needs:
- determine-builds
- build-backend-amd64
- build-backend-arm64
if: always() && needs.build-backend-amd64.result == 'success' && needs.build-backend-arm64.result == 'success'
runs-on:
- runs-on
- runner=2cpu-linux-x64
- run-id=${{ github.run_id }}-merge-backend
- extras=ecr-cache
timeout-minutes: 90
environment: release
env:
REGISTRY_IMAGE: onyxdotapp/onyx-backend
steps:
- uses: runs-on/action@4e5f72399b6b17f2e79c511c1b38a315a64d22dc
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c
with:
role-to-assume: ${{ secrets.AWS_OIDC_ROLE_ARN }}
aws-region: us-east-2
- name: Get AWS Secrets
uses: aws-actions/aws-secretsmanager-get-secrets@2cb1a461cbd4865ac4299648312e4704c646cd53
with:
secret-ids: |
DOCKER_USERNAME, deploy/docker-username
DOCKER_TOKEN, deploy/docker-token
parse-json-secrets: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # ratchet:docker/setup-buildx-action@v4
- name: Login to Docker Hub
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4
with:
username: ${{ env.DOCKER_USERNAME }}
password: ${{ env.DOCKER_TOKEN }}
- name: Docker meta
id: meta
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302
with:
images: ${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }}
flavor: |
latest=false
tags: |
type=raw,value=${{ needs.determine-builds.outputs.is-test-run == 'true' && format('backend-{0}', needs.determine-builds.outputs.sanitized-tag) || github.ref_name }}
type=raw,value=${{ needs.determine-builds.outputs.is-test-run != 'true' && needs.determine-builds.outputs.is-latest == 'true' && 'latest' || '' }}
type=raw,value=${{ needs.determine-builds.outputs.is-test-run != 'true' && env.EDGE_TAG == 'true' && 'edge' || '' }}
type=raw,value=${{ needs.determine-builds.outputs.is-test-run != 'true' && needs.determine-builds.outputs.is-beta-standalone == 'true' && 'beta' || '' }}
type=semver,pattern={{version}},enable=${{ needs.determine-builds.outputs.is-stable == 'true' }}
type=semver,pattern={{major}}.{{minor}},enable=${{ needs.determine-builds.outputs.is-stable == 'true' }}
type=semver,pattern={{major}},enable=${{ needs.determine-builds.outputs.is-stable == 'true' }}
# Same tag set with a -dev suffix, for the dev image variant (runtime + debugging tools).
# The suffix is spelled out per tag rather than via flavor `suffix=` so that disabled
# (empty) raw tags stay empty instead of degenerating to a bare "-dev" tag.
- name: Docker meta (dev)
id: meta-dev
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302
with:
images: ${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }}
flavor: |
latest=false
tags: |
type=raw,value=${{ needs.determine-builds.outputs.is-test-run == 'true' && format('backend-{0}-dev', needs.determine-builds.outputs.sanitized-tag) || format('{0}-dev', github.ref_name) }}
type=raw,value=${{ needs.determine-builds.outputs.is-test-run != 'true' && needs.determine-builds.outputs.is-latest == 'true' && 'latest-dev' || '' }}
type=raw,value=${{ needs.determine-builds.outputs.is-test-run != 'true' && env.EDGE_TAG == 'true' && 'edge-dev' || '' }}
type=raw,value=${{ needs.determine-builds.outputs.is-test-run != 'true' && needs.determine-builds.outputs.is-beta-standalone == 'true' && 'beta-dev' || '' }}
type=semver,pattern={{version}}-dev,enable=${{ needs.determine-builds.outputs.is-stable == 'true' }}
type=semver,pattern={{major}}.{{minor}}-dev,enable=${{ needs.determine-builds.outputs.is-stable == 'true' }}
type=semver,pattern={{major}}-dev,enable=${{ needs.determine-builds.outputs.is-stable == 'true' }}
- name: Create and push manifest
env:
IMAGE_REPO: ${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }}
AMD64_DIGEST: ${{ needs.build-backend-amd64.outputs.digest }}
ARM64_DIGEST: ${{ needs.build-backend-arm64.outputs.digest }}
META_TAGS: ${{ steps.meta.outputs.tags }}
run: |
IMAGES="${IMAGE_REPO}@${AMD64_DIGEST} ${IMAGE_REPO}@${ARM64_DIGEST}"
docker buildx imagetools create \
$(printf '%s\n' "${META_TAGS}" | xargs -I {} echo -t {}) \
$IMAGES
- name: Create and push dev manifest
env:
IMAGE_REPO: ${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }}
AMD64_DIGEST: ${{ needs.build-backend-amd64.outputs.digest-dev }}
ARM64_DIGEST: ${{ needs.build-backend-arm64.outputs.digest-dev }}
META_TAGS: ${{ steps.meta-dev.outputs.tags }}
run: |
IMAGES="${IMAGE_REPO}@${AMD64_DIGEST} ${IMAGE_REPO}@${ARM64_DIGEST}"
docker buildx imagetools create \
$(printf '%s\n' "${META_TAGS}" | xargs -I {} echo -t {}) \
$IMAGES
build-model-server-amd64:
needs:
- determine-builds
- audit-gate
if: needs.determine-builds.outputs.build-model-server == 'true'
runs-on:
- runs-on
- runner=2cpu-linux-x64
- run-id=${{ github.run_id }}-model-server-amd64
# Spot can place this job on an instance with no local NVMe, which leaves docker on the
# root volume. The ~7gb amd64 venv, copied again by the final stage, needs the headroom.
- volume=60gb
- extras=ecr-cache
timeout-minutes: 90
environment: release
outputs:
digest: ${{ steps.build.outputs.digest }}
env:
REGISTRY_IMAGE: onyxdotapp/onyx-model-server
steps:
- uses: runs-on/action@4e5f72399b6b17f2e79c511c1b38a315a64d22dc
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # ratchet:actions/checkout@v6
with:
persist-credentials: false
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c
with:
role-to-assume: ${{ secrets.AWS_OIDC_ROLE_ARN }}
aws-region: us-east-2
- name: Get AWS Secrets
uses: aws-actions/aws-secretsmanager-get-secrets@2cb1a461cbd4865ac4299648312e4704c646cd53
with:
secret-ids: |
DOCKER_USERNAME, deploy/docker-username
DOCKER_TOKEN, deploy/docker-token
parse-json-secrets: true
- name: Docker meta
id: meta
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302
with:
images: ${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }}
flavor: |
latest=false
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # ratchet:docker/setup-buildx-action@v4
with:
buildkitd-flags: ${{ vars.DOCKER_DEBUG == 'true' && '--debug' || '' }}
- name: Login to Docker Hub
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4
with:
username: ${{ env.DOCKER_USERNAME }}
password: ${{ env.DOCKER_TOKEN }}
# Dockerfile.model_server defaults to the public Python bases. Release builds ship
# on the hardened DHI equivalents, passed as build args below.
- name: Resolve Docker Hardened Image bases
uses: ./.github/actions/dhi-base-images
with:
docker-username: ${{ env.DOCKER_USERNAME }}
docker-token: ${{ env.DOCKER_TOKEN }}
- name: Build and push AMD64
id: build
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a
env:
DEBUG: ${{ vars.DOCKER_DEBUG == 'true' && 1 || 0 }}
with:
context: ./backend
file: ./backend/Dockerfile.model_server
platforms: linux/amd64
labels: ${{ steps.meta.outputs.labels }}
build-args: |
ONYX_VERSION=${{ github.ref_name }}
${{ env.DHI_PYTHON_BUILD_ARGS }}
cache-from: |
type=registry,ref=${{ env.REGISTRY_IMAGE }}:edge
type=registry,ref=${{ env.REGISTRY_IMAGE }}:latest
cache-to: type=inline
outputs: type=image,name=${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true
no-cache: ${{ env.EDGE_TAG != 'true' && vars.MODEL_SERVER_NO_CACHE == 'true' }}
build-model-server-arm64:
needs:
- determine-builds
- audit-gate
if: needs.determine-builds.outputs.build-model-server == 'true'
runs-on:
- runs-on
- runner=2cpu-linux-arm64
- run-id=${{ github.run_id }}-model-server-arm64
- volume=60gb
- extras=ecr-cache
timeout-minutes: 90
environment: release
outputs:
digest: ${{ steps.build.outputs.digest }}
env:
REGISTRY_IMAGE: onyxdotapp/onyx-model-server
steps:
- uses: runs-on/action@4e5f72399b6b17f2e79c511c1b38a315a64d22dc
- name: Checkout code
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # ratchet:actions/checkout@v6
with:
persist-credentials: false
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c
with:
role-to-assume: ${{ secrets.AWS_OIDC_ROLE_ARN }}
aws-region: us-east-2
- name: Get AWS Secrets
uses: aws-actions/aws-secretsmanager-get-secrets@2cb1a461cbd4865ac4299648312e4704c646cd53
with:
secret-ids: |
DOCKER_USERNAME, deploy/docker-username
DOCKER_TOKEN, deploy/docker-token
parse-json-secrets: true
- name: Docker meta
id: meta
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302
with:
images: ${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }}
flavor: |
latest=false
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # ratchet:docker/setup-buildx-action@v4
with:
buildkitd-flags: ${{ vars.DOCKER_DEBUG == 'true' && '--debug' || '' }}
- name: Login to Docker Hub
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4
with:
username: ${{ env.DOCKER_USERNAME }}
password: ${{ env.DOCKER_TOKEN }}
# Dockerfile.model_server defaults to the public Python bases. Release builds ship
# on the hardened DHI equivalents, passed as build args below.
- name: Resolve Docker Hardened Image bases
uses: ./.github/actions/dhi-base-images
with:
docker-username: ${{ env.DOCKER_USERNAME }}
docker-token: ${{ env.DOCKER_TOKEN }}
- name: Build and push ARM64
id: build
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a
env:
DEBUG: ${{ vars.DOCKER_DEBUG == 'true' && 1 || 0 }}
with:
context: ./backend
file: ./backend/Dockerfile.model_server
platforms: linux/arm64
labels: ${{ steps.meta.outputs.labels }}
build-args: |
ONYX_VERSION=${{ github.ref_name }}
${{ env.DHI_PYTHON_BUILD_ARGS }}
cache-from: |
type=registry,ref=${{ env.REGISTRY_IMAGE }}:edge
type=registry,ref=${{ env.REGISTRY_IMAGE }}:latest
cache-to: type=inline
outputs: type=image,name=${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true
no-cache: ${{ env.EDGE_TAG != 'true' && vars.MODEL_SERVER_NO_CACHE == 'true' }}
merge-model-server:
needs:
- determine-builds
- build-model-server-amd64
- build-model-server-arm64
if: always() && needs.build-model-server-amd64.result == 'success' && needs.build-model-server-arm64.result == 'success'
runs-on:
- runs-on
- runner=2cpu-linux-x64
- run-id=${{ github.run_id }}-merge-model-server
- extras=ecr-cache
timeout-minutes: 90
environment: release
env:
REGISTRY_IMAGE: onyxdotapp/onyx-model-server
steps:
- uses: runs-on/action@4e5f72399b6b17f2e79c511c1b38a315a64d22dc
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c
with:
role-to-assume: ${{ secrets.AWS_OIDC_ROLE_ARN }}
aws-region: us-east-2
- name: Get AWS Secrets
uses: aws-actions/aws-secretsmanager-get-secrets@2cb1a461cbd4865ac4299648312e4704c646cd53
with:
secret-ids: |
DOCKER_USERNAME, deploy/docker-username
DOCKER_TOKEN, deploy/docker-token
parse-json-secrets: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # ratchet:docker/setup-buildx-action@v4
- name: Login to Docker Hub
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4
with:
username: ${{ env.DOCKER_USERNAME }}
password: ${{ env.DOCKER_TOKEN }}
- name: Docker meta
id: meta
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302
with:
images: ${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }}
flavor: |
latest=false
tags: |
type=raw,value=${{ needs.determine-builds.outputs.is-test-run == 'true' && format('model-server-{0}', needs.determine-builds.outputs.sanitized-tag) || github.ref_name }}
type=raw,value=${{ needs.determine-builds.outputs.is-test-run != 'true' && needs.determine-builds.outputs.is-latest == 'true' && 'latest' || '' }}
type=raw,value=${{ needs.determine-builds.outputs.is-test-run != 'true' && env.EDGE_TAG == 'true' && 'edge' || '' }}
type=raw,value=${{ needs.determine-builds.outputs.is-test-run != 'true' && needs.determine-builds.outputs.is-beta-standalone == 'true' && 'beta' || '' }}
type=semver,pattern={{version}},enable=${{ needs.determine-builds.outputs.is-stable == 'true' }}
type=semver,pattern={{major}}.{{minor}},enable=${{ needs.determine-builds.outputs.is-stable == 'true' }}
type=semver,pattern={{major}},enable=${{ needs.determine-builds.outputs.is-stable == 'true' }}
# -dev twins of the tags above, pointing at the same manifest — see the merge-web dev note.
- name: Docker meta (dev)
id: meta-dev
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302
with:
images: ${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }}
flavor: |
latest=false
tags: |
type=raw,value=${{ needs.determine-builds.outputs.is-test-run == 'true' && format('model-server-{0}-dev', needs.determine-builds.outputs.sanitized-tag) || format('{0}-dev', github.ref_name) }}
type=raw,value=${{ needs.determine-builds.outputs.is-test-run != 'true' && needs.determine-builds.outputs.is-latest == 'true' && 'latest-dev' || '' }}
type=raw,value=${{ needs.determine-builds.outputs.is-test-run != 'true' && env.EDGE_TAG == 'true' && 'edge-dev' || '' }}
type=raw,value=${{ needs.determine-builds.outputs.is-test-run != 'true' && needs.determine-builds.outputs.is-beta-standalone == 'true' && 'beta-dev' || '' }}
type=semver,pattern={{version}}-dev,enable=${{ needs.determine-builds.outputs.is-stable == 'true' }}
type=semver,pattern={{major}}.{{minor}}-dev,enable=${{ needs.determine-builds.outputs.is-stable == 'true' }}
type=semver,pattern={{major}}-dev,enable=${{ needs.determine-builds.outputs.is-stable == 'true' }}
- name: Create and push manifest
env:
IMAGE_REPO: ${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }}
AMD64_DIGEST: ${{ needs.build-model-server-amd64.outputs.digest }}
ARM64_DIGEST: ${{ needs.build-model-server-arm64.outputs.digest }}
META_TAGS: ${{ steps.meta.outputs.tags }}
META_DEV_TAGS: ${{ steps.meta-dev.outputs.tags }}
run: |
IMAGES="${IMAGE_REPO}@${AMD64_DIGEST} ${IMAGE_REPO}@${ARM64_DIGEST}"
docker buildx imagetools create \
$(printf '%s\n%s\n' "${META_TAGS}" "${META_DEV_TAGS}" | sed '/^$/d' | xargs -I {} echo -t {}) \
$IMAGES
prepare-sandbox:
needs:
- determine-builds
- audit-gate
if: needs.determine-builds.outputs.is-test-run == 'true' || (needs.determine-builds.outputs.is-stable == 'true' || needs.determine-builds.outputs.is-beta-standalone == 'true' || needs.determine-builds.outputs.is-cloud-tag == 'true' || startsWith(github.ref_name, 'nightly-latest') || github.ref_name == 'edge')
runs-on: ubuntu-slim
timeout-minutes: 10
environment: release
permissions:
contents: read
id-token: write
outputs:
context-tag: ${{ steps.context.outputs.context-tag }}
context-exists: ${{ steps.exists.outputs.context-exists }}
env:
REGISTRY_IMAGE: onyxdotapp/sandbox
SANDBOX_CONTEXT: backend/onyx/server/features/build/sandbox/image
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # ratchet:actions/checkout@v6
with:
persist-credentials: false
- name: Compute sandbox context tag
id: context
run: |
set -euo pipefail
digest="$(git ls-files -s -z "$SANDBOX_CONTEXT" | sort -z | sha256sum | cut -d' ' -f1)"
echo "context-tag=ctx-${digest:0:20}" >> "$GITHUB_OUTPUT"
- name: Configure AWS credentials
if: needs.determine-builds.outputs.is-test-run != 'true' && vars.DOCKER_NO_CACHE != 'true'
uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c
with:
role-to-assume: ${{ secrets.AWS_OIDC_ROLE_ARN }}
aws-region: us-east-2
- name: Get AWS Secrets
if: needs.determine-builds.outputs.is-test-run != 'true' && vars.DOCKER_NO_CACHE != 'true'
uses: aws-actions/aws-secretsmanager-get-secrets@2cb1a461cbd4865ac4299648312e4704c646cd53
with:
secret-ids: |
DOCKER_USERNAME, deploy/docker-username
DOCKER_TOKEN, deploy/docker-token
parse-json-secrets: true
- name: Login to Docker Hub
if: needs.determine-builds.outputs.is-test-run != 'true' && vars.DOCKER_NO_CACHE != 'true'
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4
with:
username: ${{ env.DOCKER_USERNAME }}
password: ${{ env.DOCKER_TOKEN }}
- name: Check for existing context image
id: exists
env:
CONTEXT_TAG: ${{ steps.context.outputs.context-tag }}
DOCKER_NO_CACHE: ${{ vars.DOCKER_NO_CACHE == 'true' && 'true' || 'false' }}
IS_TEST_RUN: ${{ needs.determine-builds.outputs.is-test-run }}
run: |
if [ "${IS_TEST_RUN}" = "true" ] || [ "${DOCKER_NO_CACHE}" = "true" ]; then
echo "context-exists=false" >> "$GITHUB_OUTPUT"
exit 0
fi
if docker buildx imagetools inspect "${REGISTRY_IMAGE}:${CONTEXT_TAG}" >/dev/null 2>&1; then
echo "context-exists=true" >> "$GITHUB_OUTPUT"
else
echo "context-exists=false" >> "$GITHUB_OUTPUT"
fi
build-sandbox-amd64:
needs:
- determine-builds
- prepare-sandbox
if: needs.prepare-sandbox.result == 'success' && needs.prepare-sandbox.outputs.context-exists != 'true'
runs-on:
- runs-on
- runner=4cpu-linux-x64
- run-id=${{ github.run_id }}-sandbox-amd64
- extras=ecr-cache
timeout-minutes: 90
environment: release
permissions:
contents: read
id-token: write
outputs:
digest: ${{ steps.build.outputs.digest }}
env:
REGISTRY_IMAGE: onyxdotapp/sandbox
steps:
- uses: runs-on/action@4e5f72399b6b17f2e79c511c1b38a315a64d22dc
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # ratchet:actions/checkout@v6
with:
persist-credentials: false
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c
with:
role-to-assume: ${{ secrets.AWS_OIDC_ROLE_ARN }}
aws-region: us-east-2
- name: Get AWS Secrets
uses: aws-actions/aws-secretsmanager-get-secrets@2cb1a461cbd4865ac4299648312e4704c646cd53
with:
secret-ids: |
DOCKER_USERNAME, deploy/docker-username
DOCKER_TOKEN, deploy/docker-token
parse-json-secrets: true
- name: Docker meta
id: meta
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302
with:
images: ${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }}
flavor: |
latest=false
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # ratchet:docker/setup-buildx-action@v4
- name: Login to Docker Hub
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4
with:
username: ${{ env.DOCKER_USERNAME }}
password: ${{ env.DOCKER_TOKEN }}
- name: Build and push AMD64
id: build
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a
with:
context: ./backend/onyx/server/features/build/sandbox/image
file: ./backend/onyx/server/features/build/sandbox/image/Dockerfile
platforms: linux/amd64
labels: ${{ steps.meta.outputs.labels }}
cache-from: |
type=registry,ref=${{ env.BASE_IMAGE_REGISTRY }}/onyxdotapp/sandbox:edge
type=registry,ref=${{ env.BASE_IMAGE_REGISTRY }}/onyxdotapp/sandbox:latest
cache-to: type=inline
outputs: type=image,name=${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true
no-cache: ${{ vars.DOCKER_NO_CACHE == 'true' }}
build-sandbox-arm64:
needs:
- determine-builds
- prepare-sandbox
if: needs.prepare-sandbox.result == 'success' && needs.prepare-sandbox.outputs.context-exists != 'true'
runs-on:
- runs-on
- runner=4cpu-linux-arm64
- run-id=${{ github.run_id }}-sandbox-arm64
- extras=ecr-cache
timeout-minutes: 90
environment: release
permissions:
contents: read
id-token: write
outputs:
digest: ${{ steps.build.outputs.digest }}
env:
REGISTRY_IMAGE: onyxdotapp/sandbox
steps:
- uses: runs-on/action@4e5f72399b6b17f2e79c511c1b38a315a64d22dc
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # ratchet:actions/checkout@v6
with:
persist-credentials: false
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c
with:
role-to-assume: ${{ secrets.AWS_OIDC_ROLE_ARN }}
aws-region: us-east-2
- name: Get AWS Secrets
uses: aws-actions/aws-secretsmanager-get-secrets@2cb1a461cbd4865ac4299648312e4704c646cd53
with:
secret-ids: |
DOCKER_USERNAME, deploy/docker-username
DOCKER_TOKEN, deploy/docker-token
parse-json-secrets: true
- name: Docker meta
id: meta
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302
with:
images: ${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }}
flavor: |
latest=false
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # ratchet:docker/setup-buildx-action@v4
- name: Login to Docker Hub
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4
with:
username: ${{ env.DOCKER_USERNAME }}
password: ${{ env.DOCKER_TOKEN }}
- name: Build and push ARM64
id: build
uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a
with:
context: ./backend/onyx/server/features/build/sandbox/image
file: ./backend/onyx/server/features/build/sandbox/image/Dockerfile
platforms: linux/arm64
labels: ${{ steps.meta.outputs.labels }}
cache-from: |
type=registry,ref=${{ env.BASE_IMAGE_REGISTRY }}/onyxdotapp/sandbox:edge
type=registry,ref=${{ env.BASE_IMAGE_REGISTRY }}/onyxdotapp/sandbox:latest
cache-to: type=inline
outputs: type=image,name=${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }},push-by-digest=true,name-canonical=true,push=true
no-cache: ${{ vars.DOCKER_NO_CACHE == 'true' }}
merge-sandbox:
needs:
- determine-builds
- prepare-sandbox
- build-sandbox-amd64
- build-sandbox-arm64
if: always() && needs.prepare-sandbox.result == 'success' && (needs.prepare-sandbox.outputs.context-exists == 'true' || (needs.build-sandbox-amd64.result == 'success' && needs.build-sandbox-arm64.result == 'success'))
runs-on:
- runs-on
- runner=2cpu-linux-x64
- run-id=${{ github.run_id }}-merge-sandbox
- extras=ecr-cache
timeout-minutes: 90
environment: release
permissions:
id-token: write
env:
REGISTRY_IMAGE: onyxdotapp/sandbox
steps:
- uses: runs-on/action@4e5f72399b6b17f2e79c511c1b38a315a64d22dc
- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@e6de054238d6b7531b4efff3b6587d9aade6a06c
with:
role-to-assume: ${{ secrets.AWS_OIDC_ROLE_ARN }}
aws-region: us-east-2
- name: Get AWS Secrets
uses: aws-actions/aws-secretsmanager-get-secrets@2cb1a461cbd4865ac4299648312e4704c646cd53
with:
secret-ids: |
DOCKER_USERNAME, deploy/docker-username
DOCKER_TOKEN, deploy/docker-token
parse-json-secrets: true
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # ratchet:docker/setup-buildx-action@v4
- name: Login to Docker Hub
uses: docker/login-action@c99871dec2022cc055c062a10cc1a1310835ceb4
with:
username: ${{ env.DOCKER_USERNAME }}
password: ${{ env.DOCKER_TOKEN }}
- name: Docker meta
id: meta
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302
with:
images: ${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }}
flavor: |
latest=false
tags: |
type=raw,value=${{ needs.determine-builds.outputs.is-test-run == 'true' && format('sandbox-{0}', needs.determine-builds.outputs.sanitized-tag) || github.ref_name }}
type=raw,value=${{ needs.determine-builds.outputs.is-test-run != 'true' && needs.determine-builds.outputs.is-latest == 'true' && 'latest' || '' }}
type=raw,value=${{ needs.determine-builds.outputs.is-test-run != 'true' && env.EDGE_TAG == 'true' && 'edge' || '' }}
type=raw,value=${{ needs.determine-builds.outputs.is-test-run != 'true' && needs.determine-builds.outputs.is-beta-standalone == 'true' && 'beta' || '' }}
type=semver,pattern={{version}},enable=${{ needs.determine-builds.outputs.is-stable == 'true' }}
type=semver,pattern={{major}}.{{minor}},enable=${{ needs.determine-builds.outputs.is-stable == 'true' }}
type=semver,pattern={{major}},enable=${{ needs.determine-builds.outputs.is-stable == 'true' }}
# -dev twins of the tags above, pointing at the same manifest — see the merge-web dev note.
# The sandbox image resolves off the same version knob as the rest of the deployment
# (IMAGE_TAG in compose, global.version in the helm chart), so it needs the twins too.
- name: Docker meta (dev)
id: meta-dev
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302
with:
images: ${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }}
flavor: |
latest=false
tags: |
type=raw,value=${{ needs.determine-builds.outputs.is-test-run == 'true' && format('sandbox-{0}-dev', needs.determine-builds.outputs.sanitized-tag) || format('{0}-dev', github.ref_name) }}
type=raw,value=${{ needs.determine-builds.outputs.is-test-run != 'true' && needs.determine-builds.outputs.is-latest == 'true' && 'latest-dev' || '' }}
type=raw,value=${{ needs.determine-builds.outputs.is-test-run != 'true' && env.EDGE_TAG == 'true' && 'edge-dev' || '' }}
type=raw,value=${{ needs.determine-builds.outputs.is-test-run != 'true' && needs.determine-builds.outputs.is-beta-standalone == 'true' && 'beta-dev' || '' }}
type=semver,pattern={{version}}-dev,enable=${{ needs.determine-builds.outputs.is-stable == 'true' }}
type=semver,pattern={{major}}.{{minor}}-dev,enable=${{ needs.determine-builds.outputs.is-stable == 'true' }}
type=semver,pattern={{major}}-dev,enable=${{ needs.determine-builds.outputs.is-stable == 'true' }}
- name: Create and push manifest
env:
IMAGE_REPO: ${{ needs.determine-builds.outputs.is-test-run == 'true' && env.RUNS_ON_ECR_CACHE || env.REGISTRY_IMAGE }}
CONTEXT_TAG: ${{ needs.prepare-sandbox.outputs.context-tag }}
AMD64_DIGEST: ${{ needs.build-sandbox-amd64.outputs.digest }}
ARM64_DIGEST: ${{ needs.build-sandbox-arm64.outputs.digest }}
META_TAGS: ${{ steps.meta.outputs.tags }}
META_DEV_TAGS: ${{ steps.meta-dev.outputs.tags }}
DOCKER_NO_CACHE: ${{ vars.DOCKER_NO_CACHE == 'true' && 'true' || 'false' }}
IS_TEST_RUN: ${{ needs.determine-builds.outputs.is-test-run }}
run: |
mapfile -t tags < <(printf '%s\n%s\n' "${META_TAGS}" "${META_DEV_TAGS}" | sed '/^$/d')
tag_args=()
for tag in "${tags[@]}"; do
tag_args+=("-t" "$tag")
done
if [[ "$IS_TEST_RUN" == "true" ]]; then
if [[ -z "$AMD64_DIGEST" || -z "$ARM64_DIGEST" ]]; then
echo "::error::Missing sandbox image digest(s); cannot create test manifest"
exit 1
fi
images="${IMAGE_REPO}@${AMD64_DIGEST} ${IMAGE_REPO}@${ARM64_DIGEST}"
docker buildx imagetools create \
"${tag_args[@]}" \
$images
elif [[ "$DOCKER_NO_CACHE" != "true" ]] && docker buildx imagetools inspect "${REGISTRY_IMAGE}:${CONTEXT_TAG}" >/dev/null 2>&1; then
docker buildx imagetools create \
"${tag_args[@]}" \
"${REGISTRY_IMAGE}:${CONTEXT_TAG}"
else
if [[ -z "$AMD64_DIGEST" || -z "$ARM64_DIGEST" ]]; then
echo "::error::Missing sandbox image digest(s); cannot create ${CONTEXT_TAG}"
exit 1
fi
tag_args=("-t" "${REGISTRY_IMAGE}:${CONTEXT_TAG}" "${tag_args[@]}")
images="${IMAGE_REPO}@${AMD64_DIGEST} ${IMAGE_REPO}@${ARM64_DIGEST}"
docker buildx imagetools create \
"${tag_args[@]}" \
$images
fi
dispatch-cloud-deployment:
needs:
- determine-builds
- merge-web-cloud
- merge-backend
- merge-model-server
- merge-sandbox
if: >-
always()
&& needs.merge-web-cloud.result == 'success'
&& needs.merge-backend.result == 'success'
&& needs.merge-model-server.result == 'success'
&& needs.merge-sandbox.result == 'success'
&& needs.determine-builds.outputs.is-cloud-tag == 'true'
&& needs.determine-builds.outputs.is-test-run != 'true'
# NOTE: Github-hosted runners have about 20s faster queue times and are preferred here.
runs-on: ubuntu-slim
timeout-minutes: 10
permissions: {}
steps:
- name: Resolve dispatch target
id: target
env:
CLOUD_DEPLOYMENT_REPO: ${{ vars.CLOUD_DEPLOYMENT_REPO }}
run: |
if [ -z "$CLOUD_DEPLOYMENT_REPO" ]; then
echo "::error::CLOUD_DEPLOYMENT_REPO is not set"
exit 1
fi
echo "owner=${CLOUD_DEPLOYMENT_REPO%%/*}" >> "$GITHUB_OUTPUT"
echo "repo=${CLOUD_DEPLOYMENT_REPO#*/}" >> "$GITHUB_OUTPUT"
- name: Mint GitHub App installation token
id: app-token
uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1
with:
client-id: ${{ vars.CLOUD_DEPLOYMENT_APP_ID }}
private-key: ${{ secrets.CLOUD_DEPLOYMENT_APP_PRIVATE_KEY }}
# Scope the token to the same repo the dispatch targets, derived from CLOUD_DEPLOYMENT_REPO.
owner: ${{ steps.target.outputs.owner }}
repositories: ${{ steps.target.outputs.repo }}
permission-contents: write
- name: Trigger new-cloud-image dispatch
env:
GH_TOKEN: ${{ steps.app-token.outputs.token }}
CLOUD_DEPLOYMENT_REPO: ${{ vars.CLOUD_DEPLOYMENT_REPO }}
VERSION: ${{ github.ref_name }}
run: |
gh api "repos/${CLOUD_DEPLOYMENT_REPO}/dispatches" \
-f event_type=new-cloud-image \
-F "client_payload[version]=${VERSION}"
# A failed audit usually means a new advisory, so the CVE Alerts run opens
# its tracking issue, fix PR, and Slack post. Every release tag also gets a
# run once its images are published, since a release branch has no schedule
# and that run is what closes the branch's issues after a fix ships.
dispatch-cve-alerts:
needs: [determine-builds, audit-gate, merge-web, merge-backend, merge-model-server]
if: >-
always() && !cancelled() && needs.determine-builds.outputs.is-test-run != 'true' &&
(needs.audit-gate.result == 'failure' || startsWith(github.ref_name, 'v'))
runs-on: ubuntu-slim
timeout-minutes: 5
permissions:
actions: write # dispatch cve-alerts.yml
contents: read # look up the release branch
steps:
# A release tag's findings live on its release branch, so the run scans
# that branch. Anything else, such as the nightly tag, scans main.
- name: Dispatch CVE Alerts
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
TAG: ${{ github.ref_name }}
SERVER_URL: ${{ github.server_url }}
RUN_ID: ${{ github.run_id }}
run: |
ref=main
if [[ "${TAG}" =~ ^v([0-9]+\.[0-9]+)\. ]] && gh api "repos/${REPO}/branches/release/v${BASH_REMATCH[1]}" > /dev/null 2>&1; then
ref="release/v${BASH_REMATCH[1]}"
fi
gh workflow run cve-alerts.yml --repo "${REPO}" --ref "${ref}" -f "source-run=${SERVER_URL}/${REPO}/actions/runs/${RUN_ID}"
notify-slack-on-failure:
needs:
- determine-builds
- audit-gate
- create-release
- build-desktop
- build-web-amd64
- build-web-arm64
- merge-web
- build-web-cloud-amd64
- build-web-cloud-arm64
- merge-web-cloud
- build-backend-amd64
- build-backend-arm64
- merge-backend
- build-model-server-amd64
- build-model-server-arm64
- merge-model-server
- prepare-sandbox
- build-sandbox-amd64
- build-sandbox-arm64
- merge-sandbox
if: always() && (needs.audit-gate.result == 'failure' || needs.create-release.result == 'failure' || needs.build-desktop.result == 'failure' || needs.build-web-amd64.result == 'failure' || needs.build-web-arm64.result == 'failure' || needs.merge-web.result == 'failure' || needs.build-web-cloud-amd64.result == 'failure' || needs.build-web-cloud-arm64.result == 'failure' || needs.merge-web-cloud.result == 'failure' || needs.build-backend-amd64.result == 'failure' || needs.build-backend-arm64.result == 'failure' || needs.merge-backend.result == 'failure' || needs.build-model-server-amd64.result == 'failure' || needs.build-model-server-arm64.result == 'failure' || needs.merge-model-server.result == 'failure' || needs.prepare-sandbox.result == 'failure' || needs.build-sandbox-amd64.result == 'failure' || needs.build-sandbox-arm64.result == 'failure' || needs.merge-sandbox.result == 'failure') && needs.determine-builds.outputs.is-test-run != 'true'
# NOTE: Github-hosted runners have about 20s faster queue times and are preferred here.
runs-on: ubuntu-slim
timeout-minutes: 90
environment: release
steps:
- name: Checkout
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # ratchet:actions/checkout@v6
with:
persist-credentials: false
- name: Determine failed jobs
id: failed-jobs
shell: bash
run: |
FAILED_JOBS=""
if [ "${NEEDS_AUDIT_GATE_RESULT}" == "failure" ]; then
FAILED_JOBS="${FAILED_JOBS}• audit-gate\\n"
fi
if [ "${NEEDS_CREATE_RELEASE_RESULT}" == "failure" ]; then
FAILED_JOBS="${FAILED_JOBS}• create-release\\n"
fi
if [ "${NEEDS_BUILD_DESKTOP_RESULT}" == "failure" ]; then
FAILED_JOBS="${FAILED_JOBS}• build-desktop\\n"
fi
if [ "${NEEDS_BUILD_WEB_AMD64_RESULT}" == "failure" ]; then
FAILED_JOBS="${FAILED_JOBS}• build-web-amd64\\n"
fi
if [ "${NEEDS_BUILD_WEB_ARM64_RESULT}" == "failure" ]; then
FAILED_JOBS="${FAILED_JOBS}• build-web-arm64\\n"
fi
if [ "${NEEDS_MERGE_WEB_RESULT}" == "failure" ]; then
FAILED_JOBS="${FAILED_JOBS}• merge-web\\n"
fi
if [ "${NEEDS_BUILD_WEB_CLOUD_AMD64_RESULT}" == "failure" ]; then
FAILED_JOBS="${FAILED_JOBS}• build-web-cloud-amd64\\n"
fi
if [ "${NEEDS_BUILD_WEB_CLOUD_ARM64_RESULT}" == "failure" ]; then
FAILED_JOBS="${FAILED_JOBS}• build-web-cloud-arm64\\n"
fi
if [ "${NEEDS_MERGE_WEB_CLOUD_RESULT}" == "failure" ]; then
FAILED_JOBS="${FAILED_JOBS}• merge-web-cloud\\n"
fi
if [ "${NEEDS_BUILD_BACKEND_AMD64_RESULT}" == "failure" ]; then
FAILED_JOBS="${FAILED_JOBS}• build-backend-amd64\\n"
fi
if [ "${NEEDS_BUILD_BACKEND_ARM64_RESULT}" == "failure" ]; then
FAILED_JOBS="${FAILED_JOBS}• build-backend-arm64\\n"
fi
if [ "${NEEDS_MERGE_BACKEND_RESULT}" == "failure" ]; then
FAILED_JOBS="${FAILED_JOBS}• merge-backend\\n"
fi
if [ "${NEEDS_BUILD_MODEL_SERVER_AMD64_RESULT}" == "failure" ]; then
FAILED_JOBS="${FAILED_JOBS}• build-model-server-amd64\\n"
fi
if [ "${NEEDS_BUILD_MODEL_SERVER_ARM64_RESULT}" == "failure" ]; then
FAILED_JOBS="${FAILED_JOBS}• build-model-server-arm64\\n"
fi
if [ "${NEEDS_MERGE_MODEL_SERVER_RESULT}" == "failure" ]; then
FAILED_JOBS="${FAILED_JOBS}• merge-model-server\\n"
fi
if [ "${NEEDS_PREPARE_SANDBOX_RESULT}" == "failure" ]; then
FAILED_JOBS="${FAILED_JOBS}• prepare-sandbox\\n"
fi
if [ "${NEEDS_BUILD_SANDBOX_AMD64_RESULT}" == "failure" ]; then
FAILED_JOBS="${FAILED_JOBS}• build-sandbox-amd64\\n"
fi
if [ "${NEEDS_BUILD_SANDBOX_ARM64_RESULT}" == "failure" ]; then
FAILED_JOBS="${FAILED_JOBS}• build-sandbox-arm64\\n"
fi
if [ "${NEEDS_MERGE_SANDBOX_RESULT}" == "failure" ]; then
FAILED_JOBS="${FAILED_JOBS}• merge-sandbox\\n"
fi
# Remove trailing \n and set output
FAILED_JOBS=$(printf '%s' "$FAILED_JOBS" | sed 's/\\n$//')
echo "jobs=$FAILED_JOBS" >> "$GITHUB_OUTPUT"
env:
NEEDS_AUDIT_GATE_RESULT: ${{ needs.audit-gate.result }}
NEEDS_CREATE_RELEASE_RESULT: ${{ needs.create-release.result }}
NEEDS_BUILD_DESKTOP_RESULT: ${{ needs.build-desktop.result }}
NEEDS_BUILD_WEB_AMD64_RESULT: ${{ needs.build-web-amd64.result }}
NEEDS_BUILD_WEB_ARM64_RESULT: ${{ needs.build-web-arm64.result }}
NEEDS_MERGE_WEB_RESULT: ${{ needs.merge-web.result }}
NEEDS_BUILD_WEB_CLOUD_AMD64_RESULT: ${{ needs.build-web-cloud-amd64.result }}
NEEDS_BUILD_WEB_CLOUD_ARM64_RESULT: ${{ needs.build-web-cloud-arm64.result }}
NEEDS_MERGE_WEB_CLOUD_RESULT: ${{ needs.merge-web-cloud.result }}
NEEDS_BUILD_BACKEND_AMD64_RESULT: ${{ needs.build-backend-amd64.result }}
NEEDS_BUILD_BACKEND_ARM64_RESULT: ${{ needs.build-backend-arm64.result }}
NEEDS_MERGE_BACKEND_RESULT: ${{ needs.merge-backend.result }}
NEEDS_BUILD_MODEL_SERVER_AMD64_RESULT: ${{ needs.build-model-server-amd64.result }}
NEEDS_BUILD_MODEL_SERVER_ARM64_RESULT: ${{ needs.build-model-server-arm64.result }}
NEEDS_MERGE_MODEL_SERVER_RESULT: ${{ needs.merge-model-server.result }}
NEEDS_PREPARE_SANDBOX_RESULT: ${{ needs.prepare-sandbox.result }}
NEEDS_BUILD_SANDBOX_AMD64_RESULT: ${{ needs.build-sandbox-amd64.result }}
NEEDS_BUILD_SANDBOX_ARM64_RESULT: ${{ needs.build-sandbox-arm64.result }}
NEEDS_MERGE_SANDBOX_RESULT: ${{ needs.merge-sandbox.result }}
- name: Send Slack notification
uses: ./.github/actions/slack-notify
with:
webhook-url: ${{ secrets.MONITOR_DEPLOYMENTS_WEBHOOK }}
failed-jobs: ${{ steps.failed-jobs.outputs.jobs }}
title: "🚨 Deployment Workflow Failed"
ref-name: ${{ github.ref_name }}