fix: hide Sync commands when the service worker is disabled #228
Workflow file for this run
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: | |
| pull_request: | |
| workflow_dispatch: | |
| concurrency: | |
| # Main pushes and manual edge publishes share mutable tags and must not overlap. | |
| group: ci-${{ (github.event_name == 'workflow_dispatch' || github.ref == 'refs/heads/main') && 'edge' || github.ref }} | |
| # Never cancel an in-flight tag (release) run; cancel superseded branch runs. | |
| cancel-in-progress: ${{ github.event_name == 'workflow_dispatch' || !startsWith(github.ref, 'refs/tags/') }} | |
| env: | |
| FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true | |
| ARCHITECTURES: linux/arm64,linux/amd64,linux/arm/v7 | |
| IMAGE_NAME: silverbullet | |
| WEBSITE_IMAGE: zefhemel/silverbullet-website | |
| NAMESPACE_GITHUB: silverbulletmd | |
| NAMESPACE_DOCKER: zefhemel | |
| permissions: | |
| contents: read | |
| jobs: | |
| # Single source of truth for branch/channel decisions. | |
| config: | |
| runs-on: ubuntu-latest | |
| outputs: | |
| publish: ${{ steps.cfg.outputs.publish }} | |
| is_edge: ${{ steps.cfg.outputs.is_edge }} | |
| is_tag: ${{ steps.cfg.outputs.is_tag }} | |
| gh_release_tag: ${{ steps.cfg.outputs.gh_release_tag }} | |
| gh_release_name: ${{ steps.cfg.outputs.gh_release_name }} | |
| docker_edge_tag: ${{ steps.cfg.outputs.docker_edge_tag }} | |
| docker_slim_edge_tag: ${{ steps.cfg.outputs.docker_slim_edge_tag }} | |
| docker_legacy_edge_tag: ${{ steps.cfg.outputs.docker_legacy_edge_tag }} | |
| docker_runtime_api_tag: ${{ steps.cfg.outputs.docker_runtime_api_tag }} | |
| website_tag: ${{ steps.cfg.outputs.website_tag }} | |
| steps: | |
| - id: cfg | |
| env: | |
| EVENT: ${{ github.event_name }} | |
| REF: ${{ github.ref }} | |
| run: | | |
| set -euo pipefail | |
| # Channel configuration. Pushes to PUBLISH_BRANCH and manual dispatches | |
| # publish the edge channel; git tags publish stable releases. | |
| PUBLISH_BRANCH="main" | |
| GH_RELEASE_TAG="edge" | |
| GH_RELEASE_NAME="Edge" | |
| DOCKER_EDGE_TAG="edge" | |
| DOCKER_SLIM_EDGE_TAG="edge-slim" | |
| DOCKER_LEGACY_EDGE_TAG="v2" | |
| DOCKER_RUNTIME_API_TAG="v2-runtime-api" | |
| WEBSITE_TAG="edge" | |
| is_tag=false | |
| is_edge=false | |
| publish=false | |
| if [[ "$EVENT" == "workflow_dispatch" ]]; then | |
| # Manual dispatch always builds + publishes the edge channel, | |
| # regardless of which branch or tag was selected in the UI. | |
| is_edge=true | |
| publish=true | |
| elif [[ "$REF" == refs/tags/* ]]; then | |
| is_tag=true | |
| publish=true | |
| elif [[ "$REF" == "refs/heads/$PUBLISH_BRANCH" ]]; then | |
| is_edge=true | |
| publish=true | |
| fi | |
| { | |
| echo "publish=$publish" | |
| echo "is_edge=$is_edge" | |
| echo "is_tag=$is_tag" | |
| echo "gh_release_tag=$GH_RELEASE_TAG" | |
| echo "gh_release_name=$GH_RELEASE_NAME" | |
| echo "docker_edge_tag=$DOCKER_EDGE_TAG" | |
| echo "docker_slim_edge_tag=$DOCKER_SLIM_EDGE_TAG" | |
| echo "docker_legacy_edge_tag=$DOCKER_LEGACY_EDGE_TAG" | |
| echo "docker_runtime_api_tag=$DOCKER_RUNTIME_API_TAG" | |
| echo "website_tag=$WEBSITE_TAG" | |
| } >> "$GITHUB_OUTPUT" | |
| echo "publish=$publish is_edge=$is_edge is_tag=$is_tag" | |
| test-frontend: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Setup repo | |
| uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| fetch-tags: true | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v7 | |
| with: | |
| node-version-file: ".nvmrc" | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Run build | |
| run: npm run build | |
| - name: Run checks | |
| run: | | |
| npm run check | |
| npm test | |
| # Debug rust-embed serves from disk, so runtime tests need a built client bundle. | |
| test-rust: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Setup repo | |
| uses: actions/checkout@v7 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v7 | |
| with: | |
| node-version-file: ".nvmrc" | |
| - uses: Swatinem/rust-cache@v2 | |
| with: | |
| key: test-rust | |
| save-if: ${{ github.ref == 'refs/heads/main' }} | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Build client bundle | |
| run: npm run build | |
| - name: Install Playwright browser (chromium) | |
| run: npx playwright install --with-deps chromium | |
| # Point runtime tests at Playwright Chromium; CI must fail rather than skip them. | |
| - name: Resolve chromium path | |
| run: | | |
| echo "CHROMIUM_PATH=$(node -e "console.log(require('@playwright/test').chromium.executablePath())")" >> "$GITHUB_ENV" | |
| - name: Run Rust tests | |
| run: cargo test --workspace --all-features | |
| test-e2e: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Setup repo | |
| uses: actions/checkout@v7 | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v7 | |
| with: | |
| node-version-file: ".nvmrc" | |
| - uses: Swatinem/rust-cache@v2 | |
| with: | |
| key: e2e-debug | |
| save-if: ${{ github.ref == 'refs/heads/main' }} | |
| - name: Install dependencies | |
| run: npm ci | |
| # Debug serves assets from disk; test-e2e-release covers embedded assets. | |
| - name: Build (debug server + client bundle) | |
| run: make build-e2e | |
| - name: Install Playwright browser (chromium) | |
| run: npx playwright install --with-deps chromium | |
| - name: Run e2e tests | |
| run: npx playwright test --project=chromium | |
| - name: Run browser UI tests | |
| run: npx playwright test --config=playwright.browser.config.ts | |
| - name: Upload test results | |
| if: ${{ !cancelled() }} | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: playwright-report | |
| path: test-results/ | |
| retention-days: 7 | |
| # Validates the shipped release binary's rust-embed embedded bundle. Kept | |
| # separate from test-e2e so the fast gate isn't blocked on a release compile; | |
| # runs in parallel and gates release/docker. | |
| test-e2e-release: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Setup repo | |
| uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| fetch-tags: true | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v7 | |
| with: | |
| node-version-file: ".nvmrc" | |
| - uses: Swatinem/rust-cache@v2 | |
| with: | |
| key: e2e-release | |
| save-if: ${{ github.ref == 'refs/heads/main' }} | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Build (release server with embedded bundle) | |
| run: make build-rs | |
| - name: Install Playwright browser (chromium) | |
| run: npx playwright install --with-deps chromium | |
| - name: Run embedded-bundle e2e tests | |
| run: npx playwright test --project=release | |
| - name: Upload test results | |
| if: ${{ !cancelled() }} | |
| uses: actions/upload-artifact@v7 | |
| with: | |
| name: playwright-report-release | |
| path: test-results/ | |
| retention-days: 7 | |
| # Compile alongside tests; publishing jobs enforce the test gate. | |
| build: | |
| needs: [config] | |
| if: needs.config.outputs.publish == 'true' | |
| uses: ./.github/workflows/_build.yml | |
| secrets: inherit | |
| release: | |
| needs: [config, build, test-frontend, test-rust, test-e2e, test-e2e-release] | |
| if: needs.config.outputs.publish == 'true' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: write | |
| id-token: write # npm provenance / JSR auth on tag publishes | |
| steps: | |
| - name: Setup repo | |
| uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| fetch-tags: true | |
| - name: Setup Node.js | |
| uses: actions/setup-node@v7 | |
| with: | |
| node-version-file: ".nvmrc" | |
| registry-url: "https://registry.npmjs.org" | |
| - name: Install npm dependencies | |
| run: npm ci | |
| - name: Build plug-compile bundle | |
| run: npm run build:plug-compile | |
| - name: Download build artifacts | |
| uses: actions/download-artifact@v8 | |
| with: | |
| path: dist-artifacts | |
| pattern: release-zips-* | |
| merge-multiple: true | |
| # Branch / manual dispatch → refresh the rolling edge prerelease. | |
| - name: Publish edge prerelease | |
| if: needs.config.outputs.is_edge == 'true' | |
| uses: softprops/action-gh-release@v2 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| with: | |
| draft: false | |
| tag_name: ${{ needs.config.outputs.gh_release_tag }} | |
| name: ${{ needs.config.outputs.gh_release_name }} | |
| body: Automated build from commit ${{ github.sha }} | |
| prerelease: true | |
| files: | | |
| docs/CHANGELOG.md | |
| dist/plug-compile.js | |
| dist-artifacts/silverbullet-server-*.zip | |
| dist-artifacts/sb-*.zip | |
| # Tag push → publish that tag's (non-pre) release. Tag is inferred from ref. | |
| - name: Publish tagged release | |
| if: needs.config.outputs.is_tag == 'true' | |
| uses: softprops/action-gh-release@v2 | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | |
| with: | |
| draft: false | |
| files: | | |
| docs/CHANGELOG.md | |
| dist/plug-compile.js | |
| dist-artifacts/silverbullet-server-*.zip | |
| dist-artifacts/sb-*.zip | |
| docker: | |
| needs: [config, build, test-frontend, test-rust, test-e2e, test-e2e-release] | |
| if: needs.config.outputs.publish == 'true' | |
| runs-on: ubuntu-latest | |
| permissions: | |
| contents: read | |
| packages: write | |
| steps: | |
| - name: Setup repo | |
| uses: actions/checkout@v7 | |
| with: | |
| fetch-depth: 0 | |
| fetch-tags: true | |
| - name: Download raw docker binaries | |
| uses: actions/download-artifact@v8 | |
| with: | |
| name: docker-binaries | |
| path: . | |
| - name: Verify binaries are static (no interpreter) | |
| run: | | |
| set -euo pipefail | |
| for b in silverbullet-amd64 silverbullet-arm64 silverbullet-arm; do | |
| echo "== $b =="; file "$b" | |
| if file "$b" | grep -q "interpreter"; then | |
| echo "ERROR: $b is dynamically linked (needs a loader); expected static musl"; exit 1 | |
| fi | |
| done | |
| - name: Set up QEMU for multi-arch builds with buildx | |
| uses: docker/setup-qemu-action@v3 | |
| with: | |
| platforms: ${{ env.ARCHITECTURES }} | |
| - name: Set up Docker Buildx | |
| uses: docker/setup-buildx-action@v3 | |
| with: | |
| platforms: ${{ env.ARCHITECTURES }} | |
| - name: Login to Docker Hub | |
| uses: docker/login-action@v3 | |
| with: | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| - name: Log in to the ghcr Container registry | |
| uses: docker/login-action@v3 | |
| with: | |
| registry: ghcr.io | |
| username: ${{ github.actor }} | |
| password: ${{ secrets.GITHUB_TOKEN }} | |
| - name: "Slim: Extract metadata" | |
| id: slim_meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: | | |
| ${{ env.NAMESPACE_DOCKER }}/${{ env.IMAGE_NAME }} | |
| ghcr.io/${{ env.NAMESPACE_GITHUB }}/${{ env.IMAGE_NAME }} | |
| flavor: latest=false | |
| tags: | | |
| type=semver,pattern={{raw}}-slim,priority=910,enable=true | |
| type=semver,pattern=latest-slim,priority=900,enable=true | |
| type=raw,value=${{ needs.config.outputs.docker_slim_edge_tag }},enable=${{ needs.config.outputs.is_edge == 'true' }} | |
| - name: "Slim: Build and push" | |
| id: slim_build | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| platforms: ${{ env.ARCHITECTURES }} | |
| push: true | |
| file: Dockerfile | |
| provenance: false | |
| tags: ${{ steps.slim_meta.outputs.tags }} | |
| labels: ${{ steps.slim_meta.outputs.labels }} | |
| - name: "Default: Extract metadata" | |
| id: meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: | | |
| ${{ env.NAMESPACE_DOCKER }}/${{ env.IMAGE_NAME }} | |
| ghcr.io/${{ env.NAMESPACE_GITHUB }}/${{ env.IMAGE_NAME }} | |
| flavor: latest=false | |
| tags: | | |
| type=semver,pattern={{raw}},priority=920,enable=true | |
| type=semver,pattern=latest,priority=910,enable=true | |
| type=semver,pattern={{raw}}-runtime-api,priority=900,enable=true | |
| type=semver,pattern=latest-runtime-api,priority=890,enable=true | |
| type=raw,value=${{ needs.config.outputs.docker_edge_tag }},priority=220,enable=${{ needs.config.outputs.is_edge == 'true' }} | |
| type=raw,value=${{ needs.config.outputs.docker_legacy_edge_tag }},priority=210,enable=${{ needs.config.outputs.is_edge == 'true' }} | |
| type=raw,value=${{ needs.config.outputs.docker_runtime_api_tag }},priority=200,enable=${{ needs.config.outputs.is_edge == 'true' }} | |
| - name: "Default: Build and push" | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| platforms: ${{ env.ARCHITECTURES }} | |
| push: true | |
| file: Dockerfile.runtime-api | |
| provenance: false | |
| build-args: | | |
| BASE_IMAGE=ghcr.io/${{ env.NAMESPACE_GITHUB }}/${{ env.IMAGE_NAME }}@${{ steps.slim_build.outputs.digest }} | |
| tags: ${{ steps.meta.outputs.tags }} | |
| labels: ${{ steps.meta.outputs.labels }} | |
| docker-website: | |
| needs: [config, docker] | |
| if: needs.config.outputs.publish == 'true' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Setup repo | |
| uses: actions/checkout@v7 | |
| - name: Login to Docker Hub | |
| uses: docker/login-action@v3 | |
| with: | |
| username: ${{ secrets.DOCKERHUB_USERNAME }} | |
| password: ${{ secrets.DOCKERHUB_TOKEN }} | |
| - name: "Website: Extract metadata (tags, labels) for docker" | |
| id: website_meta | |
| uses: docker/metadata-action@v5 | |
| with: | |
| images: ${{ env.WEBSITE_IMAGE }} | |
| tags: | | |
| type=raw,value=${{ needs.config.outputs.website_tag }},enable=${{ needs.config.outputs.is_edge == 'true' }} | |
| type=semver,pattern=latest,enable=true | |
| - name: "Website: Build and push" | |
| uses: docker/build-push-action@v6 | |
| with: | |
| context: . | |
| file: Dockerfile.website | |
| push: true | |
| provenance: false | |
| build-args: | | |
| BASE_IMAGE=zefhemel/silverbullet:${{ needs.config.outputs.is_tag == 'true' && 'latest-slim' || needs.config.outputs.docker_slim_edge_tag }} | |
| tags: ${{ steps.website_meta.outputs.tags }} | |
| labels: ${{ steps.website_meta.outputs.labels }} |