Skip to content

v0.2.34: bring merge-no-auth under authorization (UN-24) #177

v0.2.34: bring merge-no-auth under authorization (UN-24)

v0.2.34: bring merge-no-auth under authorization (UN-24) #177

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