mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-10-31 00:12:39 +00:00
The Stalwart bundle's README told admins to use `releases/latest/download/bulwark-lite-stalwart.zip` as `resourceUrl`. Stalwart re-downloads that URL on its own schedule and checks no hash or signature, so whatever the newest release held would run as script on the Stalwart origin next to the web admin. The README now installs from the tagged release, explains updating by moving `resourceUrl` to the next tag, and describes the floating URL as an opt-in with that risk spelled out. Release runs upload a `.sha256` next to each Lite zip.
221 lines
6.8 KiB
YAML
221 lines
6.8 KiB
YAML
name: Build Static Lite
|
|
|
|
# Bulwark Lite: the static export of the webmail (see docs in README.md,
|
|
# "Static Lite build"). Runs the build on every push as a regression check,
|
|
# attaches the zips to releases, and lets maintainers build a custom variant
|
|
# (fixed server URL, sub-path mount, locale subset, demo) on demand.
|
|
#
|
|
# Two targets are built side by side:
|
|
# - static: bulwark-lite-<version>.zip for any static web host;
|
|
# - stalwart: bulwark-lite-stalwart.zip, a Stalwart `Application` bundle.
|
|
# Its LITE-README points `resourceUrl` at the tagged release.
|
|
#
|
|
# Each zip is published with a `.sha256` next to it.
|
|
#
|
|
# scripts/lite/prepare.mjs deletes the server-only trees in place; that is
|
|
# safe here because the Actions checkout is disposable (CI=true is the guard).
|
|
|
|
on:
|
|
release:
|
|
types: [published]
|
|
push:
|
|
branches: [main]
|
|
pull_request:
|
|
workflow_dispatch:
|
|
inputs:
|
|
jmap_server_url:
|
|
description: "Default JMAP server URL baked into config.json (static: editable after download; stalwart: leave empty for the serving Stalwart)"
|
|
required: false
|
|
app_name:
|
|
description: "App name"
|
|
required: false
|
|
default: "Bulwark Webmail"
|
|
base_path:
|
|
description: "Sub-path mount for the static target, e.g. /webmail (baked into asset URLs; the stalwart target ignores it)"
|
|
required: false
|
|
locales:
|
|
description: "Comma-separated locale subset, e.g. en,de,fr (default: all)"
|
|
required: false
|
|
demo:
|
|
description: "Build the static demo (built-in demo account, no server)"
|
|
type: boolean
|
|
default: false
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
build:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 30
|
|
strategy:
|
|
fail-fast: false
|
|
matrix:
|
|
target: [static, stalwart]
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 22
|
|
cache: npm
|
|
|
|
- name: Install dependencies
|
|
run: npm ci
|
|
|
|
- name: Build static export
|
|
env:
|
|
CI: "true"
|
|
GIT_COMMIT: ${{ github.sha }}
|
|
LITE_TARGET: ${{ matrix.target }}
|
|
# The Stalwart bundle learns its mount prefix at runtime.
|
|
NEXT_PUBLIC_BASE_PATH: ${{ matrix.target == 'static' && inputs.base_path || '' }}
|
|
LITE_JMAP_SERVER_URL: ${{ inputs.jmap_server_url }}
|
|
LITE_APP_NAME: ${{ inputs.app_name }}
|
|
LITE_LOCALES: ${{ inputs.locales }}
|
|
LITE_DEMO_MODE: ${{ inputs.demo }}
|
|
run: npm run build:lite
|
|
|
|
- name: Package zip
|
|
env:
|
|
REF_NAME: ${{ github.ref_name }}
|
|
run: |
|
|
if [ "${{ matrix.target }}" = "stalwart" ]; then
|
|
# postbuild already packed it (entries at the archive root, no
|
|
# wrapping folder); the name stays version-less on purpose.
|
|
ZIP="bulwark-lite-stalwart.zip"
|
|
else
|
|
if [ "${{ github.event_name }}" = "release" ]; then
|
|
VERSION="${REF_NAME#v}"
|
|
else
|
|
VERSION="$(cat VERSION)-${GITHUB_SHA::7}"
|
|
fi
|
|
ZIP="bulwark-lite-${VERSION}.zip"
|
|
(cd out && zip -qr "../${ZIP}" .)
|
|
fi
|
|
echo "ZIP=${ZIP}" >> "$GITHUB_ENV"
|
|
ls -la "${ZIP}"
|
|
|
|
- name: Upload artifact
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: bulwark-lite-${{ matrix.target }}
|
|
path: ${{ env.ZIP }}
|
|
retention-days: 7
|
|
|
|
release:
|
|
# The only job with a write token, and it runs no checked-out code.
|
|
needs: build
|
|
if: github.event_name == 'release'
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
permissions:
|
|
contents: write
|
|
steps:
|
|
- name: Download zips
|
|
uses: actions/download-artifact@v4
|
|
with:
|
|
pattern: bulwark-lite-*
|
|
path: zips
|
|
merge-multiple: true
|
|
|
|
- name: Checksum zips
|
|
working-directory: zips
|
|
run: for zip in *.zip; do sha256sum "$zip" > "$zip.sha256"; done
|
|
|
|
- name: Upload release assets
|
|
env:
|
|
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
GH_REPO: ${{ github.repository }}
|
|
TAG_NAME: ${{ github.event.release.tag_name }}
|
|
run: gh release upload "$TAG_NAME" zips/*.zip zips/*.zip.sha256 --clobber
|
|
|
|
smoke:
|
|
# Boots the demo export on a plain static server and drives it with
|
|
# Playwright: login, open a thread, switch surfaces, reload a deep link.
|
|
# No JMAP server involved, so it is cheap and deterministic.
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 30
|
|
if: github.event_name == 'push' || github.event_name == 'pull_request'
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 22
|
|
cache: npm
|
|
|
|
- name: Install dependencies
|
|
run: npm ci
|
|
|
|
- name: Install Playwright browser
|
|
run: npx playwright install --with-deps chromium
|
|
|
|
# prepare.mjs prunes every test tree in the checkout; the smoke suite
|
|
# lives under scripts/lite/smoke, which it leaves alone.
|
|
- name: Build demo export
|
|
env:
|
|
CI: "true"
|
|
GIT_COMMIT: ${{ github.sha }}
|
|
LITE_DEMO_MODE: "true"
|
|
LITE_LOCALES: "en,de"
|
|
run: npm run build:lite
|
|
|
|
- name: Run smoke test
|
|
run: npm run test:lite-smoke
|
|
|
|
- name: Upload Playwright report
|
|
if: failure()
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: lite-smoke-report
|
|
path: playwright-report
|
|
retention-days: 7
|
|
|
|
smoke-stalwart:
|
|
# The Stalwart Application bundle, served by scripts/lite/serve.mjs
|
|
# --stalwart (an emulation of how Stalwart serves Applications) from the
|
|
# zip itself, under two prefixes.
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 30
|
|
if: github.event_name == 'push' || github.event_name == 'pull_request'
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v4
|
|
|
|
- name: Setup Node.js
|
|
uses: actions/setup-node@v4
|
|
with:
|
|
node-version: 22
|
|
cache: npm
|
|
|
|
- name: Install dependencies
|
|
run: npm ci
|
|
|
|
- name: Install Playwright browser
|
|
run: npx playwright install --with-deps chromium
|
|
|
|
- name: Build demo bundle
|
|
env:
|
|
CI: "true"
|
|
GIT_COMMIT: ${{ github.sha }}
|
|
LITE_TARGET: stalwart
|
|
LITE_DEMO_MODE: "true"
|
|
LITE_LOCALES: "en,de"
|
|
run: npm run build:lite
|
|
|
|
- name: Run smoke test
|
|
run: npm run test:lite-smoke:stalwart
|
|
|
|
- name: Upload Playwright report
|
|
if: failure()
|
|
uses: actions/upload-artifact@v4
|
|
with:
|
|
name: lite-smoke-stalwart-report
|
|
path: playwright-report
|
|
retention-days: 7
|