chore: cut over repo tooling to git.edut.dev
Some checks are pending
check / secretapi (push) Waiting to run
Some checks are pending
check / secretapi (push) Waiting to run
This commit is contained in:
parent
2e03728583
commit
113da4df4e
@ -102,7 +102,7 @@ README.md
|
|||||||
|
|
||||||
## Scripts
|
## Scripts
|
||||||
|
|
||||||
1. `scripts/publish_split_repos.sh` - creates/pushes `launcher`, `governance`, `contracts` repos using either a provided Gitea PAT or git credential-helper auth for `git.workvsg.com`.
|
1. `scripts/publish_split_repos.sh` - creates/pushes `launcher`, `governance`, `contracts` repos using either a provided Gitea PAT or git credential-helper auth for `git.edut.dev`.
|
||||||
|
|
||||||
## Internationalization
|
## Internationalization
|
||||||
|
|
||||||
|
|||||||
328
docs/deployment/edut-dev-infra-cutover-checklist.md
Normal file
328
docs/deployment/edut-dev-infra-cutover-checklist.md
Normal file
@ -0,0 +1,328 @@
|
|||||||
|
# EDUT Dev Infra Cutover Checklist
|
||||||
|
|
||||||
|
This checklist migrates EDUT infrastructure from `git.workvsg.com` to `git.edut.dev` with deterministic validation gates.
|
||||||
|
|
||||||
|
Server target:
|
||||||
|
|
||||||
|
- Host: `edut-prod`
|
||||||
|
- IP: `5.78.148.229`
|
||||||
|
- OS: Ubuntu 24.04 LTS
|
||||||
|
|
||||||
|
## Preconditions
|
||||||
|
|
||||||
|
1. DNS records exist and point to the new server:
|
||||||
|
- `edut.dev`
|
||||||
|
- `www.edut.dev`
|
||||||
|
- `api.edut.dev`
|
||||||
|
- `git.edut.dev`
|
||||||
|
2. SSH key access as root is available.
|
||||||
|
3. Cloudflare proxy mode and SSL mode are configured to allow origin TLS.
|
||||||
|
4. Local private keys for `joshua`, `claude-code`, `codex` are available for validation.
|
||||||
|
|
||||||
|
## Phase 1 - Server Setup
|
||||||
|
|
||||||
|
### 1. Baseline inventory
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh -i ~/.ssh/edut_codex root@5.78.148.229 \
|
||||||
|
"hostnamectl --static; cat /etc/os-release | head -n 6; ufw status verbose; docker --version || true"
|
||||||
|
```
|
||||||
|
|
||||||
|
Gate:
|
||||||
|
|
||||||
|
1. Host is reachable.
|
||||||
|
2. OS is Ubuntu 24.04.
|
||||||
|
3. State is understood before changes.
|
||||||
|
|
||||||
|
### 2. Create Linux users and key-only SSH
|
||||||
|
|
||||||
|
Create users:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh -i ~/.ssh/edut_codex root@5.78.148.229 \
|
||||||
|
"useradd -m -s /bin/bash joshua || true; useradd -m -s /bin/bash claude-code || true; useradd -m -s /bin/bash codex || true"
|
||||||
|
```
|
||||||
|
|
||||||
|
Install authorized keys:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh -i ~/.ssh/edut_codex root@5.78.148.229 "install -d -m 700 /home/joshua/.ssh /home/claude-code/.ssh /home/codex/.ssh"
|
||||||
|
ssh -i ~/.ssh/edut_codex root@5.78.148.229 "printf '%s\n' '<JOSHUA_PUBKEY>' > /home/joshua/.ssh/authorized_keys"
|
||||||
|
ssh -i ~/.ssh/edut_codex root@5.78.148.229 "printf '%s\n' '<CLAUDE_CODE_PUBKEY>' > /home/claude-code/.ssh/authorized_keys"
|
||||||
|
ssh -i ~/.ssh/edut_codex root@5.78.148.229 "printf '%s\n' '<CODEX_PUBKEY>' > /home/codex/.ssh/authorized_keys"
|
||||||
|
ssh -i ~/.ssh/edut_codex root@5.78.148.229 \
|
||||||
|
"chmod 600 /home/*/.ssh/authorized_keys; chown -R joshua:joshua /home/joshua/.ssh; chown -R claude-code:claude-code /home/claude-code/.ssh; chown -R codex:codex /home/codex/.ssh"
|
||||||
|
```
|
||||||
|
|
||||||
|
Gate:
|
||||||
|
|
||||||
|
1. All three users exist.
|
||||||
|
2. Authorized keys are present with correct perms.
|
||||||
|
|
||||||
|
### 3. Sudo policy
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh -i ~/.ssh/edut_codex root@5.78.148.229 \
|
||||||
|
"usermod -aG sudo joshua; usermod -aG sudo claude-code; usermod -aG sudo codex"
|
||||||
|
ssh -i ~/.ssh/edut_codex root@5.78.148.229 \
|
||||||
|
"printf '%s\n' 'claude-code ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/90-claude-code"
|
||||||
|
ssh -i ~/.ssh/edut_codex root@5.78.148.229 \
|
||||||
|
"printf '%s\n' 'codex ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/90-codex"
|
||||||
|
ssh -i ~/.ssh/edut_codex root@5.78.148.229 "chmod 440 /etc/sudoers.d/90-claude-code /etc/sudoers.d/90-codex"
|
||||||
|
ssh -i ~/.ssh/edut_codex root@5.78.148.229 "visudo -cf /etc/sudoers"
|
||||||
|
```
|
||||||
|
|
||||||
|
Gate:
|
||||||
|
|
||||||
|
1. `visudo -cf` passes.
|
||||||
|
2. `claude-code` and `codex` have passwordless sudo.
|
||||||
|
3. `joshua` remains standard sudo (password required).
|
||||||
|
|
||||||
|
### 4. SSH hardening + disable root login
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh -i ~/.ssh/edut_codex root@5.78.148.229 "cat >/etc/ssh/sshd_config.d/99-edut-hardening.conf <<'EOF'
|
||||||
|
PermitRootLogin no
|
||||||
|
PasswordAuthentication no
|
||||||
|
KbdInteractiveAuthentication no
|
||||||
|
ChallengeResponseAuthentication no
|
||||||
|
PubkeyAuthentication yes
|
||||||
|
PermitEmptyPasswords no
|
||||||
|
EOF"
|
||||||
|
ssh -i ~/.ssh/edut_codex root@5.78.148.229 "sshd -t && systemctl restart ssh"
|
||||||
|
```
|
||||||
|
|
||||||
|
Validation:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo -n true && echo codex-sudo-ok"
|
||||||
|
ssh -i ~/.ssh/edut_joshua joshua@5.78.148.229 "id"
|
||||||
|
ssh -i ~/.ssh/edut_codex -o BatchMode=yes root@5.78.148.229 "echo should-fail"
|
||||||
|
```
|
||||||
|
|
||||||
|
Gate:
|
||||||
|
|
||||||
|
1. `codex` login works and sudo works.
|
||||||
|
2. `joshua` key login works.
|
||||||
|
3. root login is denied.
|
||||||
|
|
||||||
|
### 5. Firewall, fail2ban, unattended upgrades
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo apt-get update"
|
||||||
|
ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo apt-get install -y ufw fail2ban unattended-upgrades apt-listchanges"
|
||||||
|
ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo ufw default deny incoming && sudo ufw default allow outgoing"
|
||||||
|
ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo ufw allow OpenSSH && sudo ufw allow 80/tcp && sudo ufw allow 443/tcp"
|
||||||
|
ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo ufw --force enable"
|
||||||
|
ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo systemctl enable --now fail2ban"
|
||||||
|
ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo dpkg-reconfigure -f noninteractive unattended-upgrades"
|
||||||
|
```
|
||||||
|
|
||||||
|
Gate:
|
||||||
|
|
||||||
|
1. `ufw status verbose` shows only `22`, `80`, `443`.
|
||||||
|
2. `fail2ban` active.
|
||||||
|
3. unattended upgrades enabled.
|
||||||
|
|
||||||
|
### 6. Install Docker + Compose plugin
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo apt-get install -y ca-certificates curl gnupg"
|
||||||
|
ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo install -m 0755 -d /etc/apt/keyrings"
|
||||||
|
ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg"
|
||||||
|
ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo chmod a+r /etc/apt/keyrings/docker.gpg"
|
||||||
|
ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "echo \"deb [arch=\$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/ubuntu \$(. /etc/os-release && echo \$VERSION_CODENAME) stable\" | sudo tee /etc/apt/sources.list.d/docker.list >/dev/null"
|
||||||
|
ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo apt-get update && sudo apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin"
|
||||||
|
ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo systemctl enable --now docker"
|
||||||
|
```
|
||||||
|
|
||||||
|
Gate:
|
||||||
|
|
||||||
|
1. `docker --version` returns success.
|
||||||
|
2. `docker compose version` returns success.
|
||||||
|
|
||||||
|
### 7. Deploy Gitea + TLS on git.edut.dev
|
||||||
|
|
||||||
|
Provision directories:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo install -d -m 755 /opt/edut/gitea/{gitea,postgres,caddy}"
|
||||||
|
```
|
||||||
|
|
||||||
|
Deploy stack (Gitea + Postgres + Caddy reverse proxy with auto TLS):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo tee /opt/edut/gitea/docker-compose.yml >/dev/null <<'EOF'
|
||||||
|
services:
|
||||||
|
db:
|
||||||
|
image: postgres:16
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
POSTGRES_DB: gitea
|
||||||
|
POSTGRES_USER: gitea
|
||||||
|
POSTGRES_PASSWORD: CHANGE_ME_DB_PASSWORD
|
||||||
|
volumes:
|
||||||
|
- /opt/edut/gitea/postgres:/var/lib/postgresql/data
|
||||||
|
networks: [gitea_net]
|
||||||
|
|
||||||
|
gitea:
|
||||||
|
image: gitea/gitea:1.22
|
||||||
|
restart: unless-stopped
|
||||||
|
environment:
|
||||||
|
USER_UID: "1000"
|
||||||
|
USER_GID: "1000"
|
||||||
|
GITEA__database__DB_TYPE: postgres
|
||||||
|
GITEA__database__HOST: db:5432
|
||||||
|
GITEA__database__NAME: gitea
|
||||||
|
GITEA__database__USER: gitea
|
||||||
|
GITEA__database__PASSWD: CHANGE_ME_DB_PASSWORD
|
||||||
|
GITEA__server__ROOT_URL: https://git.edut.dev/
|
||||||
|
GITEA__server__DOMAIN: git.edut.dev
|
||||||
|
GITEA__server__HTTP_ADDR: 0.0.0.0
|
||||||
|
GITEA__server__HTTP_PORT: 3000
|
||||||
|
GITEA__security__INSTALL_LOCK: "true"
|
||||||
|
GITEA__service__DISABLE_REGISTRATION: "true"
|
||||||
|
volumes:
|
||||||
|
- /opt/edut/gitea/gitea:/data
|
||||||
|
depends_on: [db]
|
||||||
|
networks: [gitea_net]
|
||||||
|
|
||||||
|
caddy:
|
||||||
|
image: caddy:2
|
||||||
|
restart: unless-stopped
|
||||||
|
ports:
|
||||||
|
- "80:80"
|
||||||
|
- "443:443"
|
||||||
|
volumes:
|
||||||
|
- /opt/edut/gitea/caddy/Caddyfile:/etc/caddy/Caddyfile:ro
|
||||||
|
- /opt/edut/gitea/caddy/data:/data
|
||||||
|
- /opt/edut/gitea/caddy/config:/config
|
||||||
|
depends_on: [gitea]
|
||||||
|
networks: [gitea_net]
|
||||||
|
|
||||||
|
networks:
|
||||||
|
gitea_net:
|
||||||
|
EOF"
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo tee /opt/edut/gitea/caddy/Caddyfile >/dev/null <<'EOF'
|
||||||
|
git.edut.dev {
|
||||||
|
reverse_proxy gitea:3000
|
||||||
|
}
|
||||||
|
EOF"
|
||||||
|
```
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "cd /opt/edut/gitea && sudo docker compose up -d"
|
||||||
|
```
|
||||||
|
|
||||||
|
Gate:
|
||||||
|
|
||||||
|
1. `docker compose ps` shows healthy containers.
|
||||||
|
2. `curl -I https://git.edut.dev` returns `200/302`.
|
||||||
|
3. Browser shows valid TLS for `git.edut.dev`.
|
||||||
|
|
||||||
|
### 8. Bootstrap org and repos
|
||||||
|
|
||||||
|
Create admin and org:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo docker exec -u git gitea-gitea-1 gitea admin user create --username joshua --password 'CHANGE_ME_ADMIN_PASSWORD' --email j@edut.dev --admin --must-change-password=false"
|
||||||
|
ssh -i ~/.ssh/edut_codex codex@5.78.148.229 "sudo docker exec -u git gitea-gitea-1 gitea admin org create --name edut --username joshua || true"
|
||||||
|
```
|
||||||
|
|
||||||
|
Create repos:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
for r in web launcher contracts governance kernel platform-docs; do
|
||||||
|
curl -u "joshua:CHANGE_ME_ADMIN_PASSWORD" \
|
||||||
|
-H "Content-Type: application/json" \
|
||||||
|
-X POST "https://git.edut.dev/api/v1/orgs/edut/repos" \
|
||||||
|
-d "{\"name\":\"$r\",\"private\":true}"
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
Gate:
|
||||||
|
|
||||||
|
1. `https://git.edut.dev/edut` lists all repos.
|
||||||
|
|
||||||
|
## Phase 2 - Migration
|
||||||
|
|
||||||
|
### 9. Mirror push histories
|
||||||
|
|
||||||
|
```bash
|
||||||
|
for r in web launcher contracts governance kernel; do
|
||||||
|
git -C "/Users/vsg/Documents/VSG Codex/$r" push --mirror "https://git.edut.dev/edut/$r.git"
|
||||||
|
done
|
||||||
|
git -C "/Users/vsg/Documents/VSG Codex/platform-docs" push --mirror "https://git.edut.dev/edut/platform-docs.git"
|
||||||
|
```
|
||||||
|
|
||||||
|
Gate:
|
||||||
|
|
||||||
|
1. Branches/tags match source repos.
|
||||||
|
|
||||||
|
### 10. Update local remotes
|
||||||
|
|
||||||
|
```bash
|
||||||
|
for r in web launcher contracts governance kernel platform-docs; do
|
||||||
|
git -C "/Users/vsg/Documents/VSG Codex/$r" remote set-url origin "https://git.edut.dev/edut/$r.git"
|
||||||
|
done
|
||||||
|
```
|
||||||
|
|
||||||
|
Gate:
|
||||||
|
|
||||||
|
1. `git remote -v` points to `git.edut.dev` for all repos.
|
||||||
|
|
||||||
|
### 11. Update hardcoded host references
|
||||||
|
|
||||||
|
Search:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
rg -n "git\\.workvsg\\.com" /Users/vsg/Documents/VSG\ Codex
|
||||||
|
```
|
||||||
|
|
||||||
|
Replace references in scripts/docs/default flags/manifests.
|
||||||
|
|
||||||
|
Gate:
|
||||||
|
|
||||||
|
1. No remaining production references to `git.workvsg.com`.
|
||||||
|
|
||||||
|
### 12. Freeze old host read-only
|
||||||
|
|
||||||
|
Set old Gitea org/repo archival or remove write access.
|
||||||
|
|
||||||
|
Gate:
|
||||||
|
|
||||||
|
1. No new writes possible on old host.
|
||||||
|
|
||||||
|
## Phase 3 - Verification
|
||||||
|
|
||||||
|
### 13. Smoke checks
|
||||||
|
|
||||||
|
1. Push test branch to each repo.
|
||||||
|
2. Open PR in each repo.
|
||||||
|
3. Verify Gitea workflow runs.
|
||||||
|
4. Create and push lightweight tag.
|
||||||
|
5. Validate clone/fetch from clean directory.
|
||||||
|
|
||||||
|
Gate:
|
||||||
|
|
||||||
|
1. All checks pass with new host only.
|
||||||
|
|
||||||
|
### 14. Deploy path verification
|
||||||
|
|
||||||
|
1. Ensure `api.edut.dev` deployment docs/scripts reference new Git host where needed.
|
||||||
|
2. Validate deployment artifact retrieval paths.
|
||||||
|
|
||||||
|
Gate:
|
||||||
|
|
||||||
|
1. No deployment path depends on old host.
|
||||||
|
|
||||||
|
## Phase 4 - Semantic Sweep
|
||||||
|
|
||||||
|
Only after all previous gates pass:
|
||||||
|
|
||||||
|
1. Execute naming/commercial migration sweep.
|
||||||
|
2. Enforce drift checks in CI.
|
||||||
|
3. Cut release and decommission old host after soak.
|
||||||
@ -18,7 +18,7 @@ Example (replace `<TOKEN>`):
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
curl -fsSL -H "Authorization: token <TOKEN>" -H "Content-Type: application/json" \
|
curl -fsSL -H "Authorization: token <TOKEN>" -H "Content-Type: application/json" \
|
||||||
-X POST "https://git.workvsg.com/api/v1/orgs/edut/repos" \
|
-X POST "https://git.edut.dev/api/v1/orgs/edut/repos" \
|
||||||
-d '{"name":"launcher","private":true,"description":"EDUT free launcher shell and wallet onboarding app"}'
|
-d '{"name":"launcher","private":true,"description":"EDUT free launcher shell and wallet onboarding app"}'
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -31,7 +31,7 @@ cd "/Users/vsg/Documents/VSG Codex/web"
|
|||||||
./scripts/publish_split_repos.sh <gitea_pat>
|
./scripts/publish_split_repos.sh <gitea_pat>
|
||||||
```
|
```
|
||||||
|
|
||||||
Or run it without arguments to use git credential helper auth for `git.workvsg.com`:
|
Or run it without arguments to use git credential helper auth for `git.edut.dev`:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd "/Users/vsg/Documents/VSG Codex/web"
|
cd "/Users/vsg/Documents/VSG Codex/web"
|
||||||
@ -42,15 +42,15 @@ cd "/Users/vsg/Documents/VSG Codex/web"
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd "/Users/vsg/Documents/VSG Codex/launcher"
|
cd "/Users/vsg/Documents/VSG Codex/launcher"
|
||||||
git remote add origin https://git.workvsg.com/edut/launcher.git
|
git remote add origin https://git.edut.dev/edut/launcher.git
|
||||||
git push -u origin main
|
git push -u origin main
|
||||||
|
|
||||||
cd "/Users/vsg/Documents/VSG Codex/governance"
|
cd "/Users/vsg/Documents/VSG Codex/governance"
|
||||||
git remote add origin https://git.workvsg.com/edut/governance.git
|
git remote add origin https://git.edut.dev/edut/governance.git
|
||||||
git push -u origin main
|
git push -u origin main
|
||||||
|
|
||||||
cd "/Users/vsg/Documents/VSG Codex/contracts"
|
cd "/Users/vsg/Documents/VSG Codex/contracts"
|
||||||
git remote add origin https://git.workvsg.com/edut/contracts.git
|
git remote add origin https://git.edut.dev/edut/contracts.git
|
||||||
git push -u origin main
|
git push -u origin main
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -61,6 +61,6 @@ git push -u origin main
|
|||||||
3. Add cross-repo links in each README.
|
3. Add cross-repo links in each README.
|
||||||
4. Lock branch protections and required-review settings.
|
4. Lock branch protections and required-review settings.
|
||||||
|
|
||||||
## Current Blocker
|
## Current Status
|
||||||
|
|
||||||
Gitea API creation attempts with current credential material return `401 invalid username/password/token`.
|
Remote repos exist on `git.edut.dev` and this runbook remains the deterministic recovery path.
|
||||||
|
|||||||
@ -8,7 +8,7 @@ usage() {
|
|||||||
|
|
||||||
TOKEN="${1:-}"
|
TOKEN="${1:-}"
|
||||||
ORG="${2:-edut}"
|
ORG="${2:-edut}"
|
||||||
HOST="${3:-git.workvsg.com}"
|
HOST="${3:-git.edut.dev}"
|
||||||
ROOT="/Users/vsg/Documents/VSG Codex"
|
ROOT="/Users/vsg/Documents/VSG Codex"
|
||||||
AUTH_MODE=""
|
AUTH_MODE=""
|
||||||
AUTH_USERNAME=""
|
AUTH_USERNAME=""
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user