@mediamonks/canopy-cli 0.7.45 → 0.7.46
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/{ai-FBHTSCLZ.mjs → ai-CWPQiUft.mjs} +2 -2
- package/dist/{api-Bl6SG8Oq.mjs → api-DmJrIsE7.mjs} +134 -19
- package/dist/{browse-B8-0dweq.mjs → browse-BPLWhoFI.mjs} +2 -2
- package/dist/{db-BJJGydgL.mjs → db-DpCMc0uz.mjs} +4 -4
- package/dist/{deploy-ExrIeX_m.mjs → deploy-BnRQusjJ.mjs} +51 -5
- package/dist/{dev-D-vwE8G-.mjs → dev-BmKeIdMi.mjs} +7 -7
- package/dist/dev-BvVlq9np.mjs +2 -0
- package/dist/docs/ci.md +112 -50
- package/dist/docs/cli-create.md +2 -0
- package/dist/docs/cli-deploy.md +1 -1
- package/dist/docs/cli-repo.md +34 -2
- package/dist/docs/cli-token.md +8 -6
- package/dist/docs/cli.md +6 -2
- package/dist/docs/deploy.md +3 -2
- package/dist/docs/index.json +3 -3
- package/dist/docs/platform-deploys-and-rollbacks.md +4 -4
- package/dist/{docs-Cpky2XNP.mjs → docs-CtMrMpt1.mjs} +4 -4
- package/dist/{egress-C2amvqmE.mjs → egress-BM4EpLBh.mjs} +3 -3
- package/dist/{feedback-CTV6inRg.mjs → feedback-BXhbxyXI.mjs} +5 -5
- package/dist/index.mjs +35 -35
- package/dist/{local-BThs382Y.mjs → local-C9Bi9f3y.mjs} +2 -2
- package/dist/{login-CREcZfLH.mjs → login-2MW9sGJO.mjs} +3 -3
- package/dist/{logs-BnrNZvLc.mjs → logs-dZMPTJLu.mjs} +3 -3
- package/dist/{mcp-Cy8EwhhD.mjs → mcp-CGmWkGN8.mjs} +3 -3
- package/dist/{migrations-D8NqjC5I.mjs → migrations-BReMlGWP.mjs} +2 -2
- package/dist/{open-Bpz4C2Sr.mjs → open-DcQSdfx5.mjs} +2 -2
- package/dist/{package-Dph4v1Cq.mjs → package-BdNHq8QV.mjs} +1 -1
- package/dist/{project-template-Cdr1uwwE.mjs → project-template-BPW_Dgxj.mjs} +3 -3
- package/dist/project-template-Dw11151p.mjs +2 -0
- package/dist/{projects-BRG2HnBs.mjs → projects-CnU3194_.mjs} +26 -8
- package/dist/{rename-BGKa25Rq.mjs → rename-BWvERJgA.mjs} +3 -3
- package/dist/{repo-CwT2RDoW.mjs → repo-6a7gRQaf.mjs} +171 -8
- package/dist/repo-C7AXDtFZ.mjs +2 -0
- package/dist/{roles-C7pq9bIx.mjs → roles-CrYf_8wk.mjs} +1 -1
- package/dist/{roles-DAPPXf4U.mjs → roles-S2D9Id0W.mjs} +3 -3
- package/dist/{schedule-BcqBspn3.mjs → schedule-DvHSSzfR.mjs} +3 -3
- package/dist/{secrets-BjbIi7Gp.mjs → secrets-BIn2RPoe.mjs} +3 -3
- package/dist/{site-0i7wAJqg.mjs → site-BL5Ik-yj.mjs} +50 -1
- package/dist/{skill-Zj19RNo2.mjs → skill-vzcCD1b4.mjs} +2 -2
- package/dist/{staging-_2Js0Fkc.mjs → staging-Dn8kH9cK.mjs} +3 -3
- package/dist/templates/spa/.github/workflows/canopy-deploy.yml +18 -12
- package/dist/templates/spa/README.md +1 -1
- package/dist/templates/worker/.github/workflows/canopy-deploy.yml +18 -12
- package/dist/templates/worker/README.md +1 -1
- package/dist/{token-7vSNuz-X.mjs → token-DMCj88A-.mjs} +6 -6
- package/dist/{ui-CLnICOne.mjs → ui-26ETk1n8.mjs} +1 -1
- package/dist/{upgrade-B9tCAbre.mjs → upgrade-8_pFNTo5.mjs} +2 -2
- package/dist/{usage-DGGmTwjJ.mjs → usage-Bpz5B1N1.mjs} +3 -3
- package/package.json +1 -1
- package/dist/dev-CSimnUP3.mjs +0 -2
- package/dist/project-template-715H_lnu.mjs +0 -2
- package/dist/repo-DmFs6H9m.mjs +0 -2
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as apiRequest,
|
|
3
|
-
import { a as unwrap, i as requireTty, r as reportErrors, t as formatDateTime } from "./ui-
|
|
1
|
+
import { a as parseGithubRemote, c as projectDirArgs, d as resolveProject, l as projectFlagArgs, m as tryReadCanopyJson, o as parseRepositoryArg, r as siteConfig } from "./site-BL5Ik-yj.mjs";
|
|
2
|
+
import { a as apiRequest, f as noticeRenamesOnly, m as withContext, n as agentHeaders, u as followRename } from "./api-DmJrIsE7.mjs";
|
|
3
|
+
import { a as unwrap, i as requireTty, r as reportErrors, t as formatDateTime } from "./ui-26ETk1n8.mjs";
|
|
4
4
|
import { a as parseCredentialInput, i as helperCommand, o as runCredentialHelper, r as fileTokenCache, t as RepoCredentialError } from "./repo-credential-CNDRM-tG.mjs";
|
|
5
5
|
import { defineCommand } from "citty";
|
|
6
6
|
import { existsSync, realpathSync } from "node:fs";
|
|
@@ -104,6 +104,13 @@ async function setLocalConfig(dir, key, values) {
|
|
|
104
104
|
* - `show` says which repository the project has (`--json` for agents);
|
|
105
105
|
* - `delete` removes it and its history (owner only).
|
|
106
106
|
*
|
|
107
|
+
* CI deploys from GitHub Actions (OpenSpec platform-oauth-oidc), owner only:
|
|
108
|
+
*
|
|
109
|
+
* - `link` links a GitHub repository outside Canopy (a Canopy repository needs none);
|
|
110
|
+
* - `unlink` removes that link;
|
|
111
|
+
* - `branches` sets the live and staging branches CI deploys from;
|
|
112
|
+
* - `deploys` turns CI deploys on or off without unlinking.
|
|
113
|
+
*
|
|
107
114
|
* Setting up a folder points a remote at the repository and registers
|
|
108
115
|
* `canopy repo credential` as git's credential helper for that one
|
|
109
116
|
* repository. No token is ever written
|
|
@@ -208,16 +215,26 @@ const showCommand = defineCommand({
|
|
|
208
215
|
return;
|
|
209
216
|
}
|
|
210
217
|
if (repo.kind === "none") console.log(`${slug} has no git repository. Create one with \`canopy repo create\`.`);
|
|
211
|
-
else if (repo.kind === "linked")
|
|
212
|
-
|
|
218
|
+
else if (repo.kind === "linked") {
|
|
219
|
+
console.log(`${slug} is linked to ${repo.host ? `${repo.host}/` : ""}${repo.linked_repository}.`);
|
|
220
|
+
printCiDeploys(repo);
|
|
221
|
+
} else {
|
|
213
222
|
console.log(`${slug} has a Canopy git repository.`);
|
|
214
223
|
console.log(` remote ${repo.remote}`);
|
|
215
224
|
console.log(` created ${repo.created_at ? formatDateTime(repo.created_at) : "—"}`);
|
|
216
225
|
console.log(` last push ${repo.last_push_at ? formatDateTime(repo.last_push_at) : "never"}`);
|
|
226
|
+
printCiDeploys(repo);
|
|
217
227
|
console.log(`\nClone it with \`canopy repo clone ${slug}\`.`);
|
|
218
228
|
}
|
|
219
229
|
})
|
|
220
230
|
});
|
|
231
|
+
/** The CI lines of `repo show`: both branches and whether CI deploys are on. */
|
|
232
|
+
function printCiDeploys(repo) {
|
|
233
|
+
if (repo.deploys_enabled === void 0 || repo.deploys_enabled === null) return;
|
|
234
|
+
console.log(` live ${repo.ref ?? "refs/heads/main"}`);
|
|
235
|
+
console.log(` staging ${repo.staging_ref ?? "—"}`);
|
|
236
|
+
console.log(` CI deploys ${repo.deploys_enabled ? "on" : "off"}`);
|
|
237
|
+
}
|
|
221
238
|
const createCommand = defineCommand({
|
|
222
239
|
meta: {
|
|
223
240
|
name: "create",
|
|
@@ -380,13 +397,159 @@ async function readStdin() {
|
|
|
380
397
|
for await (const chunk of process.stdin) chunks.push(chunk);
|
|
381
398
|
return Buffer.concat(chunks).toString("utf8");
|
|
382
399
|
}
|
|
400
|
+
/** Plain words for the link routes' errors; `invalid_host` here is a GitHub host. */
|
|
401
|
+
const LINK_FIXES = {
|
|
402
|
+
owner_only: "Only the project owner (or a platform admin) can change where CI deploys come from.",
|
|
403
|
+
repo_exists: "This project has a Canopy repository, and CI deploys from it without a link. Set its branches with `canopy repo branches`.",
|
|
404
|
+
invalid_host: "Link a repository on github.com or this site's GitHub Enterprise host (e.g. monks.ghe.com/monks/my-tool).",
|
|
405
|
+
repository_not_linked: "Link a repository first with `canopy repo link <owner/name>`.",
|
|
406
|
+
not_linked: "This project has no linked GitHub repository — `canopy repo show` says what it has."
|
|
407
|
+
};
|
|
408
|
+
/** `main` → `refs/heads/main`; a full ref passes through. */
|
|
409
|
+
function toRef(value) {
|
|
410
|
+
const trimmed = value.trim();
|
|
411
|
+
return trimmed.startsWith("refs/") ? trimmed : `refs/heads/${trimmed}`;
|
|
412
|
+
}
|
|
413
|
+
/** `refs/heads/develop` → `develop`, for the workflow's `on.push.branches`. */
|
|
414
|
+
const branchName = (ref) => ref.replace(/^refs\/heads\//, "");
|
|
415
|
+
/** The GitHub repository the folder's `origin` remote points at, if any. */
|
|
416
|
+
async function githubOrigin(dir) {
|
|
417
|
+
if (!await isWorkingCopy(dir).catch(() => false)) return null;
|
|
418
|
+
const origin = (await remotes(dir).catch(() => /* @__PURE__ */ new Map())).get("origin");
|
|
419
|
+
return origin ? parseGithubRemote(origin) : null;
|
|
420
|
+
}
|
|
421
|
+
function printLink(slug, link) {
|
|
422
|
+
console.log(` repository ${link.host}/${link.repository}`);
|
|
423
|
+
console.log(` live ${link.ref}`);
|
|
424
|
+
console.log(` staging ${link.staging_ref ?? "—"}`);
|
|
425
|
+
console.log(` CI deploys ${link.deploys_enabled ? "on" : "off"}`);
|
|
426
|
+
if (link.staging_ref) p.log.info(`Pushes to ${branchName(link.staging_ref)} deploy ${slug}'s staging channel once the workflow runs on them: add "${branchName(link.staging_ref)}" to on.push.branches in .github/workflows/canopy-deploy.yml.`);
|
|
427
|
+
}
|
|
383
428
|
const repoCommand = defineCommand({
|
|
384
429
|
meta: {
|
|
385
430
|
name: "repo",
|
|
386
|
-
description: "The project's git repository (clone, connect, create)"
|
|
431
|
+
description: "The project's git repository (clone, connect, create, link CI deploys)"
|
|
387
432
|
},
|
|
388
433
|
subCommands: {
|
|
389
434
|
show: showCommand,
|
|
435
|
+
link: defineCommand({
|
|
436
|
+
meta: {
|
|
437
|
+
name: "link",
|
|
438
|
+
description: "Link a GitHub repository so its pushes deploy the project (owner only)"
|
|
439
|
+
},
|
|
440
|
+
args: {
|
|
441
|
+
repository: {
|
|
442
|
+
type: "positional",
|
|
443
|
+
description: "[<host>/]<owner/name> — defaults to this folder's origin remote",
|
|
444
|
+
required: false
|
|
445
|
+
},
|
|
446
|
+
...projectFlagArgs,
|
|
447
|
+
ref: {
|
|
448
|
+
type: "string",
|
|
449
|
+
description: "Live branch (default main)"
|
|
450
|
+
},
|
|
451
|
+
"staging-ref": {
|
|
452
|
+
type: "string",
|
|
453
|
+
description: "Staging branch: its pushes deploy to the staging channel only"
|
|
454
|
+
}
|
|
455
|
+
},
|
|
456
|
+
run: reportErrors("repo link", async ({ args }) => {
|
|
457
|
+
const { dir, slug } = resolveProject({ project: args.project });
|
|
458
|
+
let target;
|
|
459
|
+
if (args.repository) {
|
|
460
|
+
target = parseRepositoryArg(args.repository);
|
|
461
|
+
if (!target) throw new Error(`"${args.repository}" is not a repository — pass <owner/name> or <host>/<owner/name>, e.g. monks/my-tool.`);
|
|
462
|
+
} else {
|
|
463
|
+
target = await githubOrigin(dir);
|
|
464
|
+
if (!target) throw new Error("This folder has no GitHub origin remote to link — pass the repository: `canopy repo link <owner/name>`.");
|
|
465
|
+
}
|
|
466
|
+
const link = await withContext(apiRequest("PUT", `/projects/${slug}/github`, {
|
|
467
|
+
repository: target.repository,
|
|
468
|
+
...target.host && { host: target.host },
|
|
469
|
+
...args.ref && { ref: toRef(args.ref) },
|
|
470
|
+
...args["staging-ref"] && { staging_ref: toRef(args["staging-ref"]) }
|
|
471
|
+
}), {
|
|
472
|
+
action: `link ${slug} to ${target.repository}`,
|
|
473
|
+
fixes: LINK_FIXES
|
|
474
|
+
});
|
|
475
|
+
p.log.success(`Linked ${slug} to ${link.host}/${link.repository} @ ${link.ref}. A push there now deploys ${slug}.`);
|
|
476
|
+
printLink(slug, link);
|
|
477
|
+
})
|
|
478
|
+
}),
|
|
479
|
+
unlink: defineCommand({
|
|
480
|
+
meta: {
|
|
481
|
+
name: "unlink",
|
|
482
|
+
description: "Remove the project's GitHub repository link (owner only)"
|
|
483
|
+
},
|
|
484
|
+
args: { ...projectDirArgs },
|
|
485
|
+
run: reportErrors("repo unlink", async ({ args }) => {
|
|
486
|
+
const { slug } = resolveProject(args);
|
|
487
|
+
await withContext(apiRequest("DELETE", `/projects/${slug}/github`), {
|
|
488
|
+
action: `unlink ${slug}'s repository`,
|
|
489
|
+
fixes: LINK_FIXES
|
|
490
|
+
});
|
|
491
|
+
p.log.success(`Unlinked ${slug}. GitHub Actions runs now skip the deploy.`);
|
|
492
|
+
})
|
|
493
|
+
}),
|
|
494
|
+
branches: defineCommand({
|
|
495
|
+
meta: {
|
|
496
|
+
name: "branches",
|
|
497
|
+
description: "Set the live and staging branches CI deploys from (owner only)"
|
|
498
|
+
},
|
|
499
|
+
args: {
|
|
500
|
+
...projectDirArgs,
|
|
501
|
+
ref: {
|
|
502
|
+
type: "string",
|
|
503
|
+
description: "Live branch, e.g. main"
|
|
504
|
+
},
|
|
505
|
+
"staging-ref": {
|
|
506
|
+
type: "string",
|
|
507
|
+
description: "Staging branch, e.g. develop (`none` removes it)"
|
|
508
|
+
}
|
|
509
|
+
},
|
|
510
|
+
run: reportErrors("repo branches", async ({ args }) => {
|
|
511
|
+
const { slug } = resolveProject(args);
|
|
512
|
+
const stagingArg = args["staging-ref"];
|
|
513
|
+
if (!args.ref && stagingArg === void 0) throw new Error("Pass --ref and/or --staging-ref (see `canopy repo show` for the current ones).");
|
|
514
|
+
const link = await withContext(apiRequest("PATCH", `/projects/${slug}/github`, {
|
|
515
|
+
...args.ref && { ref: toRef(args.ref) },
|
|
516
|
+
...stagingArg !== void 0 && { staging_ref: stagingArg === "none" || stagingArg === "" ? null : toRef(stagingArg) }
|
|
517
|
+
}), {
|
|
518
|
+
action: `set ${slug}'s CI branches`,
|
|
519
|
+
fixes: LINK_FIXES
|
|
520
|
+
});
|
|
521
|
+
p.log.success(`CI deploys for ${slug} come from:`);
|
|
522
|
+
printLink(slug, link);
|
|
523
|
+
})
|
|
524
|
+
}),
|
|
525
|
+
deploys: defineCommand({
|
|
526
|
+
meta: {
|
|
527
|
+
name: "deploys",
|
|
528
|
+
description: "Turn CI deploys from GitHub Actions on or off (owner only)"
|
|
529
|
+
},
|
|
530
|
+
args: {
|
|
531
|
+
state: {
|
|
532
|
+
type: "positional",
|
|
533
|
+
description: "on | off",
|
|
534
|
+
required: true
|
|
535
|
+
},
|
|
536
|
+
...projectFlagArgs
|
|
537
|
+
},
|
|
538
|
+
run: reportErrors("repo deploys", async ({ args }) => {
|
|
539
|
+
if (args.state !== "on" && args.state !== "off") throw new Error(`Pass on or off (got "${args.state}").`);
|
|
540
|
+
const { slug } = resolveProject({ project: args.project });
|
|
541
|
+
const enabled = args.state === "on";
|
|
542
|
+
await withContext(apiRequest("PATCH", `/projects/${slug}/github`, { deploys_enabled: enabled }), {
|
|
543
|
+
action: `turn CI deploys ${args.state} for ${slug}`,
|
|
544
|
+
fixes: LINK_FIXES
|
|
545
|
+
});
|
|
546
|
+
if (enabled) p.log.success(`CI deploys are on for ${slug}: pushes to its branches deploy again.`);
|
|
547
|
+
else {
|
|
548
|
+
p.log.success(`CI deploys are off for ${slug}: GitHub Actions runs skip the deploy with a warning. The repository link stays.`);
|
|
549
|
+
p.log.info("Deploy tokens are not affected — a workflow with CANOPY_DEPLOY_TOKEN still deploys. Revoke them with `canopy token revoke` to stop those too.");
|
|
550
|
+
}
|
|
551
|
+
})
|
|
552
|
+
}),
|
|
390
553
|
clone: cloneCommand,
|
|
391
554
|
connect: connectCommand,
|
|
392
555
|
create: createCommand,
|
|
@@ -411,7 +574,7 @@ async function offerRepoOnCreate(slug, dir, flag) {
|
|
|
411
574
|
if (flag === void 0) {
|
|
412
575
|
if (!process.stdin.isTTY || !await reposAvailable()) return null;
|
|
413
576
|
const origin = await isWorkingCopy(dir).catch(() => false) ? (await remotes(dir)).get("origin") : void 0;
|
|
414
|
-
if (origin &&
|
|
577
|
+
if (origin && parseGithubRemote(origin)) return null;
|
|
415
578
|
if (!unwrap(await p.confirm({
|
|
416
579
|
message: "Create a git repository for this project on Canopy?",
|
|
417
580
|
initialValue: false
|
|
@@ -428,4 +591,4 @@ async function offerRepoOnCreate(slug, dir, flag) {
|
|
|
428
591
|
}
|
|
429
592
|
}
|
|
430
593
|
//#endregion
|
|
431
|
-
export {
|
|
594
|
+
export { offerRepoOnCreate as a, githubOrigin as i, cloneProjectRepo as n, repoCommand as o, connectFolder as r, toRef as s, cliInvocation as t };
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { i as membersCommand, o as rolesCommand } from "./roles-
|
|
1
|
+
import { i as membersCommand, o as rolesCommand } from "./roles-S2D9Id0W.mjs";
|
|
2
2
|
export { membersCommand, rolesCommand };
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as apiRequest,
|
|
3
|
-
import { a as unwrap, i as requireTty, r as reportErrors } from "./ui-
|
|
1
|
+
import { c as projectDirArgs, d as resolveProject } from "./site-BL5Ik-yj.mjs";
|
|
2
|
+
import { a as apiRequest, m as withContext } from "./api-DmJrIsE7.mjs";
|
|
3
|
+
import { a as unwrap, i as requireTty, r as reportErrors } from "./ui-26ETk1n8.mjs";
|
|
4
4
|
import { defineCommand } from "citty";
|
|
5
5
|
import * as p from "@clack/prompts";
|
|
6
6
|
//#region src/roles.ts
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as apiRequest,
|
|
3
|
-
import { r as reportErrors, t as formatDateTime } from "./ui-
|
|
1
|
+
import { d as resolveProject, l as projectFlagArgs } from "./site-BL5Ik-yj.mjs";
|
|
2
|
+
import { a as apiRequest, m as withContext } from "./api-DmJrIsE7.mjs";
|
|
3
|
+
import { r as reportErrors, t as formatDateTime } from "./ui-26ETk1n8.mjs";
|
|
4
4
|
import { defineCommand } from "citty";
|
|
5
5
|
//#region src/schedule.ts
|
|
6
6
|
/**
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as apiRequest,
|
|
3
|
-
import { a as unwrap, r as reportErrors, t as formatDateTime } from "./ui-
|
|
1
|
+
import { c as projectDirArgs, d as resolveProject } from "./site-BL5Ik-yj.mjs";
|
|
2
|
+
import { a as apiRequest, m as withContext } from "./api-DmJrIsE7.mjs";
|
|
3
|
+
import { a as unwrap, r as reportErrors, t as formatDateTime } from "./ui-26ETk1n8.mjs";
|
|
4
4
|
import { defineCommand } from "citty";
|
|
5
5
|
import * as p from "@clack/prompts";
|
|
6
6
|
//#region src/secrets.ts
|
|
@@ -259,6 +259,55 @@ function tryResolveProject(args) {
|
|
|
259
259
|
slug: args.project ?? tryReadCanopyJson(dir)?.slug
|
|
260
260
|
};
|
|
261
261
|
}
|
|
262
|
+
const GITHUB_HOST = /^(?:github\.com|[a-z0-9][a-z0-9-]*\.ghe\.com)$/;
|
|
263
|
+
/**
|
|
264
|
+
* The GitHub repository a git remote URL points at, or null when it is not on
|
|
265
|
+
* github.com or a `*.ghe.com` host (OpenSpec platform-oauth-oidc). Whether the
|
|
266
|
+
* host is one this site trusts is the API's call, not ours.
|
|
267
|
+
*
|
|
268
|
+
* git@monks.ghe.com:monks/my-tool.git
|
|
269
|
+
* ssh://git@github.com/monks/my-tool.git
|
|
270
|
+
* https://github.com/monks/my-tool(.git)
|
|
271
|
+
*/
|
|
272
|
+
function parseGithubRemote(url) {
|
|
273
|
+
const trimmed = url.trim();
|
|
274
|
+
const scp = /^[\w.-]+@([^:/]+):([^/]+)\/([^/]+?)(?:\.git)?\/?$/.exec(trimmed);
|
|
275
|
+
let host;
|
|
276
|
+
let parts;
|
|
277
|
+
if (scp) {
|
|
278
|
+
host = scp[1];
|
|
279
|
+
parts = [scp[2], scp[3]];
|
|
280
|
+
} else try {
|
|
281
|
+
const parsed = new URL(trimmed);
|
|
282
|
+
if (![
|
|
283
|
+
"https:",
|
|
284
|
+
"http:",
|
|
285
|
+
"ssh:",
|
|
286
|
+
"git:"
|
|
287
|
+
].includes(parsed.protocol)) return null;
|
|
288
|
+
host = parsed.hostname;
|
|
289
|
+
parts = parsed.pathname.replace(/^\/+|\/+$/g, "").replace(/\.git$/, "").split("/");
|
|
290
|
+
} catch {
|
|
291
|
+
return null;
|
|
292
|
+
}
|
|
293
|
+
host = host?.toLowerCase();
|
|
294
|
+
if (!host || !GITHUB_HOST.test(host) || parts?.length !== 2 || !parts[0] || !parts[1]) return null;
|
|
295
|
+
return {
|
|
296
|
+
host,
|
|
297
|
+
repository: `${parts[0]}/${parts[1]}`
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
/** `[<host>/]owner/name` as typed on the command line; null when it is neither. */
|
|
301
|
+
function parseRepositoryArg(value) {
|
|
302
|
+
const parts = value.trim().replace(/\.git$/, "").split("/");
|
|
303
|
+
if (parts.some((part) => part === "")) return null;
|
|
304
|
+
if (parts.length === 2) return { repository: `${parts[0]}/${parts[1]}` };
|
|
305
|
+
if (parts.length === 3 && parts[0].includes(".")) return {
|
|
306
|
+
host: parts[0].toLowerCase(),
|
|
307
|
+
repository: `${parts[1]}/${parts[2]}`
|
|
308
|
+
};
|
|
309
|
+
return null;
|
|
310
|
+
}
|
|
262
311
|
//#endregion
|
|
263
312
|
//#region src/site.ts
|
|
264
313
|
/**
|
|
@@ -300,4 +349,4 @@ function siteHeaderLine(cliVersion) {
|
|
|
300
349
|
return `canopy ${cliVersion} · ${c.envOverride || !c.profileId ? c.apiUrl : `${c.profileId} (${c.projectDomain ?? new URL(c.apiUrl).hostname})`}`;
|
|
301
350
|
}
|
|
302
351
|
//#endregion
|
|
303
|
-
export {
|
|
352
|
+
export { isProfileId as C, getBakedProfile as S, listBakedProfiles as _, parseGithubRemote as a, upsertProfileAuth as b, projectDirArgs as c, resolveProject as d, rewriteCanopyJsonSlug as f, clearAllProfileTokens as g, tryResolveProject as h, siteHeaderLine as i, projectFlagArgs as l, tryReadCanopyJson as m, setInvocationProfile as n, parseRepositoryArg as o, stagingUrl as p, siteConfig as r, projectDialect as s, projectDir as t, projectUrl as u, readConfigFile as v, writeConfigFile as x, setActiveProfile as y };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { t as version } from "./package-
|
|
2
|
-
import { r as reportErrors } from "./ui-
|
|
1
|
+
import { t as version } from "./package-BdNHq8QV.mjs";
|
|
2
|
+
import { r as reportErrors } from "./ui-26ETk1n8.mjs";
|
|
3
3
|
import { defineCommand } from "citty";
|
|
4
4
|
import { cpSync, existsSync, mkdirSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
|
|
5
5
|
import { homedir } from "node:os";
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as apiRequest,
|
|
3
|
-
import { a as unwrap, i as requireTty, r as reportErrors, t as formatDateTime } from "./ui-
|
|
1
|
+
import { d as resolveProject, l as projectFlagArgs, p as stagingUrl, u as projectUrl } from "./site-BL5Ik-yj.mjs";
|
|
2
|
+
import { a as apiRequest, m as withContext } from "./api-DmJrIsE7.mjs";
|
|
3
|
+
import { a as unwrap, i as requireTty, r as reportErrors, t as formatDateTime } from "./ui-26ETk1n8.mjs";
|
|
4
4
|
import { defineCommand } from "citty";
|
|
5
5
|
import * as p from "@clack/prompts";
|
|
6
6
|
//#region src/staging.ts
|
|
@@ -1,34 +1,40 @@
|
|
|
1
1
|
# Deploy to Canopy on every push to main. GitHub's runners are the build
|
|
2
2
|
# farm — the platform owns no build infrastructure.
|
|
3
3
|
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
4
|
+
# No secret needed: the run proves which repository and branch it comes from
|
|
5
|
+
# with GitHub's own identity token (the id-token permission below), and
|
|
6
|
+
# Canopy deploys it once the project owner has linked this repository:
|
|
7
|
+
#
|
|
8
|
+
# canopy repo link (in this folder: links the origin remote)
|
|
9
|
+
#
|
|
10
|
+
# Until then, every run skips the deploy with a warning instead of failing.
|
|
11
|
+
# The owner can pause CI deploys without editing this file:
|
|
12
|
+
#
|
|
13
|
+
# canopy repo deploys off (and `on` to resume)
|
|
14
|
+
#
|
|
15
|
+
# A staging branch (`canopy repo branches --staging-ref develop`) deploys to
|
|
16
|
+
# the project's staging channel only. Add it to `branches` below, e.g.
|
|
17
|
+
# [main, develop], so pushes to it run this workflow.
|
|
9
18
|
name: Deploy to Canopy
|
|
10
19
|
|
|
11
20
|
on:
|
|
12
21
|
push:
|
|
13
22
|
branches: [main]
|
|
14
23
|
|
|
24
|
+
permissions:
|
|
25
|
+
id-token: write
|
|
26
|
+
contents: read
|
|
27
|
+
|
|
15
28
|
jobs:
|
|
16
29
|
deploy:
|
|
17
30
|
runs-on: ubuntu-latest
|
|
18
|
-
env:
|
|
19
|
-
CANOPY_DEPLOY_TOKEN: ${{ secrets.CANOPY_DEPLOY_TOKEN }}
|
|
20
31
|
steps:
|
|
21
32
|
- uses: actions/checkout@v7
|
|
22
33
|
- uses: actions/setup-node@v7
|
|
23
34
|
with:
|
|
24
35
|
node-version: 24
|
|
25
36
|
- name: Install dependencies
|
|
26
|
-
if: env.CANOPY_DEPLOY_TOKEN != ''
|
|
27
37
|
run: |
|
|
28
38
|
if [ -f package-lock.json ]; then npm ci; else npm install; fi
|
|
29
39
|
- name: Deploy
|
|
30
|
-
if: env.CANOPY_DEPLOY_TOKEN != ''
|
|
31
40
|
run: npm exec --yes @mediamonks/canopy-cli deploy
|
|
32
|
-
- name: Skipped (no deploy token)
|
|
33
|
-
if: env.CANOPY_DEPLOY_TOKEN == ''
|
|
34
|
-
run: echo "CANOPY_DEPLOY_TOKEN secret not set — skipping deploy."
|
|
@@ -11,4 +11,4 @@ A [Canopy](https://dashboard.thanarie.dev) project — Vite + React. Live
|
|
|
11
11
|
- `AGENTS.md` — project guardrails and skill setup for coding agents
|
|
12
12
|
|
|
13
13
|
Push-to-main deploys are prepared in `.github/workflows/canopy-deploy.yml`
|
|
14
|
-
(
|
|
14
|
+
(no secret: link the repository once with `canopy repo link` — see the workflow file).
|
|
@@ -1,34 +1,40 @@
|
|
|
1
1
|
# Deploy to Canopy on every push to main. GitHub's runners are the build
|
|
2
2
|
# farm — the platform owns no build infrastructure.
|
|
3
3
|
#
|
|
4
|
-
#
|
|
5
|
-
#
|
|
6
|
-
#
|
|
7
|
-
#
|
|
8
|
-
#
|
|
4
|
+
# No secret needed: the run proves which repository and branch it comes from
|
|
5
|
+
# with GitHub's own identity token (the id-token permission below), and
|
|
6
|
+
# Canopy deploys it once the project owner has linked this repository:
|
|
7
|
+
#
|
|
8
|
+
# canopy repo link (in this folder: links the origin remote)
|
|
9
|
+
#
|
|
10
|
+
# Until then, every run skips the deploy with a warning instead of failing.
|
|
11
|
+
# The owner can pause CI deploys without editing this file:
|
|
12
|
+
#
|
|
13
|
+
# canopy repo deploys off (and `on` to resume)
|
|
14
|
+
#
|
|
15
|
+
# A staging branch (`canopy repo branches --staging-ref develop`) deploys to
|
|
16
|
+
# the project's staging channel only. Add it to `branches` below, e.g.
|
|
17
|
+
# [main, develop], so pushes to it run this workflow.
|
|
9
18
|
name: Deploy to Canopy
|
|
10
19
|
|
|
11
20
|
on:
|
|
12
21
|
push:
|
|
13
22
|
branches: [main]
|
|
14
23
|
|
|
24
|
+
permissions:
|
|
25
|
+
id-token: write
|
|
26
|
+
contents: read
|
|
27
|
+
|
|
15
28
|
jobs:
|
|
16
29
|
deploy:
|
|
17
30
|
runs-on: ubuntu-latest
|
|
18
|
-
env:
|
|
19
|
-
CANOPY_DEPLOY_TOKEN: ${{ secrets.CANOPY_DEPLOY_TOKEN }}
|
|
20
31
|
steps:
|
|
21
32
|
- uses: actions/checkout@v7
|
|
22
33
|
- uses: actions/setup-node@v7
|
|
23
34
|
with:
|
|
24
35
|
node-version: 24
|
|
25
36
|
- name: Install dependencies
|
|
26
|
-
if: env.CANOPY_DEPLOY_TOKEN != ''
|
|
27
37
|
run: |
|
|
28
38
|
if [ -f package-lock.json ]; then npm ci; else npm install; fi
|
|
29
39
|
- name: Deploy
|
|
30
|
-
if: env.CANOPY_DEPLOY_TOKEN != ''
|
|
31
40
|
run: npm exec --yes @mediamonks/canopy-cli deploy
|
|
32
|
-
- name: Skipped (no deploy token)
|
|
33
|
-
if: env.CANOPY_DEPLOY_TOKEN == ''
|
|
34
|
-
run: echo "CANOPY_DEPLOY_TOKEN secret not set — skipping deploy."
|
|
@@ -14,4 +14,4 @@ https://{{slug}}.thanarie.dev after the first `canopy deploy`.
|
|
|
14
14
|
- `AGENTS.md` — project guardrails and skill setup for coding agents
|
|
15
15
|
|
|
16
16
|
Push-to-main deploys are prepared in `.github/workflows/canopy-deploy.yml`
|
|
17
|
-
(
|
|
17
|
+
(no secret: link the repository once with `canopy repo link` — see the workflow file).
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as apiRequest } from "./api-
|
|
3
|
-
import { r as reportErrors, t as formatDateTime } from "./ui-
|
|
1
|
+
import { c as projectDirArgs, d as resolveProject, l as projectFlagArgs } from "./site-BL5Ik-yj.mjs";
|
|
2
|
+
import { a as apiRequest } from "./api-DmJrIsE7.mjs";
|
|
3
|
+
import { r as reportErrors, t as formatDateTime } from "./ui-26ETk1n8.mjs";
|
|
4
4
|
import { defineCommand } from "citty";
|
|
5
5
|
const tokenCommand = defineCommand({
|
|
6
6
|
meta: {
|
|
@@ -23,12 +23,12 @@ const tokenCommand = defineCommand({
|
|
|
23
23
|
run: reportErrors("token create", async ({ args }) => {
|
|
24
24
|
const { slug } = resolveProject(args);
|
|
25
25
|
const { token, idle_days } = await apiRequest("POST", `/projects/${slug}/deploy-tokens`, { name: args.name });
|
|
26
|
+
console.log("You do not need this for GitHub Actions — link the repository instead (`canopy repo link <owner/name>`) and pushes deploy with no secret.\n");
|
|
26
27
|
console.log(`Deploy token for ${slug} (shown once — store it now):\n`);
|
|
27
28
|
console.log(` ${token}\n`);
|
|
28
29
|
console.log(`It belongs to the project and stops working after ${idle_days} days without a deploy.`);
|
|
29
|
-
console.log("
|
|
30
|
-
console.log("
|
|
31
|
-
console.log("Pushes to main then deploy via .github/workflows/canopy-deploy.yml.");
|
|
30
|
+
console.log("For other CI, set it as the CANOPY_DEPLOY_TOKEN environment variable;");
|
|
31
|
+
console.log("`canopy deploy` then deploys as this project with no `canopy login`.");
|
|
32
32
|
})
|
|
33
33
|
}),
|
|
34
34
|
list: defineCommand({
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { t as PACKAGE_NAME } from "./version-check-CcsA1QjI.mjs";
|
|
2
|
-
import { r as reportErrors } from "./ui-
|
|
2
|
+
import { r as reportErrors } from "./ui-26ETk1n8.mjs";
|
|
3
3
|
import { defineCommand } from "citty";
|
|
4
4
|
import { spawn } from "node:child_process";
|
|
5
5
|
import { fileURLToPath } from "node:url";
|
|
@@ -112,7 +112,7 @@ const upgradeCommand = defineCommand({
|
|
|
112
112
|
const spec = `${PACKAGE_NAME}@${args.version ?? "latest"}`;
|
|
113
113
|
const command = pmCommand(pm);
|
|
114
114
|
const cmd = installArgs(pm, spec);
|
|
115
|
-
const { skillInstalled } = await import("./skill-
|
|
115
|
+
const { skillInstalled } = await import("./skill-vzcCD1b4.mjs");
|
|
116
116
|
const { homedir } = await import("node:os");
|
|
117
117
|
const refreshGlobal = skillInstalled(homedir());
|
|
118
118
|
const refreshRepo = process.cwd() !== homedir() && skillInstalled(process.cwd());
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { a as apiRequest,
|
|
3
|
-
import { r as reportErrors } from "./ui-
|
|
1
|
+
import { d as resolveProject, l as projectFlagArgs } from "./site-BL5Ik-yj.mjs";
|
|
2
|
+
import { a as apiRequest, m as withContext } from "./api-DmJrIsE7.mjs";
|
|
3
|
+
import { r as reportErrors } from "./ui-26ETk1n8.mjs";
|
|
4
4
|
import { defineCommand } from "citty";
|
|
5
5
|
//#region src/usage.ts
|
|
6
6
|
/**
|
package/package.json
CHANGED
package/dist/dev-CSimnUP3.mjs
DELETED
package/dist/repo-DmFs6H9m.mjs
DELETED