Skip to content

fix: hide Sync commands when the service worker is disabled #228

fix: hide Sync commands when the service worker is disabled

fix: hide Sync commands when the service worker is disabled #228

Workflow file for this run

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 }}