move dev scripts to tools/, migrate to bws, use example.com in docs
Provision server / provision (push) Successful in 7s

- Move new-app.sh, new-service.sh, remove-app.sh from scripts/ to tools/
- Migrate new-app.sh and remove-app.sh from bw to bws
- Replace real domains with example.com in documentation and help text
This commit is contained in:
2026-03-19 11:10:14 -04:00
parent 5a8d9a8aa0
commit 037b78734f
8 changed files with 127 additions and 109 deletions
+8 -7
View File
@@ -59,8 +59,9 @@ configs, deploy scripts, and Gitea Actions workflows.
scripts/bootstrap.sh # First-time server setup (manual, uses Bitwarden) scripts/bootstrap.sh # First-time server setup (manual, uses Bitwarden)
scripts/configure.sh # Idempotent server config (CI-safe) scripts/configure.sh # Idempotent server config (CI-safe)
scripts/deploy.sh # Receives deploy + cert + provision commands via SSH scripts/deploy.sh # Receives deploy + cert + provision commands via SSH
scripts/new-app.sh # Scaffolds + deploys a new static site (single command) tools/new-app.sh # Scaffolds + deploys a new static site (run from dev machine)
scripts/new-service.sh # Scaffolds a new Docker Compose service tools/new-service.sh # Scaffolds a new Docker Compose service (run from dev machine)
tools/remove-app.sh # Removes a static site (run from dev machine)
docker/nginx/ # nginx reverse proxy docker/nginx/ # nginx reverse proxy
compose.yml # nginx:alpine container compose.yml # nginx:alpine container
nginx.conf # Main nginx config nginx.conf # Main nginx config
@@ -82,7 +83,7 @@ workflow that builds/pushes a Docker image and triggers a deploy.
Uses Gitea template variables (`${REPO_NAME}`) so the image tag and deploy Uses Gitea template variables (`${REPO_NAME}`) so the image tag and deploy
command are automatically set to the repo name. command are automatically set to the repo name.
### App repos (e.g., timothykim.net) ### App repos (e.g., example.com)
Each app is its own repo created from `static-site-template`. Contains the Each app is its own repo created from `static-site-template`. Contains the
app source code and a `build.yml` workflow. app source code and a `build.yml` workflow.
@@ -96,7 +97,7 @@ app source code and a `build.yml` workflow.
| |
nginx (port 80/443) nginx (port 80/443)
| | | |
timothykim_net hcsuzuki_net ... example_com example_org ...
(shared Docker network) (shared Docker network)
``` ```
@@ -111,7 +112,7 @@ app source code and a `build.yml` workflow.
### App code change (push to app repo) ### App code change (push to app repo)
``` ```
Developer pushes to app repo (e.g., timothykim.net) Developer pushes to app repo (e.g., example.com)
-> build.yml workflow runs on Gitea runner -> build.yml workflow runs on Gitea runner
-> docker build + push to git.timothykim.net registry -> docker build + push to git.timothykim.net registry
-> SSH to deploy@hantim as deploy-<domain> -> SSH to deploy@hantim as deploy-<domain>
@@ -140,9 +141,9 @@ Developer pushes change to scripts/configure.sh
## Conventions ## Conventions
- **App name = domain** (e.g., `hcsuzuki.net`) - **App name = domain** (e.g., `example.com`)
- **Container name** = domain with dots replaced by underscores (e.g., - **Container name** = domain with dots replaced by underscores (e.g.,
`hcsuzuki_net`) `example_com`)
- **Cert name** = config filename without `.conf` (stored at `/etc/letsencrypt/live/<name>/`) - **Cert name** = config filename without `.conf` (stored at `/etc/letsencrypt/live/<name>/`)
- **One domain per conf file** -- `deploy.sh` derives the cert name from the conf - **One domain per conf file** -- `deploy.sh` derives the cert name from the conf
filename and collects all `server_name` values for that cert. Putting unrelated filename and collects all `server_name` values for that cert. Putting unrelated
+8 -7
View File
@@ -12,8 +12,9 @@ Actions workflows.
- `scripts/bootstrap.sh` -- First-time server setup (manual, uses Bitwarden) - `scripts/bootstrap.sh` -- First-time server setup (manual, uses Bitwarden)
- `scripts/configure.sh` -- Idempotent server config (firewall, certbot, start services; CI-safe) - `scripts/configure.sh` -- Idempotent server config (firewall, certbot, start services; CI-safe)
- `scripts/deploy.sh` -- SSH-triggered deploy + cert issuance + provision (deploy-*, cert-*, provision commands) - `scripts/deploy.sh` -- SSH-triggered deploy + cert issuance + provision (deploy-*, cert-*, provision commands)
- `scripts/new-app.sh` -- Single-command new static site (DNS + Gitea repo + cert + deploy + verify) - `tools/new-app.sh` -- Single-command new static site (DNS + Gitea repo + cert + deploy + verify)
- `scripts/new-service.sh` -- Scaffold a new Docker Compose service (compose + workflow) - `tools/new-service.sh` -- Scaffold a new Docker Compose service (compose + workflow)
- `tools/remove-app.sh` -- Remove a static site (local files + Gitea repo)
- `docker/nginx/` -- Reverse proxy (nginx:alpine + certbot on host) - `docker/nginx/` -- Reverse proxy (nginx:alpine + certbot on host)
- `docker/<domain>/compose.yml` -- Per-app compose files - `docker/<domain>/compose.yml` -- Per-app compose files
- `.gitea/workflows/deploy-<app>.yml` -- Per-app deploy workflows - `.gitea/workflows/deploy-<app>.yml` -- Per-app deploy workflows
@@ -21,8 +22,8 @@ Actions workflows.
## Conventions ## Conventions
- **App name = domain** (e.g., `hcsuzuki.net`) - **App name = domain** (e.g., `example.com`)
- **Container name** = domain with dots replaced by underscores (e.g., `hcsuzuki_net`) - **Container name** = domain with dots replaced by underscores (e.g., `example_com`)
- **Image** = `git.timothykim.net/hantim/<domain>:latest` - **Image** = `git.timothykim.net/hantim/<domain>:latest`
- **Cert name** = config filename without `.conf` (at `/etc/letsencrypt/live/<name>/`) - **Cert name** = config filename without `.conf` (at `/etc/letsencrypt/live/<name>/`)
- **One domain per conf file** -- deploy.sh derives cert name from filename - **One domain per conf file** -- deploy.sh derives cert name from filename
@@ -51,13 +52,13 @@ Gitea org-level (`hantim`) secrets/variables:
### Adding a new static site ### Adding a new static site
Run `./scripts/new-app.sh <domain>` from dev machine. Handles everything: Run `./tools/new-app.sh <domain>` from dev machine. Handles everything:
DNS, Gitea repo, SSL cert, commit/push, build trigger, and verification. DNS, Gitea repo, SSL cert, commit/push, build trigger, and verification.
See USECASES.md for full details. See USECASES.md for full details.
### Adding a new Docker service ### Adding a new Docker service
Run `./scripts/new-service.sh <name>` to scaffold the compose file and deploy Run `./tools/new-service.sh <name>` to scaffold the compose file and deploy
workflow, then edit the compose file and commit. workflow, then edit the compose file and commit.
### Editing scripts ### Editing scripts
@@ -85,6 +86,6 @@ No build step. No tests. Verify changes by:
## Commit style ## Commit style
- Short, lowercase commit messages (e.g., "add hcsuzuki.net", "fix deploy command") - Short, lowercase commit messages (e.g., "add example.com", "fix deploy command")
- Do NOT include "Co-Authored-By" lines - Do NOT include "Co-Authored-By" lines
- Commit only when explicitly asked - Commit only when explicitly asked
+12 -10
View File
@@ -10,9 +10,9 @@ scripts, nginx configs, and Gitea Actions workflows for each app.
docker/ docker/
nginx/ # Reverse proxy (nginx:alpine) + SSL config nginx/ # Reverse proxy (nginx:alpine) + SSL config
nginx.conf # Main nginx config nginx.conf # Main nginx config
conf.d/ # Per-app server blocks (e.g. timothykim.net.conf) conf.d/ # Per-app server blocks (e.g. example.com.conf)
compose.yml compose.yml
timothykim.net/ # timothykim.net static site <domain>/ # Per-app static site (e.g. example.com)
compose.yml compose.yml
garage/ # Garage S3-compatible object storage garage/ # Garage S3-compatible object storage
compose.yml compose.yml
@@ -21,8 +21,10 @@ scripts/
bootstrap.sh # First-time server setup (manual, uses Bitwarden) bootstrap.sh # First-time server setup (manual, uses Bitwarden)
configure.sh # Idempotent server config (CI-safe) configure.sh # Idempotent server config (CI-safe)
deploy.sh # Deploy + cert issuance + provision (called via SSH) deploy.sh # Deploy + cert issuance + provision (called via SSH)
new-app.sh # Scaffolding script to add a new static site tools/
new-service.sh # Scaffolding script to add a new Docker service new-app.sh # Add a new static site (run from dev machine)
new-service.sh # Add a new Docker service (run from dev machine)
remove-app.sh # Remove a static site (run from dev machine)
.gitea/workflows/ # Per-app deploy workflows triggered by path changes .gitea/workflows/ # Per-app deploy workflows triggered by path changes
``` ```
@@ -59,19 +61,19 @@ bash /opt/hantim/scripts/bootstrap.sh
## Adding a new app ## Adding a new app
```bash ```bash
./scripts/new-app.sh <domain> ./tools/new-app.sh <domain>
``` ```
Example: `./scripts/new-app.sh hcsuzuki.net` Example: `./tools/new-app.sh example.com`
This single command handles everything: creates DNS records on Vultr, creates This single command handles everything: creates DNS records on Vultr, creates
the Gitea repo from `static-site-template`, generates all config files, issues the Gitea repo from `static-site-template`, generates all config files, issues
the SSL cert (zero downtime), commits and pushes, triggers the first build, the SSL cert (zero downtime), commits and pushes, triggers the first build,
and verifies the site is live. and verifies the site is live.
**Prerequisites:** domain nameservers pointed to Vultr, `bw`/`jq`/`dig` **Prerequisites:** domain nameservers pointed to Vultr, `bws`/`jq`/`dig`
installed. Uses the deploy SSH key from Bitwarden -- no personal server installed. Uses the deploy SSH key from Bitwarden Secrets Manager -- no
access needed. See USECASES.md for full details. personal server access needed. See USECASES.md for full details.
After the site is live, clone the app repo and customize it: After the site is live, clone the app repo and customize it:
@@ -84,7 +86,7 @@ git add . && git commit -m "initial content" && git push
## Deploying changes ## Deploying changes
**App code change** (push to an app repo like `timothykim.net`): **App code change** (push to an app repo like `example.com`):
- The app's `build.yml` builds the Docker image, pushes to the registry, and - The app's `build.yml` builds the Docker image, pushes to the registry, and
SSHes into the server to trigger a deploy SSHes into the server to trigger a deploy
+24 -24
View File
@@ -41,52 +41,53 @@ fresh server.
From your dev machine (single command): From your dev machine (single command):
```bash ```bash
./scripts/new-app.sh hcsuzuki.net ./tools/new-app.sh example.com
``` ```
**Prerequisites:** **Prerequisites:**
- Bitwarden vault must contain `hantim-new-app-script`, `hantim-vultr-api-key`, - Bitwarden Secrets Manager must contain `hantim-new-app-script`,
and `hantim-server-deploy` `hantim-vultr-api-key`, and `hantim-deploy-ssh-private-key`
- Domain nameservers must be pointed to Vultr (configured on directnic.com) - Domain nameservers must be pointed to Vultr (configured on directnic.com)
- Dependencies: `bw`, `jq`, `dig` - Dependencies: `bws`, `jq`, `dig`
- `BWS_ACCESS_TOKEN` env var or token saved at `~/.config/hantim/bws-token`
**What happens (fully automated):** **What happens (fully automated):**
1. Resolves server IP from `hantim.net` 1. Resolves server IP from `hantim.net`
2. Fetches Gitea API token, Vultr API key, and deploy SSH key from Bitwarden 2. Fetches Gitea API token, Vultr API key, and deploy SSH key from Bitwarden Secrets Manager
3. Creates DNS zone on Vultr (if needed) and A records for bare + www 3. Creates DNS zone on Vultr (if needed) and A records for bare + www
4. Waits for DNS to propagate 4. Waits for DNS to propagate
5. Creates Gitea repo `hantim/hcsuzuki.net` from `static-site-template` 5. Creates Gitea repo `hantim/example.com` from `static-site-template`
6. Creates local files: 6. Creates local files:
- `docker/hcsuzuki.net/compose.yml` (container name: `hcsuzuki_net`) - `docker/example.com/compose.yml` (container name: `example_com`)
- `.gitea/workflows/deploy-hcsuzuki.net.yml` - `.gitea/workflows/deploy-example.com.yml`
- `docker/nginx/conf.d/hcsuzuki.net.conf` (HTTP->HTTPS, bare->www, proxy) - `docker/nginx/conf.d/example.com.conf` (HTTP->HTTPS, bare->www, proxy)
7. SSHes to server as deploy user, issues SSL cert via webroot (zero downtime) 7. SSHes to server as deploy user, issues SSL cert via webroot (zero downtime)
8. Commits and pushes hantim-server (triggers deploy) 8. Commits and pushes hantim-server (triggers deploy)
9. Triggers initial build of the app repo via Gitea API 9. Triggers initial build of the app repo via Gitea API
10. Polls `https://www.hcsuzuki.net` until it responds (up to 3 minutes) 10. Polls `https://www.example.com` until it responds (up to 3 minutes)
After the site is live, clone the app repo and customize: After the site is live, clone the app repo and customize:
```bash ```bash
git clone git@git.timothykim.net:hantim/hcsuzuki.net.git git clone git@git.timothykim.net:hantim/example.com.git
cd hcsuzuki.net cd example.com
# edit static/index.html, etc. # edit static/index.html, etc.
git add . && git commit -m "initial content" && git push git add . && git commit -m "initial content" && git push
``` ```
## 4. Update app code ## 4. Update app code
Push a change to the app repo (e.g., `timothykim.net`): Push a change to the app repo (e.g., `example.com`):
```bash ```bash
cd timothykim.net cd example.com
# make changes # make changes
git add . && git commit -m "update content" && git push git add . && git commit -m "update content" && git push
``` ```
**What happens:** **What happens:**
1. Gitea Actions runs `build.yml`: builds Docker image, pushes to registry 1. Gitea Actions runs `build.yml`: builds Docker image, pushes to registry
2. SSHes into server, runs `deploy-timothykim.net` 2. SSHes into server, runs `deploy-example.com`
3. `deploy.sh` pulls latest hantim-server, pulls new image, restarts container 3. `deploy.sh` pulls latest hantim-server, pulls new image, restarts container
No manual steps needed. No manual steps needed.
@@ -96,14 +97,14 @@ No manual steps needed.
Edit a compose file in this repo and push: Edit a compose file in this repo and push:
```bash ```bash
vim docker/timothykim.net/compose.yml vim docker/example.com/compose.yml
git add docker/timothykim.net/compose.yml git add docker/example.com/compose.yml
git commit -m "update timothykim.net config" git commit -m "update example.com config"
git push git push
``` ```
**What happens:** **What happens:**
1. `deploy-timothykim.yml` workflow triggers (path match) 1. `deploy-example.com.yml` workflow triggers (path match)
2. `deploy.sh` pulls latest code, pulls image, runs `docker compose up -d` 2. `deploy.sh` pulls latest code, pulls image, runs `docker compose up -d`
## 6. Update nginx config ## 6. Update nginx config
@@ -111,8 +112,8 @@ git push
Edit an nginx config in this repo and push: Edit an nginx config in this repo and push:
```bash ```bash
vim docker/nginx/conf.d/timothykim.net.conf vim docker/nginx/conf.d/example.com.conf
git add docker/nginx/conf.d/timothykim.net.conf git add docker/nginx/conf.d/example.com.conf
git commit -m "update nginx config" git commit -m "update nginx config"
git push git push
``` ```
@@ -127,9 +128,8 @@ git push
5. Runs `docker compose up -d` and reloads nginx 5. Runs `docker compose up -d` and reloads nginx
**Note:** Each conf file must contain server blocks for only one domain. The **Note:** Each conf file must contain server blocks for only one domain. The
cert name is derived from the filename (e.g. `hcsuzuki.net.conf` -> cert cert name is derived from the filename (e.g. `example.com.conf` -> cert
`hcsuzuki.net`). Redirect-only domains like `hcsuzukiviolin.com` get their `example.com`). Redirect-only domains get their own conf file.
own conf file.
## 7. Re-run configure on existing server ## 7. Re-run configure on existing server
+1 -1
View File
@@ -38,7 +38,7 @@ echo "==> Issuing SSL certificates..."
# Stop nginx if running so certbot can bind to port 80 # Stop nginx if running so certbot can bind to port 80
docker stop nginx 2>/dev/null || true docker stop nginx 2>/dev/null || true
for conf in "$REPO_DIR"/docker/nginx/conf.d/*.conf; do for conf in "$REPO_DIR"/docker/nginx/conf.d/*.conf; do
# Derive cert name from config filename (e.g. timothykim.net.conf -> timothykim.net) # Derive cert name from config filename (e.g. example.com.conf -> example.com)
cert_name=$(basename "$conf" .conf) cert_name=$(basename "$conf" .conf)
domains=$(grep -oP 'server_name\s+\K[^;]+' "$conf" | tr ' ' '\n' | sort -u) domains=$(grep -oP 'server_name\s+\K[^;]+' "$conf" | tr ' ' '\n' | sort -u)
if [ -z "$domains" ]; then if [ -z "$domains" ]; then
+40 -35
View File
@@ -17,10 +17,10 @@ spin_wait() {
} }
if [ -z "${1:-}" ]; then if [ -z "${1:-}" ]; then
echo "Usage: ./scripts/new-app.sh <domain>" echo "Usage: ./tools/new-app.sh <domain>"
echo " Example: ./scripts/new-app.sh hcsuzuki.net" echo " Example: ./tools/new-app.sh example.com"
echo "" echo ""
echo "Requires: bw, jq, dig" echo "Requires: bws, jq, dig"
exit 1 exit 1
fi fi
@@ -47,9 +47,9 @@ fi
# --- Check dependencies --- # --- Check dependencies ---
if ! command -v bw &>/dev/null; then if ! command -v bws &>/dev/null; then
echo "Error: Bitwarden CLI (bw) is not installed." echo "Error: Bitwarden Secrets Manager CLI (bws) is not installed."
echo " npm install -g @bitwarden/cli" echo " https://github.com/bitwarden/sdk-sm/releases"
exit 1 exit 1
fi fi
@@ -65,41 +65,46 @@ if ! command -v dig &>/dev/null; then
exit 1 exit 1
fi fi
# --- Get secrets (cached in file, or fetch from Bitwarden) --- # --- Get bws access token ---
SECRETS_CACHE="${XDG_CACHE_HOME:-$HOME/.cache}/hantim-secrets" BWS_TOKEN_FILE="${XDG_CONFIG_HOME:-$HOME/.config}/hantim/bws-token"
if [ -f "$SECRETS_CACHE" ]; then if [ -z "${BWS_ACCESS_TOKEN:-}" ]; then
echo "Using cached secrets." if [ -f "$BWS_TOKEN_FILE" ]; then
# shellcheck source=/dev/null BWS_ACCESS_TOKEN=$(cat "$BWS_TOKEN_FILE")
source "$SECRETS_CACHE"
GITEA_TOKEN="$HANTIM_GITEA_TOKEN"
VULTR_API_KEY="$HANTIM_VULTR_API_KEY"
DEPLOY_KEY="$HANTIM_DEPLOY_KEY"
else else
if bw status 2>/dev/null | grep -q '"status":"unauthenticated"'; then echo "==> Bitwarden Secrets Manager access token required."
echo "Log in to Bitwarden:" echo " Generate one for your machine account at:"
bw login echo " https://vault.bitwarden.com/#/sm/machine-accounts"
read -rp " Paste access token: " BWS_ACCESS_TOKEN
if [ -z "$BWS_ACCESS_TOKEN" ]; then
echo "ERROR: Access token cannot be empty."
exit 1
fi fi
mkdir -p "$(dirname "$BWS_TOKEN_FILE")"
if bw status 2>/dev/null | grep -q '"status":"locked"'; then echo "$BWS_ACCESS_TOKEN" > "$BWS_TOKEN_FILE"
BW_SESSION=$(bw unlock --raw) chmod 600 "$BWS_TOKEN_FILE"
export BW_SESSION
fi fi
bw sync --session "${BW_SESSION:-}"
GITEA_TOKEN=$(bw get notes hantim-new-app-script --session "${BW_SESSION:-}")
VULTR_API_KEY=$(bw get notes hantim-vultr-api-key --session "${BW_SESSION:-}")
DEPLOY_KEY=$(bw get item hantim-server-deploy --session "${BW_SESSION:-}" | jq -r '.sshKey.privateKey')
mkdir -p "$(dirname "$SECRETS_CACHE")"
cat > "$SECRETS_CACHE" <<CACHE
HANTIM_GITEA_TOKEN='$(echo "$GITEA_TOKEN" | sed "s/'/'\\\\''/g")'
HANTIM_VULTR_API_KEY='$(echo "$VULTR_API_KEY" | sed "s/'/'\\\\''/g")'
HANTIM_DEPLOY_KEY='$(echo "$DEPLOY_KEY" | sed "s/'/'\\\\''/g")'
CACHE
chmod 600 "$SECRETS_CACHE"
fi fi
export BWS_ACCESS_TOKEN
# --- Fetch secrets from Bitwarden Secrets Manager ---
bws_get() {
local name="$1"
local value
value=$(bws secret list 2>/dev/null | jq -r --arg name "$name" '.[] | select(.key == $name) | .value')
if [ -z "$value" ]; then
echo "ERROR: Secret '$name' not found in Bitwarden Secrets Manager." >&2
exit 1
fi
echo "$value"
}
echo "==> Fetching secrets from Bitwarden Secrets Manager..."
GITEA_TOKEN=$(bws_get "hantim-new-app-script")
VULTR_API_KEY=$(bws_get "hantim-vultr-api-key")
DEPLOY_KEY=$(bws_get "hantim-deploy-ssh-private-key")
DEPLOY_KEY_FILE=$(mktemp) DEPLOY_KEY_FILE=$(mktemp)
echo "$DEPLOY_KEY" > "$DEPLOY_KEY_FILE" echo "$DEPLOY_KEY" > "$DEPLOY_KEY_FILE"
@@ -2,8 +2,8 @@
set -euo pipefail set -euo pipefail
if [ -z "${1:-}" ]; then if [ -z "${1:-}" ]; then
echo "Usage: ./scripts/new-service.sh <name>" echo "Usage: ./tools/new-service.sh <name>"
echo " Example: ./scripts/new-service.sh garage" echo " Example: ./tools/new-service.sh garage"
echo "" echo ""
echo "Creates docker/<name>/compose.yml and .gitea/workflows/deploy-<name>.yml" echo "Creates docker/<name>/compose.yml and .gitea/workflows/deploy-<name>.yml"
exit 1 exit 1
+29 -20
View File
@@ -5,8 +5,8 @@ GITEA_URL="https://git.timothykim.net"
GITEA_ORG="hantim" GITEA_ORG="hantim"
if [ -z "${1:-}" ]; then if [ -z "${1:-}" ]; then
echo "Usage: ./scripts/remove-app.sh <domain>" echo "Usage: ./tools/remove-app.sh <domain>"
echo " Example: ./scripts/remove-app.sh hcsuzuki.net" echo " Example: ./tools/remove-app.sh example.com"
echo "" echo ""
echo "Removes: local files (compose, workflow, nginx conf), Gitea repo." echo "Removes: local files (compose, workflow, nginx conf), Gitea repo."
echo "Does NOT remove: DNS records, SSL certs." echo "Does NOT remove: DNS records, SSL certs."
@@ -23,33 +23,42 @@ fi
# --- Check dependencies --- # --- Check dependencies ---
if ! command -v bw &>/dev/null; then if ! command -v bws &>/dev/null; then
echo "Error: Bitwarden CLI (bw) is not installed." echo "Error: Bitwarden Secrets Manager CLI (bws) is not installed."
echo " https://github.com/bitwarden/sdk-sm/releases"
exit 1 exit 1
fi fi
# --- Get secrets (cached in file, or fetch from Bitwarden) --- # --- Get bws access token ---
SECRETS_CACHE="${XDG_CACHE_HOME:-$HOME/.cache}/hantim-secrets" BWS_TOKEN_FILE="${XDG_CONFIG_HOME:-$HOME/.config}/hantim/bws-token"
if [ -f "$SECRETS_CACHE" ]; then if [ -z "${BWS_ACCESS_TOKEN:-}" ]; then
echo "Using cached secrets." if [ -f "$BWS_TOKEN_FILE" ]; then
# shellcheck source=/dev/null BWS_ACCESS_TOKEN=$(cat "$BWS_TOKEN_FILE")
source "$SECRETS_CACHE"
GITEA_TOKEN="$HANTIM_GITEA_TOKEN"
else else
if bw status 2>/dev/null | grep -q '"status":"unauthenticated"'; then echo "==> Bitwarden Secrets Manager access token required."
echo "Log in to Bitwarden:" echo " Generate one for your machine account at:"
bw login echo " https://vault.bitwarden.com/#/sm/machine-accounts"
read -rp " Paste access token: " BWS_ACCESS_TOKEN
if [ -z "$BWS_ACCESS_TOKEN" ]; then
echo "ERROR: Access token cannot be empty."
exit 1
fi fi
mkdir -p "$(dirname "$BWS_TOKEN_FILE")"
if bw status 2>/dev/null | grep -q '"status":"locked"'; then echo "$BWS_ACCESS_TOKEN" > "$BWS_TOKEN_FILE"
BW_SESSION=$(bw unlock --raw) chmod 600 "$BWS_TOKEN_FILE"
export BW_SESSION
fi fi
fi
export BWS_ACCESS_TOKEN
bw sync --session "${BW_SESSION:-}" # --- Fetch secrets from Bitwarden Secrets Manager ---
GITEA_TOKEN=$(bw get notes hantim-new-app-script --session "${BW_SESSION:-}")
echo "==> Fetching secrets from Bitwarden Secrets Manager..."
GITEA_TOKEN=$(bws secret list 2>/dev/null | jq -r '.[] | select(.key == "hantim-new-app-script") | .value')
if [ -z "$GITEA_TOKEN" ]; then
echo "ERROR: Secret 'hantim-new-app-script' not found in Bitwarden Secrets Manager."
exit 1
fi fi
# --- Delete Gitea repo --- # --- Delete Gitea repo ---