@rashidee/co2 1.3.17 → 1.3.18
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/dist/index.js
CHANGED
|
@@ -6809,7 +6809,7 @@ var {
|
|
|
6809
6809
|
// package.json
|
|
6810
6810
|
var package_default = {
|
|
6811
6811
|
name: "@rashidee/co2",
|
|
6812
|
-
version: "1.3.
|
|
6812
|
+
version: "1.3.18",
|
|
6813
6813
|
description: "Compound Context Studio \u2014 self-hosted team context authoring for CO2 projects",
|
|
6814
6814
|
type: "module",
|
|
6815
6815
|
license: "MIT",
|
package/package.json
CHANGED
|
@@ -21,12 +21,12 @@ description: >
|
|
|
21
21
|
copy, docker run or kubectl apply, or none). Controls are disabled for any application that
|
|
22
22
|
has never completed a conductor-develop workflow run (checked against the studio's
|
|
23
23
|
/api/cicd-guard endpoint using the spawn-env token). Health/port probing marks apps already
|
|
24
|
-
running — even ones started outside this app. There is NO version selector
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
images are tagged <repo>/<app
|
|
28
|
-
Dockerfiles materialized before Build when missing; Build never
|
|
29
|
-
|
|
24
|
+
running — even ones started outside this app. There is NO version selector and there are NO
|
|
25
|
+
Build All / Start All / Stop All buttons — every control operates per application on the
|
|
26
|
+
current working tree, and the plan's commands run verbatim (no {VERSION} or other token
|
|
27
|
+
substitution) — docker images are tagged <repo>/<app>:latest when the plan's Docker section
|
|
28
|
+
is active (managed Dockerfiles materialized before Build when missing; Build never
|
|
29
|
+
pushes). When the approved plan declares an `## Infrastructure` list, the
|
|
30
30
|
main page shows an "Infrastructure" link that opens a separate status-only page listing every
|
|
31
31
|
supporting 3rd-party service with a live TCP-reachability indicator and a console link. Writes cicd/CICD_GENERATION.md with `**Status**:` IN PROGRESS →
|
|
32
32
|
COMPLETED so the studio watcher detects completion. Idempotent — re-running regenerates
|
|
@@ -75,9 +75,8 @@ Read `references/cicd-app-template.md` for the complete file templates. Produce:
|
|
|
75
75
|
`{ "path": "Dockerfile", "content": "<body>" }` for a `managed` Dockerfile from the plan's
|
|
76
76
|
`## Dockerfiles` section, or `{ "path": "Dockerfile", "content": null }` for an `existing`
|
|
77
77
|
one (`null` entirely when the app has no image). Copy managed Dockerfile bodies verbatim.
|
|
78
|
-
Commands
|
|
79
|
-
|
|
80
|
-
semver, `v` prefix stripped, fallback `latest`) — never via any user-facing selector. `deployMethod` is the plan's Deploy Method column
|
|
78
|
+
Commands are copied verbatim from the plan and run as-is — there is NO `{VERSION}` token
|
|
79
|
+
or any other placeholder substitution. `deployMethod` is the plan's Deploy Method column
|
|
81
80
|
(`none|web-server|docker|kubernetes`) and `deploy` its command (`null` when the method is
|
|
82
81
|
`none`). The `infra` array comes from the plan's `## Infrastructure` table (one entry per
|
|
83
82
|
row); `url` is `null` when the Console URL cell is `-`. Emit `"infra": []` when the plan's
|
|
@@ -164,22 +163,15 @@ port, application count) and STOP — the studio starts/embeds the app.
|
|
|
164
163
|
(`<project>/<folder>`) as working directory — command paths are relative to the application
|
|
165
164
|
folder (e.g. `java -jar target\app.jar`), never to the project root. Deploy renders only
|
|
166
165
|
when the plan assigned a deploy method other than `none`.
|
|
167
|
-
- **NO version
|
|
168
|
-
any other version choice
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
every 30 s; latest first), minus its `v` prefix, falling back to `latest` when the studio
|
|
173
|
-
is unreachable. Never render a UI control for it and never check out code.
|
|
166
|
+
- **NO version UI, NO All-apps buttons, NO command substitution**: the page carries NO
|
|
167
|
+
version dropdown or any other version choice, and NO Build All / Start All / Stop All
|
|
168
|
+
buttons — Build/Start/Stop/Deploy are per-application only and always operate on the
|
|
169
|
+
CURRENT working tree, running the plan's commands verbatim (no `{VERSION}` or other token
|
|
170
|
+
substitution). Never render a version control and never check out code.
|
|
174
171
|
- **Dockerfile materialization**: before Build, when an app's `dockerfile.content` is set and
|
|
175
172
|
no file exists at its path in the application folder, the server writes it (never
|
|
176
173
|
overwriting an existing Dockerfile — mirror of the managed-config rule). Build never
|
|
177
174
|
pushes an image; pushing belongs to a `docker` deploy command.
|
|
178
|
-
- **All-apps controls**: the top bar carries **Build All**, **Start All** and **Stop All**
|
|
179
|
-
buttons. Build All builds eligible apps SEQUENTIALLY (one at a time — honest logs, no CPU
|
|
180
|
-
storm); Start All starts every eligible stopped app; Stop All stops every running app.
|
|
181
|
-
Eligible = develop-gate not failed, not busy, and (for build/start) not already running;
|
|
182
|
-
ineligible apps are skipped silently — per-tab badges show progress.
|
|
183
175
|
- Develop gate: controls stay disabled for an application that never completed a
|
|
184
176
|
conductor-develop run — checked via `${CO2_STUDIO_URL}/api/cicd-guard/${CO2_PROJECT_ID}`
|
|
185
177
|
with the CO2_GUARD_TOKEN from the spawn env (refreshed every 30 s, last answer cached).
|
|
@@ -66,7 +66,6 @@ function currentBranch() {
|
|
|
66
66
|
// its tab controls are disabled. Refreshed every 30 s; the last known answer is kept when the
|
|
67
67
|
// studio is briefly unreachable, and `null` (never fetched) disables controls with a hint.
|
|
68
68
|
let guardApps = null // Map lower(name|folder) -> developedOnce
|
|
69
|
-
let guardVersions = [] // project versions (semver strings, studio order — latest first)
|
|
70
69
|
async function refreshGuard() {
|
|
71
70
|
if (!PROJECT_ID) return
|
|
72
71
|
try {
|
|
@@ -77,8 +76,6 @@ async function refreshGuard() {
|
|
|
77
76
|
guardApps = new Map()
|
|
78
77
|
for (const a of g.applications) guardApps.set(String(a.name).toLowerCase(), !!a.developedOnce)
|
|
79
78
|
}
|
|
80
|
-
// The topbar version selector's choices — the {VERSION} tag substituted into commands.
|
|
81
|
-
if (Array.isArray(g.versions)) guardVersions = g.versions.map((v) => String(v.semver)).filter(Boolean)
|
|
82
79
|
} catch {}
|
|
83
80
|
}
|
|
84
81
|
function developedOnce(app) {
|
|
@@ -229,13 +226,6 @@ function tcpProbe(host, port, timeout = 1000) {
|
|
|
229
226
|
})
|
|
230
227
|
}
|
|
231
228
|
|
|
232
|
-
// {VERSION} token substitution — the version comes from the page's topbar selector (the
|
|
233
|
-
// studio guard's versions, latest preselected). Tag-only: never checks out code. The `v`
|
|
234
|
-
// prefix is stripped so docker tags read `1.2.0`, not `v1.2.0`.
|
|
235
|
-
function subst(cmd, version) {
|
|
236
|
-
return String(cmd || '').replaceAll('{VERSION}', String(version || 'latest').replace(/^v/, ''))
|
|
237
|
-
}
|
|
238
|
-
|
|
239
229
|
// Managed Dockerfile from the plan's ## Dockerfiles — written before Build only when the
|
|
240
230
|
// application folder has none (an existing Dockerfile is never overwritten).
|
|
241
231
|
function ensureDockerfile(app) {
|
|
@@ -249,38 +239,37 @@ function ensureDockerfile(app) {
|
|
|
249
239
|
} catch (e) { logLine(app, `dockerfile: FAILED to write — ${e.message}`) }
|
|
250
240
|
}
|
|
251
241
|
|
|
252
|
-
function buildApp(app
|
|
253
|
-
if (activity.has(app.name))
|
|
242
|
+
function buildApp(app) {
|
|
243
|
+
if (activity.has(app.name)) return
|
|
254
244
|
activity.set(app.name, 'building'); lastError.delete(app.name)
|
|
255
245
|
ensureConfigs(app)
|
|
256
246
|
ensureDockerfile(app)
|
|
257
|
-
runLogged(app,
|
|
247
|
+
runLogged(app, app.build, (code) => {
|
|
258
248
|
activity.delete(app.name)
|
|
259
249
|
if (code !== 0) lastError.set(app.name, `build exited ${code} — see log`)
|
|
260
250
|
else logLine(app, 'build OK')
|
|
261
|
-
onDone && onDone()
|
|
262
251
|
})
|
|
263
252
|
}
|
|
264
253
|
|
|
265
|
-
function deployApp(app
|
|
254
|
+
function deployApp(app) {
|
|
266
255
|
if (activity.has(app.name) || !app.deploy) return
|
|
267
256
|
activity.set(app.name, 'deploying'); lastError.delete(app.name)
|
|
268
|
-
runLogged(app,
|
|
257
|
+
runLogged(app, app.deploy, (code) => {
|
|
269
258
|
activity.delete(app.name)
|
|
270
259
|
if (code !== 0) lastError.set(app.name, `deploy exited ${code} — see log`)
|
|
271
260
|
else logLine(app, `deploy OK (${app.deployMethod})`)
|
|
272
261
|
})
|
|
273
262
|
}
|
|
274
263
|
|
|
275
|
-
function startApp(app
|
|
264
|
+
function startApp(app) {
|
|
276
265
|
if (running.get(app.name) || activity.has(app.name)) return
|
|
277
266
|
failed.delete(app.name); lastError.delete(app.name)
|
|
278
267
|
ensureConfigs(app)
|
|
279
268
|
if (app.method === 'container') {
|
|
280
|
-
runLogged(app,
|
|
269
|
+
runLogged(app, app.start, () => {})
|
|
281
270
|
saveState(); return
|
|
282
271
|
}
|
|
283
|
-
const child = runLogged(app,
|
|
272
|
+
const child = runLogged(app, app.start, (code) => {
|
|
284
273
|
running.delete(app.name)
|
|
285
274
|
// A user-requested Stop exits nonzero (killed) — that is not a failure.
|
|
286
275
|
if (code !== 0 && !stopping.delete(app.name)) {
|
|
@@ -327,9 +316,9 @@ function killByPort(port) {
|
|
|
327
316
|
} catch { return false }
|
|
328
317
|
}
|
|
329
318
|
|
|
330
|
-
function stopApp(app
|
|
319
|
+
function stopApp(app) {
|
|
331
320
|
if (app.method === 'container' && app.stop && app.stop !== 'process-kill') {
|
|
332
|
-
const stopCmd =
|
|
321
|
+
const stopCmd = app.stop
|
|
333
322
|
try { execFileSync(stopCmd.split(' ')[0], stopCmd.split(' ').slice(1), { cwd: PROJECT_DIR }) } catch {}
|
|
334
323
|
} else {
|
|
335
324
|
const child = running.get(app.name)
|
|
@@ -368,7 +357,6 @@ body{font-family:system-ui,sans-serif;margin:0;background:#f6f7f9;color:#1c2128;
|
|
|
368
357
|
.badge.running{background:#1c8a4a}.badge.failed{background:#c23b3b}.badge.building,.badge.deploying{background:#c9922a}
|
|
369
358
|
.url{font-size:12px;color:#1a66d0;text-decoration:underline;word-break:break-all}
|
|
370
359
|
button{background:#fff;color:#1c2128;border:1px solid #c6cdd6;border-radius:6px;padding:4px 12px;font-size:12px;cursor:pointer}
|
|
371
|
-
select{background:#fff;color:#1c2128;border:1px solid #c6cdd6;border-radius:6px;padding:3px 6px;font-size:12px}
|
|
372
360
|
button:hover:enabled{background:#eef1f5}
|
|
373
361
|
button:disabled{opacity:.4;cursor:default}
|
|
374
362
|
.notice{padding:4px 12px;font-size:11px;color:#8a5a00;background:#fff7e6;display:none}
|
|
@@ -389,10 +377,6 @@ button:disabled{opacity:.4;cursor:default}
|
|
|
389
377
|
.cfg pre{background:#0f1216;color:#d7dde4;font-size:11px;padding:8px 10px;border-radius:6px;overflow:auto;max-height:320px;white-space:pre-wrap;margin:4px 0 0}
|
|
390
378
|
</style></head><body>
|
|
391
379
|
<div class="top"><strong>CI/CD</strong><span id="branch" class="meta"></span>
|
|
392
|
-
<span class="meta">version</span><select id="ver" title="Tag substituted into {VERSION} in commands (docker image tag)"><option value="">latest</option></select>
|
|
393
|
-
<button onclick="actAll('build')" title="Build every eligible application, one at a time">Build All</button>
|
|
394
|
-
<button onclick="actAll('start')" title="Start every eligible stopped application">Start All</button>
|
|
395
|
-
<button onclick="actAll('stop')" title="Stop every running application">Stop All</button>
|
|
396
380
|
<span style="flex:1"></span><a id="infralink" class="url" href="infra" target="_blank" rel="noreferrer" style="display:none">Infrastructure ↗</a></div>
|
|
397
381
|
<div class="tabs" id="tabs"></div>
|
|
398
382
|
<div class="bar" id="bar"></div>
|
|
@@ -431,7 +415,7 @@ function renderBar(){
|
|
|
431
415
|
<a class="url" href="\${url}" target="_blank" rel="noreferrer">\${url}</a>
|
|
432
416
|
<span style="flex:1"></span>
|
|
433
417
|
<button onclick="openConfig()" title="Show this application's runtime configuration files">Config</button>
|
|
434
|
-
<button onclick="act('build')" \${!gate||busy||a.phase==='running'?'disabled':''} title="\${a.image?'Builds the app AND the docker image '+a.image+'
|
|
418
|
+
<button onclick="act('build')" \${!gate||busy||a.phase==='running'?'disabled':''} title="\${a.image?'Builds the app AND the docker image '+a.image+':latest':'Runs the plan build command'}">Build</button>
|
|
435
419
|
<button onclick="act('start')" \${!gate||busy||a.phase==='running'?'disabled':''}>Start</button>
|
|
436
420
|
<button onclick="act('stop')" \${a.phase!=='running'?'disabled':''}>Stop</button>
|
|
437
421
|
\${a.deploy?\`<button onclick="act('deploy')" \${!gate||busy?'disabled':''}>Deploy (\${a.deployMethod})</button>\`:''}\`
|
|
@@ -461,7 +445,10 @@ function setActive(name){
|
|
|
461
445
|
function openWs(name){
|
|
462
446
|
// Path-relative + scheme-aware so the stream works direct (:5000/) AND behind the studio's
|
|
463
447
|
// /api/cicd/<id>/app-proxy/ prefix (which tunnels the upgrade), also under HTTPS.
|
|
464
|
-
|
|
448
|
+
// NO backslash/regex here on purpose: this string is inside server.js's PAGE template literal,
|
|
449
|
+
// where a client-side regex like /\/$/ cooks to //$/ (a line comment) and kills the script.
|
|
450
|
+
var base = location.pathname.endsWith('/') ? location.pathname.slice(0, -1) : location.pathname
|
|
451
|
+
ws = new WebSocket((location.protocol==='https:'?'wss://':'ws://')+location.host+base+'/ws?app='+encodeURIComponent(name))
|
|
465
452
|
ws.onmessage = e => appendLog(e.data)
|
|
466
453
|
ws.onclose = () => { ws = null; if (active === name) wsTimer = setTimeout(() => openWs(name), 2000) }
|
|
467
454
|
}
|
|
@@ -501,35 +488,17 @@ async function openConfig(){
|
|
|
501
488
|
function closeConfig(){ document.getElementById('cfgModal').classList.remove('open') }
|
|
502
489
|
document.addEventListener('keydown', e => { if (e.key === 'Escape') closeConfig() })
|
|
503
490
|
|
|
504
|
-
// Topbar version selector — the studio guard's project versions (latest first). Repopulated
|
|
505
|
-
// only when the list changes so the user's selection survives the 3 s poll.
|
|
506
|
-
let verChoices = ''
|
|
507
|
-
function renderVersions(versions){
|
|
508
|
-
const sel = document.getElementById('ver')
|
|
509
|
-
const list = (versions && versions.length ? versions : []).join('|')
|
|
510
|
-
if (list === verChoices) return
|
|
511
|
-
verChoices = list
|
|
512
|
-
sel.innerHTML = (versions && versions.length ? versions : ['latest'])
|
|
513
|
-
.map((v, i) => '<option value="'+esc(v)+'"'+(i===0?' selected':'')+'>'+esc(v)+'</option>').join('')
|
|
514
|
-
}
|
|
515
|
-
function selectedVersion(){ return document.getElementById('ver').value || '' }
|
|
516
|
-
|
|
517
491
|
async function refresh(){
|
|
518
492
|
const s = await fetch('api/state').then(r => r.json()).catch(() => null)
|
|
519
493
|
if (!s) return
|
|
520
494
|
state = s
|
|
521
495
|
document.getElementById('branch').textContent = s.currentBranch ? 'branch: '+s.currentBranch : ''
|
|
522
496
|
document.getElementById('infralink').style.display = s.hasInfra ? 'inline' : 'none'
|
|
523
|
-
renderVersions(s.versions)
|
|
524
497
|
if (!active && s.apps.length) setActive(s.apps[0].name)
|
|
525
498
|
renderTabs(); renderBar()
|
|
526
499
|
}
|
|
527
500
|
async function act(op){
|
|
528
|
-
await fetch('api/apps/'+encodeURIComponent(active)+'/'+op
|
|
529
|
-
refresh()
|
|
530
|
-
}
|
|
531
|
-
async function actAll(op){
|
|
532
|
-
await fetch('api/all/'+op+'?version='+encodeURIComponent(selectedVersion()), { method: 'POST' })
|
|
501
|
+
await fetch('api/apps/'+encodeURIComponent(active)+'/'+op, { method: 'POST' })
|
|
533
502
|
refresh()
|
|
534
503
|
}
|
|
535
504
|
// Rescan immediately when the user returns to this page (tab focus / regained visibility), so
|
|
@@ -614,7 +583,6 @@ const server = http.createServer(async (req, res) => {
|
|
|
614
583
|
return json(res, 200, {
|
|
615
584
|
currentBranch: currentBranch(),
|
|
616
585
|
hasInfra: (loadConfig().infra || []).length > 0,
|
|
617
|
-
versions: guardVersions,
|
|
618
586
|
apps,
|
|
619
587
|
})
|
|
620
588
|
}
|
|
@@ -624,10 +592,6 @@ const server = http.createServer(async (req, res) => {
|
|
|
624
592
|
if (!app) return json(res, 404, { error: 'unknown app' })
|
|
625
593
|
return json(res, 200, { configs: (app.configs || []).map((c) => readConfig(app, c)) })
|
|
626
594
|
}
|
|
627
|
-
// ?version=<semver> on every action — the topbar selection, substituted into {VERSION}.
|
|
628
|
-
const reqVersion = (() => {
|
|
629
|
-
try { return new URL(req.url, 'http://x').searchParams.get('version') || '' } catch { return '' }
|
|
630
|
-
})()
|
|
631
595
|
const path = (req.url || '').split('?')[0]
|
|
632
596
|
const m = path.match(/^\/api\/apps\/([^/]+)\/(build|start|stop|deploy)$/)
|
|
633
597
|
if (m && req.method === 'POST') {
|
|
@@ -636,42 +600,12 @@ const server = http.createServer(async (req, res) => {
|
|
|
636
600
|
if (m[2] !== 'stop' && developedOnce(app) === false) {
|
|
637
601
|
return json(res, 409, { error: 'application has not completed a develop workflow run' })
|
|
638
602
|
}
|
|
639
|
-
if (m[2] === 'build') buildApp(app
|
|
640
|
-
else if (m[2] === 'start') startApp(app
|
|
641
|
-
else if (m[2] === 'deploy') deployApp(app
|
|
642
|
-
else stopApp(app
|
|
603
|
+
if (m[2] === 'build') buildApp(app)
|
|
604
|
+
else if (m[2] === 'start') startApp(app)
|
|
605
|
+
else if (m[2] === 'deploy') deployApp(app)
|
|
606
|
+
else stopApp(app)
|
|
643
607
|
return json(res, 200, { ok: true })
|
|
644
608
|
}
|
|
645
|
-
// All-apps controls. Eligible = develop gate not failed and not mid-build/deploy; build and
|
|
646
|
-
// start additionally skip apps already running. Builds run SEQUENTIALLY (honest logs, no
|
|
647
|
-
// CPU storm); start/stop fire together. Ineligible apps are skipped silently.
|
|
648
|
-
const all = path.match(/^\/api\/all\/(build|start|stop)$/)
|
|
649
|
-
if (all && req.method === 'POST') {
|
|
650
|
-
const op = all[1]
|
|
651
|
-
const apps = loadConfig().apps
|
|
652
|
-
if (op === 'stop') {
|
|
653
|
-
for (const a of apps) {
|
|
654
|
-
if (running.has(a.name)) stopApp(a, reqVersion)
|
|
655
|
-
else probe(a).then((up) => { if (up) stopApp(a, reqVersion) })
|
|
656
|
-
}
|
|
657
|
-
return json(res, 200, { ok: true })
|
|
658
|
-
}
|
|
659
|
-
const eligible = []
|
|
660
|
-
for (const a of apps) {
|
|
661
|
-
if (developedOnce(a) === false || activity.has(a.name)) continue
|
|
662
|
-
if (await probe(a)) continue // already running — skip for build AND start
|
|
663
|
-
eligible.push(a)
|
|
664
|
-
}
|
|
665
|
-
if (op === 'start') {
|
|
666
|
-
for (const a of eligible) startApp(a, reqVersion)
|
|
667
|
-
return json(res, 200, { ok: true, count: eligible.length })
|
|
668
|
-
}
|
|
669
|
-
// op === 'build' — sequential chain: each build starts when the previous one exits.
|
|
670
|
-
const queue = [...eligible]
|
|
671
|
-
const next = () => { const a = queue.shift(); if (a) buildApp(a, reqVersion, next) }
|
|
672
|
-
next()
|
|
673
|
-
return json(res, 200, { ok: true, count: eligible.length })
|
|
674
|
-
}
|
|
675
609
|
json(res, 404, { error: 'not found' })
|
|
676
610
|
})
|
|
677
611
|
|
|
@@ -718,9 +652,8 @@ Docker fields (from the plan's `## Docker` / `## Dockerfiles` sections): top-lev
|
|
|
718
652
|
`"dockerfile"` is `{ "path": "Dockerfile", "content": "<managed body>" }` for a `managed`
|
|
719
653
|
Dockerfile (materialized before Build when missing — never overwriting), or
|
|
720
654
|
`{ "path": "Dockerfile", "content": null }` for an `existing` one (`null` entirely when the
|
|
721
|
-
app has no image). Commands
|
|
722
|
-
|
|
723
|
-
execution time.
|
|
655
|
+
app has no image). Commands are copied verbatim from the plan and run as-is — no `{VERSION}`
|
|
656
|
+
or any other token substitution.
|
|
724
657
|
|
|
725
658
|
Each app also carries a `configs` array — the runtime config files the application reads at start,
|
|
726
659
|
from the plan's `## Application Configs` section (one entry per file). Fields:
|
|
@@ -743,7 +676,7 @@ verbatim (this is a local dev CI/CD app).
|
|
|
743
676
|
"folder": "my-app",
|
|
744
677
|
"method": "process",
|
|
745
678
|
"port": 3000,
|
|
746
|
-
"build": "pnpm install && pnpm build && docker build -t registry.example.com/team/my-app:
|
|
679
|
+
"build": "pnpm install && pnpm build && docker build -t registry.example.com/team/my-app:latest .",
|
|
747
680
|
"start": "node dist/index.js",
|
|
748
681
|
"stop": "process-kill",
|
|
749
682
|
"deployMethod": "kubernetes",
|
|
@@ -13,8 +13,8 @@ description: >
|
|
|
13
13
|
port for the generated CI/CD app itself. When Docker AND a docker repo/registry are both
|
|
14
14
|
configured (DEVTOOL.md + ENVIRONMENT.md), it additionally plans the docker-image build path:
|
|
15
15
|
per-application image names under the repo, Build commands extended with
|
|
16
|
-
`docker build -t <image>:
|
|
17
|
-
|
|
16
|
+
`docker build -t <image>:latest` (no version token — the generated CI/CD app runs commands
|
|
17
|
+
verbatim on the current working tree), and a managed Dockerfile authored into the plan's
|
|
18
18
|
`## Dockerfiles` section for any dockerized application lacking one (materialized before
|
|
19
19
|
Build, never overwriting an existing file). It also infers each application's RUNTIME CONFIG — the
|
|
20
20
|
config files its start command reads (e.g. `.env`), deciding per file whether the values come
|
|
@@ -124,13 +124,12 @@ If the argument is missing or the file does not exist, STOP and tell the user to
|
|
|
124
124
|
any character docker rejects replaced by `-` (keep a leading `<number>_` prefix as-is;
|
|
125
125
|
e.g. `registry.example.com/team/skolafund_web`).
|
|
126
126
|
- **Build**: append the image build to the app build —
|
|
127
|
-
`<app build> && docker build -t <image>:
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
there is no user-facing version selector; only the current codebase is ever built.
|
|
127
|
+
`<app build> && docker build -t <image>:latest .` Commands carry NO `{VERSION}` token or
|
|
128
|
+
any other placeholder — the generated CI/CD app runs them verbatim; only the current
|
|
129
|
+
working tree is ever built and images are always tagged `latest`.
|
|
131
130
|
Build never pushes — pushing belongs to a `docker` Deploy method.
|
|
132
|
-
- **Container start/deploy**: when method=container, reference `<image>:
|
|
133
|
-
start/deploy commands too, so the
|
|
131
|
+
- **Container start/deploy**: when method=container, reference `<image>:latest` in the
|
|
132
|
+
start/deploy commands too, so the freshly built tag is what runs.
|
|
134
133
|
- **Dockerfile**: if the application folder already has a `Dockerfile`, use it as-is
|
|
135
134
|
(`existing`). Otherwise author a stack-appropriate Dockerfile (node / spring-boot /
|
|
136
135
|
laravel — same stack detection as depgen-k8s) into the plan's `## Dockerfiles` section
|
|
@@ -221,12 +220,6 @@ inner ```env config block renders — the plan file itself uses a normal 3-backt
|
|
|
221
220
|
> **Health** host — so an app is detected whichever family it binds. Use a `localhost` Health host
|
|
222
221
|
> for Node/Vite/Next apps (they bind IPv6 `::1`) and `127.0.0.1` for JVM apps.
|
|
223
222
|
|
|
224
|
-
> **`{VERSION}` token**: Build/Start/Deploy commands may contain the literal `{VERSION}`;
|
|
225
|
-
> the generated CI/CD app replaces it with the project's CURRENT version (semver without
|
|
226
|
-
> the `v` prefix, `latest` when unknown) at execution time — no user-facing selector.
|
|
227
|
-
> Used by the docker-image path to tag images; tag-only — the code built is always the
|
|
228
|
-
> working tree.
|
|
229
|
-
|
|
230
223
|
## Docker
|
|
231
224
|
|
|
232
225
|
Written only from Gather 2a. When Docker or a docker repo is absent, write the section as:
|