move dev scripts to tools/, migrate to bws, use example.com in docs
Provision server / provision (push) Successful in 7s
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:
+8
-7
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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"
|
else
|
||||||
GITEA_TOKEN="$HANTIM_GITEA_TOKEN"
|
echo "==> Bitwarden Secrets Manager access token required."
|
||||||
VULTR_API_KEY="$HANTIM_VULTR_API_KEY"
|
echo " Generate one for your machine account at:"
|
||||||
DEPLOY_KEY="$HANTIM_DEPLOY_KEY"
|
echo " https://vault.bitwarden.com/#/sm/machine-accounts"
|
||||||
else
|
read -rp " Paste access token: " BWS_ACCESS_TOKEN
|
||||||
if bw status 2>/dev/null | grep -q '"status":"unauthenticated"'; then
|
if [ -z "$BWS_ACCESS_TOKEN" ]; then
|
||||||
echo "Log in to Bitwarden:"
|
echo "ERROR: Access token cannot be empty."
|
||||||
bw login
|
exit 1
|
||||||
|
fi
|
||||||
|
mkdir -p "$(dirname "$BWS_TOKEN_FILE")"
|
||||||
|
echo "$BWS_ACCESS_TOKEN" > "$BWS_TOKEN_FILE"
|
||||||
|
chmod 600 "$BWS_TOKEN_FILE"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if bw status 2>/dev/null | grep -q '"status":"locked"'; then
|
|
||||||
BW_SESSION=$(bw unlock --raw)
|
|
||||||
export BW_SESSION
|
|
||||||
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
|
||||||
@@ -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"
|
else
|
||||||
GITEA_TOKEN="$HANTIM_GITEA_TOKEN"
|
echo "==> Bitwarden Secrets Manager access token required."
|
||||||
else
|
echo " Generate one for your machine account at:"
|
||||||
if bw status 2>/dev/null | grep -q '"status":"unauthenticated"'; then
|
echo " https://vault.bitwarden.com/#/sm/machine-accounts"
|
||||||
echo "Log in to Bitwarden:"
|
read -rp " Paste access token: " BWS_ACCESS_TOKEN
|
||||||
bw login
|
if [ -z "$BWS_ACCESS_TOKEN" ]; then
|
||||||
|
echo "ERROR: Access token cannot be empty."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
mkdir -p "$(dirname "$BWS_TOKEN_FILE")"
|
||||||
|
echo "$BWS_ACCESS_TOKEN" > "$BWS_TOKEN_FILE"
|
||||||
|
chmod 600 "$BWS_TOKEN_FILE"
|
||||||
fi
|
fi
|
||||||
|
fi
|
||||||
|
export BWS_ACCESS_TOKEN
|
||||||
|
|
||||||
if bw status 2>/dev/null | grep -q '"status":"locked"'; then
|
# --- Fetch secrets from Bitwarden Secrets Manager ---
|
||||||
BW_SESSION=$(bw unlock --raw)
|
|
||||||
export BW_SESSION
|
|
||||||
fi
|
|
||||||
|
|
||||||
bw sync --session "${BW_SESSION:-}"
|
echo "==> Fetching secrets from Bitwarden Secrets Manager..."
|
||||||
GITEA_TOKEN=$(bw get notes hantim-new-app-script --session "${BW_SESSION:-}")
|
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 ---
|
||||||
Reference in New Issue
Block a user