diff --git a/bin/disinto b/bin/disinto index 5f57927..46398c7 100755 --- a/bin/disinto +++ b/bin/disinto @@ -82,7 +82,7 @@ Init options: --ci-id Woodpecker CI repo ID (default: 0 = no CI) --forge-url Forge base URL (default: http://localhost:3000) --backend Orchestration backend: docker (default) | nomad - --with (nomad) Deploy services: forgejo[,...] (S1.3) + --with (nomad) Deploy services: forgejo,woodpecker-server,woodpecker-agent[,...] (S1.3, S3.4) --empty (nomad) Bring up cluster only, no jobs (S0.4) --bare Skip compose generation (bare-metal setup) --build Use local docker build instead of registry images (dev mode) @@ -783,6 +783,38 @@ _disinto_init_nomad() { fi if [ -n "$with_services" ]; then + # Normalize services: auto-include forgejo when woodpecker is requested + # (woodpecker without forgejo is nonsensical) + # Also expand 'woodpecker' to 'woodpecker-server,woodpecker-agent' + local normalized_services="$with_services" + local needs_log_note=false + + # First, expand 'woodpecker' to 'woodpecker-server,woodpecker-agent' + if echo "$normalized_services" | grep -q "woodpecker"; then + # Replace woodpecker with woodpecker-server,woodpecker-agent + normalized_services=$(echo "$normalized_services" | sed 's/woodpecker/woodpecker-server,woodpecker-agent/g') + fi + + # Then, auto-include forgejo if woodpecker was requested but forgejo is not present + if echo "$normalized_services" | grep -q "woodpecker" && ! echo "$normalized_services" | grep -q "forgejo"; then + echo "Note: --with woodpecker implies --with forgejo (OAuth dependency)" + normalized_services="forgejo,${normalized_services}" + needs_log_note=true + fi + + # Define deployment order: forgejo -> woodpecker-server -> woodpecker-agent + # Only include services that are requested (after normalization) + local DEPLOY_ORDER="" + for ordered_svc in forgejo woodpecker-server woodpecker-agent; do + if echo ",$normalized_services," | grep -q ",$ordered_svc,"; then + if [ -z "$DEPLOY_ORDER" ]; then + DEPLOY_ORDER="$ordered_svc" + else + DEPLOY_ORDER="$DEPLOY_ORDER $ordered_svc" + fi + fi + done + # Vault seed plan (S2.6, #928): one line per service whose # tools/vault-seed-.sh ships. Services without a seeder are # silently skipped — the real-run loop below mirrors this, @@ -791,7 +823,7 @@ _disinto_init_nomad() { # any further change to bin/disinto. local seed_hdr_printed=false local IFS=',' - for svc in $with_services; do + for svc in $normalized_services; do svc=$(echo "$svc" | xargs) # trim whitespace local seed_script="${FACTORY_ROOT}/tools/vault-seed-${svc}.sh" if [ -x "$seed_script" ]; then @@ -805,17 +837,24 @@ _disinto_init_nomad() { [ "$seed_hdr_printed" = true ] && echo "" echo "── Deploy services dry-run ────────────────────────────" - echo "[deploy] services to deploy: ${with_services}" - for svc in $with_services; do + echo "[deploy] services to deploy: ${normalized_services}" + echo "[deploy] deployment order: ${DEPLOY_ORDER}" + # Validate all requested services are known (before DEPLOY_ORDER loop) + local IFS=',' + for svc in $normalized_services; do svc=$(echo "$svc" | xargs) # trim whitespace - # Validate known services first case "$svc" in - forgejo) ;; + forgejo|woodpecker-server|woodpecker-agent) ;; *) - echo "Error: unknown service '${svc}' — known: forgejo" >&2 + echo "Error: unknown service '${svc}' — known: forgejo, woodpecker-server, woodpecker-agent" >&2 exit 1 ;; esac + done + # Reset IFS for DEPLOY_ORDER loop + IFS=' ' + for svc in $DEPLOY_ORDER; do + svc=$(echo "$svc" | xargs) # trim whitespace local jobspec_path="${FACTORY_ROOT}/nomad/jobs/${svc}.hcl" if [ ! -f "$jobspec_path" ]; then echo "Error: jobspec not found: ${jobspec_path}" >&2 @@ -936,9 +975,24 @@ _disinto_init_nomad() { # `env_keep` (VAULT_ADDR is not). Using `env` as the actual command # sets VAULT_ADDR in the child process regardless of sudoers policy. if [ -n "$with_services" ]; then + # Normalize services: auto-include forgejo when woodpecker is requested + # (woodpecker without forgejo is nonsensical) + # Also expand 'woodpecker' to 'woodpecker-server,woodpecker-agent' + local normalized_services="$with_services" + + # First, expand 'woodpecker' to 'woodpecker-server,woodpecker-agent' + if echo "$normalized_services" | grep -q "woodpecker"; then + normalized_services=$(echo "$normalized_services" | sed 's/woodpecker/woodpecker-server,woodpecker-agent/g') + fi + + # Then, auto-include forgejo if woodpecker was requested but forgejo is not present + if echo "$normalized_services" | grep -q "woodpecker" && ! echo "$normalized_services" | grep -q "forgejo"; then + normalized_services="forgejo,${normalized_services}" + fi + local vault_addr="${VAULT_ADDR:-http://127.0.0.1:8200}" local IFS=',' - for svc in $with_services; do + for svc in $normalized_services; do svc=$(echo "$svc" | xargs) # trim whitespace local seed_script="${FACTORY_ROOT}/tools/vault-seed-${svc}.sh" if [ -x "$seed_script" ]; then @@ -959,25 +1013,60 @@ _disinto_init_nomad() { # Deploy services if requested if [ -n "$with_services" ]; then + # Normalize services: auto-include forgejo when woodpecker is requested + # (woodpecker without forgejo is nonsensical) + # Also expand 'woodpecker' to 'woodpecker-server,woodpecker-agent' + local normalized_services="$with_services" + local needs_log_note=false + + # First, expand 'woodpecker' to 'woodpecker-server,woodpecker-agent' + if echo "$normalized_services" | grep -q "woodpecker"; then + normalized_services=$(echo "$normalized_services" | sed 's/woodpecker/woodpecker-server,woodpecker-agent/g') + fi + + # Then, auto-include forgejo if woodpecker was requested but forgejo is not present + if echo "$normalized_services" | grep -q "woodpecker" && ! echo "$normalized_services" | grep -q "forgejo"; then + echo "Note: --with woodpecker implies --with forgejo (OAuth dependency)" + normalized_services="forgejo,${normalized_services}" + needs_log_note=true + fi + + # Define deployment order: forgejo -> woodpecker-server -> woodpecker-agent + # Only include services that are requested (after normalization) + local DEPLOY_ORDER="" + for ordered_svc in forgejo woodpecker-server woodpecker-agent; do + if echo ",$normalized_services," | grep -q ",$ordered_svc,"; then + if [ -z "$DEPLOY_ORDER" ]; then + DEPLOY_ORDER="$ordered_svc" + else + DEPLOY_ORDER="$DEPLOY_ORDER $ordered_svc" + fi + fi + done + echo "" echo "── Deploying services ─────────────────────────────────" local -a deploy_cmd=("$deploy_sh") - # Split comma-separated service list into positional args + # Validate all requested services are known (before DEPLOY_ORDER loop) local IFS=',' - for svc in $with_services; do + for svc in $normalized_services; do svc=$(echo "$svc" | xargs) # trim whitespace if ! echo "$svc" | grep -qE '^[a-zA-Z0-9_-]+$'; then echo "Error: invalid service name '${svc}' — must match ^[a-zA-Z0-9_-]+$" >&2 exit 1 fi - # Validate known services FIRST (before jobspec check) case "$svc" in - forgejo) ;; + forgejo|woodpecker-server|woodpecker-agent) ;; *) - echo "Error: unknown service '${svc}' — known: forgejo" >&2 + echo "Error: unknown service '${svc}' — known: forgejo, woodpecker-server, woodpecker-agent" >&2 exit 1 ;; esac + done + # Reset IFS for DEPLOY_ORDER loop + IFS=' ' + for svc in $DEPLOY_ORDER; do + svc=$(echo "$svc" | xargs) # trim whitespace # Check jobspec exists local jobspec_path="${FACTORY_ROOT}/nomad/jobs/${svc}.hcl" if [ ! -f "$jobspec_path" ]; then @@ -1011,10 +1100,16 @@ _disinto_init_nomad() { else echo "Imported: (none — seed kv/disinto/* manually before deploying secret-dependent services)" fi - echo "Deployed: ${with_services}" - if echo "$with_services" | grep -q "forgejo"; then + echo "Deployed: ${normalized_services}" + if echo "$normalized_services" | grep -q "forgejo"; then echo "Ports: forgejo: 3000" fi + if echo "$normalized_services" | grep -q "woodpecker-server"; then + echo " woodpecker-server: 8000" + fi + if echo "$normalized_services" | grep -q "woodpecker-agent"; then + echo " woodpecker-agent: (agent connected)" + fi echo "────────────────────────────────────────────────────────" fi diff --git a/tests/disinto-init-nomad.bats b/tests/disinto-init-nomad.bats index 21f4303..2ebae63 100644 --- a/tests/disinto-init-nomad.bats +++ b/tests/disinto-init-nomad.bats @@ -215,7 +215,7 @@ setup_file() { run "$DISINTO_BIN" init placeholder/repo --backend=nomad --with unknown-service --dry-run [ "$status" -ne 0 ] [[ "$output" == *"unknown service"* ]] - [[ "$output" == *"known: forgejo"* ]] + [[ "$output" == *"known: forgejo, woodpecker-server, woodpecker-agent"* ]] } @test "disinto init --backend=nomad --with forgejo (flag=value syntax) works" { @@ -224,6 +224,42 @@ setup_file() { [[ "$output" == *"services to deploy: forgejo"* ]] } +# S3.4: woodpecker auto-expansion and forgejo auto-inclusion +@test "disinto init --backend=nomad --with woodpecker auto-expands to server+agent" { + run "$DISINTO_BIN" init placeholder/repo --backend=nomad --with woodpecker --dry-run + [ "$status" -eq 0 ] + [[ "$output" == *"services to deploy: forgejo,woodpecker-server,woodpecker-agent"* ]] + [[ "$output" == *"deployment order: forgejo woodpecker-server woodpecker-agent"* ]] +} + +@test "disinto init --backend=nomad --with woodpecker auto-includes forgejo with note" { + run "$DISINTO_BIN" init placeholder/repo --backend=nomad --with woodpecker --dry-run + [ "$status" -eq 0 ] + [[ "$output" == *"Note: --with woodpecker implies --with forgejo"* ]] +} + +@test "disinto init --backend=nomad --with woodpecker,forgejo does not duplicate forgejo" { + run "$DISINTO_BIN" init placeholder/repo --backend=nomad --with woodpecker,forgejo --dry-run + [ "$status" -eq 0 ] + # Should still expand woodpecker and have forgejo once + [[ "$output" == *"services to deploy: forgejo,woodpecker-server,woodpecker-agent"* ]] +} + +@test "disinto init --backend=nomad --with woodpecker seeds both forgejo and woodpecker" { + run "$DISINTO_BIN" init placeholder/repo --backend=nomad --with woodpecker --dry-run + [ "$status" -eq 0 ] + [[ "$output" == *"tools/vault-seed-forgejo.sh --dry-run"* ]] + [[ "$output" == *"tools/vault-seed-woodpecker.sh --dry-run"* ]] +} + +@test "disinto init --backend=nomad --with forgejo,woodpecker deploys all three services" { + run "$DISINTO_BIN" init placeholder/repo --backend=nomad --with forgejo,woodpecker --dry-run + [ "$status" -eq 0 ] + [[ "$output" == *"[deploy] [dry-run] nomad job validate"*"forgejo.hcl"* ]] + [[ "$output" == *"[deploy] [dry-run] nomad job validate"*"woodpecker-server.hcl"* ]] + [[ "$output" == *"[deploy] [dry-run] nomad job validate"*"woodpecker-agent.hcl"* ]] +} + @test "disinto init --backend=nomad --with forgejo --empty --dry-run rejects in any order" { run "$DISINTO_BIN" init placeholder/repo --with forgejo --backend=nomad --empty --dry-run [ "$status" -ne 0 ]