uniweb 0.59.2 → 0.61.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/package.json +7 -7
- package/partials/agents.md +24 -32
- package/src/backend/asset-download.js +11 -9
- package/src/backend/client.js +38 -91
- package/src/backend/foundation-bring-along.js +19 -14
- package/src/backend/service-request.js +14 -9
- package/src/backend/site-media.js +6 -6
- package/src/backend/site-sync.js +323 -221
- package/src/commands/clone.js +42 -39
- package/src/commands/deploy.js +45 -27
- package/src/commands/doctor.js +6 -7
- package/src/commands/forget.js +197 -0
- package/src/commands/i18n.js +19 -10
- package/src/commands/publish.js +112 -77
- package/src/commands/pull.js +63 -49
- package/src/commands/push.js +76 -60
- package/src/commands/refresh.js +22 -29
- package/src/commands/register.js +37 -23
- package/src/commands/status.js +13 -11
- package/src/commands/sync.js +0 -1
- package/src/framework-index.json +7 -7
- package/src/index.js +101 -76
- package/src/utils/args.js +3 -4
- package/src/utils/asset-upload.js +18 -5
- package/src/utils/config.js +79 -42
- package/src/utils/flag-guard.js +70 -27
- package/src/utils/git.js +1 -1
- package/src/utils/interactive.js +38 -0
- package/src/utils/records-guard.js +38 -30
- package/src/utils/registry-auth.js +150 -28
- package/src/utils/registry-orgs.js +19 -0
- package/src/utils/scaffold.js +11 -0
- package/src/utils/session-file.js +95 -0
- package/src/utils/site-copies.js +123 -0
- package/src/utils/site-data-upload.js +4 -3
- package/src/utils/site-identity.js +89 -299
- package/templates/site/site.yml.hbs +11 -9
package/src/commands/publish.js
CHANGED
|
@@ -20,8 +20,9 @@
|
|
|
20
20
|
* Distinct from `uniweb deploy` (third-party hosts) and `uniweb register`
|
|
21
21
|
* (foundation code → catalog). For a self-contained artifact, see `uniweb export`.
|
|
22
22
|
*
|
|
23
|
-
* Backend:
|
|
24
|
-
*
|
|
23
|
+
* Backend: the one you are logged in to — UNIWEB_REGISTER_URL overrides it for a script
|
|
24
|
+
* (resolveBackendOrigin). Auth: UNIWEB_TOKEN > the stored session > `uniweb login`.
|
|
25
|
+
* No `--backend` or `--token`: switching and signing in are `uniweb login`.
|
|
25
26
|
*
|
|
26
27
|
* Usage:
|
|
27
28
|
* uniweb publish Bring the foundation along, sync, and go live
|
|
@@ -32,14 +33,12 @@
|
|
|
32
33
|
* FIRST publish of a site reads it — that create is
|
|
33
34
|
* what decides which org owns the site and whose
|
|
34
35
|
* storage its assets are charged to. It is then
|
|
35
|
-
* recorded
|
|
36
|
+
* recorded in `sync.json` and replayed, so it
|
|
36
37
|
* never has to be re-typed.
|
|
37
38
|
* uniweb publish --personal Own the new site personally, deliberately. Sends
|
|
38
39
|
* NO `as_org` — byte-identical to the wire before
|
|
39
40
|
* the owner prompt existed. First publish only.
|
|
40
|
-
* uniweb publish --no-save
|
|
41
|
-
* uniweb publish --backend <url> Override the backend origin
|
|
42
|
-
* uniweb publish --token <bearer> Auth bearer (skips `uniweb login`)
|
|
41
|
+
* uniweb publish --no-save Do not record this publish in deploy.yml
|
|
43
42
|
*/
|
|
44
43
|
|
|
45
44
|
import { existsSync, readFileSync } from 'node:fs'
|
|
@@ -50,7 +49,7 @@ import yaml from 'js-yaml'
|
|
|
50
49
|
|
|
51
50
|
import {
|
|
52
51
|
loadDeployYml,
|
|
53
|
-
|
|
52
|
+
resolvePublishTarget,
|
|
54
53
|
recordLastDeploy,
|
|
55
54
|
collectSchemalessData,
|
|
56
55
|
collectSchemalessDataAssets,
|
|
@@ -67,16 +66,18 @@ import { isSiteRelativeExtensionUrl } from '@uniweb/build'
|
|
|
67
66
|
import { resolveDefaultLocale } from '@uniweb/core/locale-config'
|
|
68
67
|
|
|
69
68
|
import { BackendClient } from '../backend/client.js'
|
|
70
|
-
import {
|
|
69
|
+
import { DEFAULT_BACKEND_ORIGIN } from '../utils/config.js'
|
|
70
|
+
import { resolveSiteDir } from './deploy.js'
|
|
71
71
|
import { warnIfContentDoesNotConform } from '../utils/conformance.js'
|
|
72
72
|
import { readFlagValue, readOrgFlag } from '../utils/args.js'
|
|
73
73
|
import { checkFlags } from '../utils/flag-guard.js'
|
|
74
74
|
import {
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
syncedElsewhere,
|
|
76
|
+
describeSyncedElsewhere
|
|
77
77
|
} from '../utils/site-identity.js'
|
|
78
78
|
import { isNonInteractive, confirm } from '../utils/interactive.js'
|
|
79
79
|
import { guardEmptyRecords } from '../utils/records-guard.js'
|
|
80
|
+
import { findSiteCopies, describeSiteCopies } from '../utils/site-copies.js'
|
|
80
81
|
import { headProvenance } from '../utils/git.js'
|
|
81
82
|
import {
|
|
82
83
|
makeModelResolver,
|
|
@@ -92,7 +93,12 @@ import {
|
|
|
92
93
|
resolveSiteOrgForCreate
|
|
93
94
|
} from '../backend/site-sync.js'
|
|
94
95
|
import { uploadSiteMedia, describeAssetRefusal } from '../backend/site-media.js'
|
|
95
|
-
import {
|
|
96
|
+
import {
|
|
97
|
+
updateBackendMap,
|
|
98
|
+
readBackendState,
|
|
99
|
+
carryServed,
|
|
100
|
+
SYNC_STORE_FILE
|
|
101
|
+
} from '@uniweb/build/uwx'
|
|
96
102
|
import {
|
|
97
103
|
bringFoundationAlong,
|
|
98
104
|
bringExtensionsAlong
|
|
@@ -158,7 +164,7 @@ function languagesFromSiteYml(siteYml) {
|
|
|
158
164
|
return [def, ...norm.filter((l) => l !== def)]
|
|
159
165
|
}
|
|
160
166
|
|
|
161
|
-
// Persist deploy.yml
|
|
167
|
+
// Persist the deploys memo in deploy.yml (skipped on --no-save / saveDeploys: false).
|
|
162
168
|
/**
|
|
163
169
|
* A one-line, human-readable account of a service request, for a terminal.
|
|
164
170
|
*
|
|
@@ -223,7 +229,7 @@ function describeServices(rows) {
|
|
|
223
229
|
}
|
|
224
230
|
|
|
225
231
|
async function persistLastDeploy(siteDir, opts) {
|
|
226
|
-
if (opts.
|
|
232
|
+
if (opts.saveDeploys === false) return
|
|
227
233
|
try {
|
|
228
234
|
const result = await recordLastDeploy(siteDir, opts)
|
|
229
235
|
if (result?.created)
|
|
@@ -253,33 +259,48 @@ export async function publish(args = []) {
|
|
|
253
259
|
// is not a gate.
|
|
254
260
|
await warnIfContentDoesNotConform(siteDir, { args })
|
|
255
261
|
const siteYml = readSiteYml(join(siteDir, 'site.yml'))
|
|
256
|
-
//
|
|
257
|
-
//
|
|
258
|
-
//
|
|
259
|
-
//
|
|
260
|
-
//
|
|
261
|
-
// ⛔ The
|
|
262
|
-
|
|
263
|
-
|
|
262
|
+
// ⭐ THE BACKEND YOU ARE LOGGED IN TO is where this goes *[Diego, 2026-09-21]*, for every
|
|
263
|
+
// backend verb (resolveBackendOrigin). Only UNIWEB_REGISTER_URL — the automation
|
|
264
|
+
// override — outranks it, and nothing talks to a backend the user is not logged in to:
|
|
265
|
+
// logged in nowhere, the origin is the default backend and the first request asks for
|
|
266
|
+
// that login. ⛔ There is no `--backend` here: switching is `uniweb login --backend`.
|
|
267
|
+
// ⛔ The project's own record — its synced backend, deploy.yml's default target — routes
|
|
268
|
+
// NOTHING. For part of 2026-09-21 it answered a "logged in nowhere" tier, which cannot
|
|
269
|
+
// be reached: *"We do not allow any communication with backend if the user is not
|
|
270
|
+
// logged into a backend."*
|
|
264
271
|
|
|
265
272
|
const client = new BackendClient({
|
|
266
|
-
originFlag:
|
|
267
|
-
readFlagValue(args, '--backend'),
|
|
268
|
-
siteScope,
|
|
269
|
-
siteBackend,
|
|
270
|
-
token: readFlagValue(args, '--token') || undefined,
|
|
271
273
|
args,
|
|
272
274
|
command: 'Publishing'
|
|
273
275
|
})
|
|
274
276
|
|
|
275
|
-
// ⛔
|
|
276
|
-
//
|
|
277
|
-
//
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
277
|
+
// ⛔ A COPY OF ANOTHER PROJECT — before the foundation bring-along, the sync, or the
|
|
278
|
+
// go-live. A plain directory copy carries sync.json, so it holds the ORIGINAL's site
|
|
279
|
+
// on this backend and would publish over it (utils/site-copies.js). A publish is the
|
|
280
|
+
// longest of these flows and the most expensive to unwind, so it is the one that
|
|
281
|
+
// most benefits from failing at the first step rather than at the fifth.
|
|
282
|
+
// *(The backend scope check that stood here went on 2026-09-20 — see push.js.)*
|
|
283
|
+
{
|
|
284
|
+
const copies = findSiteCopies(siteDir, client.origin)
|
|
285
|
+
if (copies.length) {
|
|
286
|
+
const said = describeSiteCopies(copies, client.origin, 'publish')
|
|
287
|
+
say.err(said.headline)
|
|
288
|
+
for (const line of said.lines) say.dim(line)
|
|
289
|
+
return { exitCode: 1 }
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
// ⚠️ Logged in to a backend where this project has no site, while it has one
|
|
294
|
+
// elsewhere: following the login CREATES a second site. Say so before the owner
|
|
295
|
+
// question, which otherwise arrives with no reason attached. Not when
|
|
296
|
+
// UNIWEB_REGISTER_URL chose the backend — that is already a decision.
|
|
297
|
+
if (!process.env.UNIWEB_REGISTER_URL) {
|
|
298
|
+
const known = syncedElsewhere(siteDir, client.origin)
|
|
299
|
+
if (known) {
|
|
300
|
+
const [headline, ...rest] = describeSyncedElsewhere(known, client.origin, 'publish')
|
|
301
|
+
say.info(headline)
|
|
302
|
+
for (const line of rest) say.dim(line)
|
|
303
|
+
}
|
|
283
304
|
}
|
|
284
305
|
|
|
285
306
|
// WHO will own this site, if this publish is the one that creates it. Resolved
|
|
@@ -341,35 +362,36 @@ export async function publish(args = []) {
|
|
|
341
362
|
// nothing scaffolded ever set it, it is in no public doc, and the backend
|
|
342
363
|
// may already have been ignoring it.
|
|
343
364
|
|
|
344
|
-
// deploy.yml target (the Uniweb hosting memory)
|
|
345
|
-
//
|
|
346
|
-
//
|
|
365
|
+
// deploy.yml target (the Uniweb hosting memory) — the one for the backend this
|
|
366
|
+
// publish goes to. ⭐ The target follows the backend, never the reverse: the default
|
|
367
|
+
// target only breaks a tie (resolvePublishTarget). With no deploy.yml, or no target
|
|
368
|
+
// naming this backend, persistLastDeploy adds one.
|
|
369
|
+
//
|
|
370
|
+
// ⛔ This took deploy.yml's DEFAULT target until 2026-09-21, whatever backend it was
|
|
371
|
+
// publishing to — a publish to a second backend wrote over the default target's
|
|
372
|
+
// record and compared its request against that target's fingerprint.
|
|
347
373
|
let resolved
|
|
348
374
|
let priorRequest = null
|
|
349
375
|
try {
|
|
350
376
|
const deployYml = await loadDeployYml(siteDir)
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
//
|
|
356
|
-
//
|
|
357
|
-
|
|
358
|
-
priorRequest =
|
|
359
|
-
deployYml?.lastDeploy?.[resolved?.targetName] ||
|
|
360
|
-
deployYml?.lastDeploy?.uniweb ||
|
|
361
|
-
null
|
|
377
|
+
resolved = resolvePublishTarget(deployYml, client.origin, {
|
|
378
|
+
defaultBackend: DEFAULT_BACKEND_ORIGIN
|
|
379
|
+
})
|
|
380
|
+
// The last request we are known to have sent TO THIS BACKEND, for the declaration
|
|
381
|
+
// gate below. Read from the SAME deploy.yml load — one read, and the memo is the
|
|
382
|
+
// only durable record of it (see backend/service-request.js for why not the cache).
|
|
383
|
+
priorRequest = deployYml?.deploys?.[resolved.targetName] || null
|
|
362
384
|
} catch {
|
|
363
385
|
// Malformed/ambiguous deploy.yml — don't block the publish on the memo.
|
|
364
386
|
resolved = {
|
|
365
387
|
targetName: 'production',
|
|
366
388
|
host: 'uniweb',
|
|
367
389
|
config: {},
|
|
368
|
-
|
|
390
|
+
saveDeploys: true,
|
|
369
391
|
fromFile: false
|
|
370
392
|
}
|
|
371
393
|
}
|
|
372
|
-
const
|
|
394
|
+
const saveDeploys = !noSave && resolved.saveDeploys !== false
|
|
373
395
|
|
|
374
396
|
// A SITE-RELATIVE extension URL cannot work on Uniweb hosting: the published
|
|
375
397
|
// site ships no JS, so nothing serves that path. The request falls through to
|
|
@@ -410,7 +432,7 @@ export async function publish(args = []) {
|
|
|
410
432
|
say.info('Dry run — would bring the foundation along, sync, and go live:')
|
|
411
433
|
say.dim(`Backend : ${client.origin}`)
|
|
412
434
|
say.dim(
|
|
413
|
-
`site_uuid : ${
|
|
435
|
+
`site_uuid : ${readBackendState(siteDir, client.origin).site?.uuid || '(none — the site is created before anything uploads)'}`
|
|
414
436
|
)
|
|
415
437
|
const langs = languagesFromSiteYml(siteYml)
|
|
416
438
|
if (langs) say.dim(`Languages : ${langs.join(', ')}`)
|
|
@@ -513,12 +535,18 @@ export async function publish(args = []) {
|
|
|
513
535
|
// Non-local @std/registry Model schemas resolve through the backend (same as push).
|
|
514
536
|
const resolveModel = makeModelResolver({ client, offline: false })
|
|
515
537
|
|
|
516
|
-
// ⛔ AN EMPTY
|
|
517
|
-
// destructive — a
|
|
518
|
-
// is reported and confirmed before anything is sent.
|
|
538
|
+
// ⛔ AN EMPTY RECORDS DIRECTORY REMOVES. It is the one path where an ordinary act
|
|
539
|
+
// is destructive — a directory emptied by accident, or kept with only a
|
|
540
|
+
// placeholder — so the count is reported and confirmed before anything is sent.
|
|
541
|
+
//
|
|
542
|
+
// ⛔ `backend` WAS MISSING HERE until 2026-09-21, and it is what the count is read
|
|
543
|
+
// under: placements are banked per backend, so with none the guard counted zero
|
|
544
|
+
// and never asked — `publish` sent an empty folder without a word while `push`
|
|
545
|
+
// stopped for the same one.
|
|
519
546
|
{
|
|
520
547
|
const guard = await guardEmptyRecords({
|
|
521
548
|
siteDir,
|
|
549
|
+
backend: client.origin,
|
|
522
550
|
args,
|
|
523
551
|
warn: say.warn,
|
|
524
552
|
note: say.dim
|
|
@@ -531,8 +559,9 @@ export async function publish(args = []) {
|
|
|
531
559
|
let probe
|
|
532
560
|
try {
|
|
533
561
|
probe = await emitSyncPackages(siteDir, {
|
|
562
|
+
backend: client.origin,
|
|
534
563
|
// Placement identity for the folder — see writeFolderItemUuids.
|
|
535
|
-
folderItemUuids: readFolderItemUuids(siteDir),
|
|
564
|
+
folderItemUuids: readFolderItemUuids(siteDir, client.origin),
|
|
536
565
|
// Resolves a foundation-relative `@/x` model ref into `@org/x`.
|
|
537
566
|
...(asOrg ? { org: asOrg } : {}),
|
|
538
567
|
...(foundationDir ? { foundationDir } : {}),
|
|
@@ -553,13 +582,13 @@ export async function publish(args = []) {
|
|
|
553
582
|
// that describes one is stale — including after the documented "clear
|
|
554
583
|
// `$uuid` to re-publish as a new site" recovery. Must run BEFORE the create,
|
|
555
584
|
// which mints a uuid and would make the clone look bound.
|
|
556
|
-
const droppedState = clearRemoteSyncStateIfUnbound(siteDir)
|
|
585
|
+
const droppedState = clearRemoteSyncStateIfUnbound(siteDir, client.origin)
|
|
557
586
|
if (droppedState.length) {
|
|
558
587
|
say.dim(
|
|
559
588
|
`Cleared stale sync state from a previous site (${droppedState.join(', ')}).`
|
|
560
589
|
)
|
|
561
590
|
}
|
|
562
|
-
const droppedValues = dropSiteBoundValues(siteDir)
|
|
591
|
+
const droppedValues = dropSiteBoundValues(siteDir, client.origin)
|
|
563
592
|
if (droppedValues.length) {
|
|
564
593
|
say.dim(
|
|
565
594
|
`Dropped the previous site's ${droppedValues.join(' and ')} from site.yml.`
|
|
@@ -628,10 +657,10 @@ export async function publish(args = []) {
|
|
|
628
657
|
if (Object.keys(ids).length) assetIds = ids
|
|
629
658
|
// Identity into the COMMITTED map — see backend/asset-map.js. Merge, not
|
|
630
659
|
// replace: this publish carries only the refs its content touched.
|
|
631
|
-
const rec =
|
|
660
|
+
const rec = updateBackendMap(siteDir, client.origin, 'assets', ids, carryServed)
|
|
632
661
|
if (rec.written) {
|
|
633
662
|
say.dim(
|
|
634
|
-
`${
|
|
663
|
+
`${SYNC_STORE_FILE} : ${rec.added.length} asset(s) added, ${rec.changed.length} changed — commit it`
|
|
635
664
|
)
|
|
636
665
|
}
|
|
637
666
|
if (ballAssets.length) ball = rewriteSchemalessDataAssets(ball, map)
|
|
@@ -731,13 +760,13 @@ export async function publish(args = []) {
|
|
|
731
760
|
// the pinned foundation ref and rewriting local media refs to serve URLs.
|
|
732
761
|
// (It stamped `info.data_bundle` until 2026-08-18; the ball is gone and
|
|
733
762
|
// collection data now lands at its serving tail, so nothing records it.)
|
|
734
|
-
const priorHashes = readSyncCache(siteDir)
|
|
763
|
+
const priorHashes = readSyncCache(siteDir, client.origin)
|
|
735
764
|
// publish rides the same gated push as `uniweb push`: if an app author has
|
|
736
765
|
// edited since this clone last synced, the push is refused rather than
|
|
737
766
|
// overwriting them, and nothing goes live. `--force` drops the precondition.
|
|
738
767
|
const baseVersions = args.includes('--force')
|
|
739
768
|
? null
|
|
740
|
-
: readBaseVersions(siteDir)
|
|
769
|
+
: readBaseVersions(siteDir, client.origin)
|
|
741
770
|
// Per-item identity, recovered from the backend when this clone has never seen it.
|
|
742
771
|
// Without it the backend re-mints every page and section row (see readItemUuids).
|
|
743
772
|
const itemUuids = await ensureItemUuids({
|
|
@@ -789,15 +818,20 @@ export async function publish(args = []) {
|
|
|
789
818
|
// network blip, a site never pushed. That is the shipped behaviour and it is safe:
|
|
790
819
|
// it withholds an unchanged block and sends a changed one; it merely cannot see
|
|
791
820
|
// the app's side.
|
|
792
|
-
|
|
821
|
+
// What this site is PROVISIONED with on the backend being published to.
|
|
822
|
+
const provisioned = readBackendState(siteDir, client.origin)
|
|
823
|
+
const boundUuid = provisioned.site?.uuid || null
|
|
824
|
+
let declaration = decideDeclaration(siteYml, priorRequest, provisioned)
|
|
793
825
|
let adopted = null
|
|
794
826
|
// Before the push, so a never-synced site has no uuid and simply skips this.
|
|
795
827
|
const status =
|
|
796
|
-
|
|
797
|
-
|
|
828
|
+
// This backend's site, from sync.json. It read `site.yml::$uuid`, so after step 4
|
|
829
|
+
// the remote reconcile below never ran and every publish declared blind.
|
|
830
|
+
boundUuid
|
|
831
|
+
? await client.siteStatus(boundUuid)
|
|
798
832
|
: null
|
|
799
833
|
if (status && Array.isArray(status.services)) {
|
|
800
|
-
const r = reconcileRequest(siteYml, status.services, priorRequest)
|
|
834
|
+
const r = reconcileRequest(siteYml, status.services, priorRequest, provisioned)
|
|
801
835
|
if (r.action === 'none') {
|
|
802
836
|
declaration = { declare: false, reason: 'in-sync' }
|
|
803
837
|
} else if (r.action === 'send') {
|
|
@@ -835,7 +869,7 @@ export async function publish(args = []) {
|
|
|
835
869
|
// for one commit. Asking is the only thing that resolves it.
|
|
836
870
|
if (declaration.reason === 'conflict') {
|
|
837
871
|
say.warn('Your site\'s services were changed elsewhere, and site.yml changed too.')
|
|
838
|
-
say.dim(` in
|
|
872
|
+
say.dim(` in sync.json: ${describeServices(provisioned.services)}`)
|
|
839
873
|
say.dim(` on your site: ${describeServices(adopted)}`)
|
|
840
874
|
if (isNonInteractive(args)) {
|
|
841
875
|
say.dim(' Left your site as it is — run without --non-interactive to choose.')
|
|
@@ -886,7 +920,7 @@ export async function publish(args = []) {
|
|
|
886
920
|
// site refused, where nothing of theirs changed and ours simply did not take.
|
|
887
921
|
// We cannot tell those apart here, so the wording claims neither.
|
|
888
922
|
say.info('Your site has different services than site.yml lists.')
|
|
889
|
-
say.dim(` in
|
|
923
|
+
say.dim(` in sync.json: ${describeServices(provisioned.services)}`)
|
|
890
924
|
say.dim(` on your site: ${describeServices(adopted)}`)
|
|
891
925
|
// ⭐ OFFERED, NEVER DONE. site.yml is the owner's file, and a publish that
|
|
892
926
|
// silently rewrites an authored file is the surprise this seam exists to
|
|
@@ -911,9 +945,10 @@ export async function publish(args = []) {
|
|
|
911
945
|
let pkg
|
|
912
946
|
try {
|
|
913
947
|
pkg = await emitSyncPackages(siteDir, {
|
|
948
|
+
backend: client.origin,
|
|
914
949
|
...(declaration.declare ? {} : { declareServices: false }),
|
|
915
950
|
// Placement identity for the folder — see writeFolderItemUuids.
|
|
916
|
-
folderItemUuids: readFolderItemUuids(siteDir),
|
|
951
|
+
folderItemUuids: readFolderItemUuids(siteDir, client.origin),
|
|
917
952
|
// Resolves a foundation-relative `@/x` model ref into `@org/x`.
|
|
918
953
|
...(asOrg ? { org: asOrg } : {}),
|
|
919
954
|
...(foundationDir ? { foundationDir } : {}),
|
|
@@ -921,7 +956,7 @@ export async function publish(args = []) {
|
|
|
921
956
|
priorHashes,
|
|
922
957
|
itemUuids,
|
|
923
958
|
...(baseVersions
|
|
924
|
-
? { baseVersions, itemBaseVersions: readItemBaseVersions(siteDir) }
|
|
959
|
+
? { baseVersions, itemBaseVersions: readItemBaseVersions(siteDir, client.origin) }
|
|
925
960
|
: {}),
|
|
926
961
|
...(Object.keys(injectInfo).length ? { injectInfo } : {}),
|
|
927
962
|
...(Object.keys(ext.pins).length ? { injectExtensions: ext.pins } : {}),
|
|
@@ -988,7 +1023,7 @@ export async function publish(args = []) {
|
|
|
988
1023
|
say.err(`Publish rejected: HTTP ${pubRes.status} ${pubRes.statusText}`)
|
|
989
1024
|
if (pubRes.status === 401 || pubRes.status === 403) {
|
|
990
1025
|
say.dim(
|
|
991
|
-
"Credentials weren't accepted — run `uniweb login`
|
|
1026
|
+
"Credentials weren't accepted — run `uniweb login` again."
|
|
992
1027
|
)
|
|
993
1028
|
}
|
|
994
1029
|
if (body) say.dim(body.slice(0, 800))
|
|
@@ -1008,8 +1043,8 @@ export async function publish(args = []) {
|
|
|
1008
1043
|
const serveUrl = typeof result.url === 'string' && result.url ? result.url : null
|
|
1009
1044
|
|
|
1010
1045
|
// 8. Persist deploy.yml memory — a record of what went live (and so a re-run
|
|
1011
|
-
// reuses the resolved target without re-asking).
|
|
1012
|
-
//
|
|
1046
|
+
// reuses the resolved target without re-asking). Identity is sync.json's,
|
|
1047
|
+
// per backend. `released` records whether this publish shipped a new
|
|
1013
1048
|
// foundation version (the bring-along, §4).
|
|
1014
1049
|
// Record the ref that actually went live: the pinned `@scope/name@version`
|
|
1015
1050
|
// from the bring-along when present, else the site.yml ref verbatim.
|
|
@@ -1021,13 +1056,13 @@ export async function publish(args = []) {
|
|
|
1021
1056
|
const recordedRef = fnd.ref || siteYmlRef
|
|
1022
1057
|
await persistLastDeploy(siteDir, {
|
|
1023
1058
|
targetName: resolved.targetName,
|
|
1024
|
-
//
|
|
1025
|
-
//
|
|
1026
|
-
//
|
|
1059
|
+
// A target this file does not have yet — the first publish anywhere (the whole
|
|
1060
|
+
// file is scaffolded), or the first to a backend no target names (the target is
|
|
1061
|
+
// added beside the others) — records the backend it went live on.
|
|
1027
1062
|
targetConfig: resolved.fromFile
|
|
1028
1063
|
? null
|
|
1029
1064
|
: { host: 'uniweb', backend: client.origin },
|
|
1030
|
-
|
|
1065
|
+
saveDeploys,
|
|
1031
1066
|
lastDeploy: {
|
|
1032
1067
|
at: new Date().toISOString(),
|
|
1033
1068
|
host: 'uniweb',
|
|
@@ -1037,7 +1072,7 @@ export async function publish(args = []) {
|
|
|
1037
1072
|
// service's `config` is opaque, so recording either verbatim would write
|
|
1038
1073
|
// them into git. Absent when the file declares no block.
|
|
1039
1074
|
...(declaration.declare
|
|
1040
|
-
? fingerprintRequest(siteYml)
|
|
1075
|
+
? fingerprintRequest(siteYml, provisioned)
|
|
1041
1076
|
: {
|
|
1042
1077
|
// Nothing was sent, so the base is unchanged — carry it forward
|
|
1043
1078
|
// rather than dropping it, or the next publish would read "no record"
|