Skip to content

fix(ci): automate version sync across all packages on release #691

fix(ci): automate version sync across all packages on release

fix(ci): automate version sync across all packages on release #691

Workflow file for this run

name: CI
on:
push:
branches: ["**"]
pull_request:
branches: [main]
concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true
env:
PNPM_VERSION: "10.6.1"
NODE_VERSION: "22"
PYTHON_VERSION: "3.12"
jobs:
# ── TypeScript ────────────────────────────────────────────────────────────
ts-typecheck:
name: TS / typecheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: pnpm
- run: pnpm install --frozen-lockfile
- name: Build TS packages in dependency order
run: |
pnpm --filter @maschina/db build
pnpm --filter @maschina/crypto build
pnpm --filter @maschina/auth build
pnpm --filter @maschina/cache build
pnpm --filter @maschina/plans build
pnpm --filter @maschina/events build
pnpm --filter @maschina/nats build
pnpm --filter @maschina/jobs build
pnpm --filter @maschina/model build
pnpm --filter @maschina/telemetry build
pnpm --filter @maschina/usage build
pnpm --filter @maschina/billing build
pnpm --filter @maschina/push build
pnpm --filter @maschina/notifications build
pnpm --filter @maschina/validation build
pnpm --filter @maschina/email build
pnpm --filter @maschina/webhooks build
pnpm --filter @maschina/search build
pnpm --filter @maschina/compliance build
pnpm --filter @maschina/storage build
pnpm --filter @maschina/flags build
pnpm --filter @maschina/analytics build
pnpm --filter @maschina/chain build
pnpm --filter @maschina/connectors build
pnpm --filter @maschina/marketplace build
- run: pnpm typecheck
ts-lint:
name: TS / lint (Biome)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: pnpm
- run: pnpm install --frozen-lockfile
- run: pnpm exec biome check packages/ services/ --no-errors-on-unmatched
ts-test:
name: TS / test (Vitest)
runs-on: ubuntu-latest
services:
postgres:
image: postgres:16-alpine
env:
POSTGRES_USER: maschina
POSTGRES_PASSWORD: maschina
POSTGRES_DB: maschina_test
ports: ["5432:5432"]
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
redis:
image: redis:7-alpine
ports: ["6379:6379"]
options: >-
--health-cmd "redis-cli ping"
--health-interval 10s
--health-timeout 5s
--health-retries 5
env:
DATABASE_URL: postgresql://maschina:maschina@localhost:5432/maschina_test
REDIS_URL: redis://localhost:6379
NATS_URL: nats://localhost:4222
JWT_SECRET: ci-test-secret-minimum-32-chars-longvalue
steps:
- uses: actions/checkout@v4
# Service containers can't pass command args — start NATS manually with JetStream.
- name: Start NATS with JetStream
run: docker run -d --name nats -p 4222:4222 nats:2-alpine -js
- uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
- uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: pnpm
- run: pnpm install --frozen-lockfile
- name: Build TS packages in dependency order
run: |
pnpm --filter @maschina/db build
pnpm --filter @maschina/crypto build
pnpm --filter @maschina/auth build
pnpm --filter @maschina/cache build
pnpm --filter @maschina/plans build
pnpm --filter @maschina/events build
pnpm --filter @maschina/nats build
pnpm --filter @maschina/jobs build
pnpm --filter @maschina/model build
pnpm --filter @maschina/telemetry build
pnpm --filter @maschina/usage build
pnpm --filter @maschina/billing build
pnpm --filter @maschina/push build
pnpm --filter @maschina/notifications build
pnpm --filter @maschina/validation build
pnpm --filter @maschina/email build
pnpm --filter @maschina/webhooks build
pnpm --filter @maschina/search build
pnpm --filter @maschina/compliance build
pnpm --filter @maschina/storage build
pnpm --filter @maschina/flags build
pnpm --filter @maschina/analytics build
pnpm --filter @maschina/connectors build
pnpm --filter @maschina/marketplace build
- name: Run migrations
run: pnpm db:migrate
- run: pnpm exec vitest run --coverage
# ── Rust ──────────────────────────────────────────────────────────────────
# Scoped to server-side crates only. Tauri + CLI binaries are built and
# cross-compiled in release.yml with their own platform runners.
rust-fmt:
name: Rust / fmt
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.88"
components: rustfmt
- run: cargo fmt -p maschina-gateway -p maschina-daemon -p maschina-realtime -p maschina-node -- --check
rust-clippy:
name: Rust / clippy
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.88"
components: clippy
- uses: Swatinem/rust-cache@v2
- run: sudo apt-get install -y pkg-config libssl-dev
- run: >
cargo clippy
-p maschina-gateway
-p maschina-daemon
-p maschina-realtime
-p maschina-node
-- -D warnings
rust-test:
name: Rust / test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.88"
- uses: Swatinem/rust-cache@v2
- run: sudo apt-get install -y pkg-config libssl-dev
- run: cargo test -p maschina-gateway -p maschina-daemon -p maschina-realtime -p maschina-node
rust-build:
name: Rust / build (release)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
toolchain: "1.88"
- uses: Swatinem/rust-cache@v2
- run: sudo apt-get install -y pkg-config libssl-dev
- run: >
cargo build --release
-p maschina-gateway
-p maschina-daemon
-p maschina-realtime
-p maschina-node
# ── Python ────────────────────────────────────────────────────────────────
python-lint:
name: Python / lint (Ruff)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- run: pip install ruff
- run: ruff check packages/runtime packages/agents packages/risk packages/ml services/runtime services/worker
- run: ruff format --check packages/runtime packages/agents packages/risk packages/ml services/runtime services/worker
python-test:
name: Python / test (Pytest)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: ${{ env.PYTHON_VERSION }}
- name: Install uv
run: curl -LsSf https://astral.sh/uv/install.sh | sh && echo "$HOME/.local/bin" >> $GITHUB_PATH
- name: Install packages
run: |
uv pip install -e packages/runtime --system
uv pip install -e packages/ml --no-deps --system
uv pip install numpy scikit-learn scipy pandas tqdm --system
uv pip install -e packages/agents --system
uv pip install -e packages/risk --system
uv pip install -e "packages/sdk/python[dev]" --system
uv pip install -e "services/runtime[dev]" --system
uv pip install -e services/worker --system
uv pip install pytest pytest-asyncio pytest-mock numpy scikit-learn --system
- run: pytest packages/runtime/tests packages/agents/tests packages/risk/tests packages/ml/tests packages/sdk/python/tests services/runtime/tests services/worker/tests -v
# ── Docker ────────────────────────────────────────────────────────────────
docker-build:
name: Docker / build (${{ matrix.service }})
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
strategy:
fail-fast: false
matrix:
include:
- service: api
dockerfile: services/api/Dockerfile
context: .
- service: gateway
dockerfile: services/gateway/Dockerfile
context: services/gateway
- service: daemon
dockerfile: services/daemon/Dockerfile
context: services/daemon
- service: realtime
dockerfile: services/realtime/Dockerfile
context: services/realtime
- service: runtime
dockerfile: services/runtime/Dockerfile
context: .
- service: worker
dockerfile: services/worker/Dockerfile
context: .
steps:
- uses: actions/checkout@v4
- uses: docker/setup-buildx-action@v3
- name: Set lowercase repo owner
run: echo "REPO_OWNER=$(echo '${{ github.repository_owner }}' | tr '[:upper:]' '[:lower:]')" >> $GITHUB_ENV
- name: Log in to GHCR
if: github.ref == 'refs/heads/main' && github.event_name == 'push'
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build ${{ matrix.service }}
uses: docker/build-push-action@v6
with:
context: ${{ matrix.context }}
file: ${{ matrix.dockerfile }}
push: ${{ github.ref == 'refs/heads/main' && github.event_name == 'push' }}
tags: |
ghcr.io/${{ env.REPO_OWNER }}/maschina-${{ matrix.service }}:latest
ghcr.io/${{ env.REPO_OWNER }}/maschina-${{ matrix.service }}:${{ github.sha }}
cache-from: type=gha,scope=docker-${{ matrix.service }}
cache-to: type=gha,mode=max,scope=docker-${{ matrix.service }}
# ── Gate ──────────────────────────────────────────────────────────────────
ci-pass:
name: CI passed
runs-on: ubuntu-latest
needs:
- ts-typecheck
- ts-lint
- ts-test
- rust-fmt
- rust-clippy
- rust-test
- rust-build
- python-lint
- python-test
- docker-build
if: always()
steps:
- name: Verify all required jobs passed
id: gate
run: |
results='${{ toJSON(needs) }}'
failed=$(echo "$results" | jq '[.[].result] | map(select(. == "failure" or . == "cancelled")) | length')
if [ "$failed" -gt "0" ]; then
echo "status=failed" >> $GITHUB_OUTPUT
echo "One or more required CI jobs failed."
exit 1
fi
echo "status=passed" >> $GITHUB_OUTPUT
echo "All CI checks passed."
- name: Notify Discord
if: always() && github.event_name == 'pull_request'
env:
DISCORD_WEBHOOK: ${{ secrets.DISCORD_CI_WEBHOOK }}
STATUS: ${{ steps.gate.outputs.status }}
PR_TITLE: ${{ github.event.pull_request.title }}
PR_URL: ${{ github.event.pull_request.html_url }}
PR_NUMBER: ${{ github.event.pull_request.number }}
ACTOR: ${{ github.actor }}
SHA: ${{ github.sha }}
run: |
[ -z "$DISCORD_WEBHOOK" ] && exit 0
if [ "$STATUS" = "passed" ]; then
COLOR=5763719
LABEL="passed"
else
COLOR=15548997
LABEL="failed"
fi
SHORT_SHA="${SHA:0:7}"
curl -s -X POST "$DISCORD_WEBHOOK" \
-H "Content-Type: application/json" \
-d "{
\"embeds\": [{
\"title\": \"CI ${LABEL} — PR #${PR_NUMBER}\",
\"url\": \"${PR_URL}\",
\"description\": \"${PR_TITLE}\",
\"color\": ${COLOR},
\"fields\": [
{ \"name\": \"By\", \"value\": \"${ACTOR}\", \"inline\": true },
{ \"name\": \"Commit\", \"value\": \"\`${SHORT_SHA}\`\", \"inline\": true }
]
}]
}"