release: publish installable v0.4.0 #210
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: | |
| branches: | |
| - main | |
| - develop | |
| - codex/** | |
| pull_request: | |
| workflow_dispatch: | |
| permissions: | |
| contents: read | |
| jobs: | |
| extension: | |
| runs-on: ubuntu-latest | |
| name: Browser Extension | |
| defaults: | |
| run: | |
| working-directory: chrome/extension-src | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - name: Setup Node | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: "20" | |
| cache: npm | |
| cache-dependency-path: chrome/extension-src/package-lock.json | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Typecheck | |
| run: npm run typecheck | |
| - name: Build | |
| run: npm run build | |
| - name: Upload extension dist | |
| uses: actions/upload-artifact@v7 | |
| if: success() | |
| with: | |
| name: extension-dist | |
| path: chrome/extension-src/dist | |
| frontend: | |
| runs-on: ubuntu-latest | |
| name: Next Frontend | |
| defaults: | |
| run: | |
| working-directory: frontend | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - name: Setup Node | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version-file: .nvmrc | |
| - name: Setup Python for workflow contract check | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version: "3.13" | |
| - name: Install backend compiler dependencies | |
| run: python -m pip install -e .. | |
| - name: Install dependencies | |
| run: | | |
| corepack enable | |
| corepack prepare pnpm@11.10.0 --activate | |
| pnpm install --frozen-lockfile | |
| - name: Lint | |
| run: pnpm lint | |
| - name: Typecheck | |
| run: pnpm exec tsc --noEmit | |
| - name: Workflow regressions | |
| run: pnpm check:workflow-regressions | |
| - name: Production build | |
| run: pnpm build | |
| - name: Install Chromium | |
| run: pnpm exec playwright install --with-deps chromium | |
| - name: Browser smoke test | |
| run: pnpm test:smoke | |
| release-contract: | |
| runs-on: ubuntu-latest | |
| name: Public Install Smoke | |
| env: | |
| API_AUTH_TOKEN: ci-release-token | |
| BOOTSTRAP_ADMIN_TOKEN: ci-bootstrap-admin-token | |
| SECRET_KEY: ci-release-secret | |
| CREDENTIAL_ENCRYPTION_KEY: MDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDA= | |
| COMPOSE_PROJECT_NAME: opencli-admin-ci | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| with: | |
| persist-credentials: false | |
| - name: Validate Compose | |
| run: docker compose --env-file .env.docker.example -f docker-compose.yml -f docker-compose.build.yml config --quiet | |
| - name: Build and start public stack | |
| run: docker compose --env-file .env.docker.example -f docker-compose.yml -f docker-compose.build.yml up -d --build --wait api frontend agent-1 | |
| - name: Verify public endpoints | |
| run: | | |
| curl --fail --silent --show-error http://localhost:3010/login >/dev/null | |
| curl --fail --silent --show-error http://localhost:8031/health >/dev/null | |
| curl --fail --silent --show-error \ | |
| -H "Authorization: Bearer ci-bootstrap-admin-token" \ | |
| -H "X-API-Token: ci-release-token" \ | |
| http://localhost:8031/api/v1/auth/me | | |
| python -c 'import json,sys; assert json.load(sys.stdin)["data"]["subject"] == "bootstrap-admin"' | |
| - name: Show logs on failure | |
| if: failure() | |
| run: docker compose --env-file .env.docker.example -f docker-compose.yml -f docker-compose.build.yml logs --tail=200 api frontend agent-1 | |
| - name: Stop public stack | |
| if: always() | |
| run: docker compose --env-file .env.docker.example -f docker-compose.yml -f docker-compose.build.yml down -v | |
| backend: | |
| runs-on: ubuntu-latest | |
| name: Backend Quality | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - name: Setup Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version-file: ".python-version" | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| python -m pip install -e .[dev] | |
| - name: Backend syntax check | |
| run: python -m compileall backend | |
| # Coverage visibility, not a gate: pyproject.toml bakes in | |
| # `--cov-fail-under=80` via [tool.pytest.ini_options].addopts, but the | |
| # current suite sits at ~70% total. Enforcing 80% here would turn CI red | |
| # immediately for pre-existing gaps, not for anything this PR changes. | |
| # `--cov-fail-under=0` overrides the pyproject default (last CLI value | |
| # wins) so the job stays green while the report is printed every run. | |
| # TODO(ratchet): once real coverage is measured for a few weeks, raise | |
| # this floor incrementally (e.g. 70 -> 75 -> 80) instead of jumping | |
| # straight to the pyproject target. | |
| # tests/compat/dataflow pins the golden DataFlow contract and | |
| # tests/integration exercises the workflow HTTP surface; both must run | |
| # here or the pinned-compatibility guarantees have no regression gate. | |
| # The upstream oracle stays opt-in (DATAFLOW_RUN_UPSTREAM_ORACLE=1) and | |
| # self-skips in this job. | |
| - name: Backend tests (with coverage report) | |
| run: python -m pytest tests/unit tests/compat tests/integration -m "not live" --cov=backend --cov-report=term-missing --cov-fail-under=0 | |
| frontend-workflow-checks: | |
| runs-on: ubuntu-latest | |
| name: Frontend Workflow Checks | |
| defaults: | |
| run: | |
| working-directory: frontend | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| # frontend/pnpm-workspace.yaml is a pnpm 10 config store (allowBuilds, | |
| # overrides, no `packages` field) — pnpm 9 rejects it outright. | |
| - name: Setup pnpm | |
| uses: pnpm/action-setup@v4 | |
| with: | |
| version: 10 | |
| # test-data-operator-nodes.mjs imports the real .ts sources via | |
| # node:module registerHooks + stripTypeScriptTypes (needs Node >= 22.15). | |
| - name: Setup Node | |
| uses: actions/setup-node@v6 | |
| with: | |
| node-version: "22" | |
| cache: pnpm | |
| cache-dependency-path: frontend/pnpm-lock.yaml | |
| - name: Install dependencies | |
| run: pnpm install --frozen-lockfile | |
| - name: Data operator Canvas projection | |
| run: pnpm run test:data-operator-nodes | |
| - name: Workflow contracts | |
| run: pnpm run test:workflow-contracts | |
| migrations: | |
| runs-on: ubuntu-latest | |
| name: Alembic Migrations | |
| services: | |
| postgres: | |
| image: postgres:16-alpine | |
| env: | |
| POSTGRES_DB: opencli_admin_test | |
| POSTGRES_USER: opencli | |
| POSTGRES_PASSWORD: opencli_secret | |
| ports: | |
| - 5432:5432 | |
| options: >- | |
| --health-cmd "pg_isready -U opencli" | |
| --health-interval 5s | |
| --health-timeout 3s | |
| --health-retries 10 | |
| env: | |
| # backend/config.py Settings.database_url (case-insensitive env, | |
| # see backend/migrations/env.py which overrides alembic.ini's | |
| # sqlalchemy.url from settings.database_url at runtime). | |
| DATABASE_URL: postgresql+asyncpg://opencli:opencli_secret@localhost:5432/opencli_admin_test | |
| TEST_DATABASE_URL_PG: postgresql+asyncpg://opencli:opencli_secret@localhost:5432/opencli_admin_test | |
| REQUIRE_POSTGRES_CONFORMANCE: "1" | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - name: Setup Python | |
| uses: actions/setup-python@v6 | |
| with: | |
| python-version-file: ".python-version" | |
| - name: Install dependencies | |
| run: | | |
| python -m pip install --upgrade pip | |
| python -m pip install -e .[dev] | |
| - name: Wait for Postgres | |
| run: python -c "import time; time.sleep(2)" | |
| - name: alembic upgrade head | |
| run: alembic upgrade head | |
| # Catches broken downgrade() bodies / non-reversible migrations before | |
| # merge: a chain that only ever gets tested via upgrade() can silently | |
| # rot (dropped columns with no re-add, wrong op order, etc.). | |
| - name: alembic downgrade/upgrade smoke test | |
| run: | | |
| alembic downgrade -1 | |
| alembic upgrade head | |
| - name: Native intelligence and event spine PostgreSQL conformance | |
| run: >- | |
| pytest tests/integration/test_intelligence_session_store.py | |
| tests/integration/test_intelligence_session_migration.py | |
| tests/unit/test_workflow_run_events.py | |
| tests/integration/test_workflow_event_spine_migrations.py | |
| tests/integration/test_workflow_event_spine_integration.py | |
| -m postgres_conformance --no-cov -p no:cacheprovider -q | |
| # AUDIT follow-up (c): the cursor concurrency tests run on SQLite in the | |
| # backend job, where `SELECT ... FOR UPDATE` is a silent no-op — they | |
| # prove the code path but not that the lock serializes writers. Re-run the | |
| # Postgres-gated variant here (schema already at head above, DATABASE_URL | |
| # is Postgres so the test's skip-gate activates), where the row lock is | |
| # actually enforced, so a regression in cursor_store's per-source locking | |
| # is caught. --no-cov: this is a targeted single-test step, not a coverage | |
| # run (pyproject's addopts would otherwise fail it under the 80% gate). | |
| - name: cursor FOR UPDATE locking (Postgres) | |
| run: python -m pytest tests/unit/pipeline/test_db_cursor_store.py -k postgres --no-cov -p no:cacheprovider | |
| cargo: | |
| runs-on: ubuntu-latest | |
| name: ODP Rust (odp-rs) | |
| defaults: | |
| run: | |
| working-directory: odp-rs | |
| steps: | |
| - name: Checkout | |
| uses: actions/checkout@v6 | |
| - name: Setup Rust toolchain | |
| uses: dtolnay/rust-toolchain@stable | |
| with: | |
| components: clippy | |
| - name: Cache cargo registry + target | |
| uses: actions/cache@v5 | |
| with: | |
| path: | | |
| ~/.cargo/registry | |
| ~/.cargo/git | |
| odp-rs/target | |
| key: ${{ runner.os }}-cargo-odp-rs-${{ hashFiles('odp-rs/Cargo.lock', 'odp-rs/**/Cargo.toml') }} | |
| restore-keys: | | |
| ${{ runner.os }}-cargo-odp-rs- | |
| # No live Redis/Postgres here: odp-bus/odp-ingest/odp-store tests are | |
| # unit-level (dedup logic, contract (de)serialization, stream naming). | |
| # If integration tests that need a real broker/DB are added later, gate | |
| # them behind a feature flag or #[ignore] + a service-container job, | |
| # the same way `pytest -m live` is deselected in the backend job. | |
| - name: cargo test --workspace | |
| run: cargo test --workspace | |
| - name: cargo clippy --workspace -- -D warnings | |
| run: cargo clippy --workspace --all-targets -- -D warnings |