Skip to content
Open
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
59 changes: 48 additions & 11 deletions .github/workflows/publish-images.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@ name: Publish Images

on:
push:
branches: [main]
branches:
- main
- release/c7e-centaur-overlay*
tags: [v*]
paths:
- .github/workflows/publish-images.yml
- contrib/chart/**
- services/**
- crates/harness-server/**
- harness/**
Expand All @@ -31,22 +34,27 @@ permissions:

env:
REGISTRY: ghcr.io
IMAGE_NAMESPACE: paradigmxyz/centaur
IMAGE_SOURCE: https://github.com/paradigmxyz/centaur
IMAGE_NAMESPACE: cartridge-gg/centaur
IMAGE_SOURCE: https://github.com/cartridge-gg/centaur
# Main/tags keep optimized release images. PRs and manual branch publishes
# use debug builds so staging/dev iteration does not spend minutes optimizing
# Rust binaries that are immediately replaced by the next test build.
RUST_BUILD_PROFILE: ${{ (github.event_name == 'pull_request' || github.event_name == 'workflow_dispatch') && 'debug' || 'release' }}

jobs:
# Build each image with Depot's hosted builders, push by digest, and hand
# the digests to the merge job below which assembles the multi-arch manifest.
# Build each image natively per platform (amd64 on x64 runners, arm64 on
# arm runners — no QEMU emulation), push by digest, and hand the digests
# to the merge job below which assembles the multi-arch manifest.
# arm64 is only built on pushes to main and release tags — PR and manual
# dispatch builds stay amd64-only to keep iteration fast. Fork PRs skip both
# jobs so untrusted changes do not run on the hosted image builders.
#
# c7e overlay: upstream builds on Depot (depot-* runners + paradigmxyz's Depot
# project). This fork has no Depot project, so the build stays on GitHub-hosted
# runners with docker buildx. Reconcile if upstream reworks the build again.
build:
if: ${{ github.event_name != 'pull_request' || !github.event.pull_request.head.repo.fork }}
runs-on: ${{ matrix.platform == 'linux/arm64' && 'depot-ubuntu-24.04-arm-16' || 'depot-ubuntu-24.04-16' }}
runs-on: ${{ matrix.platform == 'linux/arm64' && 'ubuntu-24.04-arm' || 'ubuntu-latest' }}
strategy:
fail-fast: false
matrix:
Expand Down Expand Up @@ -110,8 +118,8 @@ jobs:
platform="${{ matrix.platform }}"
echo "PLATFORM_SLUG=${platform//\//-}" >> "$GITHUB_ENV"

- name: Set up Depot
uses: depot/setup-action@91bc8495a33ebfc504ffc89e5674379ccf23c29c # v1.7.2
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3

- name: Log in to GHCR
uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
Expand All @@ -133,9 +141,8 @@ jobs:

- name: Build and push ${{ matrix.image }} (${{ matrix.platform }})
id: build
uses: depot/build-push-action@98e78adca7817480b8185f474a400b451d74e287 # v1.18.0
uses: docker/build-push-action@10e90e3645eae34f1e60eeb005ba3a3d33f178e8 # v6
with:
project: d8qqlh1bmq
context: ${{ matrix.context }}
file: ${{ matrix.dockerfile }}
target: ${{ matrix.target }}
Expand All @@ -147,6 +154,14 @@ jobs:
outputs: type=image,name=${{ env.REGISTRY }}/${{ env.IMAGE_NAMESPACE }}/${{ matrix.image }},push-by-digest=true,name-canonical=true,push=${{ !github.event.pull_request.head.repo.fork }}
build-args: |
RUST_BUILD_PROFILE=${{ env.RUST_BUILD_PROFILE }}
cache-from: |
type=registry,ref=${{ env.REGISTRY }}/${{ env.IMAGE_NAMESPACE }}/${{ matrix.image }}:buildcache-${{ env.PLATFORM_SLUG }}
type=gha,scope=${{ matrix.image }}-${{ env.PLATFORM_SLUG }}
# Fork PRs run with a read-only GITHUB_TOKEN: exporting the registry
# cache would fail the build, so only export it when we can push.
cache-to: |
${{ !github.event.pull_request.head.repo.fork && format('type=registry,ref={0}/{1}/{2}:buildcache-{3},mode=max', env.REGISTRY, env.IMAGE_NAMESPACE, matrix.image, env.PLATFORM_SLUG) || '' }}
type=gha,mode=max,scope=${{ matrix.image }}-${{ env.PLATFORM_SLUG }}

- name: Export digest
if: ${{ !github.event.pull_request.head.repo.fork }}
Expand All @@ -165,7 +180,7 @@ jobs:
retention-days: 1

merge:
runs-on: depot-ubuntu-24.04-16
runs-on: ubuntu-latest
needs: build
if: ${{ github.event_name != 'pull_request' || !github.event.pull_request.head.repo.fork }}
strategy:
Expand Down Expand Up @@ -231,3 +246,25 @@ jobs:
- name: Inspect manifest
run: |
docker buildx imagetools inspect ${{ env.REGISTRY }}/${{ env.IMAGE_NAMESPACE }}/${{ matrix.image }}:${{ steps.meta.outputs.version }}

# Auto-deploy: once the canonical release branch's base images are published,
# tell the c7e overlay to roll them out (it gates on the `latitude`
# environment). Manual pin-bump + dispatch still works as a fallback.
notify-overlay:
needs: merge
if: github.event_name == 'push' && github.ref == 'refs/heads/release/c7e-centaur-overlay'
runs-on: ubuntu-latest
steps:
- name: Dispatch overlay deploy with the new base image sha
env:
GH_TOKEN: ${{ secrets.OVERLAY_DISPATCH_TOKEN }}
run: |
short="${GITHUB_SHA:0:7}"
gh api repos/cartridge-gg/agent/dispatches --method POST --input - <<EOF
{
"event_type": "base-images-published",
"client_payload": { "sha": "${GITHUB_SHA}", "tag": "sha-${short}", "ref": "${GITHUB_REF_NAME}" }
}
EOF

# (re-trigger publish for sha with 0a344cbf re-added)
12 changes: 10 additions & 2 deletions .github/workflows/release-chart.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,10 @@ jobs:
git config user.email "$GITHUB_ACTOR@users.noreply.github.com"

- name: Bootstrap gh-pages branch
if: github.event_name == 'push'
# Upstream only. In forks this would publish a competing chart repo, and
# chart-releaser below would mint centaur-<version> tags that collide with
# upstream's. Forks version themselves via tag-release.yml (c7e-* tags).
if: github.event_name == 'push' && github.repository == 'paradigmxyz/centaur'
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
Expand Down Expand Up @@ -67,6 +70,10 @@ jobs:
uses: mikefarah/yq@c14f446382944492701b16c1ddb48bb9dbe683e3 # v4.53.6

- name: Validate chart version bump
# Upstream only. Chart.yaml .version is upstream's release number; forcing forks to
# bump it would both block overlay PRs that touch contrib/chart and destroy its
# meaning as the upstream base that tag-release.yml reads.
if: github.repository == 'paradigmxyz/centaur'
run: |
set -e

Expand Down Expand Up @@ -98,7 +105,8 @@ jobs:
exit 1

- name: Run chart-releaser
if: github.event_name == 'push'
# Upstream only — this is what mints the centaur-<version> tags and releases.
if: github.event_name == 'push' && github.repository == 'paradigmxyz/centaur'
uses: helm/chart-releaser-action@cae68fefc6b5f367a0275617c9f83181ba54714f # v1.7.0
with:
skip_existing: true
Expand Down
187 changes: 187 additions & 0 deletions .github/workflows/tag-release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
name: Tag release

# Cuts a durable, fork-distinct version tag from main.
#
# Scheme: c7e-<upstream-base>-<fork-counter>, e.g. c7e-0.1.109-2
#
# The upstream base is derived from the newest paradigmxyz/centaur release tag that is an
# ancestor of HEAD, NOT from contrib/chart/Chart.yaml — release-chart.yml forces that field
# to be bumped whenever contrib/chart changes, at which point it stops meaning "the upstream
# release we sit on".
#
# Why tag at all: the previously deployed commit (1807424a) was orphaned by a rebase of the
# release branch and is reachable from no ref. A tag cannot be orphaned that way, so the
# commit — and its published sha-<short> image — stay resolvable.

on:
workflow_dispatch:
inputs:
dry_run:
description: Resolve the next version but create nothing
type: boolean
required: false
default: false
create_release:
description: Publish a GitHub Release alongside the tag
type: boolean
required: false
default: true

permissions:
contents: write

concurrency:
group: tag-release
cancel-in-progress: false

jobs:
tag:
name: Cut release tag
# ubuntu-latest is load-bearing: depot-* jobs never get a runner in this fork.
runs-on: ubuntu-latest
steps:
- name: Require main
run: |
set -euo pipefail
if [ "${GITHUB_REF_NAME}" != "main" ]; then
echo "::error::Releases are cut from main only; this ran on '${GITHUB_REF_NAME}'."
exit 1
fi

- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
fetch-depth: 0

- name: Fetch upstream tags and main
run: |
set -euo pipefail
url=https://github.com/paradigmxyz/centaur.git
git remote add upstream "$url" 2>/dev/null || git remote set-url upstream "$url"
# Local to this runner; never pushed back to origin.
git fetch --quiet upstream 'refs/tags/centaur-*:refs/tags/centaur-*'
git fetch --quiet upstream main:refs/remotes/upstream/main

- name: Resolve next version
id: version
run: |
set -euo pipefail

base_tag=$(git describe --tags --match 'centaur-*' --abbrev=0 HEAD 2>/dev/null || true)
if [ -z "$base_tag" ]; then
echo "::error::No upstream centaur-* release tag is an ancestor of HEAD; cannot derive a base version."
exit 1
fi
base="${base_tag#centaur-}"

already=$(git tag --list 'c7e-*' --points-at HEAD | head -n 1)
if [ -n "$already" ]; then
echo "::error::HEAD is already tagged '${already}'. Nothing new to release."
exit 1
fi

last=$(git tag --list "c7e-${base}-*" \
| sed "s|^c7e-${base}-||" \
| grep -E '^[0-9]+$' \
| sort -n | tail -n 1 || true)
next=$(( ${last:-0} + 1 ))
tag="c7e-${base}-${next}"

prev=$(git tag --list 'c7e-*' --sort=-creatordate | head -n 1)

# Three distinct figures. Conflating them is the trap: "commits since the upstream
# release" mixes synced upstream commits with our own, and is not staleness.
since_base=$(git rev-list --count "${base_tag}..HEAD")
overlay=$(git rev-list --count upstream/main..HEAD)
behind=$(git rev-list --count HEAD..upstream/main)
upstream_latest=$(git tag --list 'centaur-*' | sort -V | tail -n 1)

{
echo "tag=${tag}"
echo "base=${base}"
echo "base_tag=${base_tag}"
echo "prev=${prev}"
echo "since_base=${since_base}"
echo "overlay=${overlay}"
echo "behind=${behind}"
echo "upstream_latest=${upstream_latest}"
} >> "$GITHUB_OUTPUT"

{
echo "# ${tag}"
echo
echo "| | |"
echo "| --- | --- |"
echo "| Upstream base | \`${base_tag}\` (\`$(git rev-parse --short "${base_tag}^{commit}")\`) |"
echo "| Release commit | \`$(git rev-parse --short HEAD)\` |"
echo "| Overlay commits | ${overlay} |"
echo "| Behind upstream | ${behind} |"
echo "| Commits since upstream release | ${since_base} |"
if [ "$upstream_latest" != "$base_tag" ]; then
echo
echo "> [!WARNING]"
echo "> Upstream's newest release is \`${upstream_latest}\`, but this release sits on \`${base_tag}\` and is ${behind} commit(s) behind upstream main."
fi
} >> "$GITHUB_STEP_SUMMARY"

- name: Create and push tag
if: ${{ !inputs.dry_run }}
env:
TAG: ${{ steps.version.outputs.tag }}
BASE_TAG: ${{ steps.version.outputs.base_tag }}
OVERLAY: ${{ steps.version.outputs.overlay }}
BEHIND: ${{ steps.version.outputs.behind }}
SINCE_BASE: ${{ steps.version.outputs.since_base }}
UPSTREAM_LATEST: ${{ steps.version.outputs.upstream_latest }}
run: |
set -euo pipefail

git config user.name "github-actions[bot]"
git config user.email "41898282+github-actions[bot]@users.noreply.github.com"

{
echo "${TAG}"
echo
echo "Upstream base: ${BASE_TAG} ($(git rev-parse --short "${BASE_TAG}^{commit}"))"
echo "Release commit: $(git rev-parse HEAD)"
echo "Overlay commits: ${OVERLAY}"
echo "Behind upstream: ${BEHIND}"
echo "Since upstream release: ${SINCE_BASE}"
if [ "${UPSTREAM_LATEST}" != "${BASE_TAG}" ]; then
echo
echo "Upstream's newest release at tag time was ${UPSTREAM_LATEST}."
fi
} > /tmp/tag-message.txt

git tag -a "${TAG}" -F /tmp/tag-message.txt
git push origin "refs/tags/${TAG}"
echo "::notice::Created tag ${TAG}"

- name: Publish GitHub Release
if: ${{ !inputs.dry_run && inputs.create_release }}
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# Pin gh to this repo: the checkout has an `upstream` remote (added by
# the tag-fetch step), and gh otherwise resolves the base repo to
# paradigmxyz/centaur and fails with "tag ... has not been pushed".
GH_REPO: ${{ github.repository }}
TAG: ${{ steps.version.outputs.tag }}
PREV: ${{ steps.version.outputs.prev }}
run: |
set -euo pipefail
args=(--title "${TAG}" --generate-notes)
if [ -n "${PREV}" ]; then
args+=(--notes-start-tag "${PREV}")
fi
url=$(gh release create "${TAG}" "${args[@]}")
echo "::notice::Published release ${url}"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "Release: ${url}" >> "$GITHUB_STEP_SUMMARY"

- name: Report dry run
if: ${{ inputs.dry_run }}
env:
TAG: ${{ steps.version.outputs.tag }}
run: |
echo "::notice::Dry run — next version would be ${TAG}. Nothing was created."
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "**Dry run** — nothing was created." >> "$GITHUB_STEP_SUMMARY"
18 changes: 18 additions & 0 deletions contrib/chart/templates/console.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,24 @@ spec:
- name: CENTAUR_CONSOLE_SSO_EMAIL_DOMAINS
value: {{ join "," . | quote }}
{{- end }}
# GitHub App credentials used by the github_app secret source. The
# console mints short-lived installation tokens for per-sandbox
# proxies over /proxy/sync; raw tokens still never enter sandboxes.
- name: GITHUB_APP_ID
valueFrom:
secretKeyRef:
name: {{ $secretEnv }}
key: {{ printf "%sGITHUB_APP_ID" $prefix }}
- name: GITHUB_APP_INSTALLATION_ID
valueFrom:
secretKeyRef:
name: {{ $secretEnv }}
key: {{ printf "%sGITHUB_APP_INSTALLATION_ID" $prefix }}
- name: GITHUB_APP_PRIVATE_KEY_B64
valueFrom:
secretKeyRef:
name: {{ $secretEnv }}
key: {{ printf "%sGITHUB_APP_PRIVATE_KEY_B64" $prefix }}
{{- if $console.googleOauth.enabled }}
# Google OAuth app credentials (sign-in + brokered token refresh).
# Gated on console.googleOauth.enabled; the keys must exist in the
Expand Down
5 changes: 4 additions & 1 deletion docs/pages/deploying-in-production.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -337,7 +337,10 @@ Use the app page to install the bot, copy the Bot User OAuth Token for
7. Subscribe to `app_mention` and to the message events you want Centaur to see:
`message.channels`, `message.groups`, and `message.im`. To automatically join
newly-created public channels, set `slackbotv2.autoJoinCreatedChannels` to
`true` and subscribe to `channel_created`.
`true` and subscribe to `channel_created`. When the file-event workflow
trigger is enabled (`SLACKBOTV2_FILE_EVENT_WORKFLOW_NAME`), also subscribe
to the file events it matches — `file_shared` and `file_change` by default —
and grant `files:read` if the title-keyword filter is configured.
8. Enable Interactivity and set its Request URL to the same
`https://<your-host>/api/webhooks/slack` URL. Block Kit actions are emitted
to the workflow engine as `slack.block_action.<action_id>` events.
Expand Down
Loading
Loading