Skip to content

chore(main): release pkg/datacrypto 0.1.0-alpha.1 #5927

chore(main): release pkg/datacrypto 0.1.0-alpha.1

chore(main): release pkg/datacrypto 0.1.0-alpha.1 #5927

Workflow file for this run

name: ci
on:
push:
branches:
- main
- 'release-[0-9]*'
pull_request:
types:
- opened
- reopened
- synchronize
- labeled
- unlabeled
- ready_for_review
concurrency:
# PRs share a group so superseded revisions are cancelled. Pushes use their
# immutable commit SHA so every main commit can publish an image.
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
permissions:
contents: read
jobs:
build-main-image-contexts:
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
outputs:
image-version: ${{ steps.image-version.outputs.version }}
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup environment
uses: ./.github/actions/setup
- name: Derive main image version
id: image-version
shell: bash
run: |
set -euo pipefail
development_version="$(mise env --json | jq -r '.CHATTO_DEVELOPMENT_VERSION // empty')"
if [[ -z "$development_version" ]]; then
echo "::error::Could not read CHATTO_DEVELOPMENT_VERSION from mise.toml."
exit 1
fi
echo "version=${development_version}+${GITHUB_SHA:0:12}" >> "$GITHUB_OUTPUT"
- name: Build frontend for production image
env:
CHATTO_BUILD_VERSION: ${{ steps.image-version.outputs.version }}
CHATTO_FRONTEND_PRECOMPRESS: "1"
run: mise build-frontend
- name: Verify frontend build version
env:
EXPECTED_VERSION: ${{ steps.image-version.outputs.version }}
shell: bash
run: |
jq -e --arg expected "$EXPECTED_VERSION" \
'.version == $expected' \
apps/frontend/build/_app/version.json
- name: Build production image contexts
shell: bash
env:
VERSION: ${{ steps.image-version.outputs.version }}
run: |
set -euo pipefail
for arch in amd64 arm64; do
context=".context/ci/docker/linux-${arch}"
mkdir -p "$context/docker" "$context/LICENSES"
cp docker/Dockerfile.goreleaser "$context/Dockerfile"
cp docker/docker-entrypoint.sh "$context/docker/docker-entrypoint.sh"
cp docker/nats-wrapper.sh "$context/docker/nats-wrapper.sh"
cp LICENSES/AGPL-3.0-or-later.txt "$context/LICENSES/AGPL-3.0-or-later.txt"
cp NOTICE "$context/NOTICE"
(
cd cli
# Chatto does not expose Gin's MessagePack binding; omit its large codec.
CGO_ENABLED=0 GOOS=linux GOARCH="$arch" go build -trimpath \
-tags=nomsgpack \
-ldflags="-s -w -X main.Version=${VERSION}" \
-o "../$context/chatto" .
)
done
- name: Upload production image contexts
uses: actions/upload-artifact@v7
with:
name: production-image-contexts
path: .context/ci/docker
retention-days: 1
license-check:
# runs-on: ubicloud-standard-4
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup mise
uses: jdx/mise-action@v4
with:
experimental: true
- name: Check license metadata
run: mise license-check
desktop:
name: desktop (${{ matrix.platform }})
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
include:
- platform: linux
os: ubuntu-latest
artifact-path: apps/desktop/dist/chatto-desktop
- platform: macos
os: macos-latest
artifact-path: apps/desktop/dist/Chatto Desktop.app
- platform: windows
os: windows-latest
artifact-path: apps/desktop/dist/windows
runs-on: ${{ matrix.os }}
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup environment
uses: ./.github/actions/setup
with:
install-cli-deps: "false"
- name: Check and test desktop shell
run: mise test-desktop
- name: Build desktop bundle
run: mise desktop-build
- name: Verify desktop bundle exists
shell: bash
env:
ARTIFACT_PATH: ${{ matrix.artifact-path }}
run: test -e "$ARTIFACT_PATH"
- name: Verify macOS bundle signature
if: runner.os == 'macOS'
run: codesign --verify --deep --strict --verbose=2 "apps/desktop/dist/Chatto Desktop.app"
- name: Upload desktop bundle
uses: actions/upload-artifact@v7
with:
name: chatto-desktop-${{ matrix.platform }}-${{ runner.arch }}
path: ${{ matrix.artifact-path }}
if-no-files-found: error
retention-days: 7
codegen-proto-drift:
# runs-on: ubicloud-standard-4
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup environment
uses: ./.github/actions/setup
- name: Fetch protobuf breaking base
run: |
base_ref="${GITHUB_BASE_REF:-${GITHUB_REF_NAME}}"
git fetch --no-tags --depth=1 origin "${base_ref}:refs/remotes/origin/${base_ref}"
- name: Check storage, internal, and Operator protobuf breaking changes
run: |
base_ref="${GITHUB_BASE_REF:-${GITHUB_REF_NAME}}"
cd proto
mise x -- buf breaking . --against "../.git#branch=origin/${base_ref},subdir=proto" \
--exclude-imports \
--exclude-path chatto/auth/v1 \
--exclude-path chatto/discovery/v1 \
--exclude-path chatto/api/v1 \
--exclude-path chatto/admin/v1 \
--exclude-path chatto/realtime/v1 \
--exclude-path chatto/core/v1/projection_snapshots.proto
- name: Check projection snapshot schema contracts
working-directory: cli
run: mise x -- go test ./internal/core -run TestProjectionSnapshotContractsIncludeCurrentSchema -count=1
- name: Check public API protobuf breaking changes
if: ${{ github.event_name != 'pull_request' || !contains(github.event.pull_request.labels.*.name, 'api-breaking-change') }}
run: |
base_ref="${GITHUB_BASE_REF:-${GITHUB_REF_NAME}}"
cd proto
mise x -- buf breaking . --against "../.git#branch=origin/${base_ref},subdir=proto" \
--path chatto/auth/v1 \
--path chatto/discovery/v1 \
--path chatto/api/v1 \
--path chatto/admin/v1 \
--path chatto/realtime/v1
- name: Check Authling persisted protobuf breaking changes
run: |
base_ref="${GITHUB_BASE_REF:-${GITHUB_REF_NAME}}"
if git cat-file -e "origin/${base_ref}:authling/buf.yaml" 2>/dev/null; then
cd authling
mise x -- buf breaking . \
--against "../.git#branch=origin/${base_ref},subdir=authling" \
--exclude-imports
else
echo "::notice::Authling protobufs are new; no base schema exists yet."
fi
- name: Acknowledge intentional public API protobuf breaking changes
if: ${{ github.event_name == 'pull_request' && contains(github.event.pull_request.labels.*.name, 'api-breaking-change') }}
run: |
echo "::notice::Skipping public API Buf breaking check because this PR has the api-breaking-change label. Storage, internal, Operator, and codegen drift checks still run."
- name: Lint Authling protobufs
working-directory: authling
run: mise x -- buf lint
- name: Regenerate protobuf bindings
run: |
mise codegen-proto
cd authling
mise codegen-proto
- name: Check generated protobuf bindings are current
run: |
if ! git diff --exit-code; then
git status --short
exit 1
fi
test-workspace:
# runs-on: ubicloud-standard-4
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup environment
uses: ./.github/actions/setup
with:
install-cli-deps: "false"
- name: Setup Playwright
uses: ./.github/actions/setup-playwright
timeout-minutes: 15
- name: Run workspace typechecks
run: pnpm run check
- name: Run workspace linters
run: pnpm run lint
- name: Run workspace unit tests
run: pnpm test
- name: Build Storybook design-system catalog
run: pnpm run build:storybook
test-cli:
# runs-on: ubicloud-standard-4
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup environment
uses: ./.github/actions/setup
- name: Run Authling tests
working-directory: authling
run: mise test
- name: Run application configuration tests
run: mise test-appconfig
- name: Run data cryptography tests
run: mise test-datacrypto
- name: Run event framework tests
run: mise test-events
- name: Run embedded NATS runtime tests
run: mise test-natsruntime
- name: Build frontend
run: mise build-frontend
- name: Run Go tests
run: cd cli && go test -trimpath -p 1 -tags test_endpoints ./...
test-authling-e2e:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup environment
uses: ./.github/actions/setup
- name: Setup Authling E2E dependencies
working-directory: authling
run: |
mise install
mise deps-web
- name: Setup Playwright
uses: ./.github/actions/setup-playwright
timeout-minutes: 15
with:
working-directory: authling
- name: Run Authling E2E tests
working-directory: authling
run: mise test-e2e
- name: Upload Playwright report
uses: actions/upload-artifact@v7
if: ${{ failure() }}
with:
name: authling-playwright-report
path: |
authling/playwright-report/
authling/test-results/
retention-days: 14
test-e2e:
# runs-on: ubicloud-standard-4
runs-on: ubuntu-latest
name: test-e2e (${{ matrix.shard }}/4)
strategy:
fail-fast: false
matrix:
shard: [1, 2, 3, 4]
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup environment
uses: ./.github/actions/setup
- name: Setup Playwright
uses: ./.github/actions/setup-playwright
timeout-minutes: 15
- name: Build E2E server
run: mise build-e2e-server
- name: Run E2E tests
run: cd apps/frontend && pnpm exec playwright test --grep-invert @ffmpeg --shard=${{ matrix.shard }}/4
- name: Upload Playwright report
uses: actions/upload-artifact@v7
if: ${{ failure() }}
with:
name: playwright-report-${{ matrix.shard }}-of-4
path: apps/frontend/playwright-report/
retention-days: 14
test-e2e-media:
# runs-on: ubicloud-standard-4
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Setup environment
uses: ./.github/actions/setup
timeout-minutes: 15
with:
install-ffmpeg: "true"
- name: Setup Playwright
uses: ./.github/actions/setup-playwright
timeout-minutes: 15
- name: Build E2E server
run: mise build-e2e-server
- name: Run media E2E tests
run: cd apps/frontend && pnpm exec playwright test --grep @ffmpeg
- name: Upload Playwright report
uses: actions/upload-artifact@v7
if: ${{ failure() }}
with:
name: playwright-report-media
path: apps/frontend/playwright-report/
retention-days: 14
test-e2e-performance:
runs-on: ubuntu-latest
timeout-minutes: 35
steps:
- name: Checkout code
uses: actions/checkout@v6
with:
fetch-depth: 0
- name: Setup environment
uses: ./.github/actions/setup
- name: Setup Playwright
uses: ./.github/actions/setup-playwright
timeout-minutes: 15
- name: Prepare performance baseline
id: performance-baseline
shell: bash
env:
PERFORMANCE_BASE_SHA: ${{ github.event.pull_request.base.sha || github.event.before }}
run: |
set -euo pipefail
baseline_dir="$RUNNER_TEMP/chatto-performance-base"
if [[ -z "$PERFORMANCE_BASE_SHA" || "$PERFORMANCE_BASE_SHA" =~ ^0+$ ]]; then
echo "available=false" >> "$GITHUB_OUTPUT"
exit 0
fi
if ! git cat-file -e "${PERFORMANCE_BASE_SHA}:apps/frontend/e2e-performance/large-server.performance.test.ts"; then
echo "available=false" >> "$GITHUB_OUTPUT"
exit 0
fi
git worktree add --detach "$baseline_dir" "$PERFORMANCE_BASE_SHA"
echo "available=true" >> "$GITHUB_OUTPUT"
- name: Run base-revision performance test
if: steps.performance-baseline.outputs.available == 'true'
working-directory: ${{ runner.temp }}/chatto-performance-base
run: mise test-e2e-performance
- name: Preserve base-revision results
if: steps.performance-baseline.outputs.available == 'true'
shell: bash
run: |
set -euo pipefail
comparison_dir="apps/frontend/test-results/performance-comparison"
mkdir -p "$comparison_dir"
base_result=$(find "$RUNNER_TEMP/chatto-performance-base/apps/frontend/test-results/performance" -name performance-results.json -print -quit)
cp "$base_result" "$comparison_dir/base.json"
cp -R "$RUNNER_TEMP/chatto-performance-base/apps/frontend/playwright-report-performance" apps/frontend/playwright-report-performance-base
- name: Build E2E server
run: mise build-e2e-server
- name: Run large-server E2E performance test
run: mise test-e2e-performance
- name: Preserve candidate results
shell: bash
run: |
set -euo pipefail
comparison_dir="apps/frontend/test-results/performance-comparison"
mkdir -p "$comparison_dir"
candidate_result=$(find apps/frontend/test-results/performance -name performance-results.json -print -quit)
cp "$candidate_result" "$comparison_dir/candidate.json"
- name: Compare performance with base revision
if: steps.performance-baseline.outputs.available == 'true'
working-directory: apps/frontend
shell: bash
run: |
set -o pipefail
node scripts/compare-e2e-performance.mjs \
test-results/performance-comparison/base.json \
test-results/performance-comparison/candidate.json \
| tee -a "$GITHUB_STEP_SUMMARY"
- name: Summarize candidate-only performance results
if: steps.performance-baseline.outputs.available != 'true'
shell: bash
run: |
result="apps/frontend/test-results/performance-comparison/candidate.json"
{
echo "## Large-server E2E performance"
jq -r '"Fixture: \(.measurements.syntheticUsers) users, \(.measurements.messages) messages; \(.measurements.sampleCount)-sample medians (\(.measurements.startupMode) startup)"' "$result"
echo ""
echo "| Measurement | Duration (ms) |"
echo "| --- | ---: |"
jq -r '.measurements | to_entries[] | select(.key | endswith("Ms")) | "| \(.key) | \(.value | round) |"' "$result"
} >> "$GITHUB_STEP_SUMMARY"
- name: Remove performance baseline worktree
if: ${{ always() && steps.performance-baseline.outputs.available == 'true' }}
shell: bash
run: git worktree remove --force "$RUNNER_TEMP/chatto-performance-base"
- name: Upload performance results
uses: actions/upload-artifact@v7
if: ${{ always() }}
with:
name: e2e-performance-results
path: |
apps/frontend/test-results/performance/
apps/frontend/test-results/performance-comparison/
apps/frontend/playwright-report-performance/
apps/frontend/playwright-report-performance-base/
retention-days: 30
publish-main-image:
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
needs:
- build-main-image-contexts
- license-check
- codegen-proto-drift
- test-workspace
- test-cli
- test-e2e
- test-e2e-media
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
steps:
- name: Download production image contexts
uses: actions/download-artifact@v8
with:
name: production-image-contexts
path: docker-contexts
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Login to GitHub Container Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Derive immutable main image tag
id: main-image
shell: bash
run: echo "tag=main-$(date -u +%Y%m%dT%H%M%SZ)-${GITHUB_SHA:0:12}" >> "$GITHUB_OUTPUT"
- name: Build and push amd64 image
uses: docker/build-push-action@v6
with:
context: docker-contexts/linux-amd64
file: docker-contexts/linux-amd64/Dockerfile
push: true
platforms: linux/amd64
tags: ghcr.io/chattocorp/chatto:${{ github.sha }}-amd64
cache-from: type=gha,scope=chatto-main-amd64
cache-to: type=gha,scope=chatto-main-amd64,mode=max
labels: |
org.opencontainers.image.source=https://github.com/chattocorp/chatto
org.opencontainers.image.revision=${{ github.sha }}
org.opencontainers.image.version=${{ needs.build-main-image-contexts.outputs.image-version }}
org.opencontainers.image.licenses=AGPL-3.0-or-later
- name: Build and push arm64 image
uses: docker/build-push-action@v6
with:
context: docker-contexts/linux-arm64
file: docker-contexts/linux-arm64/Dockerfile
push: true
platforms: linux/arm64
tags: ghcr.io/chattocorp/chatto:${{ github.sha }}-arm64
cache-from: type=gha,scope=chatto-main-arm64
cache-to: type=gha,scope=chatto-main-arm64,mode=max
labels: |
org.opencontainers.image.source=https://github.com/chattocorp/chatto
org.opencontainers.image.revision=${{ github.sha }}
org.opencontainers.image.version=${{ needs.build-main-image-contexts.outputs.image-version }}
org.opencontainers.image.licenses=AGPL-3.0-or-later
- name: Publish multi-platform commit image
shell: bash
run: |
docker buildx imagetools create \
--tag "ghcr.io/chattocorp/chatto:${GITHUB_SHA}" \
--tag "ghcr.io/chattocorp/chatto:${{ steps.main-image.outputs.tag }}" \
"ghcr.io/chattocorp/chatto:${GITHUB_SHA}-amd64" \
"ghcr.io/chattocorp/chatto:${GITHUB_SHA}-arm64"
{
echo "### Main commit image"
echo
echo "\`ghcr.io/chattocorp/chatto:${GITHUB_SHA}\`"
echo "\`ghcr.io/chattocorp/chatto:${{ steps.main-image.outputs.tag }}\`"
echo
echo "Version: \`${{ needs.build-main-image-contexts.outputs.image-version }}\`"
} >> "$GITHUB_STEP_SUMMARY"