feat(app_setup): Improves deployment reliability for app projects and adds support for mirrormatch deployment with Prisma/Next.js requirements. #5
@ -25,6 +25,7 @@ Under `app_projects`, define projects like:
|
|||||||
- `projectA.envs.dev|qa|prod.ip/gateway/branch`
|
- `projectA.envs.dev|qa|prod.ip/gateway/branch`
|
||||||
- `projectA.guest_defaults` (cores/memory/rootfs sizing)
|
- `projectA.guest_defaults` (cores/memory/rootfs sizing)
|
||||||
- `projectA.deploy.*` (install/build/migrate/start commands)
|
- `projectA.deploy.*` (install/build/migrate/start commands)
|
||||||
|
- Optional: per-env `backend_seed_cmd` (e.g., `npm run db:seed` for dev/qa)
|
||||||
|
|
||||||
Adding **projectB** is just adding another top-level `app_projects.projectB` entry.
|
Adding **projectB** is just adding another top-level `app_projects.projectB` entry.
|
||||||
|
|
||||||
@ -77,6 +78,34 @@ Only OS/app configuration:
|
|||||||
ansible-playbook -i inventories/production playbooks/app/configure_app.yml -e app_project=projectA
|
ansible-playbook -i inventories/production playbooks/app/configure_app.yml -e app_project=projectA
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Limiting to a single env (dev/qa/prod)
|
||||||
|
- Pass `-e app_env=dev` (or qa/prod) to provision/configure only that environment for the selected project.
|
||||||
|
- Example (provision dev only):
|
||||||
|
```bash
|
||||||
|
ansible-playbook -i inventories/production playbooks/app/provision_vms.yml -e app_project=mirrormatch -e app_env=dev
|
||||||
|
```
|
||||||
|
Configure/dev only:
|
||||||
|
```bash
|
||||||
|
ansible-playbook -i inventories/production playbooks/app/configure_app.yml -e app_project=mirrormatch -e app_env=dev
|
||||||
|
```
|
||||||
|
|
||||||
|
### Example: mirrormatch project
|
||||||
|
|
||||||
|
- Config lives in `inventories/production/group_vars/all/main.yml` under `app_projects.mirrormatch` (dev/qa/prod guests, repo URL, migrate/start commands, env vars).
|
||||||
|
- Secrets live in the vault: `vault_mirrormatch_database_url_*` (and optional `vault_mirrormatch_shadow_database_url_*`), plus an optional repo deploy key `vault_mirrormatch_git_ssh_key`.
|
||||||
|
- Run end-to-end:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make app PROJECT=mirrormatch
|
||||||
|
```
|
||||||
|
|
||||||
|
Run provisioning only / configure only:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make app-provision PROJECT=mirrormatch
|
||||||
|
make app-configure PROJECT=mirrormatch
|
||||||
|
```
|
||||||
|
|
||||||
## Optional: SSH aliases on your workstation
|
## Optional: SSH aliases on your workstation
|
||||||
|
|
||||||
To write `~/.ssh/config` entries (disabled by default):
|
To write `~/.ssh/config` entries (disabled by default):
|
||||||
@ -87,4 +116,37 @@ ansible-playbook -i inventories/production playbooks/app/ssh_client_config.yml -
|
|||||||
|
|
||||||
This creates aliases like `projectA-dev`, `projectA-qa`, `projectA-prod`.
|
This creates aliases like `projectA-dev`, `projectA-qa`, `projectA-prod`.
|
||||||
|
|
||||||
|
## Project-Specific Configuration
|
||||||
|
|
||||||
|
### Environment-Specific Commands
|
||||||
|
|
||||||
|
Projects can override deploy commands per environment:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
envs:
|
||||||
|
dev:
|
||||||
|
backend_migrate_cmd: "npm run db:push" # Override default migrate command
|
||||||
|
backend_seed_cmd: "npm run db:seed" # Optional: seed database
|
||||||
|
```
|
||||||
|
|
||||||
|
**Precedence order:**
|
||||||
|
1. `env_def.backend_migrate_cmd` (per-environment override)
|
||||||
|
2. `project_def.deploy.backend_migrate_cmd` (project default)
|
||||||
|
3. Global default (`npm run migrate`)
|
||||||
|
|
||||||
|
### Environment File Naming
|
||||||
|
|
||||||
|
The systemd service uses `EnvironmentFile=/srv/app/.env.<env>` (e.g., `.env.dev`). Systemd loads these variables into the service environment.
|
||||||
|
|
||||||
|
**Note:** Next.js has its own env file loading that looks for `.env`, `.env.local`, `.env.production`, etc. If your Next.js app isn't reading env vars, consider:
|
||||||
|
- Using `.env.local` for dev (Next.js loads this automatically)
|
||||||
|
- Or ensure your app reads from `process.env` (systemd-injected vars)
|
||||||
|
|
||||||
|
### Project Types
|
||||||
|
|
||||||
|
- **Standard Node.js apps** (mirrormatch, punimTagBE/FE): Use `app_setup` role
|
||||||
|
- **Python apps** (pote): Use `pote` role (completely separate deployment path)
|
||||||
|
|
||||||
|
Changes to `app_setup` role affect all Node.js projects but are backward-compatible.
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -296,3 +296,92 @@ app_projects:
|
|||||||
env_vars:
|
env_vars:
|
||||||
APP_ENV: "prod"
|
APP_ENV: "prod"
|
||||||
SECRET_PLACEHOLDER: "change-me"
|
SECRET_PLACEHOLDER: "change-me"
|
||||||
|
|
||||||
|
mirrormatch:
|
||||||
|
description: "Mirrormatch Prisma/Node backend (dev/qa/prod)."
|
||||||
|
repo_url: "gitea@10.0.30.169:ilia/mirror_match.git"
|
||||||
|
repo_dest: "/srv/app"
|
||||||
|
backend_dir: "/srv/app"
|
||||||
|
components:
|
||||||
|
backend: true
|
||||||
|
frontend: false
|
||||||
|
# Next.js app listens on 3000 by default
|
||||||
|
backend_port: 3000
|
||||||
|
guest_defaults:
|
||||||
|
guest_type: "{{ proxmox_guest_type }}"
|
||||||
|
cores: 2
|
||||||
|
memory_mb: 2048
|
||||||
|
swap_mb: 512
|
||||||
|
rootfs_size_gb: 16
|
||||||
|
deploy:
|
||||||
|
backend_install_cmd: "npm ci"
|
||||||
|
backend_build_cmd: "npm run build"
|
||||||
|
backend_migrate_cmd: "npm run db:migrate"
|
||||||
|
backend_start_cmd: "npm run start"
|
||||||
|
envs:
|
||||||
|
dev:
|
||||||
|
name: "mirrormatch-dev"
|
||||||
|
vmid: 9401
|
||||||
|
ip: "10.0.10.141/24"
|
||||||
|
gateway: "10.0.10.1"
|
||||||
|
branch: "dev"
|
||||||
|
backend_migrate_cmd: "npm run db:push"
|
||||||
|
backend_seed_cmd: "npm run db:seed"
|
||||||
|
env_vars:
|
||||||
|
APP_ENV: "dev"
|
||||||
|
NODE_ENV: "production"
|
||||||
|
PORT: "3000"
|
||||||
|
DATABASE_URL: "{{ vault_mirrormatch_database_url_dev }}"
|
||||||
|
NEXTAUTH_URL: "https://mirrormatchdev.levkin.ca"
|
||||||
|
NEXTAUTH_SECRET: "{{ vault_mirrormatch_nextauth_secret_dev }}"
|
||||||
|
AUTH_TRUST_HOST: "true"
|
||||||
|
# Dev/QA often use Ethereal/console email; leave SMTP unset or fill if needed
|
||||||
|
# SMTP_HOST: ""
|
||||||
|
# SMTP_PORT: ""
|
||||||
|
# SMTP_USER: ""
|
||||||
|
# SMTP_PASSWORD: ""
|
||||||
|
# SMTP_FROM: ""
|
||||||
|
# Optional: provide if your Prisma workflow needs a shadow DB
|
||||||
|
# SHADOW_DATABASE_URL: "{{ vault_mirrormatch_shadow_database_url_dev }}"
|
||||||
|
qa:
|
||||||
|
name: "mirrormatch-qa"
|
||||||
|
vmid: 9402
|
||||||
|
ip: "10.0.10.144/24"
|
||||||
|
gateway: "10.0.10.1"
|
||||||
|
branch: "qa"
|
||||||
|
backend_migrate_cmd: "npm run db:push"
|
||||||
|
backend_seed_cmd: "npm run db:seed"
|
||||||
|
env_vars:
|
||||||
|
APP_ENV: "qa"
|
||||||
|
NODE_ENV: "production"
|
||||||
|
PORT: "3000"
|
||||||
|
DATABASE_URL: "{{ vault_mirrormatch_database_url_qa }}"
|
||||||
|
NEXTAUTH_URL: "https://mirrormatchqa.levkin.ca"
|
||||||
|
NEXTAUTH_SECRET: "{{ vault_mirrormatch_nextauth_secret_qa }}"
|
||||||
|
AUTH_TRUST_HOST: "true"
|
||||||
|
# SMTP_HOST: ""
|
||||||
|
# SMTP_PORT: ""
|
||||||
|
# SMTP_USER: ""
|
||||||
|
# SMTP_PASSWORD: ""
|
||||||
|
# SMTP_FROM: ""
|
||||||
|
# SHADOW_DATABASE_URL: "{{ vault_mirrormatch_shadow_database_url_qa }}"
|
||||||
|
prod:
|
||||||
|
name: "mirrormatch-prod"
|
||||||
|
vmid: 9403
|
||||||
|
ip: "10.0.10.145/24"
|
||||||
|
gateway: "10.0.10.1"
|
||||||
|
branch: "main"
|
||||||
|
backend_migrate_cmd: "npx prisma migrate deploy"
|
||||||
|
env_vars:
|
||||||
|
APP_ENV: "prod"
|
||||||
|
NODE_ENV: "production"
|
||||||
|
PORT: "3000"
|
||||||
|
DATABASE_URL: "{{ vault_mirrormatch_database_url_prod }}"
|
||||||
|
NEXTAUTH_URL: "https://mirrormatch.example.com"
|
||||||
|
NEXTAUTH_SECRET: "{{ vault_mirrormatch_nextauth_secret_prod }}"
|
||||||
|
AUTH_TRUST_HOST: "true"
|
||||||
|
SMTP_HOST: "{{ vault_mirrormatch_smtp_host | default('') }}"
|
||||||
|
SMTP_PORT: "{{ vault_mirrormatch_smtp_port | default('587') }}"
|
||||||
|
SMTP_USER: "{{ vault_mirrormatch_smtp_user | default('') }}"
|
||||||
|
SMTP_PASSWORD: "{{ vault_mirrormatch_smtp_password | default('') }}"
|
||||||
|
SMTP_FROM: "{{ vault_mirrormatch_smtp_from | default('') }}"
|
||||||
|
|||||||
@ -39,4 +39,35 @@ vault_pote_db_password_prod: "CHANGE_ME"
|
|||||||
# SMTP password for reports
|
# SMTP password for reports
|
||||||
vault_pote_smtp_password: "CHANGE_ME"
|
vault_pote_smtp_password: "CHANGE_ME"
|
||||||
|
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Mirrormatch (Prisma/Node backend) secrets
|
||||||
|
# -----------------------------------------------------------------------------
|
||||||
|
# Optional deploy key for private repo access
|
||||||
|
vault_mirrormatch_git_ssh_key: |
|
||||||
|
-----BEGIN OPENSSH PRIVATE KEY-----
|
||||||
|
CHANGE_ME
|
||||||
|
-----END OPENSSH PRIVATE KEY-----
|
||||||
|
|
||||||
|
# Per-environment database URLs (use external Postgres VM/cluster)
|
||||||
|
vault_mirrormatch_database_url_dev: "postgresql://mm_dev_user:CHANGE_ME@10.0.10.181:5432/mirrormatch_dev"
|
||||||
|
vault_mirrormatch_database_url_qa: "postgresql://mm_qa_user:CHANGE_ME@10.0.10.181:5432/mirrormatch_qa"
|
||||||
|
vault_mirrormatch_database_url_prod: "postgresql://mm_prod_user:CHANGE_ME@10.0.10.181:5432/mirrormatch_prod"
|
||||||
|
|
||||||
|
# Optional shadow DB URLs if your Prisma workflow needs them
|
||||||
|
vault_mirrormatch_shadow_database_url_dev: "postgresql://mm_dev_shadow:CHANGE_ME@10.0.10.181:5432/mirrormatch_dev_shadow"
|
||||||
|
vault_mirrormatch_shadow_database_url_qa: "postgresql://mm_qa_shadow:CHANGE_ME@10.0.10.181:5432/mirrormatch_qa_shadow"
|
||||||
|
vault_mirrormatch_shadow_database_url_prod: "postgresql://mm_prod_shadow:CHANGE_ME@10.0.10.181:5432/mirrormatch_prod_shadow"
|
||||||
|
|
||||||
|
# NEXTAUTH secrets per env
|
||||||
|
vault_mirrormatch_nextauth_secret_dev: "CHANGE_ME"
|
||||||
|
vault_mirrormatch_nextauth_secret_qa: "CHANGE_ME"
|
||||||
|
vault_mirrormatch_nextauth_secret_prod: "CHANGE_ME"
|
||||||
|
|
||||||
|
# SMTP (prod)
|
||||||
|
vault_mirrormatch_smtp_host: "smtp.example.com"
|
||||||
|
vault_mirrormatch_smtp_port: "587"
|
||||||
|
vault_mirrormatch_smtp_user: "smtp-user"
|
||||||
|
vault_mirrormatch_smtp_password: "CHANGE_ME"
|
||||||
|
vault_mirrormatch_smtp_from: "MirrorMatch <noreply@mirrormatch.com>"
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -1,47 +1,100 @@
|
|||||||
$ANSIBLE_VAULT;1.1;AES256
|
$ANSIBLE_VAULT;1.1;AES256
|
||||||
36643038376636383030343730626264613839396462366365633837636130623639393361656634
|
38316537376634623462313731323238666165383731656632373665653534623163386333303865
|
||||||
3238353261633635353662653036393835313963373562390a646535376366656163383632313835
|
3865383030316132663831303932376437346335323233630a643331663539383163306666393764
|
||||||
39646666653362336661633736333365343962346432653131613134353361366263373162386631
|
38313265656561343839616565343663353037663237663032366632373831363336306632626266
|
||||||
3134613438626132320a313765343338643535343837306339616564336564303166626164356530
|
3361643865333533340a356233663034343932323831323236356161396237346532323838373135
|
||||||
63663363656535303137663431613861343662303664313332626166373463393931323937613230
|
33393239313730363336613338373039663735323431323562613363343863326234633833663631
|
||||||
66333665316331323637663437653339353737653336633864393033336630336438646162643662
|
66343462623231663932633537373361313764393630356666393662653135356139663935613038
|
||||||
31656164363933333036376263303034646366393134636630663631353235373831303264363762
|
65383261363065633235343031346535373564373931373063386265343335623265653739613830
|
||||||
66643865616130306537383836646237613730643133656333666632326538613764383530363363
|
32656233393330633362623932316431383761306332393466313936396533333839313831663331
|
||||||
61386161646637316166303633643665383365346534323939383034613430386362303038313761
|
34353864356336303331663233653666363966376162303731626134313235306238323363303439
|
||||||
36303364396436373466653332303562653038373962616539356633373065643130303036363161
|
32333039653235326632303637303065386161616138356463623561366637376366326262303166
|
||||||
65353163326136383066393332376236386333653532326337613163346334616234643562643265
|
38323763393934666539373063323265333961666164613437316164633565393035626538353365
|
||||||
62316134386365343733636661336130623364386634383965386135616633323132643365613231
|
33386562336665383863636639643232623161643933313664396534383362303838663362653736
|
||||||
34636435333031376136396336316337666161383562343834383865316436633333333065323138
|
64393334616165336638306235363734653431646431616139373336656333623963386538646230
|
||||||
37343865363731303137666330306131373734623637343531623562353332353437646631343363
|
39663230363063386231343730663162313463666135323265613261626637626332353534396535
|
||||||
30393565376435303430396535643165616534313334326462363130626639343038643835336335
|
31623664363766646332396336396133613662643232366433323330373962633839613635333763
|
||||||
33613630336534666163356631353438373462306566376134323536373832643264633365653465
|
63306230623438346639323863353137363330316630316130326134323731326635643736373736
|
||||||
62386363326436623330653430383262653732376235626432656362306363303663623834653664
|
62336362656265633233623165376436373231656666303832373966353732313031623865316663
|
||||||
31373762306539376431353137393664396165396261613364653339373765393863633833396131
|
63356163636238346230623732326232646434623532633439646536656362393162613535613565
|
||||||
36666235666234633430373338323331313531643736656137303937653865303431643164373161
|
66616539316362376561386263373464623030636661663435383839643565393632616232663035
|
||||||
39633238383265396366386230303536613461633431333565353433643935613231333232333063
|
34653735383964653930633664346330386566343830336238306562343164366131643138643339
|
||||||
36643435376165656262623863373039393837643564366531666462376162653630626634663037
|
35313366356637643262636238366263353535306434633732623335643266396335666636666663
|
||||||
39373439336239646131306133663566343734656339346462356662373561306264333364383966
|
37333232393765306433326164663538663839623034373535653737633366303665633831303334
|
||||||
38343463616666613037636335333137633737666166633364343736646232396566373866633531
|
32303061363863386139613464326466336136396534663538643163343439343763383534306636
|
||||||
34303734376137386363373039656565323364333539626630323465666636396465323861333365
|
62353733613330376163386331626463656462336237656339356132643135363537343638303261
|
||||||
35376161663630356132373638333937376164316361303531303637396334306133373237656265
|
33366332653439313137613665386136666536356537346665333935366336623734393738346434
|
||||||
36356532623130323565396531306136363339363437376364343138653139653335343765316365
|
63326265346362636564366265373134336662626332653464646139656635313961656230336537
|
||||||
38313035366137393365316139326236326330386365343665376335313339666231333632333133
|
63666638326337643033363964643339666130386139363138656165666333356465643337396165
|
||||||
32353865626531373462346261653832386234396531653136323162653865303861396233376261
|
30336330633632353231613938646165383966613863366330646162646266346139343434393865
|
||||||
34616232363965313635373833333737336166643734373633313865323066393930666562316136
|
66346365663230626531643963383462636465363965393762336233366538393133313138616335
|
||||||
36373763356365646361656436383463393237623461383531343134373336663763663464336361
|
32353834313762363265643031343237633732393166343139363163326439666162396332353038
|
||||||
38396532383932643065303731663565353366373033353237383538636365323064396531386134
|
31306530626666343361313736313636613335376163383237303063393333386663333333336137
|
||||||
61643964613930373439383032373364316437303239393434376465393639373634663738623461
|
37346166316231623638386635613230663063653037643930333961316434643361633035633734
|
||||||
37386366616333626434363761326361373533306635316164316363393264303633353939613239
|
65643937636361653433383262643265373165613437336236633631323635613034663834646665
|
||||||
37353266303637323139653630663236663633313061306633316139666539376632306630313362
|
30373730373438613132633932333565376665333565383932356334653738646166393934626362
|
||||||
34633834326433646230303634313266303530633236353262633066396462646365623935343161
|
30666666303832613633316230623038343165396338343535663931383639623430643238656261
|
||||||
34393166643666366164313438383939386434366665613330653739383139613732396633383261
|
39623037333063306266323335303736346236636137633863353866343136346335353865303961
|
||||||
33633664303131383163356362316639353064373861343132623565636631333135663034373461
|
31346331333066376330306361396262333762393838303165383134303435353630366130303536
|
||||||
61303031616634333235303066633939643337393862653031323936363932633438303035323238
|
34386532356239326166386665623435646432636561363564656161646563306234333138333839
|
||||||
66323066353737316166383533636661336637303265343937633064626164623462656134333732
|
38316337656631313763393135396464643338386636336234346663653538353863643636323032
|
||||||
33316536336430636636646561626232666633656266326339623732363531326131643764313838
|
35326133623064363838386662653138613438386564316635373838366262656364666633636539
|
||||||
62356537326166346666313930383639386466633432626235373738633833393164646238366465
|
61306563666138656161336466323537626161313366616662623362643036636132663634313137
|
||||||
62373938363739373036666238666433303061633732663565666433333631326432626461353037
|
39653437306662646162613763343736636530356465346132646238633166373838353836326461
|
||||||
39636263636632313431353364386566383134653139393762623562643561616166633035353038
|
36326666323636353239303262623436643932353164323630326635653635653233363265316264
|
||||||
39326462356332616563303462636536636132633933336532383938373030666333363264346632
|
30653763643431626539356161376534396437636463303363663134373961616561363561333333
|
||||||
64643063373830353130613662323131353964313038323735626464313363326364653732323732
|
34306537326666383664336464656464623731656566653132613565336536323438666333366466
|
||||||
3663393964633138376665323435366463623463613237366465
|
64613738653730333633383062653837366266316536653139643362373039383831363666333934
|
||||||
|
34383833336266356436666636323239336432386133303466636138643934356266326533643161
|
||||||
|
36393664313963393930383533623565383332613933396639613037323266663439313138326261
|
||||||
|
30353861303661303836343165353362663632306430626337356562343637653164396237333566
|
||||||
|
37656230363530323836373363646334356262646633313932383161303264613238373936353036
|
||||||
|
61376264633930356465626266623930333039383032316163633037323035346130343934616261
|
||||||
|
31666166393462366561303833353135326566356637376466613934376233303162323033623031
|
||||||
|
63656131333439353537623662363530383866326432306361316465383137633536666364623662
|
||||||
|
37353561633839623530333663643130326131333330626661396636343234666139336539653162
|
||||||
|
62383636663137626637303535333862366434626161353239393232313537343865646564626331
|
||||||
|
39366665363030643764663963316163343033326434373265343664393439316333346434363563
|
||||||
|
61346164396561343865626362616433306230333130653166656230353364316536626432373333
|
||||||
|
35383133363530666263316431396462383133363965336637386632363263656261353963313161
|
||||||
|
36383632326264373436383638383064346334336238656239393833653531656461356136303434
|
||||||
|
37663434663732306631656334306361663562303863386135623066633963373034373139666332
|
||||||
|
35393433646333363839666434663535363661616330386234366132303161383063663836626561
|
||||||
|
35393064343735303032313266643338623834383838633834636536363539656466663864613366
|
||||||
|
66636363623330326436363936313938333638323939323035616232366563316364343834376630
|
||||||
|
66656434336661643861613737616138396330383832386230383331646462323363373363393733
|
||||||
|
63363237636137373566363438663966396432613964336164326138623737393636396234646232
|
||||||
|
64343361363365356135666235623833396131626663303839653535663732313831633163643638
|
||||||
|
35396262373837343238343838663635353838373338663732626330613237623332336436643136
|
||||||
|
38653833383430393837383566643765653834306636356466326364303334653034626262356630
|
||||||
|
34333338333336373433356235386337346666343830303164363235303265313134323339653339
|
||||||
|
63316238346132653663653165313635336638646362356337643766366564383531633565303431
|
||||||
|
66616433663630343439336661346266336139613537653438653432326666326137306364376137
|
||||||
|
66333939643262633532363966623439373434393862353237613135646663623236646331643537
|
||||||
|
31353566653464313433636635393330646166613232633734346639326534373163383064353732
|
||||||
|
32373861303064346266643338316465653031646633633936373738663837383162643534623131
|
||||||
|
31633662356534343636313834386139656439663733333762323962323939623032396239356437
|
||||||
|
37633739613433613365313337383835623936623530363831383535663337343264356532616434
|
||||||
|
39393634396664636166346631313764343733666534613935393637363233373331303837656463
|
||||||
|
37363266363634353136316532333462396266373733333633356239653334363835326261323661
|
||||||
|
66323032346364356230613831643236316530356132343863393361343462373433383265336333
|
||||||
|
30343730316366366234333263343965633466333439653739663333643939303631353664316435
|
||||||
|
36396139623562656632666165666662626263643436396431326135633932393965656531633761
|
||||||
|
39303634643936366438336534613532303134343164326661626363656562383564623264636132
|
||||||
|
39656636303636393761653035303832386430646162343830343834316534636263373763643765
|
||||||
|
61366335643531666232303231656336643833396238336639333437363564636566636632303364
|
||||||
|
62623738336237393638363436396662656565653839643164356565313563663561666237383036
|
||||||
|
33626464663465643230376164653062663063636630613064643632643235643662653566333333
|
||||||
|
62353763643830363638323731303537633837393235656661333263323536363330356362643333
|
||||||
|
34346666656432626365383639326538643862346265316263326531623631383962383734316330
|
||||||
|
39333430613761663337306331623461643635653431343336663163343766373464366538313335
|
||||||
|
61643538643231333636643836663663313534356662386532633331346664653262353839643066
|
||||||
|
36393366653131316636646336313362656662666163333635633132323438353435373430643839
|
||||||
|
37623936393962333065663536306238653466363634386632366637363265303734356535333735
|
||||||
|
64623330303965393533326563643063303762646664666464643239386435343065326234306632
|
||||||
|
35346338373866303838613933653230373737396134653533376265356432333933356237636338
|
||||||
|
66656536393530316435323863373962636465333331653364626162326562393565313538633264
|
||||||
|
34613633393862333731336563636136666166613037613833333063303162373339663539646631
|
||||||
|
36303962356562306239616634376339356135666663303836353061663039343836356262373932
|
||||||
|
65346466373532633365383835323062313531623130396130376531626333653862393462643631
|
||||||
|
366330333666336262373364663864336633
|
||||||
|
|||||||
@ -32,7 +32,7 @@ listmonk ansible_host=10.0.10.149 ansible_user=root
|
|||||||
nextcloud ansible_host=10.0.10.25 ansible_user=root
|
nextcloud ansible_host=10.0.10.25 ansible_user=root
|
||||||
actual ansible_host=10.0.10.158 ansible_user=root
|
actual ansible_host=10.0.10.158 ansible_user=root
|
||||||
vikanjans ansible_host=10.0.10.159 ansible_user=root
|
vikanjans ansible_host=10.0.10.159 ansible_user=root
|
||||||
n8n ansible_host=10.0.10.158 ansible_user=root
|
n8n ansible_host=10.0.10.154 ansible_user=root
|
||||||
giteaVM ansible_host=10.0.30.169 ansible_user=root
|
giteaVM ansible_host=10.0.30.169 ansible_user=root
|
||||||
portainerVM ansible_host=10.0.30.69 ansible_user=ladmin
|
portainerVM ansible_host=10.0.30.69 ansible_user=ladmin
|
||||||
homepageVM ansible_host=10.0.30.12 ansible_user=homepage
|
homepageVM ansible_host=10.0.30.12 ansible_user=homepage
|
||||||
|
|||||||
@ -20,6 +20,12 @@
|
|||||||
if (app_project is not defined or app_project | length == 0)
|
if (app_project is not defined or app_project | length == 0)
|
||||||
else [app_project]
|
else [app_project]
|
||||||
}}
|
}}
|
||||||
|
selected_envs: >-
|
||||||
|
{{
|
||||||
|
[app_env]
|
||||||
|
if (app_env is defined and app_env | length > 0)
|
||||||
|
else ['dev', 'qa', 'prod']
|
||||||
|
}}
|
||||||
app_bootstrap_user_default: root
|
app_bootstrap_user_default: root
|
||||||
# If true, configure plays will use vault_lxc_root_password for initial SSH bootstrap.
|
# If true, configure plays will use vault_lxc_root_password for initial SSH bootstrap.
|
||||||
bootstrap_with_root_password_default: false
|
bootstrap_with_root_password_default: false
|
||||||
@ -47,7 +53,7 @@
|
|||||||
}}
|
}}
|
||||||
app_project: "{{ item.0 }}"
|
app_project: "{{ item.0 }}"
|
||||||
app_env: "{{ item.1 }}"
|
app_env: "{{ item.1 }}"
|
||||||
loop: "{{ selected_projects | product(['dev', 'qa', 'prod']) | list }}"
|
loop: "{{ selected_projects | product(selected_envs) | list }}"
|
||||||
when:
|
when:
|
||||||
- app_projects[item.0] is defined
|
- app_projects[item.0] is defined
|
||||||
- app_projects[item.0].envs[item.1] is defined
|
- app_projects[item.0].envs[item.1] is defined
|
||||||
@ -116,16 +122,20 @@
|
|||||||
# app_env is already set per-host via add_host (dev/qa/prod)
|
# app_env is already set per-host via add_host (dev/qa/prod)
|
||||||
app_owner: "{{ project_def.os_user | default(appuser_name) }}"
|
app_owner: "{{ project_def.os_user | default(appuser_name) }}"
|
||||||
app_group: "{{ project_def.os_user | default(appuser_name) }}"
|
app_group: "{{ project_def.os_user | default(appuser_name) }}"
|
||||||
|
app_backend_dir: "{{ project_def.backend_dir | default(project_def.repo_dest | default(app_repo_dest)) }}"
|
||||||
|
app_frontend_dir: "{{ project_def.frontend_dir | default(project_def.repo_dest | default(app_repo_dest) + '/frontend') }}"
|
||||||
|
|
||||||
# Use different variable names to avoid self-referential recursion
|
# Use different variable names to avoid self-referential recursion
|
||||||
app_backend_port: "{{ project_def.backend_port | default(3001) }}"
|
app_backend_port: "{{ project_def.backend_port | default(3000) }}"
|
||||||
app_frontend_port: "{{ project_def.frontend_port | default(3000) }}"
|
app_frontend_port: "{{ project_def.frontend_port | default(3000) }}"
|
||||||
app_enable_backend: "{{ project_def.components.backend | default(true) }}"
|
app_enable_backend: "{{ project_def.components.backend | default(true) }}"
|
||||||
app_enable_frontend: "{{ project_def.components.frontend | default(true) }}"
|
app_enable_frontend: "{{ project_def.components.frontend | default(true) }}"
|
||||||
|
|
||||||
app_backend_install_cmd: "{{ project_def.deploy.backend_install_cmd | default('npm ci') }}"
|
app_backend_install_cmd: "{{ project_def.deploy.backend_install_cmd | default('npm ci') }}"
|
||||||
app_backend_migrate_cmd: "{{ project_def.deploy.backend_migrate_cmd | default('npm run migrate') }}"
|
app_backend_build_cmd: "{{ project_def.deploy.backend_build_cmd | default('npm run build') }}"
|
||||||
app_backend_start_cmd: "{{ project_def.deploy.backend_start_cmd | default('npm start') }}"
|
app_backend_migrate_cmd: "{{ env_def.backend_migrate_cmd | default(project_def.deploy.backend_migrate_cmd | default('npm run migrate')) }}"
|
||||||
|
app_backend_seed_cmd: "{{ env_def.backend_seed_cmd | default(project_def.deploy.backend_seed_cmd | default('')) }}"
|
||||||
|
app_backend_start_cmd: "{{ project_def.deploy.backend_start_cmd | default('npm run start') }}"
|
||||||
|
|
||||||
app_frontend_install_cmd: "{{ project_def.deploy.frontend_install_cmd | default('npm ci') }}"
|
app_frontend_install_cmd: "{{ project_def.deploy.frontend_install_cmd | default('npm ci') }}"
|
||||||
app_frontend_build_cmd: "{{ project_def.deploy.frontend_build_cmd | default('npm run build') }}"
|
app_frontend_build_cmd: "{{ project_def.deploy.frontend_build_cmd | default('npm run build') }}"
|
||||||
|
|||||||
@ -16,7 +16,7 @@
|
|||||||
- (project_def.envs | length) > 0
|
- (project_def.envs | length) > 0
|
||||||
- name: Provision each environment for project
|
- name: Provision each environment for project
|
||||||
ansible.builtin.include_tasks: provision_one_env.yml
|
ansible.builtin.include_tasks: provision_one_env.yml
|
||||||
loop: "{{ project_def.envs | dict2items }}"
|
loop: "{{ project_def.envs | dict2items | selectattr('key', 'in', selected_envs) | list }}"
|
||||||
loop_control:
|
loop_control:
|
||||||
loop_var: env_item
|
loop_var: env_item
|
||||||
|
|
||||||
|
|||||||
@ -20,6 +20,12 @@
|
|||||||
if (app_project is not defined or app_project | length == 0)
|
if (app_project is not defined or app_project | length == 0)
|
||||||
else [app_project]
|
else [app_project]
|
||||||
}}
|
}}
|
||||||
|
selected_envs: >-
|
||||||
|
{{
|
||||||
|
[app_env]
|
||||||
|
if (app_env is defined and app_env | length > 0)
|
||||||
|
else ['dev', 'qa', 'prod']
|
||||||
|
}}
|
||||||
|
|
||||||
tasks:
|
tasks:
|
||||||
- name: Validate requested project exists
|
- name: Validate requested project exists
|
||||||
|
|||||||
@ -29,6 +29,7 @@ app_frontend_port: 3000
|
|||||||
# Commands (Node defaults; override per project as needed)
|
# Commands (Node defaults; override per project as needed)
|
||||||
app_backend_install_cmd: "npm ci"
|
app_backend_install_cmd: "npm ci"
|
||||||
app_backend_migrate_cmd: "npm run migrate"
|
app_backend_migrate_cmd: "npm run migrate"
|
||||||
|
app_backend_seed_cmd: ""
|
||||||
app_backend_start_cmd: "npm start"
|
app_backend_start_cmd: "npm start"
|
||||||
|
|
||||||
app_frontend_install_cmd: "npm ci"
|
app_frontend_install_cmd: "npm ci"
|
||||||
|
|||||||
@ -10,24 +10,6 @@
|
|||||||
group: "{{ app_group }}"
|
group: "{{ app_group }}"
|
||||||
mode: "0755"
|
mode: "0755"
|
||||||
|
|
||||||
- name: Ensure backend directory exists
|
|
||||||
ansible.builtin.file:
|
|
||||||
path: "{{ app_backend_dir }}"
|
|
||||||
state: directory
|
|
||||||
owner: "{{ app_owner }}"
|
|
||||||
group: "{{ app_group }}"
|
|
||||||
mode: "0755"
|
|
||||||
when: app_enable_backend | bool
|
|
||||||
|
|
||||||
- name: Ensure frontend directory exists
|
|
||||||
ansible.builtin.file:
|
|
||||||
path: "{{ app_frontend_dir }}"
|
|
||||||
state: directory
|
|
||||||
owner: "{{ app_owner }}"
|
|
||||||
group: "{{ app_group }}"
|
|
||||||
mode: "0755"
|
|
||||||
when: app_enable_frontend | bool
|
|
||||||
|
|
||||||
- name: Deploy environment file for this env
|
- name: Deploy environment file for this env
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
src: env.j2
|
src: env.j2
|
||||||
@ -44,6 +26,16 @@
|
|||||||
group: root
|
group: root
|
||||||
mode: "0755"
|
mode: "0755"
|
||||||
|
|
||||||
|
- name: Run initial deploy (clone, install, build, migrate, seed)
|
||||||
|
ansible.builtin.command:
|
||||||
|
cmd: /usr/local/bin/deploy_app.sh
|
||||||
|
become: true
|
||||||
|
become_user: "{{ app_owner }}"
|
||||||
|
args:
|
||||||
|
chdir: "{{ app_root }}"
|
||||||
|
register: deploy_result
|
||||||
|
changed_when: true
|
||||||
|
|
||||||
- name: Deploy systemd unit for backend
|
- name: Deploy systemd unit for backend
|
||||||
ansible.builtin.template:
|
ansible.builtin.template:
|
||||||
src: app-backend.service.j2
|
src: app-backend.service.j2
|
||||||
|
|||||||
@ -11,10 +11,45 @@ ENV_FILE="{{ app_root }}/.env.{{ app_env }}"
|
|||||||
|
|
||||||
echo "[deploy] repo=${REPO_URL} branch=${BRANCH} root=${APP_ROOT}"
|
echo "[deploy] repo=${REPO_URL} branch=${BRANCH} root=${APP_ROOT}"
|
||||||
|
|
||||||
|
# Load env for build/migrate steps (needed for Prisma/Next build)
|
||||||
|
if [[ -f "${ENV_FILE}" ]]; then
|
||||||
|
set -a
|
||||||
|
# shellcheck disable=SC1090
|
||||||
|
source "${ENV_FILE}"
|
||||||
|
set +a
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ ! -d "${APP_ROOT}/.git" ]]; then
|
if [[ ! -d "${APP_ROOT}/.git" ]]; then
|
||||||
echo "[deploy] cloning repo"
|
echo "[deploy] cloning repo"
|
||||||
install -d -m 0755 "${APP_ROOT}"
|
|
||||||
git clone --branch "${BRANCH}" --single-branch "${REPO_URL}" "${APP_ROOT}"
|
# Preserve existing env files
|
||||||
|
env_tmp="$(mktemp -d)"
|
||||||
|
shopt -s nullglob dotglob
|
||||||
|
for f in "${APP_ROOT}"/.env.*; do
|
||||||
|
[[ -f "$f" ]] && cp "$f" "${env_tmp}/" || true
|
||||||
|
done
|
||||||
|
shopt -u nullglob dotglob
|
||||||
|
|
||||||
|
# Clone to temp location
|
||||||
|
clone_tmp="$(mktemp -d)"
|
||||||
|
git clone --branch "${BRANCH}" --single-branch "${REPO_URL}" "${clone_tmp}/repo"
|
||||||
|
|
||||||
|
# Clean app root (keep directory and .env files)
|
||||||
|
find "${APP_ROOT}" -mindepth 1 -maxdepth 1 ! -name '.env.*' -exec rm -rf {} + 2>/dev/null || true
|
||||||
|
|
||||||
|
# Move cloned repo contents to app root (including hidden files)
|
||||||
|
shopt -s dotglob
|
||||||
|
mv "${clone_tmp}/repo"/* "${APP_ROOT}"/ 2>/dev/null || true
|
||||||
|
shopt -u dotglob
|
||||||
|
rm -rf "${clone_tmp}"
|
||||||
|
|
||||||
|
# Restore env files
|
||||||
|
shopt -s nullglob
|
||||||
|
for f in "${env_tmp}"/.env.*; do
|
||||||
|
[[ -f "$f" ]] && cp "$f" "${APP_ROOT}/" || true
|
||||||
|
done
|
||||||
|
shopt -u nullglob
|
||||||
|
rm -rf "${env_tmp}"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
echo "[deploy] syncing branch"
|
echo "[deploy] syncing branch"
|
||||||
@ -31,8 +66,16 @@ echo "[deploy] backend install"
|
|||||||
cd "${BACKEND_DIR}"
|
cd "${BACKEND_DIR}"
|
||||||
{{ app_backend_install_cmd }}
|
{{ app_backend_install_cmd }}
|
||||||
|
|
||||||
|
echo "[deploy] backend build"
|
||||||
|
{{ app_backend_build_cmd }}
|
||||||
|
|
||||||
echo "[deploy] backend migrations"
|
echo "[deploy] backend migrations"
|
||||||
{{ app_backend_migrate_cmd }}
|
{{ app_backend_migrate_cmd }}
|
||||||
|
|
||||||
|
{% if app_backend_seed_cmd | default('') | length > 0 %}
|
||||||
|
echo "[deploy] backend seed"
|
||||||
|
{{ app_backend_seed_cmd }}
|
||||||
|
{% endif %}
|
||||||
fi
|
fi
|
||||||
|
|
||||||
if [[ "{{ app_enable_frontend | bool }}" == "True" ]]; then
|
if [[ "{{ app_enable_frontend | bool }}" == "True" ]]; then
|
||||||
@ -46,10 +89,10 @@ fi
|
|||||||
|
|
||||||
echo "[deploy] restarting services"
|
echo "[deploy] restarting services"
|
||||||
{% if app_enable_backend | bool %}
|
{% if app_enable_backend | bool %}
|
||||||
systemctl restart app-backend.service
|
sudo systemctl restart app-backend.service
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% if app_enable_frontend | bool %}
|
{% if app_enable_frontend | bool %}
|
||||||
systemctl restart app-frontend.service
|
sudo systemctl restart app-frontend.service
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
echo "[deploy] done"
|
echo "[deploy] done"
|
||||||
|
|||||||
@ -1,11 +1,6 @@
|
|||||||
# Ansible-managed environment file for {{ app_env }}
|
|
||||||
# Loaded by systemd units and deploy script.
|
|
||||||
|
|
||||||
# Common
|
|
||||||
APP_ENV={{ app_env }}
|
APP_ENV={{ app_env }}
|
||||||
BACKEND_PORT={{ app_backend_port }}
|
BACKEND_PORT={{ app_backend_port }}
|
||||||
FRONTEND_PORT={{ app_frontend_port }}
|
FRONTEND_PORT={{ app_frontend_port }}
|
||||||
|
|
||||||
{% for k, v in (app_env_vars | default({})).items() %}
|
{% for k, v in (app_env_vars | default({})).items() %}
|
||||||
{{ k }}={{ v }}
|
{{ k }}={{ v }}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|||||||
@ -12,6 +12,14 @@
|
|||||||
name: "{{ base_os_packages }}"
|
name: "{{ base_os_packages }}"
|
||||||
state: present
|
state: present
|
||||||
|
|
||||||
|
- name: Ensure /etc/sudoers.d exists
|
||||||
|
ansible.builtin.file:
|
||||||
|
path: /etc/sudoers.d
|
||||||
|
state: directory
|
||||||
|
owner: root
|
||||||
|
group: root
|
||||||
|
mode: "0750"
|
||||||
|
|
||||||
- name: Ensure app user exists
|
- name: Ensure app user exists
|
||||||
ansible.builtin.user:
|
ansible.builtin.user:
|
||||||
name: "{{ base_os_user }}"
|
name: "{{ base_os_user }}"
|
||||||
@ -37,6 +45,11 @@
|
|||||||
mode: "0440"
|
mode: "0440"
|
||||||
when: base_os_passwordless_sudo | bool
|
when: base_os_passwordless_sudo | bool
|
||||||
|
|
||||||
|
- name: Ensure ufw is installed
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name: ufw
|
||||||
|
state: present
|
||||||
|
|
||||||
- name: Ensure UFW allows SSH
|
- name: Ensure UFW allows SSH
|
||||||
community.general.ufw:
|
community.general.ufw:
|
||||||
rule: allow
|
rule: allow
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user