[Fix] Theme boot script embeds storage key without script-context esc… #1361
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| push: | |
| branches: [main, develop] | |
| pull_request: | |
| branches: [main, develop] | |
| workflow_dispatch: | |
| env: | |
| NODE_VERSION: 24.13.1 | |
| PNPM_VERSION: 10.29.3 | |
| jobs: | |
| changes: | |
| name: Detect changed paths | |
| runs-on: blacksmith-4vcpu-ubuntu-2404 | |
| outputs: | |
| docs_only: ${{ steps.changed-paths.outputs.docs_only }} | |
| worker_image_affected: ${{ steps.changed-paths.outputs.worker_image_affected }} | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| with: | |
| fetch-depth: 0 | |
| - name: Classify changed paths | |
| id: changed-paths | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| # Fail safe: when we cannot compute a reliable diff (manual dispatch, | |
| # force-push over a gone base, or an empty diff), build everything. | |
| emit_build_all() { | |
| echo "docs_only=false" >> "$GITHUB_OUTPUT" | |
| echo "worker_image_affected=true" >> "$GITHUB_OUTPUT" | |
| } | |
| if [ "$GITHUB_EVENT_NAME" = "workflow_dispatch" ]; then | |
| emit_build_all | |
| exit 0 | |
| fi | |
| if [ "$GITHUB_EVENT_NAME" = "pull_request" ]; then | |
| base="${{ github.event.pull_request.base.sha }}" | |
| head="${{ github.event.pull_request.head.sha }}" | |
| changed_files="$(git diff --name-only "$base...$head")" | |
| else | |
| before="${{ github.event.before }}" | |
| if [ "$before" = "0000000000000000000000000000000000000000" ]; then | |
| changed_files="$(git diff-tree --no-commit-id --name-only -r "$GITHUB_SHA")" | |
| elif git cat-file -e "$before" 2>/dev/null; then | |
| changed_files="$(git diff --name-only "$before" "$GITHUB_SHA")" | |
| else | |
| # Force-pushed over the previous tip; the base is gone, so run everything. | |
| emit_build_all | |
| exit 0 | |
| fi | |
| fi | |
| if [ -z "$changed_files" ]; then | |
| emit_build_all | |
| exit 0 | |
| fi | |
| # docs_only: every changed file is documentation or markdown, so all | |
| # heavy jobs can skip. | |
| # | |
| # worker_image_affected: the worker image (apps/worker/Dockerfile) | |
| # builds FROM ubuntu and copies no monorepo source, so it is affected | |
| # only by its own Dockerfile/context inputs. The app image, by | |
| # contrast, copies apps/worker (and packages the worker release), so a | |
| # worker-only change still rebuilds the app image; the app rows stay | |
| # gated on docs_only alone. | |
| # | |
| # This is a closed allowlist: worker_image_affected starts false and | |
| # only the paths matched below flip it true, so an unrecognized path | |
| # does NOT trigger a worker build. If the worker image ever consumes a | |
| # new build-context input, add its path to the case below or the | |
| # worker build will be skipped for changes to it. The build-everything | |
| # fail-safe only covers the uncertain-diff cases handled by | |
| # emit_build_all above (manual dispatch, gone base, empty diff). | |
| docs_only=true | |
| worker_image_affected=false | |
| while IFS= read -r path; do | |
| case "$path" in | |
| apps/docs/*|*.md|*.mdx) | |
| ;; | |
| *) | |
| docs_only=false | |
| ;; | |
| esac | |
| case "$path" in | |
| apps/worker/*|.docker/sandbox/*|.dockerignore|.github/workflows/CI.yml) | |
| worker_image_affected=true | |
| ;; | |
| esac | |
| done <<< "$changed_files" | |
| echo "docs_only=$docs_only" >> "$GITHUB_OUTPUT" | |
| echo "worker_image_affected=$worker_image_affected" >> "$GITHUB_OUTPUT" | |
| lint: | |
| name: Lint | |
| needs: changes | |
| if: ${{ needs.changes.outputs.docs_only != 'true' }} | |
| runs-on: blacksmith-4vcpu-ubuntu-2404 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Setup environment | |
| uses: ./.github/actions/setup-environment | |
| with: | |
| enable-turbo-cache: 'true' | |
| frozen-lockfile: 'true' | |
| node-version: ${{ env.NODE_VERSION }} | |
| pnpm-version: ${{ env.PNPM_VERSION }} | |
| turbo-cache-scope: lint | |
| - name: Run lint workflow | |
| run: pnpm lint | |
| knip: | |
| name: Knip | |
| needs: changes | |
| if: ${{ needs.changes.outputs.docs_only != 'true' }} | |
| runs-on: blacksmith-4vcpu-ubuntu-2404 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Setup environment | |
| uses: ./.github/actions/setup-environment | |
| with: | |
| frozen-lockfile: 'true' | |
| node-version: ${{ env.NODE_VERSION }} | |
| pnpm-version: ${{ env.PNPM_VERSION }} | |
| - name: Check unused dependencies and files | |
| run: pnpm knip | |
| check-types: | |
| name: Type Check | |
| needs: changes | |
| if: ${{ needs.changes.outputs.docs_only != 'true' }} | |
| runs-on: blacksmith-4vcpu-ubuntu-2404 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Setup environment | |
| uses: ./.github/actions/setup-environment | |
| with: | |
| enable-turbo-cache: 'true' | |
| frozen-lockfile: 'true' | |
| node-version: ${{ env.NODE_VERSION }} | |
| pnpm-version: ${{ env.PNPM_VERSION }} | |
| turbo-cache-scope: check-types | |
| - name: Run type checker | |
| run: pnpm check-types | |
| test: | |
| name: Test | |
| needs: changes | |
| if: ${{ needs.changes.outputs.docs_only != 'true' }} | |
| runs-on: blacksmith-4vcpu-ubuntu-2404 | |
| services: | |
| postgres: | |
| image: postgres:17.5@sha256:aadf2c0696f5ef357aa7a68da995137f0cf17bad0bf6e1f17de06ae5c769b302 | |
| env: | |
| POSTGRES_USER: postgres | |
| POSTGRES_PASSWORD: password | |
| POSTGRES_DB: test | |
| ports: | |
| - 5432:5432 | |
| options: >- | |
| --health-cmd "pg_isready -U postgres -d test" | |
| --health-interval 10s | |
| --health-timeout 5s | |
| --health-retries 5 | |
| redis: | |
| image: redis:7@sha256:b2b95679e3b46fb51864949ed25ea976fc3a6bcc00a40a1bc00d568cb2822e50 | |
| ports: | |
| - 6379:6379 | |
| options: >- | |
| --health-cmd "redis-cli ping" | |
| --health-interval 10s | |
| --health-timeout 5s | |
| --health-retries 5 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Setup environment | |
| uses: ./.github/actions/setup-environment | |
| with: | |
| enable-turbo-cache: 'true' | |
| frozen-lockfile: 'true' | |
| node-version: ${{ env.NODE_VERSION }} | |
| pnpm-version: ${{ env.PNPM_VERSION }} | |
| turbo-cache-scope: test | |
| - name: Setup database schema | |
| run: pnpm --filter @roomote/db db:push:test --force | |
| env: | |
| DATABASE_URL: postgres://postgres:password@localhost:5432/test | |
| - name: Run unit tests | |
| run: pnpm test:ci | |
| env: | |
| DATABASE_URL: postgres://postgres:password@localhost:5432/test | |
| REDIS_URL: redis://localhost:6379 | |
| - name: Run release script unit tests | |
| run: pnpm test:release-scripts | |
| backup-restore: | |
| name: Fresh-host Backup Restore | |
| needs: changes | |
| if: ${{ needs.changes.outputs.docs_only != 'true' }} | |
| runs-on: blacksmith-4vcpu-ubuntu-2404 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Restore an encrypted bundle onto empty volumes | |
| run: bash deploy/host/tests/backup-restore.integration.sh | |
| upgrade-compatibility: | |
| name: Upgrade Compatibility | |
| needs: changes | |
| if: ${{ needs.changes.outputs.docs_only != 'true' }} | |
| runs-on: blacksmith-4vcpu-ubuntu-2404 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Setup environment | |
| uses: ./.github/actions/setup-environment | |
| with: | |
| frozen-lockfile: 'true' | |
| node-version: ${{ env.NODE_VERSION }} | |
| pnpm-version: ${{ env.PNPM_VERSION }} | |
| # Boots the previous published release from GHCR, applies this branch's | |
| # migrations to it, and requires the previous release to stay healthy on | |
| # the candidate schema (the expand/contract policy in deploy/README.md). | |
| # | |
| # develop-based runs validate against the continuously published | |
| # `develop` channel. main-based runs (promote PRs) validate against the | |
| # previous published *release* rather than the raw `main` channel alias: | |
| # before the first release ships there is no usable baseline at all, and | |
| # the gate skips instead of booting a known-incompatible image. | |
| - name: Resolve upgrade baseline | |
| id: baseline | |
| shell: bash | |
| env: | |
| GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| BASE_CHANNEL: ${{ github.base_ref || github.ref_name }} | |
| run: | | |
| set -euo pipefail | |
| baseline="$BASE_CHANNEL" | |
| if [ "$baseline" = 'main' ]; then | |
| # gh prints the 404 error body to stdout; only trust the output | |
| # when the call succeeds. | |
| if ! baseline="$(gh api "repos/$GITHUB_REPOSITORY/releases/latest" --jq .tag_name 2>/dev/null)"; then | |
| baseline='' | |
| fi | |
| # v0.0.1 predates the release pipeline and has no images. | |
| if [ "$baseline" = 'v0.0.1' ]; then | |
| baseline='' | |
| fi | |
| case "$baseline" in | |
| '' | v[0-9]*) ;; | |
| *) | |
| echo "Ignoring unexpected baseline '$baseline'; skipping upgrade validation." >&2 | |
| baseline='' | |
| ;; | |
| esac | |
| fi | |
| echo "channel=$baseline" >> "$GITHUB_OUTPUT" | |
| - name: Apply candidate migrations to the previous release | |
| env: | |
| BASELINE_CHANNEL: ${{ steps.baseline.outputs.channel }} | |
| run: bash deploy/ci/upgrade-compatibility.sh | |
| docker-build-app: | |
| name: Docker Build (${{ matrix.app }}, ${{ matrix.arch }}) | |
| needs: changes | |
| if: ${{ needs.changes.outputs.docs_only != 'true' }} | |
| runs-on: ${{ matrix.runner }} | |
| strategy: | |
| fail-fast: false | |
| # Each arch builds natively, mirroring the publish workflow (no QEMU: | |
| # rustc segfaults under emulation, rust-lang/rust#147026). | |
| matrix: | |
| include: | |
| - app: app | |
| dockerfile: .docker/app/Dockerfile | |
| target: runtime-app | |
| arch: amd64 | |
| runner: blacksmith-4vcpu-ubuntu-2404 | |
| - app: app | |
| dockerfile: .docker/app/Dockerfile | |
| target: runtime-app | |
| arch: arm64 | |
| runner: blacksmith-4vcpu-ubuntu-2404-arm | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Validate production Compose security contract | |
| if: matrix.arch == 'amd64' | |
| run: deploy/scripts/validate-compose-security.sh | |
| - name: Set up Docker builder | |
| uses: useblacksmith/setup-docker-builder@47a5d0102cc44712a17a633c2599f755008cc40e # v1 | |
| - name: Build ${{ matrix.app }} Dockerfile (${{ matrix.arch }}) | |
| uses: useblacksmith/build-push-action@fb9e3e6a9299c78462bfadd0d93352c316adc9b8 # v2 | |
| with: | |
| context: . | |
| file: ${{ matrix.dockerfile }} | |
| target: ${{ matrix.target }} | |
| platforms: linux/${{ matrix.arch }} | |
| push: false | |
| build-args: R_APP_ENV=development | |
| # The worker image builds FROM ubuntu and copies no monorepo source, so it is | |
| # skipped unless its own Dockerfile or build context changed (see the | |
| # worker_image_affected classifier). The app image copies apps/worker and | |
| # packages the worker release, so worker-only changes still rebuild the app | |
| # image above via the docs_only gate. | |
| docker-build-worker: | |
| name: Docker Build (${{ matrix.app }}, ${{ matrix.arch }}) | |
| needs: changes | |
| if: ${{ needs.changes.outputs.docs_only != 'true' && needs.changes.outputs.worker_image_affected == 'true' }} | |
| runs-on: ${{ matrix.runner }} | |
| strategy: | |
| fail-fast: false | |
| # Each arch builds natively, mirroring the publish workflow (no QEMU: | |
| # rustc segfaults under emulation, rust-lang/rust#147026). | |
| matrix: | |
| include: | |
| - app: worker | |
| dockerfile: apps/worker/Dockerfile | |
| target: runtime | |
| arch: amd64 | |
| runner: blacksmith-4vcpu-ubuntu-2404 | |
| - app: worker | |
| dockerfile: apps/worker/Dockerfile | |
| target: runtime | |
| arch: arm64 | |
| runner: blacksmith-4vcpu-ubuntu-2404-arm | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Set up Docker builder | |
| uses: useblacksmith/setup-docker-builder@47a5d0102cc44712a17a633c2599f755008cc40e # v1 | |
| - name: Build ${{ matrix.app }} Dockerfile (${{ matrix.arch }}) | |
| uses: useblacksmith/build-push-action@fb9e3e6a9299c78462bfadd0d93352c316adc9b8 # v2 | |
| with: | |
| context: . | |
| file: ${{ matrix.dockerfile }} | |
| target: ${{ matrix.target }} | |
| platforms: linux/${{ matrix.arch }} | |
| push: false | |
| build-args: R_APP_ENV=development | |
| deployment-config: | |
| name: Deployment artifacts | |
| needs: changes | |
| if: ${{ needs.changes.outputs.docs_only != 'true' }} | |
| runs-on: blacksmith-4vcpu-ubuntu-2404 | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4 | |
| - name: Setup environment | |
| uses: ./.github/actions/setup-environment | |
| with: | |
| frozen-lockfile: 'true' | |
| node-version: ${{ env.NODE_VERSION }} | |
| pnpm-version: ${{ env.PNPM_VERSION }} | |
| - name: Validate Compose, installers, and platform templates | |
| run: pnpm deployment:validate |