Skip to content

Commit cb164a6

Browse files
committed
feat: adds circleci to trust command
1 parent 4cae12b commit cb164a6

File tree

5 files changed

+512
-0
lines changed

5 files changed

+512
-0
lines changed

lib/commands/trust/circleci.js

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
const Definition = require('@npmcli/config/lib/definitions/definition.js')
2+
const globalDefinitions = require('@npmcli/config/lib/definitions/definitions.js')
3+
const TrustCommand = require('../../trust-cmd.js')
4+
5+
// UUID validation regex
6+
const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
7+
8+
class TrustCircleCI extends TrustCommand {
9+
static description = 'Create a trusted relationship between a package and CircleCI'
10+
static name = 'circleci'
11+
static positionals = 1 // expects at most 1 positional (package name)
12+
static providerName = 'CircleCI'
13+
static providerEntity = 'CircleCI pipeline'
14+
15+
static usage = [
16+
'[package] --org-id <uuid> --pipeline-definition-id <uuid> --vcs-origin <origin> [--context-id <uuid>] [-y|--yes]',
17+
]
18+
19+
static definitions = {
20+
yes: globalDefinitions.yes,
21+
json: globalDefinitions.json,
22+
'dry-run': globalDefinitions['dry-run'],
23+
'org-id': new Definition('org-id', {
24+
default: null,
25+
type: String,
26+
description: 'CircleCI organization UUID',
27+
}),
28+
'pipeline-definition-id': new Definition('pipeline-definition-id', {
29+
default: null,
30+
type: String,
31+
description: 'CircleCI pipeline definition UUID',
32+
}),
33+
'vcs-origin': new Definition('vcs-origin', {
34+
default: null,
35+
type: String,
36+
description: "CircleCI repository origin in format 'provider/owner/repo'",
37+
}),
38+
'context-id': new Definition('context-id', {
39+
default: null,
40+
type: String,
41+
description: 'CircleCI context UUID',
42+
}),
43+
}
44+
45+
validateUuid (value, fieldName) {
46+
if (!UUID_REGEX.test(value)) {
47+
throw new Error(`${fieldName} must be a valid UUID`)
48+
}
49+
}
50+
51+
validateVcsOrigin (value) {
52+
// Expected format: provider/owner/repo (e.g., github.com/owner/repo, bitbucket.org/owner/repo)
53+
const parts = value.split('/')
54+
if (parts.length < 3) {
55+
throw new Error("vcs-origin must be in format 'provider/owner/repo'")
56+
}
57+
}
58+
59+
// Generate a URL from vcs-origin (e.g., github.com/npm/repo -> https://github.com/npm/repo)
60+
getVcsOriginUrl (vcsOrigin) {
61+
if (!vcsOrigin) {
62+
return null
63+
}
64+
// vcs-origin format: github.com/owner/repo or bitbucket.org/owner/repo
65+
return `https://${vcsOrigin}`
66+
}
67+
68+
static optionsToBody (options) {
69+
const { orgId, pipelineDefinitionId, vcsOrigin, contextId } = options
70+
const trustConfig = {
71+
type: 'circleci',
72+
claims: {
73+
org_id: orgId,
74+
pipeline_definition_id: pipelineDefinitionId,
75+
vcs_origin: vcsOrigin,
76+
},
77+
}
78+
if (contextId) {
79+
trustConfig.claims.context_id = contextId
80+
}
81+
return trustConfig
82+
}
83+
84+
static bodyToOptions (body) {
85+
return {
86+
...(body.id) && { id: body.id },
87+
...(body.type) && { type: body.type },
88+
...(body.claims?.org_id) && { orgId: body.claims.org_id },
89+
...(body.claims?.pipeline_definition_id) && {
90+
pipelineDefinitionId: body.claims.pipeline_definition_id,
91+
},
92+
...(body.claims?.vcs_origin) && { vcsOrigin: body.claims.vcs_origin },
93+
...(body.claims?.context_id) && { contextId: body.claims.context_id },
94+
}
95+
}
96+
97+
// Override flagsToOptions since CircleCI doesn't use file/entity pattern
98+
async flagsToOptions ({ positionalArgs, flags }) {
99+
const content = await this.optionalPkgJson()
100+
const pkgName = positionalArgs[0] || content.name
101+
102+
if (!pkgName) {
103+
throw new Error('Package name must be specified either as an argument or in package.json file')
104+
}
105+
106+
const orgId = flags['org-id']
107+
const pipelineDefinitionId = flags['pipeline-definition-id']
108+
const vcsOrigin = flags['vcs-origin']
109+
const contextId = flags['context-id']
110+
111+
// Validate required flags
112+
if (!orgId) {
113+
throw new Error('org-id is required')
114+
}
115+
if (!pipelineDefinitionId) {
116+
throw new Error('pipeline-definition-id is required')
117+
}
118+
if (!vcsOrigin) {
119+
throw new Error('vcs-origin is required')
120+
}
121+
122+
// Validate formats
123+
this.validateUuid(orgId, 'org-id')
124+
this.validateUuid(pipelineDefinitionId, 'pipeline-definition-id')
125+
this.validateVcsOrigin(vcsOrigin)
126+
if (contextId) {
127+
this.validateUuid(contextId, 'context-id')
128+
}
129+
130+
return {
131+
values: {
132+
package: pkgName,
133+
orgId,
134+
pipelineDefinitionId,
135+
vcsOrigin,
136+
...(contextId && { contextId }),
137+
},
138+
fromPackageJson: {},
139+
warnings: [],
140+
urls: {
141+
package: this.getFrontendUrl({ pkgName }),
142+
vcsOrigin: this.getVcsOriginUrl(vcsOrigin),
143+
},
144+
}
145+
}
146+
147+
async exec (positionalArgs, flags) {
148+
await this.createConfigCommand({
149+
positionalArgs,
150+
flags,
151+
})
152+
}
153+
}
154+
155+
module.exports = TrustCircleCI

lib/commands/trust/index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ class Trust extends BaseCommand {
77
static subcommands = {
88
github: require('./github.js'),
99
gitlab: require('./gitlab.js'),
10+
circleci: require('./circleci.js'),
1011
list: require('./list.js'),
1112
revoke: require('./revoke.js'),
1213
}

tap-snapshots/test/lib/commands/completion.js.test.cjs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ Array [
137137
String(
138138
github
139139
gitlab
140+
circleci
140141
list
141142
revoke
142143
),

tap-snapshots/test/lib/docs.js.test.cjs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5710,6 +5710,9 @@ Subcommands:
57105710
gitlab
57115711
Create a trusted relationship between a package and GitLab CI/CD
57125712
5713+
circleci
5714+
Create a trusted relationship between a package and CircleCI
5715+
57135716
list
57145717
List trusted relationships for a package
57155718
@@ -5743,6 +5746,15 @@ Note: This command is unaware of workspaces.
57435746
#### \`json\`
57445747
#### \`dry-run\`
57455748
#### Synopsis
5749+
#### Flags
5750+
#### \`org-id\`
5751+
#### \`pipeline-definition-id\`
5752+
#### \`vcs-origin\`
5753+
#### \`context-id\`
5754+
#### \`yes\`
5755+
#### \`json\`
5756+
#### \`dry-run\`
5757+
#### Synopsis
57465758
#### Configuration
57475759
#### \`json\`
57485760
#### Synopsis

0 commit comments

Comments
 (0)