v0.2.34: bring merge-no-auth under authorization (UN-24) #177
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: Config Validation | |
| on: | |
| pull_request: | |
| paths: | |
| - ".env.test.example" | |
| - ".github/workflows/config-validation.yml" | |
| - "Dockerfile" | |
| - "Dockerfile.git-cli" | |
| - "Dockerfile.git-smoke" | |
| - "Dockerfile.it-runtime" | |
| - "README.md" | |
| - "bin/tests/**" | |
| - "config/**" | |
| - "docker-compose.test.yml" | |
| - "docs/refactoring/config.md" | |
| - "docs/refactoring/mail.md" | |
| - "docs/refactoring/test-infra.md" | |
| - "docs/refactoring/vault.md" | |
| - "src/cli.rs" | |
| - "src/commands/config.rs" | |
| - "src/commands/service/ssh.rs" | |
| - "src/config/**" | |
| - "src/contract/vault/integration/vault_core.rs" | |
| - "src/jupiter/storage/notification_storage.rs" | |
| - "src/notification/**" | |
| - "src/server/ssh_server.rs" | |
| push: | |
| branches: | |
| - main | |
| paths: | |
| - ".env.test.example" | |
| - ".github/workflows/config-validation.yml" | |
| - "Dockerfile" | |
| - "Dockerfile.git-cli" | |
| - "Dockerfile.git-smoke" | |
| - "Dockerfile.it-runtime" | |
| - "README.md" | |
| - "bin/tests/**" | |
| - "config/**" | |
| - "docker-compose.test.yml" | |
| - "docs/refactoring/config.md" | |
| - "docs/refactoring/mail.md" | |
| - "docs/refactoring/test-infra.md" | |
| - "docs/refactoring/vault.md" | |
| - "src/cli.rs" | |
| - "src/commands/config.rs" | |
| - "src/commands/service/ssh.rs" | |
| - "src/config/**" | |
| - "src/contract/vault/integration/vault_core.rs" | |
| - "src/jupiter/storage/notification_storage.rs" | |
| - "src/notification/**" | |
| - "src/server/ssh_server.rs" | |
| jobs: | |
| validate-config: | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Checkout monoengine | |
| uses: actions/checkout@v5 | |
| with: | |
| path: monoengine | |
| - name: Validate orbit checkout token | |
| env: | |
| ORBIT_CHECKOUT_TOKEN: ${{ secrets.ORBIT_CHECKOUT_TOKEN }} | |
| run: | | |
| if [ -z "${ORBIT_CHECKOUT_TOKEN}" ]; then | |
| echo "::error title=Missing ORBIT_CHECKOUT_TOKEN::Config validation needs to checkout the private gitmono-dev/orbit sibling repository. Add an Actions secret named ORBIT_CHECKOUT_TOKEN with read-only contents access to gitmono-dev/orbit." | |
| exit 1 | |
| fi | |
| - name: Checkout orbit sibling | |
| uses: actions/checkout@v5 | |
| with: | |
| repository: gitmono-dev/orbit | |
| ref: main | |
| path: orbit | |
| token: ${{ secrets.ORBIT_CHECKOUT_TOKEN }} | |
| - name: Validate website checkout token | |
| env: | |
| # Prefer a dedicated website token; fall back to ORBIT_CHECKOUT_TOKEN | |
| # when it can read the private website sibling (same as local ../website). | |
| WEBSITE_CHECKOUT_TOKEN: ${{ secrets.WEBSITE_CHECKOUT_TOKEN || secrets.ORBIT_CHECKOUT_TOKEN }} | |
| run: | | |
| if [ -z "${WEBSITE_CHECKOUT_TOKEN}" ]; then | |
| echo "::error title=Missing WEBSITE_CHECKOUT_TOKEN::Config validation needs to checkout the private genedna/website sibling repository for the Better Auth integration test. Add an Actions secret named WEBSITE_CHECKOUT_TOKEN (or reuse ORBIT_CHECKOUT_TOKEN) with read-only contents access to genedna/website." | |
| exit 1 | |
| fi | |
| - name: Checkout website sibling | |
| uses: actions/checkout@v5 | |
| with: | |
| # Local sibling is ../website → github.com/genedna/website (private). | |
| repository: genedna/website | |
| # Pin to the same revision as docs/refactoring/website-auth.md | |
| # (Dockerfile must COPY drizzle.config.ts for PG website-db-init). | |
| ref: 2af89c874646005dd1a550053b5068f19bb7478a | |
| path: website | |
| token: ${{ secrets.WEBSITE_CHECKOUT_TOKEN || secrets.ORBIT_CHECKOUT_TOKEN }} | |
| - name: Guard website Dockerfile supports IT Postgres init | |
| run: | | |
| set -euo pipefail | |
| if ! grep -q 'drizzle.config.ts' website/apps/next-app/Dockerfile; then | |
| echo "::error title=Website Dockerfile missing PG drizzle config::apps/next-app/Dockerfile must COPY drizzle.config.ts so compose website-db-init can drizzle-kit push against the isolated website Postgres DB. See docs/refactoring/website-auth.md pin policy." | |
| exit 1 | |
| fi | |
| - name: Free runner disk before heavy builds | |
| run: | | |
| set -euo pipefail | |
| # ubuntu-latest ships large unused toolchains; website-next's pnpm install | |
| # (incl. workerd) otherwise hits ENOSPC during compose profile web build. | |
| sudo rm -rf \ | |
| /usr/share/dotnet \ | |
| /usr/local/lib/android \ | |
| /opt/ghc \ | |
| /opt/hostedtoolcache/CodeQL \ | |
| /usr/local/.ghcup \ | |
| /usr/share/swift \ | |
| /usr/local/share/chromium \ | |
| /usr/local/share/powershell \ | |
| 2>/dev/null || true | |
| sudo docker system prune -af --volumes >/dev/null 2>&1 || true | |
| df -h / | |
| - name: Install Rust toolchains | |
| run: | | |
| rustup toolchain install stable --profile minimal --component clippy | |
| rustup default stable | |
| rustup toolchain install nightly --profile minimal --component rustfmt | |
| - name: Check formatting | |
| working-directory: monoengine | |
| run: cargo +nightly fmt --all --check | |
| - name: Lint (clippy, all targets, all features, warnings denied) | |
| working-directory: monoengine | |
| run: cargo clippy --all-targets --all-features -- -D warnings | |
| - name: Mask test secrets in logs | |
| run: | | |
| echo "::add-mask::monoengine_test_password" | |
| - name: Start test services (PostgreSQL, Redis, git-cli) | |
| working-directory: monoengine | |
| run: | | |
| set -euo pipefail | |
| # Compose also starts Mailpit by default; it is reserved for website | |
| # authentication/product-email IT and is not a monoengine SMTP gate. | |
| # Pre-create the git-cli bind-mount root so Docker does not create it | |
| # as root:root (which would EACCES the unprivileged test UID). | |
| dir="${MONOENGINE_IT_GIT_WORKDIR:-/tmp/monoengine-git}" | |
| mkdir -p "$dir" && chmod 1777 "$dir" | |
| # Match the runner identity: ubuntu-latest is often UID 1001, while the | |
| # compose default is 1000:1000 (would block writes into host-created dirs). | |
| export MONOENGINE_IT_GIT_UID="$(id -u)" | |
| export MONOENGINE_IT_GIT_GID="$(id -g)" | |
| # Persist across steps: the "Start compose-hosted monoengine" step also | |
| # brings up --profile git, and its `docker compose up` recomputes the | |
| # git-cli `user:` from the environment. Without these, it recreates | |
| # git-cli as the default 1000:1000, which cannot write into the case | |
| # dirs the test process (runner UID) creates under the shared workdir. | |
| echo "MONOENGINE_IT_GIT_UID=$(id -u)" >> "$GITHUB_ENV" | |
| echo "MONOENGINE_IT_GIT_GID=$(id -g)" >> "$GITHUB_ENV" | |
| # Name the services explicitly (git-smoke lives in its own `smoke` | |
| # profile; see docker-compose.test.yml). git-smoke is brought up later | |
| # with the app+web+smoke profiles. | |
| docker compose -p monoengine-it -f docker-compose.test.yml --profile git up -d --wait postgres redis git-cli | |
| docker compose -p monoengine-it -f docker-compose.test.yml ps | |
| - name: Start compose-hosted monoengine and website (profiles app + web) | |
| working-directory: monoengine | |
| run: | | |
| set -euo pipefail | |
| cargo build -p monoengine | |
| cp target/debug/monoengine monoengine.itbin | |
| docker build -f Dockerfile.it-runtime -t monoengine:local . | |
| rm -f monoengine.itbin | |
| # Reclaim cargo artifacts before website-next pnpm install (large). | |
| # Later cargo test steps will rebuild; disk headroom is required first. | |
| rm -rf target | |
| docker builder prune -f >/dev/null 2>&1 || true | |
| df -h / | |
| # Re-assert the runner identity (also persisted via $GITHUB_ENV above): | |
| # this `up` includes --profile git, so git-cli's `user:` must stay the | |
| # runner UID or the container is recreated as 1000:1000 and the IT | |
| # harness's host-created case dirs become unwritable. | |
| export MONOENGINE_IT_GIT_UID="$(id -u)" | |
| export MONOENGINE_IT_GIT_GID="$(id -g)" | |
| docker compose -p monoengine-it -f docker-compose.test.yml --profile app --profile web --profile git --profile smoke up -d --wait | |
| curl -sf "http://127.0.0.1:19180/api/openapi.json" >/dev/null | |
| curl -sf "http://127.0.0.1:17001/api/auth/get-session" >/dev/null | |
| echo "OK: compose monoengine and website-next healthy" | |
| - name: Run config sample tests | |
| working-directory: monoengine | |
| run: | | |
| cargo test config::template -- --nocapture | |
| cargo test config::loader -- --nocapture | |
| cargo test placeholder_expansion_error_is_returned_instead_of_panicking -- --nocapture | |
| cargo test placeholder_substitution_error_redacts_context_values -- --nocapture | |
| cargo test reload_applies_subscribers_before_publishing_snapshot -- --nocapture | |
| cargo test reload_rolls_back_subscribers_and_keeps_snapshot_when_apply_fails -- --nocapture | |
| cargo test reload_reports_static_consumer_fields_as_restart_required_without_publishing_snapshot -- --nocapture | |
| cargo test test_new_with_profile_merges_profile_before_env -- --nocapture | |
| cargo test config_init_template_has_no_unconsumed_fields -- --nocapture | |
| cargo test config_validate_rejects_invalid_monorepo_settings -- --nocapture | |
| cargo test config_validate_rejects_invalid_pack_settings -- --nocapture | |
| cargo test config_validate_rejects_invalid_blame_settings -- --nocapture | |
| cargo test config_validate_rejects_object_storage_secret_ref_values_without_leaking_ref -- --nocapture | |
| cargo test config_validate_rejects_invalid_sidebar_items -- --nocapture | |
| cargo test config_validate_rejects_invalid_artifact_gc_settings -- --nocapture | |
| cargo test source_diagnostics_collects_array_element_field_paths -- --nocapture | |
| cargo test source_diagnostics_full_cross_source_matrix_for_single_field -- --nocapture | |
| cargo test source_diagnostics_redacts_notification_secret_ref_values -- --nocapture | |
| cargo test reject_unknown_fields_rejects_typo_fields -- --nocapture | |
| cargo test test_load_str_rejects_removed_mail_section -- --nocapture | |
| cargo test config_validate_rejects_legacy_mail_environment_variables -- --nocapture | |
| cargo test test_bad_environment_type_reports_variable_name_without_value -- --nocapture | |
| cargo test config_validate_uses_raw_sources_load_mode -- --nocapture | |
| cargo test config_validate_accepts_deny_warnings_flag -- --nocapture | |
| cargo test validate_config_denies_source_warnings_when_requested -- --nocapture | |
| cargo test validate_config_rejects_obsolete_section_source -- --nocapture | |
| cargo test secret_ref_from_args_rejects_redis_url_outside_namespace -- --nocapture | |
| cargo test config_validate_reports_bad_env_type_without_cli_preload -- --nocapture | |
| cargo test test_profile_type_error_reports_profile_source_without_value -- --nocapture | |
| cargo test resolve_config_secrets_reports_missing_redis_url_ref_without_leaking_ref -- --nocapture | |
| cargo test vault_secret_resolver_reports_missing_field_without_leaking_ref -- --nocapture | |
| cargo test vault_secret_resolver_reports_non_string_field_without_leaking_ref -- --nocapture | |
| cargo test test_config_secret_acl_is_not_granted_to_generic_token -- --nocapture | |
| - name: Validate bundled config | |
| working-directory: monoengine | |
| run: cargo run -p monoengine -- --config config/config.toml config validate | |
| - name: Validate generated config | |
| working-directory: monoengine | |
| run: | | |
| config_path="${RUNNER_TEMP}/monoengine.generated.toml" | |
| cargo run -p monoengine -- config init --output "${config_path}" --force | |
| cargo run -p monoengine -- --config "${config_path}" config validate | |
| cargo run -p monoengine -- --config "${config_path}" config validate --deny-warnings | |
| - name: Validate profile merge | |
| working-directory: monoengine | |
| run: | | |
| config_dir="${RUNNER_TEMP}/monoengine-profile" | |
| mkdir -p "${config_dir}" | |
| cp config/config.toml "${config_dir}/config.toml" | |
| cat > "${config_dir}/config.prod.toml" <<'EOF' | |
| [log] | |
| level = "debug" | |
| [monorepo] | |
| root_dirs = ["profile-root"] | |
| EOF | |
| cargo run -p monoengine -- --config "${config_dir}/config.toml" --profile prod config validate | |
| source_output="${config_dir}/source-diagnostics.out" | |
| MEGA_DATABASE__DB_URL="postgres://ci.example.invalid:5432/ci" cargo run -p monoengine -- --config "${config_dir}/config.toml" --profile prod config validate --show-sources >"${source_output}" 2>&1 | |
| grep -F "source field:" "${source_output}" | |
| grep -F "source override:" "${source_output}" | |
| grep -F "log.level" "${source_output}" | |
| grep -F "database.db_url" "${source_output}" | |
| grep -F "sensitive values are omitted" "${source_output}" | |
| grep -F "deployment/environment secrets" "${source_output}" | |
| grep -F "monorepo.root_dirs" "${source_output}" | |
| grep -F "arrays replace lower-precedence values rather than append" "${source_output}" | |
| ! grep -F "postgres://ci.example.invalid" "${source_output}" | |
| ! grep -F "profile-root" "${source_output}" | |
| ! grep -F "base-root" "${source_output}" | |
| - name: Validate bad config diagnostics | |
| working-directory: monoengine | |
| run: | | |
| set -euo pipefail | |
| config_dir="${RUNNER_TEMP}/monoengine-bad-configs" | |
| mkdir -p "${config_dir}" | |
| cp config/config.toml "${config_dir}/config.toml" | |
| { | |
| printf '%s\n' '[database]' | |
| printf '%s = "%s%s\n' 'db_url' 'plain-text' '-password' | |
| } > "${config_dir}/config.bad-toml.toml" | |
| bad_toml_output="${config_dir}/bad-toml.out" | |
| if cargo run -p monoengine -- --config "${config_dir}/config.toml" --profile bad-toml config validate >"${bad_toml_output}" 2>&1; then | |
| cat "${bad_toml_output}" | |
| echo "bad TOML config unexpectedly validated" | |
| exit 1 | |
| fi | |
| grep -F "TOML parse error" "${bad_toml_output}" | |
| grep -F "config.bad-toml.toml" "${bad_toml_output}" | |
| grep -F "value is redacted" "${bad_toml_output}" | |
| ! grep -F "plain-text-password" "${bad_toml_output}" | |
| cat > "${config_dir}/config.bad-placeholder.toml" <<'EOF' | |
| base_dir = "/tmp/${missing_config_root}" | |
| EOF | |
| placeholder_output="${config_dir}/bad-placeholder.out" | |
| if cargo run -p monoengine -- --config "${config_dir}/config.toml" --profile bad-placeholder config validate >"${placeholder_output}" 2>&1; then | |
| cat "${placeholder_output}" | |
| echo "bad placeholder config unexpectedly validated" | |
| exit 1 | |
| fi | |
| grep -F "unresolved placeholder" "${placeholder_output}" | |
| grep -F "base_dir" "${placeholder_output}" | |
| grep -F "value is redacted" "${placeholder_output}" | |
| cat > "${config_dir}/config.bad-secret-ref.toml" <<'EOF' | |
| [redis] | |
| url = "vault://secret/config/test/redis/url" | |
| EOF | |
| secret_ref_output="${config_dir}/bad-secret-ref.out" | |
| if cargo run -p monoengine -- --config "${config_dir}/config.toml" --profile bad-secret-ref config validate >"${secret_ref_output}" 2>&1; then | |
| cat "${secret_ref_output}" | |
| echo "bad SecretRef config unexpectedly validated" | |
| exit 1 | |
| fi | |
| grep -F "secret ref must include a #field suffix" "${secret_ref_output}" | |
| grep -F "redis.url" "${secret_ref_output}" | |
| cat > "${config_dir}/config.bad-secret-ref-namespace.toml" <<'EOF' | |
| [redis] | |
| url = "vault://secret/config/prod/database/password#value" | |
| EOF | |
| secret_ref_namespace_output="${config_dir}/bad-secret-ref-namespace.out" | |
| if cargo run -p monoengine -- --config "${config_dir}/config.toml" --profile bad-secret-ref-namespace config validate >"${secret_ref_namespace_output}" 2>&1; then | |
| cat "${secret_ref_namespace_output}" | |
| echo "bad SecretRef namespace config unexpectedly validated" | |
| exit 1 | |
| fi | |
| grep -F "redis.url" "${secret_ref_namespace_output}" | |
| grep -F "vault://secret/config/<profile>/redis/url#<field>" "${secret_ref_namespace_output}" | |
| grep -F "value is redacted" "${secret_ref_namespace_output}" | |
| ! grep -F "config/prod/database/password" "${secret_ref_namespace_output}" | |
| cat > "${config_dir}/config.bad-redis.toml" <<'EOF' | |
| [redis] | |
| url = "http://localhost:6379" | |
| EOF | |
| redis_output="${config_dir}/bad-redis.out" | |
| if cargo run -p monoengine -- --config "${config_dir}/config.toml" --profile bad-redis config validate >"${redis_output}" 2>&1; then | |
| cat "${redis_output}" | |
| echo "bad Redis config unexpectedly validated" | |
| exit 1 | |
| fi | |
| grep -F "redis.url scheme" "${redis_output}" | |
| grep -F "'redis' or 'rediss'" "${redis_output}" | |
| cat > "${config_dir}/config.removed-mail.toml" <<'EOF' | |
| [mail] | |
| enabled = false | |
| EOF | |
| removed_mail_output="${config_dir}/removed-mail.out" | |
| if cargo run -p monoengine -- --config "${config_dir}/config.toml" --profile removed-mail config validate >"${removed_mail_output}" 2>&1; then | |
| cat "${removed_mail_output}" | |
| echo "removed [mail] profile unexpectedly validated" | |
| exit 1 | |
| fi | |
| grep -F "mail" "${removed_mail_output}" | |
| grep -F "not recognized by Config" "${removed_mail_output}" | |
| # Soft source warnings remain for unknown MEGA_* env (not for removed [mail]). | |
| env_warning_output="${config_dir}/env-source-warning.out" | |
| MEGA_TOTALLY_UNKNOWN__X=1 cargo run -p monoengine -- --config "${config_dir}/config.toml" config validate --show-sources >"${env_warning_output}" 2>&1 | |
| grep -F "source warning: environment variable MEGA_TOTALLY_UNKNOWN__X" "${env_warning_output}" | |
| if MEGA_TOTALLY_UNKNOWN__X=1 cargo run -p monoengine -- --config "${config_dir}/config.toml" config validate --deny-warnings >"${env_warning_output}" 2>&1; then | |
| cat "${env_warning_output}" | |
| echo "unknown MEGA_* env unexpectedly passed --deny-warnings" | |
| exit 1 | |
| fi | |
| grep -F "source diagnostics produced" "${env_warning_output}" | |
| - name: Run integration tests (vault CLI + website auth + git-cli) | |
| working-directory: monoengine | |
| env: | |
| MEGA_DATABASE__DB_URL: postgres://monoengine:monoengine_test_password@127.0.0.1:15432/monoengine | |
| MEGA_REDIS__URL: redis://127.0.0.1:16379 | |
| WEBSITE_IT: "1" | |
| run: | | |
| # Black-box CLI integration tests (config secret ref/set/check, validate). | |
| # The integration test lives in the `monoengine` binary crate (bin/tests/). | |
| cargo test -p monoengine --test integration_vault -- --test-threads=1 | |
| # ITW-03: a real Better Auth sign-up/sign-in cookie must identify the | |
| # same user through the compose-hosted monoengine session bridge. | |
| cargo test -p monoengine --test integration_website_auth -- --test-threads=1 | |
| # WE-06: website internal product-email API (bearer + allowlisted event). | |
| cargo test -p monoengine --test integration_website_mail -- --test-threads=1 | |
| # Linux-only git-cli HTTP / LFS / SSH harnesses (env -u so SKIP cannot soft-pass). | |
| env -u MONOENGINE_IT_SKIP_GIT_CLI cargo test -p monoengine --test integration_git_cli -- --test-threads=1 | |
| env -u MONOENGINE_IT_SKIP_GIT_CLI cargo test -p monoengine --test integration_git_lfs -- --test-threads=1 | |
| env -u MONOENGINE_IT_SKIP_GIT_CLI cargo test -p monoengine --test integration_git_ssh -- --test-threads=1 | |
| # Product-email delivery is owned by website (test provider in compose). | |
| # Monoengine proves acceptance via integration_website_mail above. | |
| - name: Run linked git protocol smoke (git-smoke) | |
| working-directory: monoengine | |
| run: | | |
| set -euo pipefail | |
| # The linked git-smoke service joins networks.default and exercises | |
| # push/pull against the compose-hosted monoengine (profile app). | |
| # Seed a one-off access token (same pattern as git-protocol-smoke CI). | |
| smoke_token="monoengine-it-git-smoke-token-0001" | |
| docker compose -p monoengine-it -f docker-compose.test.yml exec -T postgres \ | |
| psql -U monoengine -d monoengine -v ON_ERROR_STOP=1 -v token="${smoke_token}" <<'SQL' | |
| INSERT INTO access_token (id, username, token, created_at) | |
| VALUES ((extract(epoch from clock_timestamp()) * 1000000)::bigint, 'ci-smoke', :'token', now()) | |
| ON CONFLICT DO NOTHING; | |
| SQL | |
| docker compose -p monoengine-it -f docker-compose.test.yml --profile smoke exec -T git-smoke \ | |
| bash -c 'export MONOENGINE_HTTP_REPO_URL="http://ci-smoke:monoengine-it-git-smoke-token-0001@monoengine:8000/"; export MONOENGINE_GIT_SMOKE_PUSH=1; export MONOENGINE_GIT_SMOKE_LFS=0; export MONOENGINE_GIT_SMOKE_WORKDIR=/work/ci-smoke; mkdir -p /work/ci-smoke; bash /repo/scripts/git_protocol_smoke.sh' | |
| - name: Stop test services | |
| if: always() | |
| working-directory: monoengine | |
| run: docker compose -p monoengine-it -f docker-compose.test.yml --profile git --profile app --profile web --profile smoke down -v |