gowalk-cicd 1.0.49 → 1.0.50

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CLAUDE.md CHANGED
@@ -108,6 +108,9 @@ node /path/to/gowalk-cicd/bin/cli.mjs
108
108
  - The mobile workflow's feature-branch-only `backend-preview` job bootstraps a
109
109
  new app before GitHub registers `deploy-backend.yml` on the default branch.
110
110
  Default-branch deploys continue through the separate backend workflow.
111
+ - Domain-backed backend deploys fail closed on certificate issuance and the
112
+ public HTTPS health probe. `select_certbot_account.sh` chooses one existing
113
+ ACME account deterministically so multi-account hosts stay non-interactive.
111
114
  - The Android action serves two build systems. `android_config.project_kind()`
112
115
  is the only place that decides which; every downstream step branches on the
113
116
  `project_kind` output rather than re-sniffing the repo. Flutter wins the tie
package/README.md CHANGED
@@ -73,6 +73,8 @@ The backend workflow runs on a GitHub-hosted runner and, on a push touching
73
73
  it rsyncs the backend dir to `/opt/gowalk-backends/<app>/`, runs
74
74
  `docker compose up -d --build`, auto-detects the published `127.0.0.1:<port>`,
75
75
  wires an nginx vhost + Let's Encrypt cert for the API domain, and health-checks.
76
+ Hosts with several existing Let's Encrypt accounts select one deterministically,
77
+ and a domain deploy fails unless its public HTTPS certificate and health route validate.
76
78
 
77
79
  Requirements on the consumer repo:
78
80
 
@@ -1 +1 @@
1
- 1.0.49
1
+ 1.0.50
@@ -1 +1 @@
1
- 1.0.49
1
+ 1.0.50
@@ -1 +1 @@
1
- 1.0.49
1
+ 1.0.50
@@ -122,9 +122,12 @@ runs:
122
122
  rsync -az -e "$SSH" "$COMPOSE_FILE" \
123
123
  "${{ inputs.ssh-user }}@${{ inputs.host }}:$DEST/docker-compose.yml"
124
124
  fi
125
- # ship the remote deploy script (kept out of the app checkout)
126
- rsync -az -e "$SSH" "${{ github.action_path }}/remote_deploy.sh" \
127
- "${{ inputs.ssh-user }}@${{ inputs.host }}:$DEST/.remote_deploy.sh"
125
+ # Ship remote helpers after the application mirror. Both are package
126
+ # owned and kept out of the consumer's source tree.
127
+ rsync -az -e "$SSH" \
128
+ "${{ github.action_path }}/remote_deploy.sh" \
129
+ "${{ github.action_path }}/select_certbot_account.sh" \
130
+ "${{ inputs.ssh-user }}@${{ inputs.host }}:$DEST/"
128
131
 
129
132
  - name: Deploy on the host
130
133
  shell: bash
@@ -140,7 +143,7 @@ runs:
140
143
  "umask 077; cat > '$DEST/.runtime.env'"
141
144
  fi
142
145
  $SSH "${{ inputs.ssh-user }}@${{ inputs.host }}" \
143
- "bash '$DEST/.remote_deploy.sh' \
146
+ "bash '$DEST/remote_deploy.sh' \
144
147
  '${{ inputs.app-name }}' '${{ inputs.api-domain }}' \
145
148
  '${{ inputs.health-path }}' '${{ inputs.cert-email }}'"
146
149
 
@@ -10,6 +10,7 @@ HEALTH="${3:-/health}"
10
10
  CERT_EMAIL="${4:-admin@gowalk.com}"
11
11
  DIR="/opt/gowalk-backends/$APP"
12
12
  cd "$DIR"
13
+ source "$DIR/select_certbot_account.sh"
13
14
 
14
15
  log() { echo "[deploy $APP] $*"; }
15
16
 
@@ -95,16 +96,34 @@ fi
95
96
  # ── Let's Encrypt cert (idempotent; certbot skips if current)
96
97
  if ! certbot certificates 2>/dev/null | grep -q "Domains: $DOMAIN\b"; then
97
98
  log "requesting LE cert for $DOMAIN"
98
- certbot --nginx -d "$DOMAIN" --non-interactive --agree-tos \
99
- --email "$CERT_EMAIL" --redirect || log "warn: certbot failed (DNS not pointed yet?)"
99
+ CERTBOT_ACCOUNT="$(select_certbot_account)"
100
+ CERTBOT_ARGS=()
101
+ if [ -n "$CERTBOT_ACCOUNT" ]; then
102
+ log "using an existing Let's Encrypt account"
103
+ CERTBOT_ARGS+=(--account "$CERTBOT_ACCOUNT")
104
+ fi
105
+ if ! certbot --nginx -d "$DOMAIN" --non-interactive --agree-tos \
106
+ --email "$CERT_EMAIL" --redirect "${CERTBOT_ARGS[@]}"; then
107
+ log "ERROR: certbot failed for $DOMAIN"
108
+ exit 1
109
+ fi
100
110
  else
101
111
  log "cert for $DOMAIN already present"
102
112
  fi
103
113
 
104
- # ── public health-check (best effort — cert/DNS may still be propagating)
105
- if curl -fsSk "https://$DOMAIN$HEALTH" >/dev/null 2>&1; then
106
- log "public https://$DOMAIN$HEALTH healthy"
107
- else
108
- log "warn: public URL not answering yet (DNS/cert propagation) — container is up on :$PORT"
114
+ # ── public health-check. This validates DNS, the certificate, nginx routing,
115
+ # and the application together; a green deploy must mean the public API works.
116
+ PUBLIC_HEALTHY=""
117
+ for i in $(seq 1 15); do
118
+ if curl -fsS "https://$DOMAIN$HEALTH" >/dev/null 2>&1; then
119
+ log "public https://$DOMAIN$HEALTH healthy (after ${i}x2s)"
120
+ PUBLIC_HEALTHY=1
121
+ break
122
+ fi
123
+ sleep 2
124
+ done
125
+ if [ -z "$PUBLIC_HEALTHY" ]; then
126
+ log "ERROR: public https://$DOMAIN$HEALTH failed TLS or health validation"
127
+ exit 1
109
128
  fi
110
129
  log "done"
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env bash
2
+
3
+ # Print the stable id of one existing Certbot account. Hosts may contain more
4
+ # than one registered account; non-interactive Certbot otherwise stops to ask
5
+ # which account should authorize a new certificate.
6
+ select_certbot_account() {
7
+ local accounts_dir="${1:-/etc/letsencrypt/accounts}"
8
+ local registration
9
+
10
+ registration="$(
11
+ find "$accounts_dir" -type f -name regr.json -print 2>/dev/null \
12
+ | LC_ALL=C sort \
13
+ | head -1
14
+ )"
15
+ [ -n "$registration" ] || return 0
16
+ basename "$(dirname "$registration")"
17
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gowalk-cicd",
3
- "version": "1.0.49",
3
+ "version": "1.0.50",
4
4
  "description": "Zero-config GitHub Actions delivery for iOS TestFlight and Android Google Play.",
5
5
  "type": "module",
6
6
  "bin": {