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
243 changes: 243 additions & 0 deletions .github/workflows/build-and-deploy.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,243 @@
name: Build, push, and deploy images

# Builds all 7 Rodan images, pushes them to the private GitHub Container Registry, and
# (on manual dispatch or version tags) deploys the app tier to the k3s cluster.
# Replaces the DockerHub autobuild hooks (hooks/build, hooks/push).
#
# Tags:
# git tag v* -> ghcr.io/ddmal/<name>:<version> (+ deploy)
# push to develop -> ghcr.io/ddmal/<name>:nightly
# pull_request -> build only, no push (validates Dockerfiles)
# workflow_dispatch -> optional tag input (defaults to "nightly") (+ deploy)
# Every pushed build also gets an immutable ghcr.io/ddmal/<name>:sha-<gitsha> tag,
# which the deploy job pins so the rollout is guaranteed to pull the new image.

on:
push:
branches: [develop]
tags: ['v*']
workflow_dispatch:
inputs:
tag:
description: "Image tag to build/push (e.g. v3.4.0 or nightly)"
required: false
default: "nightly"

permissions:
contents: read
packages: write

concurrency:
group: build-and-deploy-${{ github.ref }}
cancel-in-progress: true

env:
REGISTRY: ghcr.io
OWNER: ddmal

jobs:
setup:
runs-on: ubuntu-latest
outputs:
tag: ${{ steps.meta.outputs.tag }}
push: ${{ steps.meta.outputs.push }}
short_sha: ${{ steps.meta.outputs.short_sha }}
steps:
- id: meta
env:
EVENT: ${{ github.event_name }}
REF_TYPE: ${{ github.ref_type }}
REF_NAME: ${{ github.ref_name }}
INPUT_TAG: ${{ github.event.inputs.tag }}
PR_NUMBER: ${{ github.event.number }}
run: |
set -euo pipefail
if [ "$EVENT" = "workflow_dispatch" ] && [ -n "$INPUT_TAG" ]; then
TAG="$INPUT_TAG"; PUSH=true
elif [ "$REF_TYPE" = "tag" ]; then
TAG="$REF_NAME"; PUSH=true # e.g. v3.4.0
elif [ "$EVENT" = "pull_request" ]; then
TAG="pr-$PR_NUMBER"; PUSH=false # build only, never push
elif [ "$REF_NAME" = "develop" ]; then
TAG="nightly"; PUSH=true
else
TAG="$REF_NAME"; PUSH=true
fi
# Docker tags cannot contain "/"
TAG="$(printf '%s' "$TAG" | tr '/' '-')"
echo "tag=$TAG" >> "$GITHUB_OUTPUT"
echo "push=$PUSH" >> "$GITHUB_OUTPUT"
echo "short_sha=${GITHUB_SHA::7}" >> "$GITHUB_OUTPUT"
echo "Resolved tag=$TAG push=$PUSH sha=${GITHUB_SHA::7} (event=$EVENT ref_type=$REF_TYPE ref=$REF_NAME)"

# Ordered dependency chain on ONE runner so the hardcoded
# `FROM ddmal/<base>:${VERSION}` lines resolve to the locally-built images.
chain:
needs: setup
runs-on: ubuntu-latest
env:
TAG: ${{ needs.setup.outputs.tag }}
PUSH: ${{ needs.setup.outputs.push }}
SHA_TAG: sha-${{ needs.setup.outputs.short_sha }}
steps:
- uses: actions/checkout@v4

- name: Free disk space
# Reclaims ~25-35 GB on ubuntu-latest.
uses: jlumbroso/free-disk-space@v1.3.1
with:
tool-cache: true
large-packages: false
docker-images: false

- name: Log in to GHCR
if: env.PUSH == 'true'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build rodan-python3-celery (base)
run: |
docker build \
--build-arg BRANCHES=develop \
--build-arg VERSION="$TAG" \
-t "$REGISTRY/$OWNER/rodan-python3-celery:$TAG" \
-t "$REGISTRY/$OWNER/rodan-python3-celery:$SHA_TAG" \
-t "ddmal/rodan-python3-celery:$TAG" \
-f python3-celery/Dockerfile \
.

- name: Build rodan-main (FROM rodan-python3-celery)
run: |
docker build \
--build-arg BRANCHES=develop \
--build-arg VERSION="$TAG" \
--build-arg build_hash="${{ github.sha }}" \
-t "$REGISTRY/$OWNER/rodan-main:$TAG" \
-t "$REGISTRY/$OWNER/rodan-main:$SHA_TAG" \
-t "ddmal/rodan-main:$TAG" \
-f rodan-main/Dockerfile \
.

- name: Build nginx (FROM rodan-main, bakes static files)
run: |
docker build \
--build-arg VERSION="$TAG" \
-t "$REGISTRY/$OWNER/nginx:$TAG" \
-t "$REGISTRY/$OWNER/nginx:$SHA_TAG" \
./nginx

- name: Push chain images
if: env.PUSH == 'true'
run: |
docker push "$REGISTRY/$OWNER/rodan-python3-celery:$TAG"
docker push "$REGISTRY/$OWNER/rodan-python3-celery:$SHA_TAG"
docker push "$REGISTRY/$OWNER/rodan-main:$TAG"
docker push "$REGISTRY/$OWNER/rodan-main:$SHA_TAG"
docker push "$REGISTRY/$OWNER/nginx:$TAG"
docker push "$REGISTRY/$OWNER/nginx:$SHA_TAG"

