diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5f0fe181..7e0f6c8d 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -362,7 +362,7 @@ jobs: build-args: | VOGT_SOURCE_REF=dev VOGT_SOURCE_SHA=${{ github.sha }} - VOGT_PRODUCT_VERSION=0.2.2 + VOGT_PRODUCT_VERSION=0.3.0 cache-from: | type=registry,ref=${{ env.CACHE_IMAGE }}:stack-dev type=registry,ref=${{ env.CACHE_IMAGE }}:demo-dev @@ -460,7 +460,7 @@ jobs: build-args: | VOGT_SOURCE_REF=dev VOGT_SOURCE_SHA=${{ github.sha }} - VOGT_PRODUCT_VERSION=0.2.2 + VOGT_PRODUCT_VERSION=0.3.0 provenance: true sbom: true cache-from: | @@ -626,7 +626,7 @@ jobs: CORE_IMAGE=${{ env.IMAGE }}@${{ needs.image.outputs.digest }} VOGT_SOURCE_REF=${{ github.ref_name }} VOGT_SOURCE_SHA=${{ github.sha }} - VOGT_PRODUCT_VERSION=0.2.2 + VOGT_PRODUCT_VERSION=0.3.0 INSTALL_AI_CLIENTS=${{ github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/prod' }} INSTALL_CADASTRE_MCP=${{ github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/prod' }} INSTALL_THECLAWBAY=${{ github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/prod' }} @@ -691,7 +691,7 @@ jobs: CORE_IMAGE=${{ env.IMAGE }}@${{ needs.image.outputs.digest }} VOGT_SOURCE_REF=${{ github.ref_name }} VOGT_SOURCE_SHA=${{ github.sha }} - VOGT_PRODUCT_VERSION=0.2.2 + VOGT_PRODUCT_VERSION=0.3.0 INSTALL_AI_CLIENTS=${{ github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/prod' }} INSTALL_CADASTRE_MCP=${{ github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/prod' }} INSTALL_THECLAWBAY=${{ github.ref == 'refs/heads/dev' || github.ref == 'refs/heads/prod' }} diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e90824b3..d008c74f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -219,7 +219,7 @@ jobs: - run: uv sync --locked - name: canonical product version is aligned - run: uv run --no-sync python scripts/check_product_version.py 0.2.2 + run: uv run --no-sync python scripts/check_product_version.py 0.3.0 - name: ruff (lint) run: uv run --no-sync ruff check . @@ -541,8 +541,8 @@ jobs: needs: changes if: needs.changes.outputs.mobile == 'true' runs-on: [self-hosted] - # Pull requests, including fork pull requests, must not receive the - # Infisical machine identity. They build against the sanitized example. + # Pull requests, including fork pull requests, must not receive the dev + # Firebase secret. They build against the sanitized example. steps: - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 @@ -571,36 +571,17 @@ jobs: working-directory: mobile run: pnpm audit - - name: install Infisical CLI - if: github.event_name != 'pull_request' - run: | - set -euo pipefail - if command -v infisical >/dev/null 2>&1; then - exit 0 - fi - install -m 0755 -d /usr/share/keyrings - curl -1sLf https://artifacts-cli.infisical.com/infisical.gpg \ - | gpg --dearmor -o /usr/share/keyrings/infisical-archive-keyring.gpg - chmod 0644 /usr/share/keyrings/infisical-archive-keyring.gpg - printf '%s\n' \ - 'deb [arch=amd64 signed-by=/usr/share/keyrings/infisical-archive-keyring.gpg] https://artifacts-cli.infisical.com/deb stable main' \ - > /etc/apt/sources.list.d/infisical.list - apt-get update - apt-get install -y --no-install-recommends infisical - rm -rf /var/lib/apt/lists/* - - - name: fetch the dev Firebase config + - name: write the dev Firebase config if: github.event_name != 'pull_request' + # The dev Firebase config is a plain GitHub Actions secret — no secret + # broker and no CLI, so this runs on any self-hosted runner and any + # fork can supply its own. The helper writes it without echoing and + # verifies it carries the dev Android client. env: - INFISICAL_API_URL: ${{ vars.INFISICAL_API_URL }} - INFISICAL_CLIENT_ID: ${{ secrets.INFISICAL_CLIENT_ID }} - INFISICAL_CLIENT_SECRET: ${{ secrets.INFISICAL_CLIENT_SECRET }} - INFISICAL_PROJECT_ID: ${{ vars.INFISICAL_PROJECT_ID }} - INFISICAL_ENV: prod - VOGT_FIREBASE_SECRET_NAME: VOGT_FIREBASE_DEV_JSON + VOGT_FIREBASE_JSON: ${{ secrets.VOGT_FIREBASE_DEV_JSON }} VOGT_FIREBASE_OUTPUT: mobile/android/app/google-services.json VOGT_ANDROID_EXPECTED_PACKAGE: com.sprooty.vogt.dev - run: bash scripts/fetch_infisical_secret.sh + run: bash scripts/write_firebase_config.sh - name: verify the dev Firebase config is available if: github.event_name == 'pull_request' diff --git a/.github/workflows/deploy-dev.yml b/.github/workflows/deploy-dev.yml index b6a81b34..73879fc1 100644 --- a/.github/workflows/deploy-dev.yml +++ b/.github/workflows/deploy-dev.yml @@ -17,8 +17,12 @@ on: required: true type: string public_url: - description: Dev front-door URL used for live smoke checks - required: true + description: >- + Optional override for the live-smoke base URL. Leave blank to use the + VOGT_DEV_SMOKE_URL repository variable, which must name an address the + self-hosted runner can reach (the dev front door resolves to a + LAN-only IP, so a tailnet address is required here). + required: false type: string permissions: @@ -115,11 +119,17 @@ jobs: python3 scripts/deploy_dev.py - name: run the live dev smoke contract + # The dev front door resolves to a LAN-only IP the tailnet runner cannot + # route to, so the smoke base must be a runner-reachable address. It + # comes from the VOGT_DEV_SMOKE_URL repository variable (operator + # configuration, like VOGT_KOMODO_URL), overridable per-dispatch via the + # public_url input. env: - VOGT_SMOKE_BASE: ${{ inputs.public_url }} + VOGT_SMOKE_BASE: ${{ inputs.public_url || vars.VOGT_DEV_SMOKE_URL }} SOURCE_SHA: ${{ inputs.source_sha }} run: | set -euo pipefail + : "${VOGT_SMOKE_BASE:?Set the VOGT_DEV_SMOKE_URL repository variable or pass public_url}" smoke_token="$(cat "$RUNNER_TEMP/vogt-dev-smoke-token")" test -n "$smoke_token" trap 'rm -f -- "$RUNNER_TEMP/vogt-dev-smoke-token"' EXIT diff --git a/CHANGELOG.md b/CHANGELOG.md index ad197ed2..400117ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,10 +14,73 @@ git log rather than being reconstructed here. Nothing yet. +## [0.3.0] - 2026-08-28 + +The first release since the merged core+engine stack reached production. No +operation was renamed or removed; no schema migration is required (the declared +schema stays at 0015, the observed schema at 0004). Pre-1.0, this remains a +minor bump per the compatibility policy in [`opensource.md`](opensource.md). + +### Added + +- **Voice assistant, first-party.** A sidecar foundation plus opt-in + subprocess speech backends and in-process speech inference, moving voice + from an unproven adoption toward a validated path. +- **Fabro workflow provider** in the engine, with checkpoint-timeline + collection and workflow gate/steering bridging for agent sessions. +- **Conditional HTTP reads.** Stable read endpoints now emit validators + (ETag / `If-None-Match`-style) so clients can revalidate cheaply instead of + refetching. + +### Changed + +- **Performance — engine.** HTTP responses are compressed; the authentication + check is split from operational status so health/status probes are cheaper; + inactive terminals are parked and silent sockets detected. +- **Performance — web/PWA.** Terminal replay work is bounded and Monaco + languages load on demand; foreground reconciliation and FileTree wake + refreshes are coordinated through a shared wake coordinator; a typed TTL/SWR + cache backs stable reads; taxonomy reads and assistant hydration are shared + and deferred across surfaces; shell "place" metrics are aggregated; a + repeatable large-estate rendering profile was added. +- **Documentation** reconciled across engine workflow-provider status, the + voice POC/delivery gates, and public image examples now matching the current + release. + +### Fixed + +- **Sessions/terminals.** PTY output is drained before a run concludes; + session reconciliation stays cancellable; parked terminals reactivate when + ready; foreground API reads are bounded and cancellable. +- **Caching correctness.** Conditional stable-read validators are honored, + metadata ordering is preserved through the cache, taxonomy cache request + order is preserved, and shared taxonomy reads stay authoritative. +- **Workflows.** Duplicate workflow gate answers are rejected; workflow event + subscriptions retry once; checkpoints are preserved during poll fallback. +- **Mobile.** Native FCM listeners are awaited before registration. +- **Web.** File-tree conflict resolution is retained; demo assistant approvals + hydrate correctly. + +### Security + +- Tracked Firebase credentials are guarded, and open-source-readiness code gaps + were closed ahead of publishing the container publicly. +- The tailscale auth key is kept out of the container environment. + +### Internal + +- Release/CI: self-contained GitHub Release job; canonical GHCR retention + policy consumed; CodeQL (JS/TS) and RustSec audit made runnable on the + self-hosted runners; estate deployment receipts correlated; dev live-smoke + window and startup tolerances tuned; `pnpm-workspace.yaml` copied into the + web build stage; Komodo credentials read from GitHub secrets (Infisical + dropped for the dev deploy). + ## [0.2.0] - 2026-08-14 Baseline entry for this changelog. See the git log and release notes for the full history up to this tag. -[Unreleased]: https://github.com/TheDancingDeveloper-org/vogt/compare/v0.2.0...HEAD +[Unreleased]: https://github.com/TheDancingDeveloper-org/vogt/compare/v0.3.0...HEAD +[0.3.0]: https://github.com/TheDancingDeveloper-org/vogt/compare/v0.2.0...v0.3.0 [0.2.0]: https://github.com/TheDancingDeveloper-org/vogt/releases/tag/v0.2.0 diff --git a/deploy/.env.example b/deploy/.env.example index d53efd9c..bc709ec6 100644 --- a/deploy/.env.example +++ b/deploy/.env.example @@ -19,7 +19,7 @@ VOGT_BIND_IP=127.0.0.1 # The image to run. Pin a digest in anything you would call a deployment. # Ignored when you add deploy/vogt.build.yml, which builds from the checkout. -# VOGT_IMAGE=ghcr.io/thedancingdeveloper-org/vogt:0.2.2 +# VOGT_IMAGE=ghcr.io/thedancingdeveloper-org/vogt:0.3.0 # The uid the container runs as. The gid is always 0 — that is what makes the # data directory writable at any uid. Set this to whoever owns the files this diff --git a/deploy/engine.overlay.yml b/deploy/engine.overlay.yml index 2702c2f5..8d6ecca9 100644 --- a/deploy/engine.overlay.yml +++ b/deploy/engine.overlay.yml @@ -59,7 +59,7 @@ services: # container carries both halves. Defaults to the same public image the # base would run, so the embedded core matches it; pin a digest for a # real deployment. The Dockerfile already carries a public default too. - CORE_IMAGE: "${VOGT_IMAGE:-ghcr.io/thedancingdeveloper-org/vogt:0.2.2}" + CORE_IMAGE: "${VOGT_IMAGE:-ghcr.io/thedancingdeveloper-org/vogt:0.3.0}" # Optional image-managed agent CLIs. When enabled, versions come from # engine/agent-versions.env unless an operator deliberately overrides # one with a reproducible build arg. diff --git a/deploy/vogt.compose.yml b/deploy/vogt.compose.yml index c0832c96..0d834a69 100644 --- a/deploy/vogt.compose.yml +++ b/deploy/vogt.compose.yml @@ -25,7 +25,7 @@ services: # digest — publishing an image and moving a deployment are separate acts # (NFR-D10), and a digest is the only form of "which image is this" a # rebuild cannot silently change. - image: ${VOGT_IMAGE:-ghcr.io/thedancingdeveloper-org/vogt:0.2.2} + image: ${VOGT_IMAGE:-ghcr.io/thedancingdeveloper-org/vogt:0.3.0} restart: unless-stopped # The image runs as any uid so long as the gid is 0: `/var/lib/vogt` is diff --git a/docs/CUSTOMISATION.md b/docs/CUSTOMISATION.md index d67d99a1..98e4cfd9 100644 --- a/docs/CUSTOMISATION.md +++ b/docs/CUSTOMISATION.md @@ -359,7 +359,7 @@ The published image is built to be a base: to it. ```dockerfile -FROM ghcr.io/thedancingdeveloper-org/vogt:0.2.2 +FROM ghcr.io/thedancingdeveloper-org/vogt:0.3.0 USER root RUN apt-get update \ && apt-get install -y --no-install-recommends ripgrep \ diff --git a/docs/DEPLOYMENT.md b/docs/DEPLOYMENT.md index 6e0514e2..e0b74ec2 100644 --- a/docs/DEPLOYMENT.md +++ b/docs/DEPLOYMENT.md @@ -91,7 +91,7 @@ What the base does, and why it does it that way: | `VOGT_PUBLIC_URL` | yes | — | The URL clients use to reach this instance. | | `VOGT_PORT` | no | `8080` | Host port the container's 8000 is published on. | | `VOGT_BIND_IP` | no | `127.0.0.1` | Host interface the port is published on. | -| `VOGT_IMAGE` | no | `ghcr.io/thedancingdeveloper-org/vogt:0.2.2` | The image to run. | +| `VOGT_IMAGE` | no | `ghcr.io/thedancingdeveloper-org/vogt:0.3.0` | The image to run. | | `VOGT_UID` | no | `1000` | The uid the container runs as (gid is always 0). | | `VOGT_LOG_LEVEL` | no | `info` | Verbosity of Vogt's own logger. | @@ -118,7 +118,7 @@ cosign verify \ ``` ```console -docker buildx imagetools inspect ghcr.io/thedancingdeveloper-org/vogt:0.2.2 \ +docker buildx imagetools inspect ghcr.io/thedancingdeveloper-org/vogt:0.3.0 \ | grep -m1 Digest # then, in deploy/.env: VOGT_IMAGE=ghcr.io/thedancingdeveloper-org/vogt@sha256: diff --git a/engine/Dockerfile b/engine/Dockerfile index 940a90d1..4672222d 100644 --- a/engine/Dockerfile +++ b/engine/Dockerfile @@ -163,7 +163,16 @@ COPY --from=demo-web /app/web/dist /app/dist COPY engine/deploy/demo-server.mjs /app/demo-server.mjs # A restrictive checkout umask may make COPY preserve mode 0600. Normalize # the one executable input before dropping to the unprivileged node user. -RUN chmod 0444 /app/demo-server.mjs +# +# The demo server imports only `node:` builtins (see demo-server.mjs), so the +# npm CLI the base image bundles is dead weight — and its own bundled +# dependencies (tar, brace-expansion, pacote, sigstore, …) are what the fatal +# Trivy gate flags as CRITICAL/HIGH. Removing npm/npx clears every one of those +# findings without touching anything the demo runtime uses. +RUN chmod 0444 /app/demo-server.mjs \ + && rm -rf /usr/local/lib/node_modules/npm \ + /usr/local/bin/npm \ + /usr/local/bin/npx USER node EXPOSE 8910 ENV DEMO_BIND=0.0.0.0 \ diff --git a/mobile/package.json b/mobile/package.json index f921f24f..7273f734 100644 --- a/mobile/package.json +++ b/mobile/package.json @@ -1,6 +1,6 @@ { "name": "vogt-mobile", - "version": "0.2.2", + "version": "0.3.0", "private": true, "type": "module", "scripts": { diff --git a/pyproject.toml b/pyproject.toml index c96397a9..8e7379a8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "vogt" -version = "0.2.2" +version = "0.3.0" description = "A product development environment for the AI era" readme = "README.md" requires-python = ">=3.11" diff --git a/scripts/write_firebase_config.sh b/scripts/write_firebase_config.sh new file mode 100755 index 00000000..447b4bfc --- /dev/null +++ b/scripts/write_firebase_config.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +# Write one Firebase config, provided as an environment value, into a private, +# short-lived build file and validate the Android package it is meant to build. +# +# Generic and broker-free: the workflow passes the config through a plain +# GitHub Actions secret (VOGT_FIREBASE_JSON) and names the package it expects. +# The value is never echoed, uploaded as a standalone artifact, or committed. + +set -euo pipefail + +: "${VOGT_FIREBASE_JSON:?VOGT_FIREBASE_JSON is required}" +: "${VOGT_FIREBASE_OUTPUT:?VOGT_FIREBASE_OUTPUT is required}" +: "${VOGT_ANDROID_EXPECTED_PACKAGE:?VOGT_ANDROID_EXPECTED_PACKAGE is required}" + +output_dir="$(dirname -- "$VOGT_FIREBASE_OUTPUT")" +mkdir -p -- "$output_dir" +umask 077 + +temp_output="${VOGT_FIREBASE_OUTPUT}.tmp.$$" +trap 'rm -f -- "$temp_output"' EXIT + +# The value arrives through the environment, so it is written without ever +# being echoed to the log. +printf '%s' "$VOGT_FIREBASE_JSON" >"$temp_output" + +python3 - "$temp_output" "$VOGT_ANDROID_EXPECTED_PACKAGE" <<'PY' +from __future__ import annotations + +import json +import sys +from pathlib import Path + +path = Path(sys.argv[1]) +expected_package = sys.argv[2] +try: + document = json.loads(path.read_text(encoding="utf-8")) +except (OSError, json.JSONDecodeError) as exc: + raise SystemExit(f"Firebase config is not valid JSON: {exc}") from exc + +packages = { + client.get("client_info", {}).get("android_client_info", {}).get("package_name") + for client in document.get("client", []) +} +if expected_package not in packages: + raise SystemExit( + f"Firebase config has no Android client for {expected_package}" + ) +PY + +mv -- "$temp_output" "$VOGT_FIREBASE_OUTPUT" diff --git a/src/vogt/__init__.py b/src/vogt/__init__.py index e50992bb..0d089ceb 100644 --- a/src/vogt/__init__.py +++ b/src/vogt/__init__.py @@ -2,6 +2,6 @@ from __future__ import annotations -__version__ = "0.2.2" +__version__ = "0.3.0" __all__ = ["__version__"] diff --git a/tests/test_demo_delivery.py b/tests/test_demo_delivery.py index eb68882c..a391be9e 100644 --- a/tests/test_demo_delivery.py +++ b/tests/test_demo_delivery.py @@ -70,6 +70,9 @@ def test_demo_image_branches_from_the_normal_web_build() -> None: demo = text.split("FROM web-build AS demo-web", 1)[1].split("# ─── Stage 2:", 1)[0] assert "FROM ${NODE_IMAGE} AS demo-runtime" in demo assert "chmod 0444 /app/demo-server.mjs" in demo + # The demo server uses only node builtins, so the bundled npm CLI is removed + # — its transitive deps are what the fatal Trivy gate flags (#454). + assert "rm -rf /usr/local/lib/node_modules/npm" in demo assert "COPY --from=server-build" not in demo assert "COPY --from=core" not in demo assert "vogt-engine" not in demo diff --git a/tests/test_mobile_identity.py b/tests/test_mobile_identity.py index 277c0d34..fc8d42f6 100644 --- a/tests/test_mobile_identity.py +++ b/tests/test_mobile_identity.py @@ -54,6 +54,7 @@ CI_WORKFLOW = WORKFLOWS / "ci.yml" RELEASE_WORKFLOW = WORKFLOWS / "release.yml" FIREBASE_FETCH = REPO_ROOT / "scripts" / "fetch_infisical_secret.sh" +FIREBASE_WRITE = REPO_ROOT / "scripts" / "write_firebase_config.sh" #: The variable both build files read. Named once here so a rename shows up as #: one failure rather than as a silent divergence. @@ -163,39 +164,58 @@ def test_the_dev_stream_builds_under_its_own_id() -> None: ) -def test_android_workflows_fetch_the_matching_infisical_firebase_config() -> None: - """The APK build must use the Firebase project for the id it assembles. +def test_ci_android_writes_firebase_from_a_github_secret() -> None: + """CI's dev APK build reads the dev Firebase config from a plain secret. - The JSON files are intentionally not GitHub secrets or repository files: - Infisical is the source of truth and the runner removes the material after - Gradle has consumed it. Keeping these assertions here prevents a release - job from silently reverting to a stale local copy or the dev project. + #453: the dev path is deliberately broker-free — the config is a GitHub + Actions secret (``VOGT_FIREBASE_DEV_JSON``) written by a generic helper, so + the job runs on any self-hosted runner and a fork can supply its own. These + assertions prevent a silent regression back to the Infisical CLI for the + dev build, and keep the built package matched to the dev id. """ ci = CI_WORKFLOW.read_text(encoding="utf-8") - release = RELEASE_WORKFLOW.read_text(encoding="utf-8") - assert "VOGT_FIREBASE_SECRET_NAME: VOGT_FIREBASE_DEV_JSON" in ci + assert "secrets.VOGT_FIREBASE_DEV_JSON" in ci assert "VOGT_ANDROID_EXPECTED_PACKAGE: com.sprooty.vogt.dev" in ci + assert "scripts/write_firebase_config.sh" in ci + assert ci.count("remove Firebase config") == 1 + # No secret broker for the dev build. + assert "INFISICAL" not in ci + assert "infisical" not in ci + assert "scripts/fetch_infisical_secret.sh" not in ci + + +def test_release_android_fetches_the_matching_infisical_firebase_config() -> None: + """The release APK build still fetches its prod Firebase project via Infisical. + + The prod path is out of scope for #453 and unchanged: Infisical remains the + source of truth and the runner removes the material after Gradle consumes + it. Keeping these assertions prevents the release job from silently + reverting to a stale local copy or the dev project. + """ + release = RELEASE_WORKFLOW.read_text(encoding="utf-8") + assert "VOGT_FIREBASE_SECRET_NAME: VOGT_FIREBASE_PROD_JSON" in release assert "VOGT_ANDROID_EXPECTED_PACKAGE: com.sprooty.vogt" in release - assert "scripts/fetch_infisical_secret.sh" in ci assert "scripts/fetch_infisical_secret.sh" in release - assert "vars.INFISICAL_API_URL" in ci - assert "vars.INFISICAL_PROJECT_ID" in ci - assert "secrets.INFISICAL_CLIENT_ID" in ci - assert "secrets.INFISICAL_CLIENT_SECRET" in ci - assert "install Infisical CLI" in ci assert "vars.INFISICAL_API_URL" in release assert "vars.INFISICAL_PROJECT_ID" in release assert "secrets.INFISICAL_CLIENT_ID" in release assert "secrets.INFISICAL_CLIENT_SECRET" in release assert "install Infisical CLI" in release - assert ci.count("remove Firebase config") == 1 assert release.count("remove Firebase config") == 1 - assert "secrets.VOGT_FIREBASE_DEV_JSON" not in ci assert "secrets.VOGT_FIREBASE_PROD_JSON" not in release +def test_firebase_writer_never_prints_the_secret_and_checks_package() -> None: + script = FIREBASE_WRITE.read_text(encoding="utf-8") + # The value arrives through the environment and is written without echo. + assert 'printf \'%s\' "$VOGT_FIREBASE_JSON" >"$temp_output"' in script + assert "json.loads" in script + assert "VOGT_ANDROID_EXPECTED_PACKAGE" in script + assert 'mv -- "$temp_output" "$VOGT_FIREBASE_OUTPUT"' in script + + def test_firebase_fetcher_never_prints_the_secret_and_checks_package() -> None: script = FIREBASE_FETCH.read_text(encoding="utf-8") assert '--plain --silent >"$temp_output"' in script diff --git a/tests/test_product_version.py b/tests/test_product_version.py index 9042fc1b..fc6c523c 100644 --- a/tests/test_product_version.py +++ b/tests/test_product_version.py @@ -16,7 +16,7 @@ def test_product_version_contract_is_in_sync() -> None: result = subprocess.run( - [sys.executable, "scripts/check_product_version.py", "0.2.2"], + [sys.executable, "scripts/check_product_version.py", "0.3.0"], cwd=ROOT, capture_output=True, text=True, @@ -28,6 +28,6 @@ def test_product_version_contract_is_in_sync() -> None: def test_android_version_name_is_derived_from_the_canonical_mobile_manifest() -> None: package = (ROOT / "mobile/package.json").read_text(encoding="utf-8") gradle = (ROOT / "mobile/android/app/build.gradle").read_text(encoding="utf-8") - assert '"version": "0.2.2"' in package + assert '"version": "0.3.0"' in package assert "new JsonSlurper().parse(file('../../package.json'))" in gradle assert "versionName androidVersionName" in gradle diff --git a/uv.lock b/uv.lock index a976b77b..9887c61d 100644 --- a/uv.lock +++ b/uv.lock @@ -995,7 +995,7 @@ wheels = [ [[package]] name = "vogt" -version = "0.2.2" +version = "0.3.0" source = { editable = "." } dependencies = [ { name = "cryptography" }, diff --git a/web/package.json b/web/package.json index 669f9895..61ccd2f8 100644 --- a/web/package.json +++ b/web/package.json @@ -1,6 +1,6 @@ { "name": "vogt-web", - "version": "0.2.2", + "version": "0.3.0", "private": true, "type": "module", "scripts": {