@cicore/cli 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/bin/ci.js +13 -0
- package/dist/commands/addon/api-actions.d.ts +45 -0
- package/dist/commands/addon/api-actions.d.ts.map +1 -0
- package/dist/commands/addon/api-actions.js +281 -0
- package/dist/commands/addon/api-actions.js.map +1 -0
- package/dist/commands/addon/build.d.ts +11 -0
- package/dist/commands/addon/build.d.ts.map +1 -0
- package/dist/commands/addon/build.js +182 -0
- package/dist/commands/addon/build.js.map +1 -0
- package/dist/commands/addon/create.d.ts +11 -0
- package/dist/commands/addon/create.d.ts.map +1 -0
- package/dist/commands/addon/create.js +1186 -0
- package/dist/commands/addon/create.js.map +1 -0
- package/dist/commands/addon/delete.d.ts +13 -0
- package/dist/commands/addon/delete.d.ts.map +1 -0
- package/dist/commands/addon/delete.js +83 -0
- package/dist/commands/addon/delete.js.map +1 -0
- package/dist/commands/addon/deploy.d.ts +27 -0
- package/dist/commands/addon/deploy.d.ts.map +1 -0
- package/dist/commands/addon/deploy.js +459 -0
- package/dist/commands/addon/deploy.js.map +1 -0
- package/dist/commands/addon/dev-deploy.d.ts +31 -0
- package/dist/commands/addon/dev-deploy.d.ts.map +1 -0
- package/dist/commands/addon/dev-deploy.js +128 -0
- package/dist/commands/addon/dev-deploy.js.map +1 -0
- package/dist/commands/addon/dev.d.ts +36 -0
- package/dist/commands/addon/dev.d.ts.map +1 -0
- package/dist/commands/addon/dev.js +323 -0
- package/dist/commands/addon/dev.js.map +1 -0
- package/dist/commands/addon/extract-classes.d.ts +23 -0
- package/dist/commands/addon/extract-classes.d.ts.map +1 -0
- package/dist/commands/addon/extract-classes.js +281 -0
- package/dist/commands/addon/extract-classes.js.map +1 -0
- package/dist/commands/addon/generate-safelist.d.ts +24 -0
- package/dist/commands/addon/generate-safelist.d.ts.map +1 -0
- package/dist/commands/addon/generate-safelist.js +276 -0
- package/dist/commands/addon/generate-safelist.js.map +1 -0
- package/dist/commands/addon/index.d.ts +19 -0
- package/dist/commands/addon/index.d.ts.map +1 -0
- package/dist/commands/addon/index.js +296 -0
- package/dist/commands/addon/index.js.map +1 -0
- package/dist/commands/addon/init-repo.d.ts +25 -0
- package/dist/commands/addon/init-repo.d.ts.map +1 -0
- package/dist/commands/addon/init-repo.js +171 -0
- package/dist/commands/addon/init-repo.js.map +1 -0
- package/dist/commands/addon/install.d.ts +23 -0
- package/dist/commands/addon/install.d.ts.map +1 -0
- package/dist/commands/addon/install.js +84 -0
- package/dist/commands/addon/install.js.map +1 -0
- package/dist/commands/addon/list.d.ts +10 -0
- package/dist/commands/addon/list.d.ts.map +1 -0
- package/dist/commands/addon/list.js +102 -0
- package/dist/commands/addon/list.js.map +1 -0
- package/dist/commands/addon/manifest-refresh.d.ts +17 -0
- package/dist/commands/addon/manifest-refresh.d.ts.map +1 -0
- package/dist/commands/addon/manifest-refresh.js +48 -0
- package/dist/commands/addon/manifest-refresh.js.map +1 -0
- package/dist/commands/addon/migrate.d.ts +40 -0
- package/dist/commands/addon/migrate.d.ts.map +1 -0
- package/dist/commands/addon/migrate.js +236 -0
- package/dist/commands/addon/migrate.js.map +1 -0
- package/dist/commands/addon/publish.d.ts +33 -0
- package/dist/commands/addon/publish.d.ts.map +1 -0
- package/dist/commands/addon/publish.js +236 -0
- package/dist/commands/addon/publish.js.map +1 -0
- package/dist/commands/addon/scaffold-quality.d.ts +21 -0
- package/dist/commands/addon/scaffold-quality.d.ts.map +1 -0
- package/dist/commands/addon/scaffold-quality.js +90 -0
- package/dist/commands/addon/scaffold-quality.js.map +1 -0
- package/dist/commands/addon/sign.d.ts +9 -0
- package/dist/commands/addon/sign.d.ts.map +1 -0
- package/dist/commands/addon/sign.js +83 -0
- package/dist/commands/addon/sign.js.map +1 -0
- package/dist/commands/addon/toggle.d.ts +6 -0
- package/dist/commands/addon/toggle.d.ts.map +1 -0
- package/dist/commands/addon/toggle.js +46 -0
- package/dist/commands/addon/toggle.js.map +1 -0
- package/dist/commands/agent/index.d.ts +34 -0
- package/dist/commands/agent/index.d.ts.map +1 -0
- package/dist/commands/agent/index.js +564 -0
- package/dist/commands/agent/index.js.map +1 -0
- package/dist/commands/brand/index.d.ts +54 -0
- package/dist/commands/brand/index.d.ts.map +1 -0
- package/dist/commands/brand/index.js +367 -0
- package/dist/commands/brand/index.js.map +1 -0
- package/dist/commands/build/index.d.ts +53 -0
- package/dist/commands/build/index.d.ts.map +1 -0
- package/dist/commands/build/index.js +726 -0
- package/dist/commands/build/index.js.map +1 -0
- package/dist/commands/cache/flush-local.d.ts +31 -0
- package/dist/commands/cache/flush-local.d.ts.map +1 -0
- package/dist/commands/cache/flush-local.js +161 -0
- package/dist/commands/cache/flush-local.js.map +1 -0
- package/dist/commands/cache/index.d.ts +14 -0
- package/dist/commands/cache/index.d.ts.map +1 -0
- package/dist/commands/cache/index.js +453 -0
- package/dist/commands/cache/index.js.map +1 -0
- package/dist/commands/check/index.d.ts +8 -0
- package/dist/commands/check/index.d.ts.map +1 -0
- package/dist/commands/check/index.js +1316 -0
- package/dist/commands/check/index.js.map +1 -0
- package/dist/commands/cloudflare/index.d.ts +8 -0
- package/dist/commands/cloudflare/index.d.ts.map +1 -0
- package/dist/commands/cloudflare/index.js +453 -0
- package/dist/commands/cloudflare/index.js.map +1 -0
- package/dist/commands/core/create.d.ts +12 -0
- package/dist/commands/core/create.d.ts.map +1 -0
- package/dist/commands/core/create.js +206 -0
- package/dist/commands/core/create.js.map +1 -0
- package/dist/commands/core/delete.d.ts +11 -0
- package/dist/commands/core/delete.d.ts.map +1 -0
- package/dist/commands/core/delete.js +64 -0
- package/dist/commands/core/delete.js.map +1 -0
- package/dist/commands/core/env.d.ts +12 -0
- package/dist/commands/core/env.d.ts.map +1 -0
- package/dist/commands/core/env.js +95 -0
- package/dist/commands/core/env.js.map +1 -0
- package/dist/commands/core/health.d.ts +6 -0
- package/dist/commands/core/health.d.ts.map +1 -0
- package/dist/commands/core/health.js +215 -0
- package/dist/commands/core/health.js.map +1 -0
- package/dist/commands/core/index.d.ts +15 -0
- package/dist/commands/core/index.d.ts.map +1 -0
- package/dist/commands/core/index.js +86 -0
- package/dist/commands/core/index.js.map +1 -0
- package/dist/commands/core/list.d.ts +11 -0
- package/dist/commands/core/list.d.ts.map +1 -0
- package/dist/commands/core/list.js +58 -0
- package/dist/commands/core/list.js.map +1 -0
- package/dist/commands/core/rebuild.d.ts +13 -0
- package/dist/commands/core/rebuild.d.ts.map +1 -0
- package/dist/commands/core/rebuild.js +119 -0
- package/dist/commands/core/rebuild.js.map +1 -0
- package/dist/commands/db/index.d.ts +23 -0
- package/dist/commands/db/index.d.ts.map +1 -0
- package/dist/commands/db/index.js +355 -0
- package/dist/commands/db/index.js.map +1 -0
- package/dist/commands/db/promote-silo.d.ts +320 -0
- package/dist/commands/db/promote-silo.d.ts.map +1 -0
- package/dist/commands/db/promote-silo.js +930 -0
- package/dist/commands/db/promote-silo.js.map +1 -0
- package/dist/commands/db/relocate.d.ts +41 -0
- package/dist/commands/db/relocate.d.ts.map +1 -0
- package/dist/commands/db/relocate.js +482 -0
- package/dist/commands/db/relocate.js.map +1 -0
- package/dist/commands/db/rollback-silo.d.ts +44 -0
- package/dist/commands/db/rollback-silo.d.ts.map +1 -0
- package/dist/commands/db/rollback-silo.js +402 -0
- package/dist/commands/db/rollback-silo.js.map +1 -0
- package/dist/commands/deploy/index.d.ts +26 -0
- package/dist/commands/deploy/index.d.ts.map +1 -0
- package/dist/commands/deploy/index.js +107 -0
- package/dist/commands/deploy/index.js.map +1 -0
- package/dist/commands/devops/index.d.ts +6 -0
- package/dist/commands/devops/index.d.ts.map +1 -0
- package/dist/commands/devops/index.js +220 -0
- package/dist/commands/devops/index.js.map +1 -0
- package/dist/commands/domain/index.d.ts +8 -0
- package/dist/commands/domain/index.d.ts.map +1 -0
- package/dist/commands/domain/index.js +386 -0
- package/dist/commands/domain/index.js.map +1 -0
- package/dist/commands/image/index.d.ts +8 -0
- package/dist/commands/image/index.d.ts.map +1 -0
- package/dist/commands/image/index.js +308 -0
- package/dist/commands/image/index.js.map +1 -0
- package/dist/commands/install/factory-reset.d.ts +21 -0
- package/dist/commands/install/factory-reset.d.ts.map +1 -0
- package/dist/commands/install/factory-reset.js +83 -0
- package/dist/commands/install/factory-reset.js.map +1 -0
- package/dist/commands/install/index.d.ts +17 -0
- package/dist/commands/install/index.d.ts.map +1 -0
- package/dist/commands/install/index.js +44 -0
- package/dist/commands/install/index.js.map +1 -0
- package/dist/commands/install/install.d.ts +35 -0
- package/dist/commands/install/install.d.ts.map +1 -0
- package/dist/commands/install/install.js +171 -0
- package/dist/commands/install/install.js.map +1 -0
- package/dist/commands/login/index.d.ts +15 -0
- package/dist/commands/login/index.d.ts.map +1 -0
- package/dist/commands/login/index.js +58 -0
- package/dist/commands/login/index.js.map +1 -0
- package/dist/commands/nginx/index.d.ts +11 -0
- package/dist/commands/nginx/index.d.ts.map +1 -0
- package/dist/commands/nginx/index.js +580 -0
- package/dist/commands/nginx/index.js.map +1 -0
- package/dist/commands/server/bootstrap.d.ts +25 -0
- package/dist/commands/server/bootstrap.d.ts.map +1 -0
- package/dist/commands/server/bootstrap.js +260 -0
- package/dist/commands/server/bootstrap.js.map +1 -0
- package/dist/commands/server/index.d.ts +8 -0
- package/dist/commands/server/index.d.ts.map +1 -0
- package/dist/commands/server/index.js +2524 -0
- package/dist/commands/server/index.js.map +1 -0
- package/dist/commands/setup/index.d.ts +34 -0
- package/dist/commands/setup/index.d.ts.map +1 -0
- package/dist/commands/setup/index.js +423 -0
- package/dist/commands/setup/index.js.map +1 -0
- package/dist/commands/ssl/index.d.ts +8 -0
- package/dist/commands/ssl/index.d.ts.map +1 -0
- package/dist/commands/ssl/index.js +275 -0
- package/dist/commands/ssl/index.js.map +1 -0
- package/dist/commands/superadmin/index.d.ts +16 -0
- package/dist/commands/superadmin/index.d.ts.map +1 -0
- package/dist/commands/superadmin/index.js +81 -0
- package/dist/commands/superadmin/index.js.map +1 -0
- package/dist/commands/tenant/index.d.ts +6 -0
- package/dist/commands/tenant/index.d.ts.map +1 -0
- package/dist/commands/tenant/index.js +192 -0
- package/dist/commands/tenant/index.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +107 -0
- package/dist/index.js.map +1 -0
- package/dist/lib/addon-sign.d.ts +23 -0
- package/dist/lib/addon-sign.d.ts.map +1 -0
- package/dist/lib/addon-sign.js +39 -0
- package/dist/lib/addon-sign.js.map +1 -0
- package/dist/lib/addon-sign.test.d.ts +2 -0
- package/dist/lib/addon-sign.test.d.ts.map +1 -0
- package/dist/lib/addon-sign.test.js +27 -0
- package/dist/lib/addon-sign.test.js.map +1 -0
- package/dist/lib/cdn.d.ts +25 -0
- package/dist/lib/cdn.d.ts.map +1 -0
- package/dist/lib/cdn.js +131 -0
- package/dist/lib/cdn.js.map +1 -0
- package/dist/lib/cloudflare.d.ts +133 -0
- package/dist/lib/cloudflare.d.ts.map +1 -0
- package/dist/lib/cloudflare.js +435 -0
- package/dist/lib/cloudflare.js.map +1 -0
- package/dist/lib/config.d.ts +96 -0
- package/dist/lib/config.d.ts.map +1 -0
- package/dist/lib/config.js +132 -0
- package/dist/lib/config.js.map +1 -0
- package/dist/lib/env.d.ts +8 -0
- package/dist/lib/env.d.ts.map +1 -0
- package/dist/lib/env.js +64 -0
- package/dist/lib/env.js.map +1 -0
- package/dist/lib/hosts.d.ts +194 -0
- package/dist/lib/hosts.d.ts.map +1 -0
- package/dist/lib/hosts.js +183 -0
- package/dist/lib/hosts.js.map +1 -0
- package/dist/lib/logger.d.ts +68 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/logger.js +130 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/nginx-config.d.ts +78 -0
- package/dist/lib/nginx-config.d.ts.map +1 -0
- package/dist/lib/nginx-config.js +736 -0
- package/dist/lib/nginx-config.js.map +1 -0
- package/dist/lib/ops/addon-dev.d.ts +93 -0
- package/dist/lib/ops/addon-dev.d.ts.map +1 -0
- package/dist/lib/ops/addon-dev.js +237 -0
- package/dist/lib/ops/addon-dev.js.map +1 -0
- package/dist/lib/ops/addon-quality.d.ts +38 -0
- package/dist/lib/ops/addon-quality.d.ts.map +1 -0
- package/dist/lib/ops/addon-quality.js +338 -0
- package/dist/lib/ops/addon-quality.js.map +1 -0
- package/dist/lib/ops/addon-routes.d.ts +49 -0
- package/dist/lib/ops/addon-routes.d.ts.map +1 -0
- package/dist/lib/ops/addon-routes.js +189 -0
- package/dist/lib/ops/addon-routes.js.map +1 -0
- package/dist/lib/ops/addon.d.ts +120 -0
- package/dist/lib/ops/addon.d.ts.map +1 -0
- package/dist/lib/ops/addon.js +260 -0
- package/dist/lib/ops/addon.js.map +1 -0
- package/dist/lib/ops/cdn.d.ts +87 -0
- package/dist/lib/ops/cdn.d.ts.map +1 -0
- package/dist/lib/ops/cdn.js +170 -0
- package/dist/lib/ops/cdn.js.map +1 -0
- package/dist/lib/ops/cf.d.ts +36 -0
- package/dist/lib/ops/cf.d.ts.map +1 -0
- package/dist/lib/ops/cf.js +114 -0
- package/dist/lib/ops/cf.js.map +1 -0
- package/dist/lib/ops/compose.d.ts +95 -0
- package/dist/lib/ops/compose.d.ts.map +1 -0
- package/dist/lib/ops/compose.js +165 -0
- package/dist/lib/ops/compose.js.map +1 -0
- package/dist/lib/ops/core.d.ts +117 -0
- package/dist/lib/ops/core.d.ts.map +1 -0
- package/dist/lib/ops/core.js +322 -0
- package/dist/lib/ops/core.js.map +1 -0
- package/dist/lib/ops/db.d.ts +116 -0
- package/dist/lib/ops/db.d.ts.map +1 -0
- package/dist/lib/ops/db.js +351 -0
- package/dist/lib/ops/db.js.map +1 -0
- package/dist/lib/ops/dns.d.ts +111 -0
- package/dist/lib/ops/dns.d.ts.map +1 -0
- package/dist/lib/ops/dns.js +306 -0
- package/dist/lib/ops/dns.js.map +1 -0
- package/dist/lib/ops/image.d.ts +94 -0
- package/dist/lib/ops/image.d.ts.map +1 -0
- package/dist/lib/ops/image.js +159 -0
- package/dist/lib/ops/image.js.map +1 -0
- package/dist/lib/ops/nginx.d.ts +114 -0
- package/dist/lib/ops/nginx.d.ts.map +1 -0
- package/dist/lib/ops/nginx.js +388 -0
- package/dist/lib/ops/nginx.js.map +1 -0
- package/dist/lib/ops/redis.d.ts +7 -0
- package/dist/lib/ops/redis.d.ts.map +1 -0
- package/dist/lib/ops/redis.js +35 -0
- package/dist/lib/ops/redis.js.map +1 -0
- package/dist/lib/ops/ssh.d.ts +127 -0
- package/dist/lib/ops/ssh.d.ts.map +1 -0
- package/dist/lib/ops/ssh.js +269 -0
- package/dist/lib/ops/ssh.js.map +1 -0
- package/dist/lib/prompts.d.ts +46 -0
- package/dist/lib/prompts.d.ts.map +1 -0
- package/dist/lib/prompts.js +113 -0
- package/dist/lib/prompts.js.map +1 -0
- package/dist/lib/sast.d.ts +43 -0
- package/dist/lib/sast.d.ts.map +1 -0
- package/dist/lib/sast.js +79 -0
- package/dist/lib/sast.js.map +1 -0
- package/dist/lib/sast.test.d.ts +2 -0
- package/dist/lib/sast.test.d.ts.map +1 -0
- package/dist/lib/sast.test.js +33 -0
- package/dist/lib/sast.test.js.map +1 -0
- package/dist/lib/shell.d.ts +61 -0
- package/dist/lib/shell.d.ts.map +1 -0
- package/dist/lib/shell.js +183 -0
- package/dist/lib/shell.js.map +1 -0
- package/dist/lib/ssh-config.d.ts +37 -0
- package/dist/lib/ssh-config.d.ts.map +1 -0
- package/dist/lib/ssh-config.js +122 -0
- package/dist/lib/ssh-config.js.map +1 -0
- package/dist/lib/tenant-scope.d.ts +38 -0
- package/dist/lib/tenant-scope.d.ts.map +1 -0
- package/dist/lib/tenant-scope.js +129 -0
- package/dist/lib/tenant-scope.js.map +1 -0
- package/dist/lib/tenant-scope.test.d.ts +2 -0
- package/dist/lib/tenant-scope.test.d.ts.map +1 -0
- package/dist/lib/tenant-scope.test.js +223 -0
- package/dist/lib/tenant-scope.test.js.map +1 -0
- package/package.json +58 -0
- package/templates/bootstrap/.env.template +54 -0
- package/templates/bootstrap/docker-compose.yml +145 -0
- package/templates/vhost.conf.tmpl +446 -0
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CiCore CLI — DNS / TLS provider abstraction
|
|
3
|
+
*
|
|
4
|
+
* Vendor-neutral {@link DnsProvider} interface + a Cloudflare implementation.
|
|
5
|
+
* The interface exists so a second vendor (Route53, …) can slot in later
|
|
6
|
+
* without touching callers (see brand-layer plan §K2 / §8). For now the only
|
|
7
|
+
* implementation is {@link CloudflareDnsProvider}.
|
|
8
|
+
*
|
|
9
|
+
* Two routing modes — both first-class, both modelled by the control plane
|
|
10
|
+
* (`cp_domains.dns_mode`):
|
|
11
|
+
*
|
|
12
|
+
* - `owned_zone` — the hostname lives in a zone *we* manage (e.g.
|
|
13
|
+
* `lab.multinodex.com`). We upsert a proxied CNAME → our origin target;
|
|
14
|
+
* TLS is covered by the zone's Universal / Advanced certificate. The
|
|
15
|
+
* zone id is resolved from the hostname at call time (a single host can
|
|
16
|
+
* serve several owned zones — mkt fronts both multinodex.com and
|
|
17
|
+
* randevu360.net — so we can't rely on `cfg.cfZoneId` alone).
|
|
18
|
+
*
|
|
19
|
+
* - `custom_hostname` (PRIMARY) — a domain the *customer* owns (BYO domain,
|
|
20
|
+
* Shopify/Webflow model). We register it as a Cloudflare for SaaS custom
|
|
21
|
+
* hostname in our SaaS zone (`cfg.cfZoneId`); DV TLS is issued + renewed
|
|
22
|
+
* automatically, and the customer points a CNAME at our fallback origin.
|
|
23
|
+
* This retires the per-host tunnel-ingress + `api-lab` SSL-depth hack:
|
|
24
|
+
* deep subdomains (`api.lab.x`) that Universal SSL's single-level wildcard
|
|
25
|
+
* can't cover get their own per-hostname cert for free.
|
|
26
|
+
*
|
|
27
|
+
* All calls use the host's scoped token `cfg.cfApiToken` (Bearer) — NOT the
|
|
28
|
+
* stored-config token that `lib/cloudflare.ts` reads. Keep new DNS work here,
|
|
29
|
+
* on the host-aware path. Every mutation honours `--dry-run`.
|
|
30
|
+
*/
|
|
31
|
+
import { isDryRun } from '../config.js';
|
|
32
|
+
import { log } from '../logger.js';
|
|
33
|
+
const OK_RESULT = Object.freeze({ success: true, errorMessage: '' });
|
|
34
|
+
/** Construct the configured provider for a host. Cloudflare-only today. */
|
|
35
|
+
export function createDnsProvider(cfg) {
|
|
36
|
+
return new CloudflareDnsProvider(cfg);
|
|
37
|
+
}
|
|
38
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
39
|
+
// Cloudflare implementation
|
|
40
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
41
|
+
const CF_API_BASE = 'https://api.cloudflare.com/client/v4';
|
|
42
|
+
const REQUEST_TIMEOUT_MS = 30_000;
|
|
43
|
+
class CloudflareDnsProvider {
|
|
44
|
+
cfg;
|
|
45
|
+
constructor(cfg) {
|
|
46
|
+
this.cfg = cfg;
|
|
47
|
+
}
|
|
48
|
+
async detectMode(hostname) {
|
|
49
|
+
const zoneId = await this.resolveZoneId(hostname);
|
|
50
|
+
return zoneId ? 'owned_zone' : 'custom_hostname';
|
|
51
|
+
}
|
|
52
|
+
async ensureHost(req) {
|
|
53
|
+
return req.mode === 'owned_zone'
|
|
54
|
+
? this.ensureOwnedZone(req)
|
|
55
|
+
: this.ensureCustomHostname(req);
|
|
56
|
+
}
|
|
57
|
+
async getHostState(hostname, mode) {
|
|
58
|
+
if (mode === 'owned_zone') {
|
|
59
|
+
const zoneId = await this.resolveZoneId(hostname);
|
|
60
|
+
if (!zoneId)
|
|
61
|
+
return this.failState(hostname, mode, `no managed zone found for ${hostname}`);
|
|
62
|
+
const record = await this.findRecord(zoneId, hostname);
|
|
63
|
+
if (!record)
|
|
64
|
+
return this.failState(hostname, mode, `no DNS record for ${hostname}`);
|
|
65
|
+
return Object.freeze({
|
|
66
|
+
success: true,
|
|
67
|
+
hostname,
|
|
68
|
+
mode,
|
|
69
|
+
routingStatus: 'active',
|
|
70
|
+
sslStatus: 'active', // covered by zone Universal/ACM cert
|
|
71
|
+
cfZoneId: zoneId,
|
|
72
|
+
cnameTarget: record.content,
|
|
73
|
+
errorMessage: '',
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
const existing = await this.findCustomHostname(hostname);
|
|
77
|
+
if (!existing)
|
|
78
|
+
return this.failState(hostname, mode, `no custom hostname for ${hostname}`);
|
|
79
|
+
return this.customHostnameToState(hostname, existing);
|
|
80
|
+
}
|
|
81
|
+
async removeHost(hostname, mode) {
|
|
82
|
+
if (mode === 'owned_zone') {
|
|
83
|
+
const zoneId = await this.resolveZoneId(hostname);
|
|
84
|
+
if (!zoneId)
|
|
85
|
+
return OK_RESULT; // nothing to remove
|
|
86
|
+
const record = await this.findRecord(zoneId, hostname);
|
|
87
|
+
if (!record)
|
|
88
|
+
return OK_RESULT;
|
|
89
|
+
if (isDryRun()) {
|
|
90
|
+
log.debug(`[dry-run] DELETE dns_record ${hostname} (${record.id}) in zone ${zoneId}`);
|
|
91
|
+
return OK_RESULT;
|
|
92
|
+
}
|
|
93
|
+
const res = await this.cfFetch(`/zones/${zoneId}/dns_records/${record.id}`, { method: 'DELETE' });
|
|
94
|
+
return res.success ? OK_RESULT : Object.freeze({ success: false, errorMessage: res.errorMessage });
|
|
95
|
+
}
|
|
96
|
+
const existing = await this.findCustomHostname(hostname);
|
|
97
|
+
if (!existing)
|
|
98
|
+
return OK_RESULT;
|
|
99
|
+
if (isDryRun()) {
|
|
100
|
+
log.debug(`[dry-run] DELETE custom_hostname ${hostname} (${existing.id})`);
|
|
101
|
+
return OK_RESULT;
|
|
102
|
+
}
|
|
103
|
+
const res = await this.cfFetch(`/zones/${this.cfg.cfZoneId}/custom_hostnames/${existing.id}`, { method: 'DELETE' });
|
|
104
|
+
return res.success ? OK_RESULT : Object.freeze({ success: false, errorMessage: res.errorMessage });
|
|
105
|
+
}
|
|
106
|
+
async ensureFallbackOrigin(originHostname) {
|
|
107
|
+
log.info(`[dns] ensure SaaS fallback origin → ${originHostname} (zone ${this.cfg.cfZoneId})`);
|
|
108
|
+
if (isDryRun()) {
|
|
109
|
+
log.debug(`[dry-run] PUT custom_hostnames/fallback_origin { origin: ${originHostname} }`);
|
|
110
|
+
return OK_RESULT;
|
|
111
|
+
}
|
|
112
|
+
const res = await this.cfFetch(`/zones/${this.cfg.cfZoneId}/custom_hostnames/fallback_origin`, { method: 'PUT', body: { origin: originHostname } });
|
|
113
|
+
return res.success ? OK_RESULT : Object.freeze({ success: false, errorMessage: res.errorMessage });
|
|
114
|
+
}
|
|
115
|
+
// ── owned_zone ───────────────────────────────────────────────────────
|
|
116
|
+
async ensureOwnedZone(req) {
|
|
117
|
+
const { hostname, originTarget } = req;
|
|
118
|
+
const zoneId = await this.resolveZoneId(hostname);
|
|
119
|
+
if (!zoneId) {
|
|
120
|
+
return this.failState(hostname, 'owned_zone', `no Cloudflare zone manages '${hostname}'. Use custom_hostname mode for BYO domains, ` +
|
|
121
|
+
`or add the zone to the account.`);
|
|
122
|
+
}
|
|
123
|
+
log.info(`[dns] owned_zone: ${hostname} → CNAME ${originTarget} (proxied, zone ${zoneId})`);
|
|
124
|
+
if (isDryRun()) {
|
|
125
|
+
log.debug(`[dry-run] upsert proxied CNAME ${hostname} → ${originTarget}`);
|
|
126
|
+
return this.okOwnedState(hostname, zoneId, originTarget);
|
|
127
|
+
}
|
|
128
|
+
const existing = await this.findRecord(zoneId, hostname);
|
|
129
|
+
const body = { type: 'CNAME', name: hostname, content: originTarget, proxied: true, ttl: 1 };
|
|
130
|
+
const res = existing
|
|
131
|
+
? await this.cfFetch(`/zones/${zoneId}/dns_records/${existing.id}`, { method: 'PUT', body })
|
|
132
|
+
: await this.cfFetch(`/zones/${zoneId}/dns_records`, { method: 'POST', body });
|
|
133
|
+
if (!res.success)
|
|
134
|
+
return this.failState(hostname, 'owned_zone', res.errorMessage);
|
|
135
|
+
return this.okOwnedState(hostname, zoneId, originTarget);
|
|
136
|
+
}
|
|
137
|
+
okOwnedState(hostname, zoneId, cnameTarget) {
|
|
138
|
+
// Universal SSL covers the apex + one wildcard level. A deeper subdomain
|
|
139
|
+
// (api.lab.x) is NOT covered by `*.x` — warn rather than silently claim
|
|
140
|
+
// active, so the operator knows to use custom_hostname or ACM there.
|
|
141
|
+
const sslStatus = isCoveredByUniversalSsl(hostname) ? 'active' : 'pending';
|
|
142
|
+
if (sslStatus === 'pending') {
|
|
143
|
+
log.warn(`[dns] '${hostname}' is a deep subdomain — Universal SSL ('*.<zone>') does not cover it. ` +
|
|
144
|
+
`Use custom_hostname mode or Advanced Certificate Manager for a valid cert.`);
|
|
145
|
+
}
|
|
146
|
+
return Object.freeze({
|
|
147
|
+
success: true,
|
|
148
|
+
hostname,
|
|
149
|
+
mode: 'owned_zone',
|
|
150
|
+
routingStatus: 'active',
|
|
151
|
+
sslStatus,
|
|
152
|
+
cfZoneId: zoneId,
|
|
153
|
+
cnameTarget,
|
|
154
|
+
errorMessage: '',
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
// ── custom_hostname (Cloudflare for SaaS) ────────────────────────────
|
|
158
|
+
async ensureCustomHostname(req) {
|
|
159
|
+
const { hostname, originTarget } = req;
|
|
160
|
+
log.info(`[dns] custom_hostname: register ${hostname} (CF for SaaS, DV auto-SSL)`);
|
|
161
|
+
if (isDryRun()) {
|
|
162
|
+
log.debug(`[dry-run] POST custom_hostnames { hostname: ${hostname}, ssl.method: http, type: dv }`);
|
|
163
|
+
return Object.freeze({
|
|
164
|
+
success: true,
|
|
165
|
+
hostname,
|
|
166
|
+
mode: 'custom_hostname',
|
|
167
|
+
routingStatus: 'pending',
|
|
168
|
+
sslStatus: 'pending',
|
|
169
|
+
cfZoneId: this.cfg.cfZoneId,
|
|
170
|
+
cnameTarget: originTarget,
|
|
171
|
+
errorMessage: '',
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
// Idempotent: reuse an existing custom hostname rather than 409-ing.
|
|
175
|
+
const existing = await this.findCustomHostname(hostname);
|
|
176
|
+
if (existing) {
|
|
177
|
+
log.debug(`[dns] custom hostname ${hostname} already registered (${existing.id})`);
|
|
178
|
+
return this.customHostnameToState(hostname, existing, originTarget);
|
|
179
|
+
}
|
|
180
|
+
const body = {
|
|
181
|
+
hostname,
|
|
182
|
+
ssl: {
|
|
183
|
+
method: 'http',
|
|
184
|
+
type: 'dv',
|
|
185
|
+
settings: { min_tls_version: '1.2' },
|
|
186
|
+
},
|
|
187
|
+
};
|
|
188
|
+
const res = await this.cfFetch(`/zones/${this.cfg.cfZoneId}/custom_hostnames`, { method: 'POST', body });
|
|
189
|
+
if (!res.success || !res.result) {
|
|
190
|
+
return this.failState(hostname, 'custom_hostname', res.errorMessage);
|
|
191
|
+
}
|
|
192
|
+
return this.customHostnameToState(hostname, res.result, originTarget);
|
|
193
|
+
}
|
|
194
|
+
customHostnameToState(hostname, ch, cnameTarget) {
|
|
195
|
+
const sslStatus = ch.ssl?.status === 'active' ? 'active' : 'pending';
|
|
196
|
+
const routingStatus = ch.status === 'active' ? 'active' : 'pending';
|
|
197
|
+
return Object.freeze({
|
|
198
|
+
success: true,
|
|
199
|
+
hostname,
|
|
200
|
+
mode: 'custom_hostname',
|
|
201
|
+
routingStatus,
|
|
202
|
+
sslStatus,
|
|
203
|
+
customHostnameId: ch.id,
|
|
204
|
+
cfZoneId: this.cfg.cfZoneId,
|
|
205
|
+
cnameTarget,
|
|
206
|
+
verification: {
|
|
207
|
+
ownership: ch.ownership_verification ?? null,
|
|
208
|
+
ssl: ch.ssl ?? null,
|
|
209
|
+
},
|
|
210
|
+
errorMessage: '',
|
|
211
|
+
});
|
|
212
|
+
}
|
|
213
|
+
// ── zone / record helpers ────────────────────────────────────────────
|
|
214
|
+
/**
|
|
215
|
+
* Resolve the Cloudflare zone id that manages `hostname`, or `undefined`
|
|
216
|
+
* if no zone in the account does (→ treat as a customer BYO domain).
|
|
217
|
+
*
|
|
218
|
+
* Walks the hostname's parent labels (e.g. `api.lab.multinodex.com` →
|
|
219
|
+
* `lab.multinodex.com` → `multinodex.com` → `com`) and returns the first
|
|
220
|
+
* label set that exactly matches a zone name. Handles multi-part TLDs
|
|
221
|
+
* (`.com.tr`) naturally because we test every suffix, not just the last two.
|
|
222
|
+
*/
|
|
223
|
+
async resolveZoneId(hostname) {
|
|
224
|
+
const labels = hostname.split('.');
|
|
225
|
+
for (let i = 0; i < labels.length - 1; i++) {
|
|
226
|
+
const candidate = labels.slice(i).join('.');
|
|
227
|
+
const res = await this.cfFetch(`/zones?name=${encodeURIComponent(candidate)}`);
|
|
228
|
+
if (res.success && res.result && res.result.length > 0) {
|
|
229
|
+
return res.result[0].id;
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
return undefined;
|
|
233
|
+
}
|
|
234
|
+
async findRecord(zoneId, name) {
|
|
235
|
+
const res = await this.cfFetch(`/zones/${zoneId}/dns_records?name=${encodeURIComponent(name)}`);
|
|
236
|
+
return res.success && res.result && res.result.length > 0 ? res.result[0] : undefined;
|
|
237
|
+
}
|
|
238
|
+
async findCustomHostname(hostname) {
|
|
239
|
+
const res = await this.cfFetch(`/zones/${this.cfg.cfZoneId}/custom_hostnames?hostname=${encodeURIComponent(hostname)}`);
|
|
240
|
+
return res.success && res.result && res.result.length > 0 ? res.result[0] : undefined;
|
|
241
|
+
}
|
|
242
|
+
// ── HTTP ─────────────────────────────────────────────────────────────
|
|
243
|
+
failState(hostname, mode, errorMessage) {
|
|
244
|
+
return Object.freeze({
|
|
245
|
+
success: false,
|
|
246
|
+
hostname,
|
|
247
|
+
mode,
|
|
248
|
+
routingStatus: 'error',
|
|
249
|
+
sslStatus: 'error',
|
|
250
|
+
errorMessage,
|
|
251
|
+
});
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Single Cloudflare v4 fetch. Bearer-auths with the host-scoped token,
|
|
255
|
+
* times out at {@link REQUEST_TIMEOUT_MS}, and normalises CF's
|
|
256
|
+
* `{ success, result, errors }` envelope into {@link CfApiResult}.
|
|
257
|
+
*/
|
|
258
|
+
async cfFetch(endpoint, options = {}) {
|
|
259
|
+
const controller = new AbortController();
|
|
260
|
+
const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
261
|
+
try {
|
|
262
|
+
const response = await fetch(`${CF_API_BASE}${endpoint}`, {
|
|
263
|
+
method: options.method ?? 'GET',
|
|
264
|
+
headers: {
|
|
265
|
+
Authorization: `Bearer ${this.cfg.cfApiToken}`,
|
|
266
|
+
'Content-Type': 'application/json',
|
|
267
|
+
},
|
|
268
|
+
body: options.body !== undefined ? JSON.stringify(options.body) : undefined,
|
|
269
|
+
signal: controller.signal,
|
|
270
|
+
});
|
|
271
|
+
const data = (await response.json());
|
|
272
|
+
if (data.success) {
|
|
273
|
+
return { success: true, result: data.result, errorMessage: '' };
|
|
274
|
+
}
|
|
275
|
+
const msg = data.errors?.map((e) => `${e.code ?? ''} ${e.message}`.trim()).join('; ');
|
|
276
|
+
return { success: false, errorMessage: msg || `HTTP ${response.status}` };
|
|
277
|
+
}
|
|
278
|
+
catch (error) {
|
|
279
|
+
const message = error instanceof Error
|
|
280
|
+
? error.name === 'AbortError'
|
|
281
|
+
? `Cloudflare request timed out after ${REQUEST_TIMEOUT_MS / 1000}s`
|
|
282
|
+
: error.message
|
|
283
|
+
: String(error);
|
|
284
|
+
return { success: false, errorMessage: message };
|
|
285
|
+
}
|
|
286
|
+
finally {
|
|
287
|
+
clearTimeout(timeout);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
292
|
+
// Internal helpers
|
|
293
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
294
|
+
/**
|
|
295
|
+
* Cloudflare Universal SSL issues a cert for the apex plus a single wildcard
|
|
296
|
+
* level (`example.com` + `*.example.com`). A hostname with two or more labels
|
|
297
|
+
* below the registrable domain (e.g. `api.lab.example.com`) is therefore NOT
|
|
298
|
+
* covered. We approximate "registrable domain = last two labels" which is
|
|
299
|
+
* correct for plain TLDs and conservative (false-pending, never false-active)
|
|
300
|
+
* for multi-part TLDs like `.com.tr` — there it flags one level early, which
|
|
301
|
+
* only nudges the operator toward a proper per-hostname cert.
|
|
302
|
+
*/
|
|
303
|
+
function isCoveredByUniversalSsl(hostname) {
|
|
304
|
+
return hostname.split('.').length <= 3;
|
|
305
|
+
}
|
|
306
|
+
//# sourceMappingURL=dns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dns.js","sourceRoot":"","sources":["../../../src/lib/ops/dns.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CiCore CLI — Docker image primitives
|
|
3
|
+
*
|
|
4
|
+
* Build / push / pull / retag operations for the three core service images
|
|
5
|
+
* (`nuxt`, `php`, `nginx`). Image names are derived from {@link HostConfig}:
|
|
6
|
+
*
|
|
7
|
+
* push target = `<registry>/<imagePrefix>-<service>:<tag>`
|
|
8
|
+
* e.g. `cisofttr/cicore-nuxt:baea044`
|
|
9
|
+
*
|
|
10
|
+
* compose name = `<composeImageName>-<service>:latest`
|
|
11
|
+
* e.g. `baynis/vucore-nuxt:latest`
|
|
12
|
+
*
|
|
13
|
+
* For vucore (registry=baynis, imagePrefix=vucore, composeImageName=baynis/vucore)
|
|
14
|
+
* the two converge; the retag is just `TAG → latest`. For cicore (registry=
|
|
15
|
+
* cisofttr, imagePrefix=cicore, composeImageName=baynis/vucore) the retag
|
|
16
|
+
* also crosses namespaces so the unmodified docker-compose.yml on the server
|
|
17
|
+
* keeps working without source changes.
|
|
18
|
+
*/
|
|
19
|
+
import type { AppServiceKey, HostConfig } from '../hosts.js';
|
|
20
|
+
import { type ExecResult } from './ssh.js';
|
|
21
|
+
/** Push-target reference for a service+tag combo: `<registry>/<prefix>-<svc>:<tag>`. */
|
|
22
|
+
export declare function pushImageRef(cfg: HostConfig, service: AppServiceKey, tag: string): string;
|
|
23
|
+
/** Compose-target reference (what docker-compose.yml expects): `<composeImageName>-<svc>:latest`. */
|
|
24
|
+
export declare function composeImageRef(cfg: HostConfig, service: AppServiceKey): string;
|
|
25
|
+
export interface BuildOptions {
|
|
26
|
+
/** Working directory for the docker build (where Dockerfile lives). Default: cwd. */
|
|
27
|
+
readonly cwd?: string;
|
|
28
|
+
/**
|
|
29
|
+
* Dockerfile path relative to `cwd`. Default: `Dockerfile.<service>.production`
|
|
30
|
+
* matching the existing repo convention.
|
|
31
|
+
*/
|
|
32
|
+
readonly dockerfile?: string;
|
|
33
|
+
/**
|
|
34
|
+
* Buildx builder name. Auto-created on first use. Default `vu-cli-builder`.
|
|
35
|
+
* Separate builder = isolated cache from default `desktop-linux`.
|
|
36
|
+
*/
|
|
37
|
+
readonly builderName?: string;
|
|
38
|
+
/** Suppress per-step log lines. Errors still surface in the returned result. */
|
|
39
|
+
readonly silent?: boolean;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Build a service image with buildx for `linux/amd64` (Mac M-series cross-build).
|
|
43
|
+
*
|
|
44
|
+
* Two tags are applied in one build pass: `:<tag>` and `:latest`. Buildx
|
|
45
|
+
* `--load` materializes the image in the local Docker daemon so the next
|
|
46
|
+
* push step finds it. The amd64 platform pin is mandatory — without it,
|
|
47
|
+
* arm64 hosts produce images that crash via emulation on the amd64 server.
|
|
48
|
+
*
|
|
49
|
+
* Builder is created lazily on first call; subsequent calls reuse it.
|
|
50
|
+
*/
|
|
51
|
+
export declare function buildImage(cfg: HostConfig, service: AppServiceKey, tag: string, opts?: BuildOptions): Promise<ExecResult>;
|
|
52
|
+
/**
|
|
53
|
+
* Push a previously-built image to Docker Hub (or whichever registry the
|
|
54
|
+
* local docker daemon is logged into for `cfg.registry`).
|
|
55
|
+
*
|
|
56
|
+
* Pushes the specific `tag` and `:latest` in sequence — Docker dedupes shared
|
|
57
|
+
* layers, so the second push is mostly metadata.
|
|
58
|
+
*/
|
|
59
|
+
export declare function pushImage(cfg: HostConfig, service: AppServiceKey, tag: string, opts?: {
|
|
60
|
+
readonly silent?: boolean;
|
|
61
|
+
}): Promise<ExecResult>;
|
|
62
|
+
/**
|
|
63
|
+
* Retag a locally-built image without rebuilding — useful when the same
|
|
64
|
+
* binary needs to ship under multiple names (e.g. `vu deploy core` reusing
|
|
65
|
+
* an already-pushed image for a hotfix tag).
|
|
66
|
+
*/
|
|
67
|
+
export declare function tagImage(fromRef: string, toRef: string, opts?: {
|
|
68
|
+
readonly silent?: boolean;
|
|
69
|
+
}): Promise<ExecResult>;
|
|
70
|
+
/**
|
|
71
|
+
* `docker pull` the push-target image on the remote host. Assumes the host
|
|
72
|
+
* is already `docker login`-ed for `cfg.registry` (setup flow handles this).
|
|
73
|
+
*/
|
|
74
|
+
export declare function pullImage(cfg: HostConfig, service: AppServiceKey, tag: string, opts?: {
|
|
75
|
+
readonly silent?: boolean;
|
|
76
|
+
}): Promise<ExecResult>;
|
|
77
|
+
/**
|
|
78
|
+
* Retag a pulled image to the name the compose file references.
|
|
79
|
+
*
|
|
80
|
+
* For cicore this crosses namespaces (`cisofttr/cicore-* → baynis/vucore-*`)
|
|
81
|
+
* so the unmodified docker-compose.yml keeps working. For vucore it's a
|
|
82
|
+
* within-namespace tag rename (`baynis/vucore-*:TAG → :latest`).
|
|
83
|
+
*
|
|
84
|
+
* Idempotent — retagging the same image multiple times has no effect.
|
|
85
|
+
*/
|
|
86
|
+
export declare function retagForCompose(cfg: HostConfig, service: AppServiceKey, srcTag: string, opts?: {
|
|
87
|
+
readonly silent?: boolean;
|
|
88
|
+
}): Promise<ExecResult>;
|
|
89
|
+
/**
|
|
90
|
+
* Check whether an image with the given reference exists in the local
|
|
91
|
+
* docker daemon. Returns `false` for "doesn't exist" or any daemon error.
|
|
92
|
+
*/
|
|
93
|
+
export declare function imageExistsLocally(ref: string): Promise<boolean>;
|
|
94
|
+
//# sourceMappingURL=image.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image.d.ts","sourceRoot":"","sources":["../../../src/lib/ops/image.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AAE5D,OAAO,EAAuB,KAAK,UAAU,EAAE,MAAM,UAAU,CAAA;AAM/D,wFAAwF;AACxF,wBAAgB,YAAY,CAAC,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,aAAa,EAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAEzF;AAED,qGAAqG;AACrG,wBAAgB,eAAe,CAAC,GAAG,EAAE,UAAU,EAAE,OAAO,EAAE,aAAa,GAAG,MAAM,CAE/E;AAMD,MAAM,WAAW,YAAY;IAC3B,qFAAqF;IACrF,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAA;IACrB;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAA;IAC5B;;;OAGG;IACH,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,gFAAgF;IAChF,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAC1B;AAED;;;;;;;;;GASG;AACH,wBAAsB,UAAU,CAC9B,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,aAAa,EACtB,GAAG,EAAE,MAAM,EACX,IAAI,GAAE,YAAiB,GACtB,OAAO,CAAC,UAAU,CAAC,CA+BrB;AAiBD;;;;;;GAMG;AACH,wBAAsB,SAAS,CAC7B,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,aAAa,EACtB,GAAG,EAAE,MAAM,EACX,IAAI,GAAE;IAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GACvC,OAAO,CAAC,UAAU,CAAC,CAUrB;AAED;;;;GAIG;AACH,wBAAsB,QAAQ,CAC5B,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,MAAM,EACb,IAAI,GAAE;IAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GACvC,OAAO,CAAC,UAAU,CAAC,CAGrB;AAMD;;;GAGG;AACH,wBAAsB,SAAS,CAC7B,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,aAAa,EACtB,GAAG,EAAE,MAAM,EACX,IAAI,GAAE;IAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GACvC,OAAO,CAAC,UAAU,CAAC,CAIrB;AAED;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CACnC,GAAG,EAAE,UAAU,EACf,OAAO,EAAE,aAAa,EACtB,MAAM,EAAE,MAAM,EACd,IAAI,GAAE;IAAE,QAAQ,CAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GACvC,OAAO,CAAC,UAAU,CAAC,CAOrB;AAED;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAGtE"}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CiCore CLI — Docker image primitives
|
|
3
|
+
*
|
|
4
|
+
* Build / push / pull / retag operations for the three core service images
|
|
5
|
+
* (`nuxt`, `php`, `nginx`). Image names are derived from {@link HostConfig}:
|
|
6
|
+
*
|
|
7
|
+
* push target = `<registry>/<imagePrefix>-<service>:<tag>`
|
|
8
|
+
* e.g. `cisofttr/cicore-nuxt:baea044`
|
|
9
|
+
*
|
|
10
|
+
* compose name = `<composeImageName>-<service>:latest`
|
|
11
|
+
* e.g. `baynis/vucore-nuxt:latest`
|
|
12
|
+
*
|
|
13
|
+
* For vucore (registry=baynis, imagePrefix=vucore, composeImageName=baynis/vucore)
|
|
14
|
+
* the two converge; the retag is just `TAG → latest`. For cicore (registry=
|
|
15
|
+
* cisofttr, imagePrefix=cicore, composeImageName=baynis/vucore) the retag
|
|
16
|
+
* also crosses namespaces so the unmodified docker-compose.yml on the server
|
|
17
|
+
* keeps working without source changes.
|
|
18
|
+
*/
|
|
19
|
+
import { log } from '../logger.js';
|
|
20
|
+
import { runLocal, runRemote } from './ssh.js';
|
|
21
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
22
|
+
// Image name builders
|
|
23
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
24
|
+
/** Push-target reference for a service+tag combo: `<registry>/<prefix>-<svc>:<tag>`. */
|
|
25
|
+
export function pushImageRef(cfg, service, tag) {
|
|
26
|
+
return `${cfg.registry}/${cfg.imagePrefix}-${service}:${tag}`;
|
|
27
|
+
}
|
|
28
|
+
/** Compose-target reference (what docker-compose.yml expects): `<composeImageName>-<svc>:latest`. */
|
|
29
|
+
export function composeImageRef(cfg, service) {
|
|
30
|
+
return `${cfg.composeImageName}-${service}:latest`;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Build a service image with buildx for `linux/amd64` (Mac M-series cross-build).
|
|
34
|
+
*
|
|
35
|
+
* Two tags are applied in one build pass: `:<tag>` and `:latest`. Buildx
|
|
36
|
+
* `--load` materializes the image in the local Docker daemon so the next
|
|
37
|
+
* push step finds it. The amd64 platform pin is mandatory — without it,
|
|
38
|
+
* arm64 hosts produce images that crash via emulation on the amd64 server.
|
|
39
|
+
*
|
|
40
|
+
* Builder is created lazily on first call; subsequent calls reuse it.
|
|
41
|
+
*/
|
|
42
|
+
export async function buildImage(cfg, service, tag, opts = {}) {
|
|
43
|
+
const builder = opts.builderName ?? 'vu-cli-builder';
|
|
44
|
+
await ensureBuilder(builder, opts.silent === true);
|
|
45
|
+
const dockerfile = opts.dockerfile ?? `Dockerfile.${service}.production`;
|
|
46
|
+
const tagged = pushImageRef(cfg, service, tag);
|
|
47
|
+
const latest = pushImageRef(cfg, service, 'latest');
|
|
48
|
+
if (!opts.silent) {
|
|
49
|
+
log.info(`Building ${tagged} (+latest) from ${dockerfile} ...`);
|
|
50
|
+
}
|
|
51
|
+
return runLocal('docker', [
|
|
52
|
+
'buildx',
|
|
53
|
+
'build',
|
|
54
|
+
'--builder',
|
|
55
|
+
builder,
|
|
56
|
+
'--platform',
|
|
57
|
+
'linux/amd64',
|
|
58
|
+
'--load',
|
|
59
|
+
'-f',
|
|
60
|
+
dockerfile,
|
|
61
|
+
'-t',
|
|
62
|
+
tagged,
|
|
63
|
+
'-t',
|
|
64
|
+
latest,
|
|
65
|
+
'.',
|
|
66
|
+
], { cwd: opts.cwd, stream: true });
|
|
67
|
+
}
|
|
68
|
+
async function ensureBuilder(name, silent) {
|
|
69
|
+
const inspect = await runLocal('docker', ['buildx', 'inspect', name], { silent: true });
|
|
70
|
+
if (inspect.success)
|
|
71
|
+
return;
|
|
72
|
+
if (!silent)
|
|
73
|
+
log.info(`Creating buildx builder '${name}' ...`);
|
|
74
|
+
await runLocal('docker', ['buildx', 'create', '--name', name, '--use', '--driver-opt', 'network=host'], { silent });
|
|
75
|
+
}
|
|
76
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
77
|
+
// Push (local → Docker Hub)
|
|
78
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
79
|
+
/**
|
|
80
|
+
* Push a previously-built image to Docker Hub (or whichever registry the
|
|
81
|
+
* local docker daemon is logged into for `cfg.registry`).
|
|
82
|
+
*
|
|
83
|
+
* Pushes the specific `tag` and `:latest` in sequence — Docker dedupes shared
|
|
84
|
+
* layers, so the second push is mostly metadata.
|
|
85
|
+
*/
|
|
86
|
+
export async function pushImage(cfg, service, tag, opts = {}) {
|
|
87
|
+
const tagged = pushImageRef(cfg, service, tag);
|
|
88
|
+
const latest = pushImageRef(cfg, service, 'latest');
|
|
89
|
+
if (!opts.silent)
|
|
90
|
+
log.info(`Pushing ${tagged} ...`);
|
|
91
|
+
const taggedResult = await runLocal('docker', ['push', tagged], { stream: true });
|
|
92
|
+
if (!taggedResult.success)
|
|
93
|
+
return taggedResult;
|
|
94
|
+
if (!opts.silent)
|
|
95
|
+
log.info(`Pushing ${latest} ...`);
|
|
96
|
+
return runLocal('docker', ['push', latest], { stream: true });
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* Retag a locally-built image without rebuilding — useful when the same
|
|
100
|
+
* binary needs to ship under multiple names (e.g. `vu deploy core` reusing
|
|
101
|
+
* an already-pushed image for a hotfix tag).
|
|
102
|
+
*/
|
|
103
|
+
export async function tagImage(fromRef, toRef, opts = {}) {
|
|
104
|
+
if (!opts.silent)
|
|
105
|
+
log.info(`Tagging ${fromRef} → ${toRef}`);
|
|
106
|
+
return runLocal('docker', ['tag', fromRef, toRef], { silent: opts.silent });
|
|
107
|
+
}
|
|
108
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
109
|
+
// Remote pull + retag (server side)
|
|
110
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
111
|
+
/**
|
|
112
|
+
* `docker pull` the push-target image on the remote host. Assumes the host
|
|
113
|
+
* is already `docker login`-ed for `cfg.registry` (setup flow handles this).
|
|
114
|
+
*/
|
|
115
|
+
export async function pullImage(cfg, service, tag, opts = {}) {
|
|
116
|
+
const ref = pushImageRef(cfg, service, tag);
|
|
117
|
+
if (!opts.silent)
|
|
118
|
+
log.info(`Pulling ${ref} on ${cfg.brandName} ...`);
|
|
119
|
+
return runRemote(cfg, `docker pull ${shellQuote(ref)}`, { silent: opts.silent });
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Retag a pulled image to the name the compose file references.
|
|
123
|
+
*
|
|
124
|
+
* For cicore this crosses namespaces (`cisofttr/cicore-* → baynis/vucore-*`)
|
|
125
|
+
* so the unmodified docker-compose.yml keeps working. For vucore it's a
|
|
126
|
+
* within-namespace tag rename (`baynis/vucore-*:TAG → :latest`).
|
|
127
|
+
*
|
|
128
|
+
* Idempotent — retagging the same image multiple times has no effect.
|
|
129
|
+
*/
|
|
130
|
+
export async function retagForCompose(cfg, service, srcTag, opts = {}) {
|
|
131
|
+
const src = pushImageRef(cfg, service, srcTag);
|
|
132
|
+
const dst = composeImageRef(cfg, service);
|
|
133
|
+
if (!opts.silent)
|
|
134
|
+
log.info(`Retag ${src} → ${dst}`);
|
|
135
|
+
return runRemote(cfg, `docker tag ${shellQuote(src)} ${shellQuote(dst)}`, {
|
|
136
|
+
silent: opts.silent,
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Check whether an image with the given reference exists in the local
|
|
141
|
+
* docker daemon. Returns `false` for "doesn't exist" or any daemon error.
|
|
142
|
+
*/
|
|
143
|
+
export async function imageExistsLocally(ref) {
|
|
144
|
+
const result = await runLocal('docker', ['image', 'inspect', ref], { silent: true });
|
|
145
|
+
return result.success;
|
|
146
|
+
}
|
|
147
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
148
|
+
// Helpers
|
|
149
|
+
// ─────────────────────────────────────────────────────────────────────────
|
|
150
|
+
/**
|
|
151
|
+
* Single-quote-wrap a string for safe shell interpolation, escaping any
|
|
152
|
+
* embedded `'`. Used for image refs that may contain `/` (always) and `:`
|
|
153
|
+
* (always) — both shell-safe but quoting defends against future names with
|
|
154
|
+
* other characters.
|
|
155
|
+
*/
|
|
156
|
+
function shellQuote(s) {
|
|
157
|
+
return `'${s.replace(/'/g, `'\\''`)}'`;
|
|
158
|
+
}
|
|
159
|
+
//# sourceMappingURL=image.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"image.js","sourceRoot":"","sources":["../../../src/lib/ops/image.ts"],"names":[],"mappings":"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"}
|