Something went wrong. Try again.
[READ-ONLY] Mirror of https://github.com/openstatusHQ/openstatus. ๐ซ Status page with uptime monitoring & API monitoring as code ๐ซ openstatus.dev
bun drizzle-orm monitoring monitoring-as-code nextjs observability on-call open-source shadcn-ui status-page statuspage synthetic-monitoring tinybird turso uptime uptime-checker uptime-monitor
Something went wrong. Try again.
9.7 kB ยท 257 lines
YAML
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258name: Publish Docker Images
on: push: branches: - main paths: - "apps/server/**" - "apps/dashboard/**" - "apps/workflows/**" - "apps/private-location/**" - "apps/status-page/**" - "apps/checker/**" - "packages/**" - "docker-compose.yaml" workflow_dispatch: inputs: services: description: 'Services to build (comma-separated)' required: false default: 'server,dashboard,workflows,private-location,status-page,checker,db-migrate' type: string
concurrency: group: docker-${{ github.ref }} cancel-in-progress: true
env: REGISTRY: ghcr.io IMAGE_NAME: openstatus
jobs: prepare: runs-on: ubuntu-latest outputs: services: ${{ steps.set-services.outputs.services }} matrix: ${{ steps.set-matrix.outputs.matrix }} steps: - name: Checkout repository uses: actions/checkout@v6 with: fetch-depth: 2
- name: Determine services to build id: set-services run: | if [ "${{ github.event_name }}" = "workflow_dispatch" ]; then # Convert comma-separated input to JSON array SERVICES=$(echo "${{ inputs.services }}" | tr ',' '\n' | jq -R . | jq -sc .) else # Detect changed files CHANGED=$(git diff --name-only HEAD~1 HEAD) SERVICES_LIST=()
# packages/** changes affect all services if echo "$CHANGED" | grep -q '^packages/'; then SERVICES_LIST=("server" "dashboard" "workflows" "private-location" "status-page" "checker" "db-migrate") else for svc in server dashboard workflows private-location status-page checker; do if echo "$CHANGED" | grep -q "^apps/$svc/"; then SERVICES_LIST+=("$svc") fi done fi
SERVICES=$(printf '%s\n' "${SERVICES_LIST[@]}" | jq -R . | jq -sc 'map(select(. != ""))') fi
echo "services=$SERVICES" >> "$GITHUB_OUTPUT" echo "Building services: $SERVICES"
# Expand the selected services into explicit build legs (service ร platform). # Done here rather than with matrix `include`: an include entry whose `service` # is not in the selected list is not merged but becomes a new combination, # which used to make every push build all seven images (and now would leave # legs without a runner). - name: Build matrix id: set-matrix env: SERVICES: ${{ steps.set-services.outputs.services }} run: | MATRIX=$(jq -c --argjson services "$SERVICES" ' { "server": {context: ".", dockerfile: "apps/server/Dockerfile"}, "dashboard": {context: ".", dockerfile: "apps/dashboard/Dockerfile"}, "workflows": {context: ".", dockerfile: "apps/workflows/Dockerfile"}, "private-location": {context: "apps/private-location", dockerfile: "apps/private-location/Dockerfile"}, "status-page": {context: ".", dockerfile: "apps/status-page/Dockerfile"}, "checker": {context: "apps/checker", dockerfile: "apps/checker/Dockerfile"}, "db-migrate": {context: ".", dockerfile: "packages/db/Dockerfile"} } as $defs | [ {platform: "linux/amd64", arch: "amd64", runner: "ubuntu-latest"}, {platform: "linux/arm64", arch: "arm64", runner: "ubuntu-24.04-arm"} ] as $platforms | [ $services[] as $svc | $platforms[] | . + {service: $svc} + $defs[$svc] ] ' <<< 'null') echo "matrix=$MATRIX" >> "$GITHUB_OUTPUT" echo "$MATRIX" | jq .
# One job per service ร platform. arm64 runs on a native arm runner instead of # QEMU emulation on x86 (7ร slower). Each job pushes its single-arch image by # digest; `merge` below stitches the digests into one multi-arch manifest. build: name: build (${{ matrix.service }}, ${{ matrix.arch }}) runs-on: ${{ matrix.runner }} needs: [prepare] if: needs.prepare.outputs.services != '[]' timeout-minutes: 30 permissions: contents: read packages: write
strategy: fail-fast: false matrix: include: ${{ fromJson(needs.prepare.outputs.matrix) }}
steps: - name: Checkout repository uses: actions/checkout@v6
- name: Lowercase owner run: | echo "OWNER_LC=${GITHUB_REPOSITORY_OWNER,,}" >> $GITHUB_ENV echo "IMAGE=${{ env.REGISTRY }}/${GITHUB_REPOSITORY_OWNER,,}/${{ env.IMAGE_NAME }}-${{ matrix.service }}" >> $GITHUB_ENV
- name: Set up Docker Buildx uses: docker/setup-buildx-action@v3
- name: Log in to Container Registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata id: meta uses: docker/metadata-action@v5 with: images: ${{ env.IMAGE }}
# Layer cache lives in the registry (one ref per service ร arch) rather than # the 10 GB GitHub Actions cache, which the multi-GB builder layers of the # Next.js apps kept evicting. Blobs already in the registry are not re-uploaded. - name: Build and push Docker image (by digest) id: build uses: docker/build-push-action@v6 with: context: ${{ matrix.context }} file: ${{ matrix.dockerfile }} platforms: ${{ matrix.platform }} labels: ${{ steps.meta.outputs.labels }} outputs: type=image,name=${{ env.IMAGE }},push-by-digest=true,name-canonical=true,push=true cache-from: type=registry,ref=${{ env.IMAGE }}:buildcache-${{ matrix.arch }} cache-to: type=registry,ref=${{ env.IMAGE }}:buildcache-${{ matrix.arch }},mode=max,image-manifest=true,oci-mediatypes=true provenance: false
- name: Export digest run: | mkdir -p /tmp/digests digest="${{ steps.build.outputs.digest }}" touch "/tmp/digests/${digest#sha256:}"
- name: Upload digest uses: actions/upload-artifact@v4 with: name: digests-${{ matrix.service }}-${{ matrix.arch }} path: /tmp/digests/* if-no-files-found: error retention-days: 1
# Combine the per-arch digests into a multi-arch manifest list carrying the # real tags, then generate the SBOM from that manifest. # Runs even if some `build` legs failed so one broken service does not block # publishing the others; a service missing a digest fails its own merge below. merge: runs-on: ubuntu-latest needs: [prepare, build] if: ${{ !cancelled() && needs.prepare.outputs.services != '[]' }} timeout-minutes: 15 permissions: contents: read id-token: write packages: write
strategy: fail-fast: false matrix: service: ${{ fromJson(needs.prepare.outputs.services) }}
steps: - name: Lowercase owner run: | echo "OWNER_LC=${GITHUB_REPOSITORY_OWNER,,}" >> $GITHUB_ENV echo "IMAGE=${{ env.REGISTRY }}/${GITHUB_REPOSITORY_OWNER,,}/${{ env.IMAGE_NAME }}-${{ matrix.service }}" >> $GITHUB_ENV
- name: Download digests uses: actions/download-artifact@v4 with: path: /tmp/digests pattern: digests-${{ matrix.service }}-* merge-multiple: true
- name: Set up Docker Buildx uses: docker/setup-buildx-action@v3
- name: Log in to Container Registry uses: docker/login-action@v3 with: registry: ${{ env.REGISTRY }} username: ${{ github.repository_owner }} password: ${{ secrets.GITHUB_TOKEN }}
- name: Extract metadata id: meta uses: docker/metadata-action@v5 with: images: ${{ env.IMAGE }} tags: | type=ref,event=branch type=ref,event=pr type=sha,prefix= type=raw,value=latest,enable={{is_default_branch}}
- name: Create manifest list and push id: manifest working-directory: /tmp/digests run: | # Never publish a partial (single-arch) manifest under the real tags. count=$(ls | wc -l) if [ "$count" -ne 2 ]; then echo "::error::expected 2 digests (amd64 + arm64) for ${{ matrix.service }}, found $count" ls -la exit 1 fi docker buildx imagetools create \ $(jq -cr '.tags | map("-t " + .) | join(" ")' <<< "$DOCKER_METADATA_OUTPUT_JSON") \ $(printf '${{ env.IMAGE }}@sha256:%s ' *) digest=$(docker buildx imagetools inspect "${{ env.IMAGE }}:${{ steps.meta.outputs.version }}" --format '{{json .Manifest.Digest}}' | tr -d '"') echo "digest=$digest" >> "$GITHUB_OUTPUT" docker buildx imagetools inspect "${{ env.IMAGE }}@${digest}"
- name: Generate SBOM uses: anchore/sbom-action@v0 with: image: ${{ env.IMAGE }}@${{ steps.manifest.outputs.digest }} format: spdx-json output-file: sbom-${{ matrix.service }}.spdx.json
- name: Upload SBOM uses: actions/upload-artifact@v4 with: name: sbom-${{ matrix.service }} path: sbom-${{ matrix.service }}.spdx.json retention-days: 30