From ff4c4a4a344f600c3ee19c4510c53ba9eaec72a5 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Thu, 8 Oct 2026 19:24:58 +0200 Subject: [PATCH] :paperclip: Backport .github and scripts/ci from develop --- .github/workflows/auto-label.yml | 10 +- .github/workflows/build-docker-devenv.yml | 27 ++- .github/workflows/build-renovate.yml | 256 ++++++++++++++++++++++ .github/workflows/tests-backend.yml | 4 + scripts/ci | 6 + 5 files changed, 297 insertions(+), 6 deletions(-) create mode 100644 .github/workflows/build-renovate.yml diff --git a/.github/workflows/auto-label.yml b/.github/workflows/auto-label.yml index e3c435e40d..84615efe4b 100644 --- a/.github/workflows/auto-label.yml +++ b/.github/workflows/auto-label.yml @@ -13,8 +13,14 @@ permissions: {} jobs: triage: - # Dependabot PRs are labelled by dependabot.yml and do not need triage. - if: github.actor != 'dependabot[bot]' + # The Renovate Dependency Dashboard and Renovate's GitHub Actions PRs do + # not need triage. Other Renovate PRs (base images, Node.js, pnpm) do. + if: >- + !(github.event_name == 'issues' + && github.event.issue.user.login == 'penpot-renovate[bot]') + && !(github.event_name == 'pull_request_target' + && github.event.pull_request.user.login == 'penpot-renovate[bot]' + && github.event.pull_request.head.ref == 'renovate-github-actions') runs-on: ubuntu-latest steps: - name: Generate GitHub App token diff --git a/.github/workflows/build-docker-devenv.yml b/.github/workflows/build-docker-devenv.yml index 906adcd369..a1c99e2bdd 100644 --- a/.github/workflows/build-docker-devenv.yml +++ b/.github/workflows/build-docker-devenv.yml @@ -6,22 +6,34 @@ on: permissions: contents: read +concurrency: + group: build-docker-devenv + cancel-in-progress: false + jobs: build-and-push: name: Build and push DevEnv Docker image runs-on: penpot-standar-runner + timeout-minutes: 90 + env: + DOCKER_IMAGE: 'penpotapp/devenv' steps: - name: Set common environment variables run: | # Each job execution will use its own docker configuration. - echo "DOCKER_CONFIG=${{ runner.temp }}/.docker-${{ github.run_id }}-${{ github.job }}" >> $GITHUB_ENV + echo "DOCKER_CONFIG=${RUNNER_TEMP}/.docker-${GITHUB_RUN_ID}-${GITHUB_JOB}" >> "$GITHUB_ENV" - name: Checkout code uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: persist-credentials: false + - name: Set up QEMU + uses: docker/setup-qemu-action@99012661954931238ded8c8b007157a8430204e1 # v4.4.0 + with: + platforms: linux/amd64,linux/arm64 + - name: Set up Docker Buildx uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1 @@ -40,8 +52,6 @@ jobs: - name: Build and push DevEnv Docker image uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0 - env: - DOCKER_IMAGE: 'penpotapp/devenv' with: context: ./docker/devenv/ file: ./docker/devenv/Dockerfile @@ -61,4 +71,13 @@ jobs: TEXT: | 🚀 *[PENPOT] New devenv available* 📄 You may want to update your devenv. - @alvaro + + - name: Notify Mattermost (developers) + uses: mattermost/action-mattermost-notify@ae31bb6f9e26a54336e79696f108a2c91cf55b4e # v2.1.0 + with: + MATTERMOST_WEBHOOK_URL: ${{ secrets.MATTERMOST_WEBHOOK }} + MATTERMOST_CHANNEL: penpotdev + TEXT: | + 🚀 *[PENPOT] New devenv available* + 📄 You may want to update your devenv. + (\cc @alvaro) diff --git a/.github/workflows/build-renovate.yml b/.github/workflows/build-renovate.yml new file mode 100644 index 0000000000..6156aed086 --- /dev/null +++ b/.github/workflows/build-renovate.yml @@ -0,0 +1,256 @@ +name: Renovate Docker Image Check + +# Triggered by pushes to Renovate branches, not by pull_request: this is a +# public repository and these jobs run on the self-hosted runners, so a PR +# from a fork must never be able to start them. Only users with write +# access can push here. +on: + push: + branches: + - 'renovate-*' + +permissions: {} + +concurrency: + group: renovate-image-check-${{ github.ref_name }} + cancel-in-progress: true + +env: + BUNDLE_CACHE: /var/tmp/penpot-bundle-cache + +jobs: + # ── 1. Pick an existing develop bundle and detect what changed ──────── + prepare: + name: Prepare + runs-on: penpot-standar-runner + timeout-minutes: 10 + permissions: + contents: read + outputs: + short_sha: ${{ steps.bundle.outputs.short_sha }} + images: ${{ steps.changes.outputs.images }} + devenv: ${{ steps.changes.outputs.devenv }} + imagemagick: ${{ steps.changes.outputs.imagemagick }} + + steps: + - name: Checkout code + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + fetch-depth: 0 + + - name: Find the develop commit this branch is based on + id: base + run: | + echo "sha=$(git merge-base HEAD origin/develop)" >> "$GITHUB_OUTPUT" + + - name: Detect changed images + id: changes + env: + BASE_SHA: ${{ steps.base.outputs.sha }} + run: | + changed() { + git diff --quiet "$BASE_SHA" HEAD -- "$@" && echo false || echo true + } + { + # Anything outside .github can affect the production images. + echo "images=$(changed . ':!.github')" + echo "devenv=$(changed docker/devenv)" + echo "imagemagick=$(changed docker/imagemagick)" + } >> "$GITHUB_OUTPUT" + + # Renovate only changes Dockerfiles and package manager versions, so + # the most recent develop bundle is enough to build the images. + - name: Find the latest develop bundle + id: bundle + 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 }} + S3_BUCKET: ${{ secrets.S3_BUCKET }} + BASE_SHA: ${{ steps.base.outputs.sha }} + run: | + for SHA in $(git rev-list --first-parent --max-count=50 "$BASE_SHA"); do + SHORT_SHA=$(git rev-parse --short=12 "$SHA") + if aws s3api head-object --bucket "$S3_BUCKET" \ + --key "penpot-sha-${SHORT_SHA}.zip" > /dev/null 2>&1; then + echo "short_sha=$SHORT_SHA" >> "$GITHUB_OUTPUT" + echo "Using bundle \`penpot-sha-${SHORT_SHA}.zip\`" >> "$GITHUB_STEP_SUMMARY" + exit 0 + fi + done + echo "::error::No bundle found for the last 50 develop commits" + exit 1 + + # ── 2. Production images, built from the PR Dockerfiles ─────────────── + images: + name: Build ${{ matrix.image }} + runs-on: penpot-standar-runner + timeout-minutes: 60 + needs: prepare + if: needs.prepare.outputs.images == 'true' + permissions: + contents: read + strategy: + fail-fast: false + max-parallel: 3 + matrix: + image: [backend, frontend, exporter, storybook, mcp] + + steps: + - name: Set common environment variables + env: + MATRIX_IMAGE: ${{ matrix.image }} + run: | + echo "DOCKER_CONFIG=${RUNNER_TEMP}/.docker-${GITHUB_RUN_ID}-${GITHUB_JOB}-${MATRIX_IMAGE}" >> "$GITHUB_ENV" + + - name: Checkout code + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Login to Docker Registry + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + with: + registry: ${{ secrets.DOCKER_REGISTRY }} + username: ${{ secrets.DOCKER_USERNAME }} + password: ${{ secrets.DOCKER_PASSWORD }} + + - name: Login to DockerHub Registry + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + with: + username: ${{ secrets.PUB_DOCKER_USERNAME }} + password: ${{ secrets.PUB_DOCKER_PASSWORD }} + + - name: Login to Docker Hardened Images registry + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + with: + registry: dhi.io + username: ${{ secrets.PUB_DOCKER_USERNAME }} + password: ${{ secrets.PUB_DOCKER_PASSWORD }} + + - name: Prepare Penpot bundle + 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 }} + S3_BUCKET: ${{ secrets.S3_BUCKET }} + SHORT_SHA: ${{ needs.prepare.outputs.short_sha }} + IMAGE: ${{ matrix.image }} + run: | + ZIP="$BUNDLE_CACHE/penpot-sha-${SHORT_SHA}.zip" + if [ ! -f "$ZIP" ]; then + mkdir -p "$BUNDLE_CACHE" + aws s3 cp "s3://${S3_BUCKET}/penpot-sha-${SHORT_SHA}.zip" "$ZIP.$$.tmp" + mv "$ZIP.$$.tmp" "$ZIP" + fi + pushd docker/images + unzip -q "$ZIP" "penpot/${IMAGE}/*" + mv "penpot/${IMAGE}" "bundle-${IMAGE}" + popd + + - name: Set up QEMU + uses: docker/setup-qemu-action@99012661954931238ded8c8b007157a8430204e1 # v4.4.0 + with: + platforms: linux/amd64,linux/arm64 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1 + + - name: Build Docker image + uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0 + with: + context: ./docker/images/ + file: ./docker/images/Dockerfile.${{ matrix.image }} + platforms: linux/amd64,linux/arm64 + push: false + cache-from: type=registry,ref=${{ secrets.DOCKER_REGISTRY }}/${{ matrix.image }}:buildcache + + # ── 3. Devenv, only when the PR touches it ──────────────────────────── + devenv: + name: Build devenv + runs-on: penpot-standar-runner + timeout-minutes: 90 + needs: prepare + if: needs.prepare.outputs.devenv == 'true' + permissions: + contents: read + + steps: + - name: Set common environment variables + run: | + echo "DOCKER_CONFIG=${RUNNER_TEMP}/.docker-${GITHUB_RUN_ID}-${GITHUB_JOB}" >> "$GITHUB_ENV" + + - name: Checkout code + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Login to DockerHub Registry + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + with: + username: ${{ secrets.PUB_DOCKER_USERNAME }} + password: ${{ secrets.PUB_DOCKER_PASSWORD }} + + - name: Login to Docker Hardened Images registry + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + with: + registry: dhi.io + username: ${{ secrets.PUB_DOCKER_USERNAME }} + password: ${{ secrets.PUB_DOCKER_PASSWORD }} + + - name: Set up QEMU + uses: docker/setup-qemu-action@99012661954931238ded8c8b007157a8430204e1 # v4.4.0 + with: + platforms: linux/amd64,linux/arm64 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1 + + - name: Build devenv image + uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0 + with: + context: ./docker/devenv/ + file: ./docker/devenv/Dockerfile + platforms: linux/amd64,linux/arm64 + push: false + cache-from: type=registry,ref=penpotapp/devenv:buildcache + + # ── 4. ImageMagick, only when the PR touches it ─────────────────────── + # amd64 only: compiling ImageMagick under QEMU takes too long for a check. + imagemagick: + name: Build imagemagick + runs-on: penpot-standar-runner + timeout-minutes: 60 + needs: prepare + if: needs.prepare.outputs.imagemagick == 'true' + permissions: + contents: read + + steps: + - name: Set common environment variables + run: | + echo "DOCKER_CONFIG=${RUNNER_TEMP}/.docker-${GITHUB_RUN_ID}-${GITHUB_JOB}" >> "$GITHUB_ENV" + + - name: Checkout code + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + persist-credentials: false + + - name: Login to Docker Hardened Images registry + uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0 + with: + registry: dhi.io + username: ${{ secrets.PUB_DOCKER_USERNAME }} + password: ${{ secrets.PUB_DOCKER_PASSWORD }} + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@f87e5991a6d7451dcb8d9637bfbc97413f497069 # v4.4.1 + + - name: Build imagemagick image + uses: docker/build-push-action@c3c9e263c25d99ce0380d002d59b67737d91b0dc # v7.4.0 + with: + context: ./docker/imagemagick/ + file: ./docker/imagemagick/Dockerfile + platforms: linux/amd64 + push: false diff --git a/.github/workflows/tests-backend.yml b/.github/workflows/tests-backend.yml index a1df06c91a..40b99ff3c5 100644 --- a/.github/workflows/tests-backend.yml +++ b/.github/workflows/tests-backend.yml @@ -80,6 +80,10 @@ jobs: - name: Tests working-directory: ./backend env: + # `clojure.core/assert` is elided at compile time, so without this + # flag the precondition asserts do not exist in the compiled code + # and the suite silently stops checking them. + PENPOT_FLAGS: "enable-backend-asserts" PENPOT_TEST_DATABASE_URI: "postgresql://postgres/penpot_test" PENPOT_TEST_DATABASE_USERNAME: penpot_test PENPOT_TEST_DATABASE_PASSWORD: penpot_test diff --git a/scripts/ci b/scripts/ci index cff93eca3a..4428b673f9 100755 --- a/scripts/ci +++ b/scripts/ci @@ -8,6 +8,12 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(dirname "$SCRIPT_DIR")" LOG_DIR="$PROJECT_ROOT/.ci-logs" +# `clojure.core/assert` is elided at compile time when `*assert*` is false, +# and `app.config` only sets that var from these flags. Without this the +# backend suite runs with every assertion compiled out, silently. Appending +# keeps whatever flags the caller already exported. +export PENPOT_FLAGS="${PENPOT_FLAGS:+$PENPOT_FLAGS } enable-backend-asserts" + # Color codes RED='\033[0;31m' GREEN='\033[0;32m'