# Independent images — each on its own runner, in parallel.
independent:
needs: setup
runs-on: ubuntu-latest
env:
TAG: ${{ needs.setup.outputs.tag }}
PUSH: ${{ needs.setup.outputs.push }}
SHA_TAG: sha-${{ needs.setup.outputs.short_sha }}
strategy:
fail-fast: false
matrix:
include:
- name: rodan-client
dockerfile: rodan-client/Dockerfile
context: ./rodan-client
args: "--build-arg BRANCHES=develop"
- name: rodan-gpu-celery
dockerfile: gpu-celery/Dockerfile
context: .
args: "--build-arg BRANCHES=develop"
- name: postgres-plpython
dockerfile: postgres/Dockerfile
context: .
args: ""
- name: iipsrv
dockerfile: iipsrv/Dockerfile
context: ./iipsrv
args: ""
steps:
- uses: actions/checkout@v4

- name: Free disk space
uses: jlumbroso/free-disk-space@v1.3.1
with:
tool-cache: true
large-packages: false
docker-images: false

- name: Log in to GHCR
if: env.PUSH == 'true'
uses: docker/login-action@v3
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

- name: Build ${{ matrix.name }}
run: |
docker build ${{ matrix.args }} \
--build-arg VERSION="$TAG" \
-t "$REGISTRY/$OWNER/${{ matrix.name }}:$TAG" \
-t "$REGISTRY/$OWNER/${{ matrix.name }}:$SHA_TAG" \
-f "${{ matrix.dockerfile }}" \
"${{ matrix.context }}"

- name: Push ${{ matrix.name }}
if: env.PUSH == 'true'
run: |
docker push "$REGISTRY/$OWNER/${{ matrix.name }}:$TAG"
docker push "$REGISTRY/$OWNER/${{ matrix.name }}:$SHA_TAG"

# Deploy the app tier by pinning the immutable sha-<gitsha> tag (guarantees a fresh pull +
# gives `kubectl rollout undo` rollback). Runs only on manual dispatch or version tags.
# postgres / redis / rabbitmq are intentionally left alone (don't bounce the DB on every deploy).
deploy:
needs: [setup, chain, independent]
if: needs.setup.outputs.push == 'true' && (github.event_name == 'workflow_dispatch' || github.ref_type == 'tag')
runs-on: ubuntu-latest
env:
NS: rodan
REG: ghcr.io/ddmal
SHA_TAG: sha-${{ needs.setup.outputs.short_sha }}
steps:
- name: Install kubectl
uses: azure/setup-kubectl@v4

- name: Configure kubeconfig from secret
env:
KUBECONFIG_DATA: ${{ secrets.KUBECONFIG }}
run: |
set -euo pipefail
printf '%s' "$KUBECONFIG_DATA" > "$RUNNER_TEMP/kubeconfig"
chmod 600 "$RUNNER_TEMP/kubeconfig"
echo "KUBECONFIG=$RUNNER_TEMP/kubeconfig" >> "$GITHUB_ENV"

- name: Roll app-tier images to the immutable SHA tag
run: |
set -euo pipefail
kubectl -n "$NS" set image deployment/rodan-main rodan-main="$REG/rodan-main:$SHA_TAG"
kubectl -n "$NS" set image deployment/celery celery="$REG/rodan-main:$SHA_TAG"
kubectl -n "$NS" set image deployment/py3-celery py3-celery="$REG/rodan-python3-celery:$SHA_TAG"
kubectl -n "$NS" set image deployment/gpu-celery gpu-celery="$REG/rodan-gpu-celery:$SHA_TAG"
kubectl -n "$NS" set image deployment/iipsrv iipsrv="$REG/iipsrv:$SHA_TAG"
kubectl -n "$NS" set image deployment/rodan-client rodan-client="$REG/rodan-client:$SHA_TAG"
kubectl -n "$NS" set image deployment/nginx nginx="$REG/nginx:$SHA_TAG"

- name: Wait for rollouts
run: |
set -euo pipefail
for d in rodan-main celery py3-celery gpu-celery iipsrv rodan-client nginx; do
kubectl -n "$NS" rollout status deployment/"$d" --timeout=600s
done
20 changes: 14 additions & 6 deletions .github/workflows/release-bot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ jobs:
steps:
- name: Checkout repo
uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Create output folder
run: mkdir -p ${{ github.workspace }}/checklist_items
- name: Write checklist items to files
Expand Down Expand Up @@ -38,12 +40,18 @@ jobs:

- name: Collect checklist items
run: |
msg=$(awk 'FNR==1 && NR!=1 {print "---"}{print}' checklist_items/*.log)
echo "$msg" >> aggregated.log
sha="*SHA: ${{ github.event.pull_request.head.sha }}*"
sed -i "1i\\
$sha
" aggregated.log
set -o pipefail
shopt -s nullglob
files=(checklist_items/*.log)
{
echo "*SHA: ${{ github.event.pull_request.head.sha }}*"
echo
if [ ${#files[@]} -eq 0 ]; then
echo "_No merge-commit checklist items found ahead of \`master\`._"
else
awk 'FNR==1 && NR!=1 {print "---"}{print}' "${files[@]}"
fi
} > aggregated.log

- name: Update Pull Request
uses: actions/github-script@v5
Expand Down
3 changes: 0 additions & 3 deletions Dockerfile

This file was deleted.

Loading
Loading