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
3 changes: 0 additions & 3 deletions .github/workflows/discovery-service-docker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ permissions:
on:
push:
branches: [main]
tags: ['*']
paths:
- 'crates/**'
- 'Cargo.toml'
Expand Down Expand Up @@ -46,7 +45,6 @@ jobs:
tags: |
type=ref,event=branch
type=ref,event=pr
type=ref,event=tag
- uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5
id: build
with:
Expand Down Expand Up @@ -87,7 +85,6 @@ jobs:
tags: |
type=ref,event=branch
type=ref,event=pr
type=ref,event=tag
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
path: /tmp/digests
Expand Down
141 changes: 141 additions & 0 deletions .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
name: Docker-Publish

# Release-only. Builds the multi-arch discovery-service and proof-interceptor images
# and publishes them to ghcr.io when a `PRIVACY-*` tag is pushed. The image tag is the
# git tag verbatim (PRIVACY-0.14.3-RC.8 -> :PRIVACY-0.14.3-RC.8), which is what the
# per-env values files pin.
#
# The third image the chart deploys, transaction-prover, is built from
# starkware-libs/sequencer by starknet_transaction_prover_docker_publish.yml on the
# same `PRIVACY-*` tag convention.
#
# discovery-service-docker.yaml and proof-interceptor-docker.yaml still build on PRs
# and main, but no longer on tags — this workflow is the only writer of release tags.

on:
workflow_dispatch:
push:
tags:
- "PRIVACY-*"

permissions:
contents: read
packages: write

# A tag is an immutable release — never cancel a run that may already have pushed.
concurrency:
group: docker-publish-${{ github.ref }}
cancel-in-progress: false

env:
REGISTRY: ghcr.io
IMAGE_PREFIX: ghcr.io/${{ github.repository }}

jobs:
build:
name: Build ${{ matrix.image.name }} (${{ matrix.platform.name }})
runs-on: ${{ matrix.platform.runner }}
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
image:
- name: discovery-service
file: deploy/discovery-service/Dockerfile
- name: proof-interceptor
file: deploy/proof-interceptor/Dockerfile
platform:
- name: linux/amd64
arch: amd64
runner: ubuntu-latest
- name: linux/arm64
arch: arm64
runner: ubuntu-24.04-arm
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6

- uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3

- uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- id: meta
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6
with:
images: ${{ env.IMAGE_PREFIX }}/${{ matrix.image.name }}

- uses: docker/build-push-action@53b7df96c91f9c12dcc8a07bcb9ccacbed38856a # v7
id: build
with:
context: .
file: ${{ matrix.image.file }}
platforms: ${{ matrix.platform.name }}
cache-from: type=gha,scope=${{ matrix.image.name }}-${{ matrix.platform.arch }}
cache-to: type=gha,mode=max,scope=${{ matrix.image.name }}-${{ matrix.platform.arch }}
labels: ${{ steps.meta.outputs.labels }}
outputs: type=image,"name=${{ env.IMAGE_PREFIX }}/${{ matrix.image.name }}",push-by-digest=true,name-canonical=true,push=true

- run: |
mkdir -p /tmp/digests
digest="${{ steps.build.outputs.digest }}"
touch "/tmp/digests/${digest#sha256:}"
shell: bash

- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: digest-${{ matrix.image.name }}-${{ matrix.platform.arch }}
path: /tmp/digests/*
if-no-files-found: error
retention-days: 1

merge:
name: Merge ${{ matrix.image }} manifest
runs-on: ubuntu-latest
needs: build
timeout-minutes: 10
strategy:
fail-fast: false
matrix:
image:
- discovery-service
- proof-interceptor
steps:
- uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f # v3

- uses: docker/login-action@af1e73f918a031802d376d3c8bbc3fe56130a9b0 # v4
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- id: meta
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302 # v6
with:
images: ${{ env.IMAGE_PREFIX }}/${{ matrix.image }}
# No `type=semver`: PRIVACY-* tags do not parse as semver, so it would emit nothing.
tags: |
# The git tag verbatim, e.g. PRIVACY-0.14.3-RC.8 -> :PRIVACY-0.14.3-RC.8
type=raw,enable=${{ github.event_name == 'push' }},value={{tag}}
# Manual dispatch — never writes a release tag.
type=raw,value=test-{{sha}},enable=${{ github.event_name == 'workflow_dispatch' }}

- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
path: /tmp/digests
pattern: digest-${{ matrix.image }}-*
merge-multiple: true

- name: Create multi-arch manifest
run: |
cd /tmp/digests
tags=$(jq -cr '.tags | map("-t " + .) | join(" ")' <<< '${{ steps.meta.outputs.json }}')
if [ -z "$tags" ]; then
echo "No tags were generated by docker/metadata-action; refusing to publish an untagged manifest."
exit 1
fi
sources=$(printf '${{ env.IMAGE_PREFIX }}/${{ matrix.image }}@sha256:%s ' *)
docker buildx imagetools create $tags $sources
shell: bash
175 changes: 175 additions & 0 deletions .github/workflows/helm-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
name: Helm-Publish

# Publishes the privacy-starknet Helm chart to ghcr.io as a public OCI artifact, on
# the SAME trigger as docker-publish.yml — a `PRIVACY-*` tag, plus manual dispatch.
#
# VERSIONING — Chart.yaml is the source of truth. Release tags (PRIVACY-0.14.3-RC.8)
# are not semver and `helm package` rejects a non-semver chart version, so the tag is
# carried in appVersion instead. BUMPING Chart.yaml IS MANUAL: forget, and this
# workflow finds the version already published and skips, loudly, in the summary.
#
# ghcr.io has NO immutable tags — a re-push would silently replace a published
# version under every consumer pinned to it. The already-published check below is
# the only thing preventing that.
#
# Consumed by starkware-envs-production via apps.yaml `helm.oci_registry`.
# infra-tools only logs in to *-docker.pkg.dev hosts, so it pulls this chart
# ANONYMOUSLY: the ghcr package must be Public.

on:
workflow_dispatch: {}
push:
tags:
- "PRIVACY-*"

# A tag is an immutable release — never cancel a run that may already have pushed.
concurrency:
group: helm-publish-${{ github.ref }}
cancel-in-progress: false

env:
REGISTRY: ghcr.io
OCI_REPO: oci://ghcr.io/${{ github.repository }}/charts

jobs:
publish:
runs-on: ubuntu-latest
timeout-minutes: 15
permissions:
contents: read
packages: write
env:
CHART_DIR: deploy/helm/privacy-starknet
EXPECTED_CHART_NAME: privacy-starknet
HELM_VERSION: v3.16.4
BIN_DIR: ${{ github.workspace }}/.chart-release-bin
steps:
- name: Checkout repository
uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6

# Pinned rather than the runner's preinstalled helm, so the tool producing a
# released artifact doesn't drift when GitHub rolls the runner image.
- name: Install helm
run: |
set -euo pipefail
mkdir -p "$BIN_DIR"
curl -fsSL "https://get.helm.sh/helm-${HELM_VERSION}-linux-amd64.tar.gz" \
| tar -xz --strip-components=1 -C "$BIN_DIR" linux-amd64/helm
chmod +x "$BIN_DIR/helm"
echo "$BIN_DIR" >> "$GITHUB_PATH"

- name: Resolve chart name and version
id: chart
run: |
set -euo pipefail
# Parse the YAML rather than grepping it: `helm show chart` alphabetizes
# keys, so a future `dependencies:` block would put a subchart's `name:`
# first. python3 with PyYAML is on the runner; yq is not guaranteed.
meta_name='import sys,yaml; print((yaml.safe_load(sys.stdin) or {}).get("name",""))'
meta_vers='import sys,yaml; print((yaml.safe_load(sys.stdin) or {}).get("version",""))'
name=$(helm show chart "$CHART_DIR" | python3 -c "$meta_name" | tr -d '[:space:]')
version=$(helm show chart "$CHART_DIR" | python3 -c "$meta_vers" | tr -d '[:space:]')

if [ "$name" != "$EXPECTED_CHART_NAME" ]; then
echo "::error::resolved chart name '$name', expected '$EXPECTED_CHART_NAME' from $CHART_DIR/Chart.yaml"
exit 1
fi
if ! printf '%s' "$version" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.-]+)?(\+[0-9A-Za-z.-]+)?$'; then
echo "::error::'$version' is not valid semver — $CHART_DIR/Chart.yaml version must be X.Y.Z"
exit 1
fi

# appVersion: the release tag on a tag push (the same tag docker-publish.yml
# gives the images), the short SHA on a manual dispatch.
if [ "${{ github.event_name }}" = "push" ]; then
app_version="${GITHUB_REF_NAME}"
else
app_version=$(git rev-parse --short HEAD)
fi

{
echo "name=$name"
echo "version=$version"
echo "app_version=$app_version"
echo "ref=${OCI_REPO#oci://}/${name}:${version}"
} >> "$GITHUB_OUTPUT"

- name: Log in to ghcr.io
run: |
set -euo pipefail
printf '%s' "${{ secrets.GITHUB_TOKEN }}" \
| helm registry login "$REGISTRY" -u "${{ github.actor }}" --password-stdin

# ghcr has no immutable tags, so re-pushing an existing version would REPLACE
# it. Checking first turns a re-run into a green skip instead.
- name: Check whether this version is already published
id: exists
run: |
set -uo pipefail
if helm show chart "${{ env.OCI_REPO }}/${{ steps.chart.outputs.name }}" \
--version "${{ steps.chart.outputs.version }}" >/dev/null 2>&1; then
echo "published=true" >> "$GITHUB_OUTPUT"
else
echo "published=false" >> "$GITHUB_OUTPUT"
fi

- name: helm lint
if: steps.exists.outputs.published == 'false'
run: helm lint "$CHART_DIR" -f "$CHART_DIR/ci/default-values.yaml"

- name: Package and push
if: steps.exists.outputs.published == 'false'
run: |
set -euo pipefail
# --app-version overrides Chart.yaml without editing it.
rm -rf dist && mkdir -p dist
helm package "$CHART_DIR" \
--destination dist \
--version "${{ steps.chart.outputs.version }}" \
--app-version "${{ steps.chart.outputs.app_version }}"

# Push the artifact helm actually wrote rather than re-deriving its filename.
shopt -s nullglob
pkgs=(dist/*.tgz)
if [ "${#pkgs[@]}" -ne 1 ]; then
echo "::error::expected exactly one packaged chart in dist/, found ${#pkgs[@]}: ${pkgs[*]:-none}"
exit 1
fi
echo "pushing ${pkgs[0]}"
helm push "${pkgs[0]}" "${{ env.OCI_REPO }}"

- name: Summary
run: |
set -euo pipefail
if [ "${{ steps.exists.outputs.published }}" = "true" ]; then
{
echo "### ⏭️ Nothing published"
echo ""
echo "Chart version \`${{ steps.chart.outputs.version }}\` is ALREADY published at"
echo "\`${{ steps.chart.outputs.ref }}\` and was left untouched."
echo ""
echo "If this run was meant to publish chart changes, **bump \`version\` in"
echo "\`$CHART_DIR/Chart.yaml\`** and re-run — otherwise this release ships"
echo "images with no matching chart."
} >> "$GITHUB_STEP_SUMMARY"
else
{
echo "### ✅ Published \`${{ steps.chart.outputs.ref }}\`"
echo ""
echo "appVersion \`${{ steps.chart.outputs.app_version }}\`."
echo ""
echo "Consume it from a gitops env (\`starknet-privacy/<env>/apps.yaml\`) with:"
echo ""
echo '```yaml'
echo " helm:"
echo " oci_registry: ${{ env.OCI_REPO }}/${{ steps.chart.outputs.name }}"
echo " oci_version: ${{ steps.chart.outputs.version }}"
echo '```'
echo ""
echo "The package must be Public for anonymous pulls:"
echo ""
echo '```bash'
echo "helm pull ${{ env.OCI_REPO }}/${{ steps.chart.outputs.name }} --version ${{ steps.chart.outputs.version }}"
echo '```'
} >> "$GITHUB_STEP_SUMMARY"
fi
3 changes: 0 additions & 3 deletions .github/workflows/proof-interceptor-docker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ permissions:
on:
push:
branches: [main]
tags: ['*']
paths:
- 'proof-interceptor/**'
- 'sdk/**'
Expand Down Expand Up @@ -46,7 +45,6 @@ jobs:
tags: |
type=ref,event=branch
type=ref,event=pr
type=ref,event=tag
- uses: docker/build-push-action@ca052bb54ab0790a636c9b5f226502c73d547a25 # v5
id: build
with:
Expand Down Expand Up @@ -87,7 +85,6 @@ jobs:
tags: |
type=ref,event=branch
type=ref,event=pr
type=ref,event=tag
- uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
with:
path: /tmp/digests
Expand Down
Loading