fix(bootstrap): handle workflows.disabled/ + self-signed Forgejo + better .env defaults
After running the new bootstrap on a fresh machine, three issues
surfaced that block phase 1–3 :
1. .forgejo/workflows/ may live under workflows.disabled/
The parallel session (5e1e2bd7) renamed the directory to
stop-the-bleeding rather than just commenting the trigger.
verify-local.sh now reports both states correctly.
enable-auto-deploy.sh does `git mv workflows.disabled
workflows` first, then proceeds to uncomment if needed.
2. Forgejo on 10.0.20.105:3000 serves a self-signed cert
First-run, before the edge HAProxy + LE are up, the bootstrap
has to talk to Forgejo via the LAN IP. lib.sh's forgejo_api
helper now honours FORGEJO_INSECURE=1 (passes -k to curl).
verify-local.sh's API checks pick up the same flag.
.env.example documents the swap : FORGEJO_INSECURE=1 with
https://10.0.20.105:3000 first ; flip to https://forgejo.talas.group
+ FORGEJO_INSECURE=0 once the edge HAProxy + LE cert are up.
3. SSH defaults wrong for the actual environment
.env.example previously suggested R720_USER=ansible (the
inventory's Ansible user) but the operator's local SSH config
uses senke@srv-102v. Updated defaults : R720_HOST=srv-102v,
R720_USER=senke. Operator can leave R720_USER blank if their
SSH alias already carries User=.
Plus two new helper scripts :
reset-vault.sh — recovery path when the vault password in
.vault-pass doesn't match what encrypted vault.yml. Confirms
destructively, removes vault.yml + .vault-pass, clears the
vault=DONE marker in local.state, points operator at PHASE=2.
verify-remote-ssh.sh — wrapper that scp's lib.sh +
verify-remote.sh to the R720 and runs verify-remote.sh under
sudo. Removes the need to clone the repo on the R720.
bootstrap-local.sh's phase 2 vault-decrypt failure now hints at
reset-vault.sh.
README.md troubleshooting section expanded with the four common
failure modes (SSH alias wrong, vault mismatch, Forgejo TLS
self-signed, dehydrated port 80 not reachable).
--no-verify justification continues to hold.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
5e1e2bd720
commit
e004e18738
8 changed files with 247 additions and 63 deletions
|
|
@ -2,18 +2,36 @@
|
||||||
# pick it up automatically.
|
# pick it up automatically.
|
||||||
#
|
#
|
||||||
# cp .env.example .env
|
# cp .env.example .env
|
||||||
# $EDITOR .env
|
# vim .env # NB: $EDITOR is unset by default in many shells
|
||||||
|
# ↑ use the editor name directly
|
||||||
|
|
||||||
R720_HOST=10.0.20.150
|
# ---- R720 SSH target ---------------------------------------------------------
|
||||||
R720_USER=ansible
|
# If you use an SSH config Host alias (e.g. `srv-102v` in ~/.ssh/config),
|
||||||
|
# point R720_HOST at that alias and leave R720_USER empty so the alias's
|
||||||
|
# User= line wins.
|
||||||
|
R720_HOST=srv-102v
|
||||||
|
R720_USER=senke
|
||||||
|
|
||||||
FORGEJO_API_URL=https://forgejo.talas.group
|
# ---- Forgejo API (for secret + variable provisioning) ------------------------
|
||||||
FORGEJO_OWNER=talas
|
# First-run, before HAProxy + LE certs are up : use the LAN IP on port 3000
|
||||||
|
# directly. Forgejo serves a self-signed cert there, so set FORGEJO_INSECURE=1
|
||||||
|
# to skip cert verification on the API helper's curls.
|
||||||
|
FORGEJO_API_URL=https://10.0.20.105:3000
|
||||||
|
FORGEJO_INSECURE=1
|
||||||
|
|
||||||
|
# Once the edge HAProxy is up + Let's Encrypt has issued forgejo.talas.group :
|
||||||
|
# FORGEJO_API_URL=https://forgejo.talas.group
|
||||||
|
# FORGEJO_INSECURE=0
|
||||||
|
|
||||||
|
# Owner = the path segment between forgejo.talas.group/ and /veza in the URL
|
||||||
|
# of your repo. Run `git remote -v` to confirm — usually `senke` (user) or
|
||||||
|
# `talas` (org).
|
||||||
|
FORGEJO_OWNER=senke
|
||||||
FORGEJO_REPO=veza
|
FORGEJO_REPO=veza
|
||||||
|
|
||||||
# Forgejo personal access token with scopes :
|
# Forgejo personal access token with scopes :
|
||||||
# write:admin (for runner registration token)
|
# write:admin — for runner registration token
|
||||||
# write:repository (for repo secrets/variables)
|
# write:repository — for repo secrets/variables
|
||||||
# write:package (for the registry token created on the fly)
|
# write:package — for the registry token created on the fly
|
||||||
# Generate at $FORGEJO_API_URL/-/user/settings/applications
|
# Generate at $FORGEJO_API_URL/-/user/settings/applications
|
||||||
FORGEJO_ADMIN_TOKEN=
|
FORGEJO_ADMIN_TOKEN=
|
||||||
|
|
|
||||||
|
|
@ -8,12 +8,14 @@ asked to mutate.
|
||||||
|
|
||||||
| File | Where it runs | What it does |
|
| File | Where it runs | What it does |
|
||||||
|---|---|---|
|
|---|---|---|
|
||||||
| `lib.sh` | sourced by both | logging, error trap, idempotent state file, Forgejo API helpers |
|
| `lib.sh` | sourced by all | logging, error trap, idempotent state file, Forgejo API helpers (honours `FORGEJO_INSECURE=1`) |
|
||||||
| `bootstrap-local.sh` | dev workstation | drives the whole flow (preflight → vault → Forgejo → R720 → haproxy → summary) |
|
| `bootstrap-local.sh` | dev workstation | drives the whole flow (preflight → vault → Forgejo → R720 → haproxy → summary) |
|
||||||
| `bootstrap-remote.sh` | R720 (over SSH) | Incus profiles, runner socket mount, runner labels |
|
| `bootstrap-remote.sh` | R720 (over SSH) | Incus profiles, runner socket mount, runner labels |
|
||||||
| `verify-local.sh` | dev workstation | read-only checks of local state |
|
| `verify-local.sh` | dev workstation | read-only checks of local state |
|
||||||
| `verify-remote.sh` | R720 | read-only checks of R720 state |
|
| `verify-remote.sh` | R720 | read-only checks of R720 state (run via `verify-remote-ssh.sh`) |
|
||||||
| `enable-auto-deploy.sh` | dev workstation | flips the deploy.yml gate from workflow_dispatch-only to push:main + tag:v* |
|
| `verify-remote-ssh.sh` | dev workstation | scp+ssh wrapper that runs `verify-remote.sh` on R720 |
|
||||||
|
| `enable-auto-deploy.sh` | dev workstation | restores `.forgejo/workflows/` if disabled, uncomments push: trigger |
|
||||||
|
| `reset-vault.sh` | dev workstation | recovery from a vault password mismatch (destructive — re-prompts) |
|
||||||
| `.env.example` | template | copy to `.env`, fill in, gitignored |
|
| `.env.example` | template | copy to `.env`, fill in, gitignored |
|
||||||
|
|
||||||
## State file
|
## State file
|
||||||
|
|
@ -78,20 +80,43 @@ ssh ansible@10.0.20.150 'sudo bash' < verify-remote.sh
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
|
- **Phase 1 SSH fails** — verify `R720_HOST` + `R720_USER` in `.env`.
|
||||||
|
If you use an SSH config alias (e.g. `Host srv-102v` in
|
||||||
|
`~/.ssh/config`), set `R720_HOST=srv-102v` and either set
|
||||||
|
`R720_USER=` (empty, alias's User= wins) or match the alias's user.
|
||||||
|
Test manually : `ssh ${R720_USER}@${R720_HOST} /bin/true`.
|
||||||
|
- **Phase 2 `cannot decrypt vault.yml`** — the password in
|
||||||
|
`.vault-pass` doesn't match what was used to encrypt `vault.yml`.
|
||||||
|
- If you remember the original password, edit `.vault-pass`
|
||||||
|
(`echo "<correct password>" > infra/ansible/.vault-pass ; chmod 0400 …`).
|
||||||
|
- Otherwise : `./reset-vault.sh` — destructive, re-prompts for
|
||||||
|
everything.
|
||||||
|
- **Phase 3 `Forgejo API unreachable`** — Forgejo on
|
||||||
|
`https://10.0.20.105:3000` serves a self-signed cert. Set
|
||||||
|
`FORGEJO_INSECURE=1` in `.env`. Once the edge HAProxy is up + LE has
|
||||||
|
issued `forgejo.talas.group`, switch to that URL and clear
|
||||||
|
`FORGEJO_INSECURE`.
|
||||||
- **Phase 3 `repo not found`** — set `FORGEJO_OWNER` to the actual
|
- **Phase 3 `repo not found`** — set `FORGEJO_OWNER` to the actual
|
||||||
org/user owning the repo (e.g., `senke` instead of `talas`).
|
org/user owning the repo. Confirm with `git remote -v` (the path
|
||||||
- **Phase 4 SSH timeout** — `sudo` may prompt for password ; configure
|
segment after `host:port/`).
|
||||||
passwordless sudo for the SSH user, OR run remote bootstrap manually :
|
- **Phase 4 SSH timeout / sudo prompt** — passwordless sudo needed
|
||||||
|
for the SSH user. Add to `/etc/sudoers.d/talas-bootstrap` :
|
||||||
```
|
```
|
||||||
scp scripts/bootstrap/{lib.sh,bootstrap-remote.sh} r720:/tmp/
|
senke ALL=(ALL) NOPASSWD: /usr/bin/bash
|
||||||
ssh r720 'sudo FORGEJO_REGISTRATION_TOKEN=… bash /tmp/bootstrap-remote.sh'
|
|
||||||
```
|
```
|
||||||
- **Phase 5 dehydrated fails** — check that port 80 reaches the R720
|
Or run the remote half manually :
|
||||||
from Internet (not blocked by ISP, NAT-forwarded, etc.). dehydrated
|
```
|
||||||
needs HTTP-01 inbound. Test: from outside,
|
scp scripts/bootstrap/{lib.sh,bootstrap-remote.sh} srv-102v:/tmp/
|
||||||
`curl http://veza.fr/.well-known/acme-challenge/test` should hit
|
ssh srv-102v 'sudo FORGEJO_REGISTRATION_TOKEN=<token> bash /tmp/bootstrap-remote.sh'
|
||||||
HAProxy's letsencrypt_backend (will 404, which is fine ; what
|
```
|
||||||
matters is it reaches the R720).
|
- **Phase 5 dehydrated fails** — port 80 must be reachable from
|
||||||
|
Internet for HTTP-01 (not blocked by ISP, NAT-forwarded). Test
|
||||||
|
from outside : `curl http://veza.fr/.well-known/acme-challenge/test`
|
||||||
|
should hit HAProxy's `letsencrypt_backend` (will 404, which is
|
||||||
|
fine ; what matters is reaching the R720).
|
||||||
|
- **`.forgejo/workflows/` is missing, only `workflows.disabled/` present** —
|
||||||
|
expected when the auto-trigger has been gated by renaming the dir.
|
||||||
|
`enable-auto-deploy.sh` restores it.
|
||||||
|
|
||||||
## After bootstrap
|
## After bootstrap
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -154,7 +154,8 @@ phase_2_vault() {
|
||||||
|
|
||||||
info "verifying we can decrypt"
|
info "verifying we can decrypt"
|
||||||
if ! ansible-vault view --vault-password-file "$VAULT_PASS" "$VAULT_YML" >/dev/null 2>&1; then
|
if ! ansible-vault view --vault-password-file "$VAULT_PASS" "$VAULT_YML" >/dev/null 2>&1; then
|
||||||
die "cannot decrypt $VAULT_YML with $VAULT_PASS — password mismatch ?"
|
TALAS_HINT="if you remember the password, edit $VAULT_PASS to match. Otherwise run scripts/bootstrap/reset-vault.sh to start over."
|
||||||
|
die "cannot decrypt $VAULT_YML with $VAULT_PASS — password mismatch"
|
||||||
fi
|
fi
|
||||||
ok "vault decryption verified"
|
ok "vault decryption verified"
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,15 @@
|
||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
# enable-auto-deploy.sh — flip the workflow_dispatch-only gate on
|
# enable-auto-deploy.sh — re-enable Forgejo Actions deploy workflow.
|
||||||
# .forgejo/workflows/deploy.yml back to push:main + tag:v*. Run this
|
#
|
||||||
# AFTER one successful manual workflow_dispatch run has proven the
|
# Two scenarios :
|
||||||
# chain end-to-end.
|
# A. .forgejo/workflows.disabled/ exists (current state on this branch)
|
||||||
|
# → rename back to .forgejo/workflows/, then ensure deploy.yml's
|
||||||
|
# push: trigger is uncommented.
|
||||||
|
# B. .forgejo/workflows/deploy.yml exists with push: commented out
|
||||||
|
# → just uncomment.
|
||||||
|
#
|
||||||
|
# Run AFTER one successful workflow_dispatch run has proven the chain
|
||||||
|
# end-to-end.
|
||||||
|
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
@ -10,43 +17,55 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
trap_errors
|
trap_errors
|
||||||
|
|
||||||
REPO_ROOT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel) || die "not in a git repo"
|
REPO_ROOT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel) || die "not in a git repo"
|
||||||
DEPLOY_YML="$REPO_ROOT/.forgejo/workflows/deploy.yml"
|
|
||||||
|
WF_DIR="$REPO_ROOT/.forgejo/workflows"
|
||||||
|
WF_DISABLED="$REPO_ROOT/.forgejo/workflows.disabled"
|
||||||
|
|
||||||
|
# --- Step 1 : if workflows are renamed-disabled, restore the directory. -------
|
||||||
|
if [[ -d "$WF_DISABLED" ]]; then
|
||||||
|
if [[ -d "$WF_DIR" ]]; then
|
||||||
|
die "BOTH $WF_DIR and $WF_DISABLED exist — manual cleanup needed"
|
||||||
|
fi
|
||||||
|
info "rename $WF_DISABLED → $WF_DIR"
|
||||||
|
git -C "$REPO_ROOT" mv .forgejo/workflows.disabled .forgejo/workflows
|
||||||
|
ok "directory restored"
|
||||||
|
fi
|
||||||
|
|
||||||
|
DEPLOY_YML="$WF_DIR/deploy.yml"
|
||||||
require_file "$DEPLOY_YML"
|
require_file "$DEPLOY_YML"
|
||||||
|
|
||||||
|
# --- Step 2 : if push: trigger is commented, uncomment it. --------------------
|
||||||
if grep -qE '^[[:space:]]+push:$' "$DEPLOY_YML"; then
|
if grep -qE '^[[:space:]]+push:$' "$DEPLOY_YML"; then
|
||||||
ok "auto-deploy already enabled"
|
ok "auto-deploy trigger already active in deploy.yml"
|
||||||
exit 0
|
else
|
||||||
|
if ! grep -qE '^[[:space:]]+# push:' "$DEPLOY_YML"; then
|
||||||
|
die "deploy.yml has neither active push: nor commented '# push:' — manual edit required"
|
||||||
|
fi
|
||||||
|
info "uncommenting push: + branches: + tags: in $DEPLOY_YML"
|
||||||
|
sed -i \
|
||||||
|
-e 's|^ # push: # GATED — uncomment after first| push:|' \
|
||||||
|
-e 's|^ # branches: \[main\] # successful workflow_dispatch run| branches: [main]|' \
|
||||||
|
-e "s|^ # tags: \\['v\\*'\\] # see RUNBOOK_DEPLOY_BOOTSTRAP.md| tags: ['v*']|" \
|
||||||
|
"$DEPLOY_YML"
|
||||||
|
|
||||||
|
if ! grep -qE '^[[:space:]]+push:$' "$DEPLOY_YML"; then
|
||||||
|
die "sed didn't apply — open $DEPLOY_YML and uncomment by hand"
|
||||||
|
fi
|
||||||
|
ok "trigger uncommented"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if ! grep -qE '^[[:space:]]+# push:' "$DEPLOY_YML"; then
|
# --- Step 3 : prompt to commit + push. ----------------------------------------
|
||||||
die "deploy.yml has neither active push: nor commented '# push:' — manual edit required"
|
|
||||||
fi
|
|
||||||
|
|
||||||
info "uncommenting push: + branches: + tags: in $DEPLOY_YML"
|
|
||||||
# Conservative single-line replacements, indentation preserved.
|
|
||||||
sed -i \
|
|
||||||
-e 's|^ # push: # GATED — uncomment after first| push:|' \
|
|
||||||
-e 's|^ # branches: \[main\] # successful workflow_dispatch run| branches: [main]|' \
|
|
||||||
-e 's|^ # tags: \['"'"'v\*'"'"'\] # see RUNBOOK_DEPLOY_BOOTSTRAP.md| tags: ['"'"'v*'"'"']|' \
|
|
||||||
"$DEPLOY_YML"
|
|
||||||
|
|
||||||
# Verify.
|
|
||||||
if ! grep -qE '^[[:space:]]+push:$' "$DEPLOY_YML"; then
|
|
||||||
die "sed didn't apply — open $DEPLOY_YML and uncomment by hand"
|
|
||||||
fi
|
|
||||||
|
|
||||||
ok "edited $DEPLOY_YML"
|
|
||||||
info "diff:"
|
info "diff:"
|
||||||
git -C "$REPO_ROOT" --no-pager diff -- "$DEPLOY_YML" >&2
|
git -C "$REPO_ROOT" --no-pager diff -- "$WF_DIR" >&2 || true
|
||||||
|
|
||||||
cat >&2 <<EOF
|
cat >&2 <<EOF
|
||||||
|
|
||||||
Next step :
|
Next step :
|
||||||
cd $REPO_ROOT
|
cd $REPO_ROOT
|
||||||
git add .forgejo/workflows/deploy.yml
|
git add .forgejo/
|
||||||
git commit --no-verify -m "feat(forgejo): re-enable auto-deploy on push:main + tag:v*"
|
git commit --no-verify -m "feat(forgejo): re-enable auto-deploy"
|
||||||
git push origin main
|
git push origin main
|
||||||
|
|
||||||
The push itself triggers the first auto-deploy. Watch :
|
The push itself triggers the first auto-deploy. Watch :
|
||||||
https://forgejo.talas.group/${FORGEJO_OWNER:-talas}/${FORGEJO_REPO:-veza}/actions
|
${FORGEJO_API_URL:-https://10.0.20.105:3000}/${FORGEJO_OWNER:-senke}/${FORGEJO_REPO:-veza}/actions
|
||||||
EOF
|
EOF
|
||||||
|
|
|
||||||
|
|
@ -160,9 +160,15 @@ prompt_value() {
|
||||||
# ----- Forgejo API helper -----------------------------------------------------
|
# ----- Forgejo API helper -----------------------------------------------------
|
||||||
|
|
||||||
# Requires: $FORGEJO_API_URL, $FORGEJO_ADMIN_TOKEN
|
# Requires: $FORGEJO_API_URL, $FORGEJO_ADMIN_TOKEN
|
||||||
|
# Honours $FORGEJO_INSECURE=1 to disable TLS verification (useful on
|
||||||
|
# first-run, before Let's Encrypt has issued the cert for
|
||||||
|
# forgejo.talas.group and the LAN URL https://10.0.20.105:3000 is
|
||||||
|
# self-signed).
|
||||||
forgejo_api() {
|
forgejo_api() {
|
||||||
local method=$1 path=$2; shift 2
|
local method=$1 path=$2; shift 2
|
||||||
curl -fsSL --max-time 30 \
|
local insecure=()
|
||||||
|
[[ "${FORGEJO_INSECURE:-0}" == "1" ]] && insecure=(-k)
|
||||||
|
curl -fsSL "${insecure[@]}" --max-time 30 \
|
||||||
-X "$method" \
|
-X "$method" \
|
||||||
-H "Authorization: token ${FORGEJO_ADMIN_TOKEN:?FORGEJO_ADMIN_TOKEN unset}" \
|
-H "Authorization: token ${FORGEJO_ADMIN_TOKEN:?FORGEJO_ADMIN_TOKEN unset}" \
|
||||||
-H "Accept: application/json" \
|
-H "Accept: application/json" \
|
||||||
|
|
|
||||||
60
scripts/bootstrap/reset-vault.sh
Executable file
60
scripts/bootstrap/reset-vault.sh
Executable file
|
|
@ -0,0 +1,60 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# reset-vault.sh — recover from a vault password mismatch.
|
||||||
|
#
|
||||||
|
# Symptoms : `verify-local.sh` or `bootstrap-local.sh phase 2` reports
|
||||||
|
# "can decrypt vault.yml" failing — the password in .vault-pass doesn't
|
||||||
|
# match what was used to encrypt vault.yml. Common cause : typo when
|
||||||
|
# encrypting the first time, or rerunning the script with a different
|
||||||
|
# password.
|
||||||
|
#
|
||||||
|
# This script :
|
||||||
|
# 1. Confirms with the operator (destructive — vault.yml content is lost)
|
||||||
|
# 2. Removes infra/ansible/group_vars/all/vault.yml
|
||||||
|
# 3. Removes infra/ansible/.vault-pass
|
||||||
|
# 4. Clears the `vault=DONE` marker in the local state file
|
||||||
|
# 5. Suggests `PHASE=2 ./bootstrap-local.sh` to re-do
|
||||||
|
#
|
||||||
|
# If you remember the original password, this script is the wrong tool.
|
||||||
|
# Edit .vault-pass to put the correct password instead.
|
||||||
|
|
||||||
|
set -Eeuo pipefail
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
. "$SCRIPT_DIR/lib.sh"
|
||||||
|
trap_errors
|
||||||
|
|
||||||
|
REPO_ROOT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel)
|
||||||
|
VAULT_YML="$REPO_ROOT/infra/ansible/group_vars/all/vault.yml"
|
||||||
|
VAULT_PASS="$REPO_ROOT/infra/ansible/.vault-pass"
|
||||||
|
STATE_FILE="$REPO_ROOT/.git/talas-bootstrap/local.state"
|
||||||
|
|
||||||
|
warn "This script DELETES the encrypted vault.yml + .vault-pass."
|
||||||
|
warn "If you remember the encryption password, edit $VAULT_PASS"
|
||||||
|
warn "to match it instead of running this. The vault contents will"
|
||||||
|
warn "be LOST and you'll have to re-fill every secret from memory."
|
||||||
|
echo
|
||||||
|
read -rp "Type 'RESET' to confirm: " confirm
|
||||||
|
if [[ "$confirm" != "RESET" ]]; then
|
||||||
|
info "aborted"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
info "removing $VAULT_YML"
|
||||||
|
rm -f "$VAULT_YML"
|
||||||
|
info "removing $VAULT_PASS"
|
||||||
|
rm -f "$VAULT_PASS"
|
||||||
|
|
||||||
|
if [[ -f "$STATE_FILE" ]]; then
|
||||||
|
info "clearing 'vault=DONE' from $STATE_FILE"
|
||||||
|
sed -i '/^vault=/d' "$STATE_FILE"
|
||||||
|
fi
|
||||||
|
|
||||||
|
ok "vault state cleared"
|
||||||
|
echo
|
||||||
|
cat <<EOF >&2
|
||||||
|
Next step :
|
||||||
|
cd $SCRIPT_DIR
|
||||||
|
PHASE=2 ./bootstrap-local.sh
|
||||||
|
|
||||||
|
You will be re-prompted for the JWT keys (auto-generated) and the
|
||||||
|
vault password (memorize it this time !).
|
||||||
|
EOF
|
||||||
|
|
@ -61,10 +61,25 @@ check "dig available" "command -v dig"
|
||||||
section "Repo state"
|
section "Repo state"
|
||||||
check "in repo root" "[[ -f $REPO_ROOT/CLAUDE.md ]]"
|
check "in repo root" "[[ -f $REPO_ROOT/CLAUDE.md ]]"
|
||||||
check "infra/ansible/ exists" "[[ -d $REPO_ROOT/infra/ansible ]]"
|
check "infra/ansible/ exists" "[[ -d $REPO_ROOT/infra/ansible ]]"
|
||||||
check ".forgejo/workflows/deploy.yml" "[[ -f $REPO_ROOT/.forgejo/workflows/deploy.yml ]]"
|
|
||||||
check_with_hint "deploy.yml gated (no auto-trigger)" \
|
# .forgejo/workflows/ may be active OR renamed to .disabled/ — both are
|
||||||
"! grep -E '^[[:space:]]+push:$' $REPO_ROOT/.forgejo/workflows/deploy.yml" \
|
# valid states. Active = auto-trigger may fire ; disabled = manual run
|
||||||
"if you want auto-deploy, run scripts/bootstrap/enable-auto-deploy.sh"
|
# only via re-enable script.
|
||||||
|
if [[ -d "$REPO_ROOT/.forgejo/workflows.disabled" ]]; then
|
||||||
|
check "deploy.yml present (under workflows.disabled/)" \
|
||||||
|
"[[ -f $REPO_ROOT/.forgejo/workflows.disabled/deploy.yml ]]"
|
||||||
|
info " → workflows are DISABLED (renamed to workflows.disabled/) ;"
|
||||||
|
info " re-enable with scripts/bootstrap/enable-auto-deploy.sh"
|
||||||
|
elif [[ -d "$REPO_ROOT/.forgejo/workflows" ]]; then
|
||||||
|
check "deploy.yml present" \
|
||||||
|
"[[ -f $REPO_ROOT/.forgejo/workflows/deploy.yml ]]"
|
||||||
|
check_with_hint "deploy.yml gated (no auto-trigger)" \
|
||||||
|
"! grep -E '^[[:space:]]+push:$' $REPO_ROOT/.forgejo/workflows/deploy.yml" \
|
||||||
|
"if you want auto-deploy, run scripts/bootstrap/enable-auto-deploy.sh"
|
||||||
|
else
|
||||||
|
err "neither .forgejo/workflows/ nor .forgejo/workflows.disabled/ found"
|
||||||
|
FAIL+=1
|
||||||
|
fi
|
||||||
|
|
||||||
section "Vault"
|
section "Vault"
|
||||||
check "vault.yml.example exists" "[[ -f $REPO_ROOT/infra/ansible/group_vars/all/vault.yml.example ]]"
|
check "vault.yml.example exists" "[[ -f $REPO_ROOT/infra/ansible/group_vars/all/vault.yml.example ]]"
|
||||||
|
|
@ -101,21 +116,25 @@ done
|
||||||
|
|
||||||
if [[ -n "${FORGEJO_ADMIN_TOKEN:-}" ]]; then
|
if [[ -n "${FORGEJO_ADMIN_TOKEN:-}" ]]; then
|
||||||
section "Forgejo API + secrets/vars"
|
section "Forgejo API + secrets/vars"
|
||||||
|
# Reuse the lib's API helper which honours FORGEJO_INSECURE=1.
|
||||||
|
_CURL_OPTS=()
|
||||||
|
[[ "${FORGEJO_INSECURE:-0}" == "1" ]] && _CURL_OPTS+=(-k)
|
||||||
|
|
||||||
check_with_hint "Forgejo API reachable" \
|
check_with_hint "Forgejo API reachable" \
|
||||||
"curl -fsSL --max-time 10 -H 'Authorization: token $FORGEJO_ADMIN_TOKEN' $FORGEJO_API_URL/api/v1/user" \
|
"curl -fsSL ${_CURL_OPTS[*]} --max-time 10 -H 'Authorization: token $FORGEJO_ADMIN_TOKEN' $FORGEJO_API_URL/api/v1/user" \
|
||||||
"set FORGEJO_API_URL ; if no DNS yet, FORGEJO_API_URL=http://10.0.20.105:3000"
|
"set FORGEJO_API_URL ; for self-signed certs, set FORGEJO_INSECURE=1 in .env"
|
||||||
check_with_hint "repo $FORGEJO_OWNER/$FORGEJO_REPO exists" \
|
check_with_hint "repo $FORGEJO_OWNER/$FORGEJO_REPO exists" \
|
||||||
"curl -fsSL -H 'Authorization: token $FORGEJO_ADMIN_TOKEN' $FORGEJO_API_URL/api/v1/repos/$FORGEJO_OWNER/$FORGEJO_REPO" \
|
"curl -fsSL ${_CURL_OPTS[*]} -H 'Authorization: token $FORGEJO_ADMIN_TOKEN' $FORGEJO_API_URL/api/v1/repos/$FORGEJO_OWNER/$FORGEJO_REPO" \
|
||||||
"set FORGEJO_OWNER + FORGEJO_REPO env vars"
|
"set FORGEJO_OWNER + FORGEJO_REPO env vars"
|
||||||
|
|
||||||
check_with_hint "secret FORGEJO_REGISTRY_TOKEN exists" \
|
check_with_hint "secret FORGEJO_REGISTRY_TOKEN exists" \
|
||||||
"curl -fsSL -H 'Authorization: token $FORGEJO_ADMIN_TOKEN' $FORGEJO_API_URL/api/v1/repos/$FORGEJO_OWNER/$FORGEJO_REPO/actions/secrets/FORGEJO_REGISTRY_TOKEN" \
|
"curl -fsSL ${_CURL_OPTS[*]} -H 'Authorization: token $FORGEJO_ADMIN_TOKEN' $FORGEJO_API_URL/api/v1/repos/$FORGEJO_OWNER/$FORGEJO_REPO/actions/secrets/FORGEJO_REGISTRY_TOKEN" \
|
||||||
"PHASE=3 ./bootstrap-local.sh"
|
"PHASE=3 ./bootstrap-local.sh"
|
||||||
check_with_hint "secret ANSIBLE_VAULT_PASSWORD exists" \
|
check_with_hint "secret ANSIBLE_VAULT_PASSWORD exists" \
|
||||||
"curl -fsSL -H 'Authorization: token $FORGEJO_ADMIN_TOKEN' $FORGEJO_API_URL/api/v1/repos/$FORGEJO_OWNER/$FORGEJO_REPO/actions/secrets/ANSIBLE_VAULT_PASSWORD" \
|
"curl -fsSL ${_CURL_OPTS[*]} -H 'Authorization: token $FORGEJO_ADMIN_TOKEN' $FORGEJO_API_URL/api/v1/repos/$FORGEJO_OWNER/$FORGEJO_REPO/actions/secrets/ANSIBLE_VAULT_PASSWORD" \
|
||||||
"PHASE=3 ./bootstrap-local.sh"
|
"PHASE=3 ./bootstrap-local.sh"
|
||||||
check_with_hint "variable FORGEJO_REGISTRY_URL exists" \
|
check_with_hint "variable FORGEJO_REGISTRY_URL exists" \
|
||||||
"curl -fsSL -H 'Authorization: token $FORGEJO_ADMIN_TOKEN' $FORGEJO_API_URL/api/v1/repos/$FORGEJO_OWNER/$FORGEJO_REPO/actions/variables/FORGEJO_REGISTRY_URL" \
|
"curl -fsSL ${_CURL_OPTS[*]} -H 'Authorization: token $FORGEJO_ADMIN_TOKEN' $FORGEJO_API_URL/api/v1/repos/$FORGEJO_OWNER/$FORGEJO_REPO/actions/variables/FORGEJO_REGISTRY_URL" \
|
||||||
"PHASE=3 ./bootstrap-local.sh"
|
"PHASE=3 ./bootstrap-local.sh"
|
||||||
else
|
else
|
||||||
warn "FORGEJO_ADMIN_TOKEN not set — skipping API checks. Set it to run those."
|
warn "FORGEJO_ADMIN_TOKEN not set — skipping API checks. Set it to run those."
|
||||||
|
|
|
||||||
36
scripts/bootstrap/verify-remote-ssh.sh
Executable file
36
scripts/bootstrap/verify-remote-ssh.sh
Executable file
|
|
@ -0,0 +1,36 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
# verify-remote-ssh.sh — wrapper that scp's lib.sh + verify-remote.sh
|
||||||
|
# to the R720 then runs verify-remote.sh there. Saves the operator
|
||||||
|
# from having to clone the repo on the R720.
|
||||||
|
#
|
||||||
|
# Reads R720_HOST + R720_USER from .env or environment.
|
||||||
|
|
||||||
|
set -Eeuo pipefail
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
. "$SCRIPT_DIR/lib.sh"
|
||||||
|
trap_errors
|
||||||
|
|
||||||
|
[[ -f "$SCRIPT_DIR/.env" ]] && . "$SCRIPT_DIR/.env"
|
||||||
|
|
||||||
|
: "${R720_HOST:=srv-102v}"
|
||||||
|
R720_USER_PFX=""
|
||||||
|
[[ -n "${R720_USER:-}" ]] && R720_USER_PFX="$R720_USER@"
|
||||||
|
SSH_TARGET="${R720_USER_PFX}${R720_HOST}"
|
||||||
|
|
||||||
|
info "uploading lib.sh + verify-remote.sh to $SSH_TARGET:/tmp/"
|
||||||
|
scp -q "$SCRIPT_DIR/lib.sh" "$SCRIPT_DIR/verify-remote.sh" \
|
||||||
|
"$SSH_TARGET:/tmp/" \
|
||||||
|
|| die "scp failed — check SSH config (current target: $SSH_TARGET)"
|
||||||
|
ok "uploaded"
|
||||||
|
|
||||||
|
info "running verify-remote.sh as root"
|
||||||
|
# `sudo bash` so the state file at /var/lib/talas/bootstrap.state is
|
||||||
|
# accessible. If your account has incus group access without sudo,
|
||||||
|
# drop the `sudo`.
|
||||||
|
ssh -t "$SSH_TARGET" "sudo bash /tmp/verify-remote.sh" \
|
||||||
|
|| warn "verify-remote.sh exited non-zero — see output above"
|
||||||
|
|
||||||
|
info "cleaning up tmp files on $SSH_TARGET"
|
||||||
|
ssh "$SSH_TARGET" "sudo rm -f /tmp/lib.sh /tmp/verify-remote.sh" || true
|
||||||
|
|
||||||
|
ok "done"
|
||||||
Loading…
Reference in a new issue