Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
103 changes: 103 additions & 0 deletions .github/workflows/publish-image.yml
Comment thread
aaltshuler marked this conversation as resolved.
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
name: Publish container image
Comment thread
aaltshuler marked this conversation as resolved.

# Build and publish the omnigraph-server container image to GHCR so
# downstream deployments can pull it without building from source.
#
# Kept separate from release.yml on purpose: an image-publish failure must
# not block the binary release / Homebrew chain, and a manual backfill of an
# old tag must not re-run the four-platform release matrix.
#
# Triggers (same dual pattern as release.yml):
# - push of a v* tag (normal release)
# - workflow_dispatch with an explicit `tag` (publish an image for a past
# tag; resolves the same `${{ inputs.tag || github.ref_name }}`)
#
# The binaries are compiled inside a rust:1-bookworm container, NOT on the
# runner host: the Dockerfile's runtime base is debian bookworm-slim
# (glibc 2.36), and binaries built on ubuntu-latest (glibc 2.39) do not run
# there. Builder and runtime must share the same distro release.

on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
tag:
description: "Tag to publish an image for (e.g. v0.8.1). Required for manual dispatches."
required: true
type: string

jobs:
publish_image:
name: Build and push image
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
env:
RELEASE_TAG: ${{ inputs.tag || github.ref_name }}
steps:
# The dispatch `tag` input is free-form; without this guard a dispatcher
# could pass a branch name or SHA and publish non-release code under a
# release-looking image tag. Require an existing v* tag ref.
- name: Validate dispatch tag
if: github.event_name == 'workflow_dispatch'
env:
GH_TOKEN: ${{ github.token }}
run: |
[[ "${RELEASE_TAG}" == v* ]] \
|| { echo "tag must be a v* release tag, got: ${RELEASE_TAG}" >&2; exit 1; }
gh api "repos/${GITHUB_REPOSITORY}/git/ref/tags/${RELEASE_TAG}" >/dev/null \
|| { echo "no such tag: ${RELEASE_TAG}" >&2; exit 1; }

- name: Checkout source
uses: actions/checkout@v5.0.1
with:
# Fully qualified so a branch of the same name can never shadow the tag.
ref: refs/tags/${{ env.RELEASE_TAG }}

- name: Build release binaries (bookworm builder)
run: |
docker run --rm -v "$PWD:/work" -w /work rust:1-bookworm bash -euxc '
apt-get update
apt-get install -y --no-install-recommends protobuf-compiler libprotobuf-dev
# --features omnigraph-server/aws: include the AWS Secrets Manager
# bearer-token source so one public image serves both token modes.
cargo build --release --locked -p omnigraph-cli -p omnigraph-server --features omnigraph-server/aws
'

- name: Log in to GHCR
run: |
echo "${{ secrets.GITHUB_TOKEN }}" \
| docker login ghcr.io --username "${{ github.actor }}" --password-stdin

- name: Build and push image
run: |
owner="${GITHUB_REPOSITORY_OWNER,,}"
image="ghcr.io/${owner}/omnigraph-server"
docker build -t "${image}:${RELEASE_TAG}" .
docker push "${image}:${RELEASE_TAG}"

# Move `latest` only on real tag pushes — a dispatch backfill of an
# old tag must not repoint `latest` at it.
if [[ "${GITHUB_EVENT_NAME}" == "push" ]]; then
docker tag "${image}:${RELEASE_TAG}" "${image}:latest"
docker push "${image}:latest"
fi

- name: Report pinned digest
run: |
owner="${GITHUB_REPOSITORY_OWNER,,}"
image="ghcr.io/${owner}/omnigraph-server"
digest=$(docker inspect --format='{{index .RepoDigests 0}}' "${image}:${RELEASE_TAG}")
{
echo "## Container image published"
echo
echo '```'
echo "${image}:${RELEASE_TAG}"
echo "${digest}"
echo '```'
echo
echo "Pin deployments to the digest form."
} >> "$GITHUB_STEP_SUMMARY"
1 change: 1 addition & 0 deletions docs/dev/ci.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@
- **release-edge.yml**: on every push to main, retags `edge`, builds Linux x86_64 / Linux arm64 / macOS arm64 archives and Windows x86_64 zip + sha256, publishes a rolling prerelease, then smoke-tests the Windows PowerShell installer against `edge`.
- **release.yml**: on `v*` tags, builds the Linux x86_64 / Linux arm64 / macOS arm64 archives and Windows x86_64 zip release matrix, updates the Homebrew tap (`scripts/update-homebrew-formula.sh`) by pushing the regenerated formula to `ModernRelay/homebrew-tap`, and smoke-tests the Windows PowerShell installer against the tag.
- **package.yml**: manual ECR image build; emits two image tags per commit (`<sha>`, `<sha>-aws`) via CodeBuild.
- **publish-image.yml**: builds and pushes the public `omnigraph-server` container image to GHCR (`ghcr.io/modernrelay/omnigraph-server:<tag>`) on `v*` tag pushes, plus manual `workflow_dispatch` with an explicit existing `v*` tag to backfill an image for a past release. Compiles the binaries inside a `rust:1-bookworm` builder container to match the Dockerfile's bookworm-slim runtime glibc (host-built ubuntu binaries do not run there), builds with `--features omnigraph-server/aws`, and moves `latest` only on real tag pushes — never on backfill dispatches. Separate from release.yml so an image-publish failure cannot block the binary release / Homebrew chain.
9 changes: 8 additions & 1 deletion docs/user/deployment.md
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,14 @@ endpoint and credentials. CI exercises this path against containerized RustFS.

## Container Deployment

Build the image:
Pull the prebuilt public image (published to GHCR for every `v*` release by
`publish-image.yml`; built with the `aws` feature, linux/amd64):

```bash
docker pull ghcr.io/modernrelay/omnigraph-server:v0.8.1
```

Or build it yourself:

```bash
docker build -t omnigraph-server:local .
Expand Down