trustci 0.1.0 → 0.2.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/dist/cli.js +112 -7
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -19,6 +19,7 @@ var options = {
|
|
|
19
19
|
project: { type: "string" },
|
|
20
20
|
env: { type: "string" },
|
|
21
21
|
file: { type: "string" },
|
|
22
|
+
"circle-token": { type: "string" },
|
|
22
23
|
"org-id": { type: "string" },
|
|
23
24
|
"project-id": { type: "string" },
|
|
24
25
|
"pipeline-definition-id": { type: "string" },
|
|
@@ -105,12 +106,26 @@ function parseCliArgs(argv) {
|
|
|
105
106
|
if (!vcsOrigin.includes("://")) {
|
|
106
107
|
throw new ArgError(`--vcs-origin must include a protocol, e.g. https://github.com/org/repo (got "${vcsOrigin}").`);
|
|
107
108
|
}
|
|
109
|
+
const circleToken = values["circle-token"];
|
|
110
|
+
const orgId = values["org-id"];
|
|
111
|
+
const projectId = values["project-id"];
|
|
112
|
+
const pipelineDefinitionId = values["pipeline-definition-id"];
|
|
113
|
+
if (!circleToken) {
|
|
114
|
+
if (!orgId)
|
|
115
|
+
throw new ArgError("--org-id is required for --provider circleci (or provide --circle-token for auto-resolution).");
|
|
116
|
+
if (!projectId)
|
|
117
|
+
throw new ArgError("--project-id is required for --provider circleci (or provide --circle-token for auto-resolution).");
|
|
118
|
+
if (!pipelineDefinitionId)
|
|
119
|
+
throw new ArgError("--pipeline-definition-id is required for --provider circleci (or provide --circle-token for auto-resolution).");
|
|
120
|
+
}
|
|
108
121
|
providerOptions = {
|
|
109
122
|
provider: "circleci",
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
123
|
+
circleToken,
|
|
124
|
+
orgId,
|
|
125
|
+
projectId,
|
|
126
|
+
pipelineDefinitionId,
|
|
113
127
|
vcsOrigin,
|
|
128
|
+
file: values.file,
|
|
114
129
|
contextIds: values["context-id"] ?? [],
|
|
115
130
|
...common
|
|
116
131
|
};
|
|
@@ -126,6 +141,62 @@ function parseCliArgs(argv) {
|
|
|
126
141
|
};
|
|
127
142
|
}
|
|
128
143
|
|
|
144
|
+
// src/circleci.ts
|
|
145
|
+
var BASE_URL = "https://circleci.com/api/v2";
|
|
146
|
+
function vcsTypeFromUrl(url) {
|
|
147
|
+
const { hostname } = new URL(url);
|
|
148
|
+
if (hostname === "github.com" || hostname.endsWith(".github.com"))
|
|
149
|
+
return "github";
|
|
150
|
+
if (hostname === "gitlab.com" || hostname.endsWith(".gitlab.com"))
|
|
151
|
+
return "gitlab";
|
|
152
|
+
if (hostname === "bitbucket.org" || hostname.endsWith(".bitbucket.org"))
|
|
153
|
+
return "bitbucket";
|
|
154
|
+
throw new Error(`Cannot determine VCS type from URL: ${url}`);
|
|
155
|
+
}
|
|
156
|
+
function projectSlugFromVcsOrigin(url) {
|
|
157
|
+
const vcsType = vcsTypeFromUrl(url);
|
|
158
|
+
const { pathname } = new URL(url);
|
|
159
|
+
const parts = pathname.replace(/^\//, "").replace(/\.git$/, "").split("/");
|
|
160
|
+
if (parts.length < 2 || !parts[0] || !parts[1]) {
|
|
161
|
+
throw new Error(`Cannot extract org/repo from URL: ${url}`);
|
|
162
|
+
}
|
|
163
|
+
return `${vcsType}/${parts[0]}/${parts[1]}`;
|
|
164
|
+
}
|
|
165
|
+
async function circleGet(token, path) {
|
|
166
|
+
const response = await fetch(`${BASE_URL}${path}`, {
|
|
167
|
+
headers: { "Circle-Token": token, Accept: "application/json" }
|
|
168
|
+
});
|
|
169
|
+
if (!response.ok) {
|
|
170
|
+
const body = await response.text();
|
|
171
|
+
throw new Error(`CircleCI API error (${response.status}) for ${path}: ${body}`);
|
|
172
|
+
}
|
|
173
|
+
return response.json();
|
|
174
|
+
}
|
|
175
|
+
async function resolveOrgAndProjectId(token, vcsOrigin) {
|
|
176
|
+
const slug = projectSlugFromVcsOrigin(vcsOrigin);
|
|
177
|
+
const project = await circleGet(token, `/project/${slug}`);
|
|
178
|
+
return { orgId: project.organization_id, projectId: project.id };
|
|
179
|
+
}
|
|
180
|
+
async function resolvePipelineDefinitionId(token, projectId, file) {
|
|
181
|
+
const data = await circleGet(token, `/pipeline-definitions?project_id=${encodeURIComponent(projectId)}`);
|
|
182
|
+
if (data.items.length === 0) {
|
|
183
|
+
throw new Error(`No pipeline definitions found for project "${projectId}".`);
|
|
184
|
+
}
|
|
185
|
+
if (file) {
|
|
186
|
+
const match = data.items.find((d) => d.config_file_path === file || d.name === file);
|
|
187
|
+
if (!match) {
|
|
188
|
+
const available2 = data.items.map((d) => d.config_file_path ?? d.name ?? d.id).join(", ");
|
|
189
|
+
throw new Error(`No pipeline definition found matching "${file}". Available: ${available2}`);
|
|
190
|
+
}
|
|
191
|
+
return match.id;
|
|
192
|
+
}
|
|
193
|
+
if (data.items.length === 1) {
|
|
194
|
+
return data.items[0].id;
|
|
195
|
+
}
|
|
196
|
+
const available = data.items.map((d) => d.config_file_path ?? d.name ?? d.id).join(", ");
|
|
197
|
+
throw new Error(`Multiple pipeline definitions found for project "${projectId}". Specify --file to select one: ${available}`);
|
|
198
|
+
}
|
|
199
|
+
|
|
129
200
|
// src/exec.ts
|
|
130
201
|
import { spawnSync } from "node:child_process";
|
|
131
202
|
function run(cmd, args) {
|
|
@@ -435,6 +506,12 @@ function discoverPackages(root) {
|
|
|
435
506
|
all.push(pkg);
|
|
436
507
|
}
|
|
437
508
|
}
|
|
509
|
+
if (source === "none") {
|
|
510
|
+
const pkg = readPkg(path2.resolve(root));
|
|
511
|
+
if (pkg) {
|
|
512
|
+
return { source: "package.json (root)", patterns: [], all: [pkg] };
|
|
513
|
+
}
|
|
514
|
+
}
|
|
438
515
|
return { source, patterns, all };
|
|
439
516
|
}
|
|
440
517
|
function filterPackages(all, only, exclude) {
|
|
@@ -461,8 +538,11 @@ Providers:
|
|
|
461
538
|
github --repo <owner/repo> --file <workflow> [--env <name>]
|
|
462
539
|
gitlab --project <group/project> --file <workflow> [--env <name>]
|
|
463
540
|
(--repo is accepted as an alias for --project)
|
|
464
|
-
circleci --
|
|
465
|
-
--
|
|
541
|
+
circleci --vcs-origin <url> --circle-token <token> [--file <config>]
|
|
542
|
+
[--org-id <uuid>] [--project-id <uuid>] [--pipeline-definition-id <uuid>]
|
|
543
|
+
[--context-id <uuid> ...]
|
|
544
|
+
UUIDs are auto-resolved via CircleCI API v2 when --circle-token is provided.
|
|
545
|
+
Explicit UUIDs override auto-resolution and work without a token.
|
|
466
546
|
|
|
467
547
|
Permissions (at least one required):
|
|
468
548
|
--allow-publish allow npm publish
|
|
@@ -477,11 +557,14 @@ Options:
|
|
|
477
557
|
-v, --version show the trustci version
|
|
478
558
|
|
|
479
559
|
Workspaces are detected from pnpm-workspace.yaml, package.json "workspaces",
|
|
480
|
-
or lerna.json.
|
|
560
|
+
or lerna.json. If no workspace config is found, the root package.json is used
|
|
561
|
+
as a single-package fallback. Private packages ("private": true) are always excluded.
|
|
481
562
|
|
|
482
563
|
Examples:
|
|
483
564
|
trustci --dry-run --provider github --repo me/repo --file release.yml --allow-publish
|
|
484
565
|
trustci --provider gitlab --project me/repo --file .gitlab-ci.yml --allow-publish -y
|
|
566
|
+
trustci --provider circleci --vcs-origin https://github.com/me/repo \\
|
|
567
|
+
--circle-token $CIRCLECI_TOKEN --allow-publish
|
|
485
568
|
trustci --provider circleci --org-id <uuid> --project-id <uuid> \\
|
|
486
569
|
--pipeline-definition-id <uuid> --vcs-origin https://github.com/me/repo --allow-publish
|
|
487
570
|
trustci list
|
|
@@ -626,7 +709,29 @@ async function main() {
|
|
|
626
709
|
return 1;
|
|
627
710
|
}
|
|
628
711
|
}
|
|
629
|
-
|
|
712
|
+
if (parsed.kind === "list")
|
|
713
|
+
return runList(parsed.pkg, parsed.dryRun);
|
|
714
|
+
if (parsed.options.provider === "circleci" && !parsed.dryRun) {
|
|
715
|
+
const opts = parsed.options;
|
|
716
|
+
if (opts.circleToken) {
|
|
717
|
+
try {
|
|
718
|
+
if (!opts.orgId || !opts.projectId) {
|
|
719
|
+
console.error("Resolving org-id and project-id via CircleCI API...");
|
|
720
|
+
const resolved = await resolveOrgAndProjectId(opts.circleToken, opts.vcsOrigin);
|
|
721
|
+
opts.orgId ??= resolved.orgId;
|
|
722
|
+
opts.projectId ??= resolved.projectId;
|
|
723
|
+
}
|
|
724
|
+
if (!opts.pipelineDefinitionId) {
|
|
725
|
+
console.error("Resolving pipeline-definition-id via CircleCI API...");
|
|
726
|
+
opts.pipelineDefinitionId = await resolvePipelineDefinitionId(opts.circleToken, opts.projectId, opts.file);
|
|
727
|
+
}
|
|
728
|
+
} catch (err) {
|
|
729
|
+
console.error(`error: ${err.message}`);
|
|
730
|
+
return 1;
|
|
731
|
+
}
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
return runTrust(parsed);
|
|
630
735
|
}
|
|
631
736
|
main().then((code) => {
|
|
632
737
|
process2.exitCode = code;
|
package/package.json
CHANGED