mirror of
https://github.com/penpot/penpot.git
synced 2026-08-01 19:06:18 +00:00
🔧 Optimize the bundle and docker images build process
This commit is contained in:
parent
b907bc4cd3
commit
3fc5360df5
102
.github/workflows/build-bundle.yml
vendored
102
.github/workflows/build-bundle.yml
vendored
@ -9,16 +9,6 @@ on:
|
|||||||
type: string
|
type: string
|
||||||
required: true
|
required: true
|
||||||
default: 'develop'
|
default: 'develop'
|
||||||
build_wasm:
|
|
||||||
description: 'BUILD_WASM. Valid values: yes, no'
|
|
||||||
type: string
|
|
||||||
required: false
|
|
||||||
default: 'yes'
|
|
||||||
build_storybook:
|
|
||||||
description: 'BUILD_STORYBOOK. Valid values: yes, no'
|
|
||||||
type: string
|
|
||||||
required: false
|
|
||||||
default: 'yes'
|
|
||||||
workflow_call:
|
workflow_call:
|
||||||
inputs:
|
inputs:
|
||||||
gh_ref:
|
gh_ref:
|
||||||
@ -26,29 +16,21 @@ on:
|
|||||||
type: string
|
type: string
|
||||||
required: true
|
required: true
|
||||||
default: 'develop'
|
default: 'develop'
|
||||||
build_wasm:
|
|
||||||
description: 'BUILD_WASM. Valid values: yes, no'
|
|
||||||
type: string
|
|
||||||
required: false
|
|
||||||
default: 'yes'
|
|
||||||
build_storybook:
|
|
||||||
description: 'BUILD_STORYBOOK. Valid values: yes, no'
|
|
||||||
type: string
|
|
||||||
required: false
|
|
||||||
default: 'yes'
|
|
||||||
|
|
||||||
concurrency:
|
concurrency:
|
||||||
group: ${{ github.workflow }}-${{ inputs.gh_ref }}
|
group: ${{ github.workflow }}-${{ inputs.gh_ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build-bundle:
|
# ── 1. Decide whether there is anything to build ───────────────────────
|
||||||
name: Build and Upload Penpot Bundle
|
check:
|
||||||
|
name: Check current bundle
|
||||||
runs-on: penpot-runner-01
|
runs-on: penpot-runner-01
|
||||||
env:
|
timeout-minutes: 10
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
outputs:
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
gh_ref: ${{ steps.vars.outputs.gh_ref }}
|
||||||
AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }}
|
bundle_version: ${{ steps.vars.outputs.bundle_version }}
|
||||||
|
exists: ${{ steps.check.outputs.exists }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
@ -63,10 +45,52 @@ jobs:
|
|||||||
echo "gh_ref=${{ inputs.gh_ref || github.ref_name }}" >> $GITHUB_OUTPUT
|
echo "gh_ref=${{ inputs.gh_ref || github.ref_name }}" >> $GITHUB_OUTPUT
|
||||||
echo "bundle_version=$(git describe --tags --always)" >> $GITHUB_OUTPUT
|
echo "bundle_version=$(git describe --tags --always)" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
# The uploaded zip carries its version as S3 metadata. If the
|
||||||
|
# existing object was already built from this same commit, the
|
||||||
|
# whole build job is skipped.
|
||||||
|
- name: Check if this bundle is already built
|
||||||
|
id: check
|
||||||
|
env:
|
||||||
|
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
|
AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }}
|
||||||
|
run: |
|
||||||
|
EXISTING_VERSION=$(aws s3api head-object \
|
||||||
|
--bucket ${{ secrets.S3_BUCKET }} \
|
||||||
|
--key "penpot-${{ steps.vars.outputs.gh_ref }}.zip" \
|
||||||
|
--query 'Metadata."bundle-version"' \
|
||||||
|
--output text 2>/dev/null || echo "none")
|
||||||
|
|
||||||
|
if [ "$EXISTING_VERSION" = "${{ steps.vars.outputs.bundle_version }}" ]; then
|
||||||
|
echo "exists=true" >> $GITHUB_OUTPUT
|
||||||
|
{
|
||||||
|
echo "### ⏭️ Bundle build skipped"
|
||||||
|
echo ""
|
||||||
|
echo "The bundle in S3 was already built from \`${{ steps.vars.outputs.bundle_version }}\`."
|
||||||
|
} >> "$GITHUB_STEP_SUMMARY"
|
||||||
|
else
|
||||||
|
echo "exists=false" >> $GITHUB_OUTPUT
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 2. Build and upload, only when needed ──────────────────────────────
|
||||||
|
build:
|
||||||
|
name: Build and Upload Penpot Bundle
|
||||||
|
runs-on: penpot-runner-01
|
||||||
|
timeout-minutes: 90
|
||||||
|
needs: check
|
||||||
|
if: needs.check.outputs.exists == 'false'
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v6
|
||||||
|
with:
|
||||||
|
fetch-depth: 0
|
||||||
|
ref: ${{ inputs.gh_ref }}
|
||||||
|
|
||||||
- name: Build bundle
|
- name: Build bundle
|
||||||
env:
|
env:
|
||||||
BUILD_WASM: ${{ inputs.build_wasm }}
|
BUILD_WASM: 'yes'
|
||||||
BUILD_STORYBOOK: ${{ inputs.build_storybook }}
|
BUILD_STORYBOOK: 'yes'
|
||||||
run: ./manage.sh build-bundle
|
run: ./manage.sh build-bundle
|
||||||
|
|
||||||
- name: Prepare directories for zipping
|
- name: Prepare directories for zipping
|
||||||
@ -80,18 +104,32 @@ jobs:
|
|||||||
zip -r zips/penpot.zip penpot
|
zip -r zips/penpot.zip penpot
|
||||||
|
|
||||||
- name: Upload Penpot bundle to S3
|
- name: Upload Penpot bundle to S3
|
||||||
|
env:
|
||||||
|
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
|
AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }}
|
||||||
run: |
|
run: |
|
||||||
aws s3 cp zips/penpot.zip s3://${{ secrets.S3_BUCKET }}/penpot-${{ steps.vars.outputs.gh_ref }}.zip --metadata bundle-version=${{ steps.vars.outputs.bundle_version }}
|
aws s3 cp zips/penpot.zip \
|
||||||
|
s3://${{ secrets.S3_BUCKET }}/penpot-${{ needs.check.outputs.gh_ref }}.zip \
|
||||||
|
--metadata bundle-version=${{ needs.check.outputs.bundle_version }}
|
||||||
|
|
||||||
|
# ── 3. Single failure notification for the whole workflow ─────────────
|
||||||
|
notify:
|
||||||
|
name: Notify failure
|
||||||
|
runs-on: penpot-runner-01
|
||||||
|
timeout-minutes: 5
|
||||||
|
needs: [check, build]
|
||||||
|
if: failure()
|
||||||
|
|
||||||
|
steps:
|
||||||
- name: Notify Mattermost
|
- name: Notify Mattermost
|
||||||
if: failure()
|
|
||||||
uses: mattermost/action-mattermost-notify@ae31bb6f9e26a54336e79696f108a2c91cf55b4e # v2.1.0
|
uses: mattermost/action-mattermost-notify@ae31bb6f9e26a54336e79696f108a2c91cf55b4e # v2.1.0
|
||||||
with:
|
with:
|
||||||
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
|
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
|
||||||
MATTERMOST_CHANNEL: bot-alerts-cicd
|
MATTERMOST_CHANNEL: bot-alerts-cicd
|
||||||
TEXT: |
|
TEXT: |
|
||||||
❌ 📦 *[PENPOT] Error building penpot bundles.*
|
❌ 📦 *[PENPOT] Error building penpot bundles.*
|
||||||
📄 Triggered from ref: `${{ steps.vars.outputs.gh_ref }}`
|
📄 Triggered from ref: `${{ needs.check.outputs.gh_ref || inputs.gh_ref }}`
|
||||||
Bundle version: `${{ steps.vars.outputs.bundle_version }}`
|
Bundle version: `${{ needs.check.outputs.bundle_version || 'n/a' }}`
|
||||||
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||||
@infra
|
@infra
|
||||||
|
|||||||
2
.github/workflows/build-develop.yml
vendored
2
.github/workflows/build-develop.yml
vendored
@ -11,8 +11,6 @@ jobs:
|
|||||||
secrets: inherit
|
secrets: inherit
|
||||||
with:
|
with:
|
||||||
gh_ref: "develop"
|
gh_ref: "develop"
|
||||||
build_wasm: "yes"
|
|
||||||
build_storybook: "yes"
|
|
||||||
|
|
||||||
build-docker:
|
build-docker:
|
||||||
needs: build-bundle
|
needs: build-bundle
|
||||||
|
|||||||
170
.github/workflows/build-docker.yml
vendored
170
.github/workflows/build-docker.yml
vendored
@ -20,16 +20,25 @@ concurrency:
|
|||||||
group: ${{ github.workflow }}-${{ inputs.gh_ref }}
|
group: ${{ github.workflow }}-${{ inputs.gh_ref }}
|
||||||
cancel-in-progress: true
|
cancel-in-progress: true
|
||||||
|
|
||||||
|
env:
|
||||||
|
ALL_IMAGES: backend frontend exporter storybook mcp
|
||||||
|
# All runner instances live on the same server, so the bundle is
|
||||||
|
# downloaded from S3 once and shared between build jobs through this
|
||||||
|
# host-local directory. Each build job falls back to S3 if the file is
|
||||||
|
# missing (e.g. if runners ever move to separate machines).
|
||||||
|
BUNDLE_CACHE: /var/tmp/penpot-bundle-cache
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
# ── 1. Resolve the build key shared by the whole image set ─────────────
|
# ── 1. Resolve the build key and check the whole set at once ───────────
|
||||||
prepare:
|
prepare:
|
||||||
name: Prepare
|
name: Prepare
|
||||||
runs-on: penpot-runner-02
|
runs-on: penpot-runner-02
|
||||||
timeout-minutes: 10
|
timeout-minutes: 15
|
||||||
outputs:
|
outputs:
|
||||||
gh_ref: ${{ steps.vars.outputs.gh_ref }}
|
gh_ref: ${{ steps.vars.outputs.gh_ref }}
|
||||||
bundle_version: ${{ steps.vars.outputs.bundle_version }}
|
bundle_version: ${{ steps.vars.outputs.bundle_version }}
|
||||||
build_key: ${{ steps.vars.outputs.build_key }}
|
build_key: ${{ steps.vars.outputs.build_key }}
|
||||||
|
exists: ${{ steps.check.outputs.exists }}
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout code
|
- name: Checkout code
|
||||||
@ -55,30 +64,61 @@ jobs:
|
|||||||
echo "bundle_version=$BUNDLE_VERSION" >> $GITHUB_OUTPUT
|
echo "bundle_version=$BUNDLE_VERSION" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
# Image content = bundle + docker build context, so the build key
|
# Image content = bundle + docker build context, so the build key
|
||||||
# (used for immutable tags and the skip check) combines both.
|
# combines both.
|
||||||
CTX_HASH=$(git rev-parse "HEAD:docker/images" | cut -c1-12)
|
CTX_HASH=$(git rev-parse "HEAD:docker/images" | cut -c1-12)
|
||||||
echo "build_key=${BUNDLE_VERSION}-${CTX_HASH}" >> $GITHUB_OUTPUT
|
echo "build_key=${BUNDLE_VERSION}-${CTX_HASH}" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
# ── 2. One build per image, in parallel ────────────────────────────────
|
# The image set is a single block, so a single set-level check is
|
||||||
|
# enough: `promote` drops a marker object in S3 only after every
|
||||||
|
# image was built AND every branch tag was moved. Marker present
|
||||||
|
# means there is nothing at all to do for this build key.
|
||||||
|
- name: Check if this image set is already built
|
||||||
|
id: check
|
||||||
|
env:
|
||||||
|
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
|
AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }}
|
||||||
|
run: |
|
||||||
|
if aws s3api head-object \
|
||||||
|
--bucket ${{ secrets.S3_BUCKET }} \
|
||||||
|
--key "markers/images-${{ steps.vars.outputs.build_key }}" \
|
||||||
|
> /dev/null 2>&1; then
|
||||||
|
echo "exists=true" >> $GITHUB_OUTPUT
|
||||||
|
{
|
||||||
|
echo "### ⏭️ Image set build skipped"
|
||||||
|
echo ""
|
||||||
|
echo "The whole set was already built and promoted for \`${{ steps.vars.outputs.build_key }}\`."
|
||||||
|
} >> "$GITHUB_STEP_SUMMARY"
|
||||||
|
else
|
||||||
|
echo "exists=false" >> $GITHUB_OUTPUT
|
||||||
|
|
||||||
|
# Stage the bundle in the host-local cache, once, for all the
|
||||||
|
# build jobs. Download to a temp name and mv for atomicity;
|
||||||
|
# prune stale bundles while at it.
|
||||||
|
mkdir -p "$BUNDLE_CACHE"
|
||||||
|
find "$BUNDLE_CACHE" -type f -mtime +1 -delete || true
|
||||||
|
ZIP="$BUNDLE_CACHE/penpot-${{ steps.vars.outputs.build_key }}.zip"
|
||||||
|
if [ ! -f "$ZIP" ]; then
|
||||||
|
aws s3 cp "s3://${{ secrets.S3_BUCKET }}/penpot-${{ steps.vars.outputs.gh_ref }}.zip" "$ZIP.$$.tmp"
|
||||||
|
mv "$ZIP.$$.tmp" "$ZIP"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
# ── 2. One build per image, in parallel, only when needed ──────────────
|
||||||
build:
|
build:
|
||||||
name: Build ${{ matrix.image }}
|
name: Build ${{ matrix.image }}
|
||||||
runs-on: penpot-runner-02
|
runs-on: penpot-runner-02
|
||||||
timeout-minutes: 60
|
timeout-minutes: 60
|
||||||
needs: prepare
|
needs: prepare
|
||||||
|
if: needs.prepare.outputs.exists == 'false'
|
||||||
strategy:
|
strategy:
|
||||||
fail-fast: true
|
fail-fast: true
|
||||||
|
# 4 runner slots are available for build jobs on this server; cap the
|
||||||
|
# matrix at 3 so short jobs (prepare and other workflows' checks)
|
||||||
|
# never queue behind long builds.
|
||||||
|
max-parallel: 3
|
||||||
matrix:
|
matrix:
|
||||||
include:
|
image: [backend, frontend, exporter, storybook, mcp]
|
||||||
- image: backend
|
|
||||||
bundle: backend
|
|
||||||
- image: frontend
|
|
||||||
bundle: frontend
|
|
||||||
- image: exporter
|
|
||||||
bundle: exporter
|
|
||||||
- image: storybook
|
|
||||||
bundle: storybook
|
|
||||||
- image: mcp
|
|
||||||
bundle: mcp
|
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Set common environment variables
|
- name: Set common environment variables
|
||||||
@ -107,12 +147,12 @@ jobs:
|
|||||||
username: ${{ secrets.PUB_DOCKER_USERNAME }}
|
username: ${{ secrets.PUB_DOCKER_USERNAME }}
|
||||||
password: ${{ secrets.PUB_DOCKER_PASSWORD }}
|
password: ${{ secrets.PUB_DOCKER_PASSWORD }}
|
||||||
|
|
||||||
# Frontend, backend, exporter, storybook and mcp now build FROM
|
# Images now build FROM Docker Hardened Images (dhi.io). DHI
|
||||||
# Docker Hardened Images (dhi.io). DHI is free (Apache 2.0, no
|
# is free (Apache 2.0, no subscription), but pulling from it
|
||||||
# subscription), but pulling from it still requires an
|
# still requires an authenticated login -- a separate `docker
|
||||||
# authenticated login -- a separate `docker login` against a
|
# login` against a different registry host, even though it
|
||||||
# different registry host, even though it reuses the same
|
# reuses the same PUB_DOCKER_* credentials as the DockerHub
|
||||||
# PUB_DOCKER_* credentials as the DockerHub login above.
|
# login above.
|
||||||
- name: Login to Docker Hardened Images registry (base image pull)
|
- name: Login to Docker Hardened Images registry (base image pull)
|
||||||
uses: docker/login-action@v4
|
uses: docker/login-action@v4
|
||||||
with:
|
with:
|
||||||
@ -120,48 +160,37 @@ jobs:
|
|||||||
username: ${{ secrets.PUB_DOCKER_USERNAME }}
|
username: ${{ secrets.PUB_DOCKER_USERNAME }}
|
||||||
password: ${{ secrets.PUB_DOCKER_PASSWORD }}
|
password: ${{ secrets.PUB_DOCKER_PASSWORD }}
|
||||||
|
|
||||||
# Every build pushes an immutable `build-<key>` tag; if it already
|
# Bundle staged once by `prepare` on this host; the S3 fallback only
|
||||||
# exists in the registry, this exact image was already built and the
|
# triggers if the cache is unavailable (runners on another machine,
|
||||||
# whole build can be skipped. Moving tags are handled by `promote`.
|
# cache pruned mid-run, ...).
|
||||||
- name: Check if this image is already built
|
- name: Prepare Penpot bundle
|
||||||
id: check
|
|
||||||
run: |
|
|
||||||
IMAGE="${{ secrets.DOCKER_REGISTRY }}/${{ matrix.image }}:build-${{ needs.prepare.outputs.build_key }}"
|
|
||||||
if docker manifest inspect "$IMAGE" > /dev/null 2>&1; then
|
|
||||||
echo "exists=true" >> $GITHUB_OUTPUT
|
|
||||||
echo "### ⏭️ ${{ matrix.image }}: already built (\`${{ needs.prepare.outputs.build_key }}\`)" >> "$GITHUB_STEP_SUMMARY"
|
|
||||||
else
|
|
||||||
echo "exists=false" >> $GITHUB_OUTPUT
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Download Penpot bundle
|
|
||||||
if: steps.check.outputs.exists == 'false'
|
|
||||||
env:
|
env:
|
||||||
FILE_NAME: penpot-${{ needs.prepare.outputs.gh_ref }}.zip
|
|
||||||
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }}
|
AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }}
|
||||||
run: |
|
run: |
|
||||||
pushd docker/images
|
ZIP="$BUNDLE_CACHE/penpot-${{ needs.prepare.outputs.build_key }}.zip"
|
||||||
aws s3 cp "s3://${{ secrets.S3_BUCKET }}/$FILE_NAME" .
|
if [ ! -f "$ZIP" ]; then
|
||||||
|
echo "Bundle not found in host cache; falling back to S3."
|
||||||
|
mkdir -p "$BUNDLE_CACHE"
|
||||||
|
aws s3 cp "s3://${{ secrets.S3_BUCKET }}/penpot-${{ needs.prepare.outputs.gh_ref }}.zip" "$ZIP.$$.tmp"
|
||||||
|
mv "$ZIP.$$.tmp" "$ZIP"
|
||||||
|
fi
|
||||||
# Extract only the bundle this job needs.
|
# Extract only the bundle this job needs.
|
||||||
unzip -q "$FILE_NAME" "penpot/${{ matrix.bundle }}/*"
|
pushd docker/images
|
||||||
mv "penpot/${{ matrix.bundle }}" "bundle-${{ matrix.bundle }}"
|
unzip -q "$ZIP" "penpot/${{ matrix.image }}/*"
|
||||||
rm -f "$FILE_NAME"
|
mv "penpot/${{ matrix.image }}" "bundle-${{ matrix.image }}"
|
||||||
popd
|
popd
|
||||||
|
|
||||||
- name: Set up QEMU (stable)
|
- name: Set up QEMU (stable)
|
||||||
if: steps.check.outputs.exists == 'false'
|
|
||||||
uses: docker/setup-qemu-action@v4
|
uses: docker/setup-qemu-action@v4
|
||||||
with:
|
with:
|
||||||
platforms: linux/amd64,linux/arm64
|
platforms: linux/amd64,linux/arm64
|
||||||
|
|
||||||
- name: Set up Docker Buildx
|
- name: Set up Docker Buildx
|
||||||
if: steps.check.outputs.exists == 'false'
|
|
||||||
uses: docker/setup-buildx-action@v4
|
uses: docker/setup-buildx-action@v4
|
||||||
|
|
||||||
- name: Extract metadata (tags, labels)
|
- name: Extract metadata (tags, labels)
|
||||||
if: steps.check.outputs.exists == 'false'
|
|
||||||
id: meta
|
id: meta
|
||||||
uses: docker/metadata-action@v6
|
uses: docker/metadata-action@v6
|
||||||
with:
|
with:
|
||||||
@ -170,7 +199,6 @@ jobs:
|
|||||||
bundle_version=${{ needs.prepare.outputs.bundle_version }}
|
bundle_version=${{ needs.prepare.outputs.bundle_version }}
|
||||||
|
|
||||||
- name: Build and push Docker image
|
- name: Build and push Docker image
|
||||||
if: steps.check.outputs.exists == 'false'
|
|
||||||
uses: docker/build-push-action@v7
|
uses: docker/build-push-action@v7
|
||||||
with:
|
with:
|
||||||
context: ./docker/images/
|
context: ./docker/images/
|
||||||
@ -186,23 +214,10 @@ jobs:
|
|||||||
cache-from: type=registry,ref=${{ secrets.DOCKER_REGISTRY }}/${{ matrix.image }}:buildcache
|
cache-from: type=registry,ref=${{ secrets.DOCKER_REGISTRY }}/${{ matrix.image }}:buildcache
|
||||||
cache-to: type=registry,ref=${{ secrets.DOCKER_REGISTRY }}/${{ matrix.image }}:buildcache,mode=max
|
cache-to: type=registry,ref=${{ secrets.DOCKER_REGISTRY }}/${{ matrix.image }}:buildcache,mode=max
|
||||||
|
|
||||||
- name: Notify Mattermost
|
|
||||||
if: failure()
|
|
||||||
uses: mattermost/action-mattermost-notify@ae31bb6f9e26a54336e79696f108a2c91cf55b4e # v2.1.0
|
|
||||||
with:
|
|
||||||
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
|
|
||||||
MATTERMOST_CHANNEL: bot-alerts-cicd
|
|
||||||
TEXT: |
|
|
||||||
❌ 🐳 *[PENPOT] Error building the ${{ matrix.image }} docker image.*
|
|
||||||
📄 Triggered from ref: `${{ needs.prepare.outputs.gh_ref }}`
|
|
||||||
📦 Bundle: `${{ needs.prepare.outputs.bundle_version }}`
|
|
||||||
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
|
||||||
@infra
|
|
||||||
|
|
||||||
# ── 3. Move the branch tags of ALL images together ─────────────────────
|
# ── 3. Move the branch tags of ALL images together ─────────────────────
|
||||||
# This is what makes the set behave as a single block: either every image
|
# Runs only when every build succeeded (default `needs` semantics); if
|
||||||
# was built (or already existed) and all branch tags advance to the same
|
# the set was already complete, `build` is skipped and so is this job —
|
||||||
# build key, or none of them move.
|
# the S3 marker guarantees the branch tags were already moved.
|
||||||
promote:
|
promote:
|
||||||
name: Promote image set
|
name: Promote image set
|
||||||
runs-on: penpot-runner-02
|
runs-on: penpot-runner-02
|
||||||
@ -227,26 +242,45 @@ jobs:
|
|||||||
- name: Point branch tags to the new build key
|
- name: Point branch tags to the new build key
|
||||||
run: |
|
run: |
|
||||||
set -e
|
set -e
|
||||||
for image in backend frontend exporter storybook mcp; do
|
for image in $ALL_IMAGES; do
|
||||||
docker buildx imagetools create \
|
docker buildx imagetools create \
|
||||||
-t "${{ secrets.DOCKER_REGISTRY }}/$image:${{ needs.prepare.outputs.gh_ref }}" \
|
-t "${{ secrets.DOCKER_REGISTRY }}/$image:${{ needs.prepare.outputs.gh_ref }}" \
|
||||||
"${{ secrets.DOCKER_REGISTRY }}/$image:build-${{ needs.prepare.outputs.build_key }}"
|
"${{ secrets.DOCKER_REGISTRY }}/$image:build-${{ needs.prepare.outputs.build_key }}"
|
||||||
done
|
done
|
||||||
|
|
||||||
|
# The marker is written LAST: its presence certifies that all five
|
||||||
|
# images exist and all branch tags point to this build key.
|
||||||
|
- name: Write set-completed marker
|
||||||
|
env:
|
||||||
|
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
|
||||||
|
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
|
||||||
|
AWS_DEFAULT_REGION: ${{ secrets.AWS_REGION }}
|
||||||
|
run: |
|
||||||
|
echo "${{ github.run_id }}" | aws s3 cp - \
|
||||||
|
"s3://${{ secrets.S3_BUCKET }}/markers/images-${{ needs.prepare.outputs.build_key }}"
|
||||||
{
|
{
|
||||||
echo "### ✅ Image set promoted"
|
echo "### ✅ Image set promoted"
|
||||||
echo ""
|
echo ""
|
||||||
echo "All \`:${{ needs.prepare.outputs.gh_ref }}\` tags now point to \`build-${{ needs.prepare.outputs.build_key }}\`."
|
echo "All \`:${{ needs.prepare.outputs.gh_ref }}\` tags now point to \`build-${{ needs.prepare.outputs.build_key }}\`."
|
||||||
} >> "$GITHUB_STEP_SUMMARY"
|
} >> "$GITHUB_STEP_SUMMARY"
|
||||||
|
|
||||||
|
# ── 4. Single failure notification for the whole workflow ─────────────
|
||||||
|
notify:
|
||||||
|
name: Notify failure
|
||||||
|
runs-on: penpot-runner-02
|
||||||
|
timeout-minutes: 5
|
||||||
|
needs: [prepare, build, promote]
|
||||||
|
if: failure()
|
||||||
|
|
||||||
|
steps:
|
||||||
- name: Notify Mattermost
|
- name: Notify Mattermost
|
||||||
if: failure()
|
|
||||||
uses: mattermost/action-mattermost-notify@ae31bb6f9e26a54336e79696f108a2c91cf55b4e # v2.1.0
|
uses: mattermost/action-mattermost-notify@ae31bb6f9e26a54336e79696f108a2c91cf55b4e # v2.1.0
|
||||||
with:
|
with:
|
||||||
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
|
MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }}
|
||||||
MATTERMOST_CHANNEL: bot-alerts-cicd
|
MATTERMOST_CHANNEL: bot-alerts-cicd
|
||||||
TEXT: |
|
TEXT: |
|
||||||
❌ 🐳 *[PENPOT] Error promoting the penpot docker image set.*
|
❌ 🐳 *[PENPOT] Error building/promoting the penpot docker image set.*
|
||||||
📄 Triggered from ref: `${{ needs.prepare.outputs.gh_ref }}`
|
📄 Triggered from ref: `${{ needs.prepare.outputs.gh_ref || inputs.gh_ref }}`
|
||||||
📦 Bundle: `${{ needs.prepare.outputs.bundle_version }}`
|
📦 Bundle: `${{ needs.prepare.outputs.bundle_version || 'n/a' }}`
|
||||||
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
🔗 Run: https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }}
|
||||||
@infra
|
@infra
|
||||||
|
|||||||
2
.github/workflows/build-staging.yml
vendored
2
.github/workflows/build-staging.yml
vendored
@ -11,8 +11,6 @@ jobs:
|
|||||||
secrets: inherit
|
secrets: inherit
|
||||||
with:
|
with:
|
||||||
gh_ref: "staging"
|
gh_ref: "staging"
|
||||||
build_wasm: "yes"
|
|
||||||
build_storybook: "yes"
|
|
||||||
|
|
||||||
build-docker:
|
build-docker:
|
||||||
needs: build-bundle
|
needs: build-bundle
|
||||||
|
|||||||
2
.github/workflows/build-tag.yml
vendored
2
.github/workflows/build-tag.yml
vendored
@ -12,8 +12,6 @@ jobs:
|
|||||||
secrets: inherit
|
secrets: inherit
|
||||||
with:
|
with:
|
||||||
gh_ref: ${{ github.ref_name }}
|
gh_ref: ${{ github.ref_name }}
|
||||||
build_wasm: "yes"
|
|
||||||
build_storybook: "yes"
|
|
||||||
|
|
||||||
build-docker:
|
build-docker:
|
||||||
needs: build-bundle
|
needs: build-bundle
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user