@lanes-sh/link 0.6.1 → 0.6.2
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
CHANGED
|
@@ -42,16 +42,24 @@ export async function migratedRenamedProviders(
|
|
|
42
42
|
): Promise<boolean> {
|
|
43
43
|
if (!(refusal instanceof ConfigError)) return false;
|
|
44
44
|
|
|
45
|
-
|
|
46
|
-
|
|
45
|
+
// **The target's workspace, not this machine's.** `resolveSelection` defaults
|
|
46
|
+
// to the local root, which is where this looked before ADR-052 — and there was
|
|
47
|
+
// only one place a profile could be. The row that needs renaming is in the
|
|
48
|
+
// file the *named target* holds, so a bucket's stale row was invisible from
|
|
49
|
+
// here while the refusal kept naming this command as the fix.
|
|
50
|
+
const target = flags.target ?? '';
|
|
51
|
+
const localRoot = resolveWorkspaceRoot();
|
|
52
|
+
const root = await resolveTargetWorkspace(localRoot, target).catch(() => localRoot);
|
|
53
|
+
|
|
54
|
+
const selection = await resolveSelection({ profileFlag: flags.profile, root });
|
|
55
|
+
const document = await ConfigDocument.open(root, selection.profile);
|
|
47
56
|
if (pendingRenames(document).length === 0) return false;
|
|
48
57
|
|
|
49
58
|
// Shape-only, because the check this document fails runs after the schema.
|
|
50
59
|
// Throws when it is malformed beyond a rename, which is a better sentence
|
|
51
60
|
// than the referential one it would otherwise be reported under.
|
|
52
61
|
const config = shapeOf(document);
|
|
53
|
-
const
|
|
54
|
-
const credentials = await openSecretStoreFor(config, selection.workspaceRoot, target);
|
|
62
|
+
const credentials = await openSecretStoreFor(config, root, target);
|
|
55
63
|
|
|
56
64
|
const migration = await migrateRenamedProviders(document, credentials, {
|
|
57
65
|
apply: flags.fix === true,
|
|
@@ -70,6 +78,7 @@ export async function migratedRenamedProviders(
|
|
|
70
78
|
ok: applied && migration.blocked.length === 0,
|
|
71
79
|
profile: selection.profile,
|
|
72
80
|
target,
|
|
81
|
+
workspace: root,
|
|
73
82
|
applied,
|
|
74
83
|
rows: migration.rows,
|
|
75
84
|
changes: migration.changes,
|
|
@@ -272,7 +272,18 @@ export async function deploy(flags: DeployFlags): Promise<void> {
|
|
|
272
272
|
// Before the rollout, so the revision that comes up finds a config to read.
|
|
273
273
|
// Uploading after would leave a window where the service is serving and the
|
|
274
274
|
// workspace it was told to read is not there yet.
|
|
275
|
-
|
|
275
|
+
// **Only when there is somewhere to copy from.** After ADR-052 the profiles
|
|
276
|
+
// a deployed target serves *live in* that target's workspace, so
|
|
277
|
+
// `resolution.workspaceRoot` and `workspace` are the same bucket and this is
|
|
278
|
+
// a copy onto itself. It ran, and the self-copy is how the bucket's registry
|
|
279
|
+
// came to be overwritten.
|
|
280
|
+
//
|
|
281
|
+
// What it is still for is the one-way trip: a first deploy, where the
|
|
282
|
+
// profile is on this machine and the bucket does not hold it yet. That is a
|
|
283
|
+
// move, not a sync — the next deploy finds it already there.
|
|
284
|
+
if (resolution.workspaceRoot !== workspace) {
|
|
285
|
+
await uploadWorkspace(resolution.workspaceRoot, workspace, serving);
|
|
286
|
+
}
|
|
276
287
|
|
|
277
288
|
// **The bucket's own migration, here and nowhere else.**
|
|
278
289
|
//
|
|
@@ -304,7 +315,11 @@ export async function deploy(flags: DeployFlags): Promise<void> {
|
|
|
304
315
|
last_deploy: stamped,
|
|
305
316
|
});
|
|
306
317
|
|
|
307
|
-
|
|
318
|
+
// **This machine's registry, not the target's.** `resolution.workspaceRoot`
|
|
319
|
+
// is the workspace the profile was *found* in, which for a deployed target is
|
|
320
|
+
// the bucket — so writing the pointer there pointed the bucket at itself, and
|
|
321
|
+
// the revision that came up refused to open its own target.
|
|
322
|
+
await recordTarget(resolveWorkspaceRoot(), target, {
|
|
308
323
|
workspace,
|
|
309
324
|
primary: resolution.profile,
|
|
310
325
|
last_deploy: stamped,
|
|
@@ -7,7 +7,8 @@ import type {
|
|
|
7
7
|
SurveyInput,
|
|
8
8
|
SurveyResult,
|
|
9
9
|
} from '../driver.ts';
|
|
10
|
-
import
|
|
10
|
+
import { join } from 'node:path';
|
|
11
|
+
import { installRoot, type DeployConfig } from '#profile';
|
|
11
12
|
import { encodeRef } from '../adapters/gcp-secret-manager.ts';
|
|
12
13
|
import {
|
|
13
14
|
captureGcloud,
|
|
@@ -50,6 +51,10 @@ export function deployPlan(input: PlanInput): DeployStep[] {
|
|
|
50
51
|
const cloudrun = requireProject(input.deploy, input.target);
|
|
51
52
|
const image = imageReference(cloudrun, input.tag);
|
|
52
53
|
const scope = ['--project', cloudrun.project, '--region', cloudrun.region];
|
|
54
|
+
// Where this package is installed, which is where the Dockerfile and the build
|
|
55
|
+
// config live. Never the working directory: `lanes link deploy` is run from
|
|
56
|
+
// wherever somebody happens to be standing.
|
|
57
|
+
const root = installRoot(import.meta.dir);
|
|
53
58
|
|
|
54
59
|
return [
|
|
55
60
|
{
|
|
@@ -80,10 +85,15 @@ export function deployPlan(input: PlanInput): DeployStep[] {
|
|
|
80
85
|
'--project',
|
|
81
86
|
cloudrun.project,
|
|
82
87
|
'--config',
|
|
83
|
-
'src/deployments/gcp/cloudbuild.yaml',
|
|
88
|
+
join(root, 'src/deployments/gcp/cloudbuild.yaml'),
|
|
84
89
|
'--substitutions',
|
|
85
90
|
`_IMAGE=${image}`,
|
|
86
|
-
|
|
91
|
+
// The build context, and the config beside it, are the *installed
|
|
92
|
+
// package* — not whatever directory the operator happened to run from.
|
|
93
|
+
// Both were relative, so `lanes link deploy` worked from a checkout and
|
|
94
|
+
// failed everywhere else with a missing cloudbuild.yaml, which reads as
|
|
95
|
+
// a broken install rather than as a wrong working directory.
|
|
96
|
+
root,
|
|
87
97
|
],
|
|
88
98
|
},
|
|
89
99
|
{
|
|
@@ -64,7 +64,15 @@ export function deployedWorkspace(declared: TargetConfig): string | undefined {
|
|
|
64
64
|
* between matching it and not is a credential in a bucket.
|
|
65
65
|
*/
|
|
66
66
|
export function isWorkspaceConfig(key: string, profiles?: readonly string[]): boolean {
|
|
67
|
-
|
|
67
|
+
// **Never the workspace file.** It was sent, and it is the one file that must
|
|
68
|
+
// not be: it holds the *target registry*, and the two workspaces have
|
|
69
|
+
// different ones. This machine's says `cloud: workspace: gs://…`, so copying
|
|
70
|
+
// it into that bucket left the bucket pointing at itself — a loop `openTarget`
|
|
71
|
+
// refuses, on the target it had just deployed (ADR-052).
|
|
72
|
+
//
|
|
73
|
+
// The bucket's own registry is written by `deploy`, from the declaration, once
|
|
74
|
+
// the upload is done.
|
|
75
|
+
if (key === WORKSPACE_FILE) return false;
|
|
68
76
|
|
|
69
77
|
// A set rather than one name, because a deploy now sends every profile that
|
|
70
78
|
// declares the target rather than the single one it was told. `undefined`
|
|
@@ -39,11 +39,14 @@ export async function recordTarget(
|
|
|
39
39
|
): Promise<void> {
|
|
40
40
|
await editRegistry(workspaceRoot, (targets) => {
|
|
41
41
|
const previous = targets[target];
|
|
42
|
+
// Neither shape merges into the other, and the symmetry is the point: an
|
|
43
|
+
// entry carrying both a `workspace:` and adapters is what the schema refuses.
|
|
44
|
+
//
|
|
42
45
|
// A pointer replacing a declaration is `deploy` handing the target over to
|
|
43
|
-
// the workspace it just wrote
|
|
44
|
-
//
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
// the workspace it just wrote. A declaration replacing a pointer is the same
|
|
47
|
+
// command writing the bucket's own registry — and merging there left the
|
|
48
|
+
// laptop's `workspace:` on it, pointing the bucket at itself.
|
|
49
|
+
targets[target] = { ...pick(previous), ...entry };
|
|
47
50
|
});
|
|
48
51
|
}
|
|
49
52
|
|
|
@@ -54,7 +57,7 @@ export async function removeTarget(workspaceRoot: string, target: string): Promi
|
|
|
54
57
|
});
|
|
55
58
|
}
|
|
56
59
|
|
|
57
|
-
/** The fields that survive
|
|
60
|
+
/** The fields that survive an entry changing shape: the deploy record, and only it. */
|
|
58
61
|
function pick(previous: WorkspaceTarget | undefined): Partial<WorkspaceTarget> {
|
|
59
62
|
if (!previous) return {};
|
|
60
63
|
return {
|