hammerkit 1.23.0 → 1.25.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/build.schema.json +10 -0
- package/dist/cli.js +6 -0
- package/dist/executer/execute-command.js +7 -1
- package/dist/optimizer/get-cache-directory.js +5 -0
- package/dist/planner/utils/append-work-task.js +1 -0
- package/dist/planner/work-secret.js +6 -2
- package/dist/program.js +6 -0
- package/dist/remote/bundle.js +59 -0
- package/dist/remote/collect-bundle.js +41 -0
- package/dist/remote/credentials.js +93 -0
- package/dist/remote/device-login.js +177 -0
- package/dist/remote/host-config.js +170 -0
- package/dist/remote/location.js +46 -0
- package/dist/remote/remote-commands.js +158 -0
- package/dist/remote/remote-work-commands.js +224 -0
- package/dist/remote/server-client.js +88 -0
- package/dist/remote/source-manifest.js +79 -0
- package/dist/remote/text-diff.js +89 -0
- package/dist/schema/build-file-base-task-schema.js +4 -0
- package/dist/schema/build-file-schema.js +3 -0
- package/dist/schema/reference-parser.js +9 -2
- package/dist/schema/runner-schema.js +13 -0
- package/dist/secrets/provider-values.js +4 -0
- package/dist/server/accounts-api.js +42 -0
- package/dist/server/approvals-api.js +104 -0
- package/dist/server/definitions-api.js +163 -0
- package/dist/server/definitions.js +160 -0
- package/dist/server/discovery.js +9 -0
- package/dist/server/executor.js +237 -0
- package/dist/server/git-checkout.js +115 -0
- package/dist/server/http.js +39 -0
- package/dist/server/oauth.js +226 -0
- package/dist/server/plan.js +296 -0
- package/dist/server/policy.js +138 -0
- package/dist/server/proposals-api.js +145 -0
- package/dist/server/proposals.js +173 -0
- package/dist/server/repo-access.js +127 -0
- package/dist/server/repo.js +94 -0
- package/dist/server/run-environment.js +20 -0
- package/dist/server/run-plan.js +44 -0
- package/dist/server/run-request.js +85 -0
- package/dist/server/run-store.js +61 -0
- package/dist/server/run-worker-command.js +17 -0
- package/dist/server/run-worker-main.js +7 -0
- package/dist/server/run-worker.js +179 -0
- package/dist/server/runs.js +432 -0
- package/dist/server/server-command.js +61 -0
- package/dist/server/server-config.js +394 -0
- package/dist/server/server.js +434 -0
- package/dist/server/sources-api.js +106 -0
- package/dist/server/sources.js +258 -0
- package/package.json +1 -1
|
@@ -0,0 +1,394 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.detectPlatform = exports.getSecretCatalog = exports.readServerConfig = exports.getSourceAccounts = exports.serverConfigSchema = exports.sourceSchema = exports.entitlementSchema = exports.buildsLevelSchema = exports.accountLevelSchema = exports.accountSchema = exports.definitionName = exports.issuerSchema = exports.serverPlatformSchema = void 0;
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const yaml_1 = require("yaml");
|
|
6
|
+
const log_1 = require("../log");
|
|
7
|
+
const units_1 = require("../utils/units");
|
|
8
|
+
const build_file_secret_provider_schema_1 = require("../schema/build-file-secret-provider-schema");
|
|
9
|
+
const repo_1 = require("./repo");
|
|
10
|
+
const LOOPBACK_HOSTS = ['127.0.0.1', 'localhost', '::1'];
|
|
11
|
+
exports.serverPlatformSchema = zod_1.z
|
|
12
|
+
.object({
|
|
13
|
+
os: zod_1.z.enum(['linux', 'macos', 'windows']),
|
|
14
|
+
arch: zod_1.z.enum(['amd64', 'arm64']),
|
|
15
|
+
})
|
|
16
|
+
.strict();
|
|
17
|
+
// The issuer is compared with the `iss` of a token as written, and its keys are
|
|
18
|
+
// fetched from it, so it has to be https (http only for local testing).
|
|
19
|
+
const issuerUrlSchema = zod_1.z.string().refine((value) => {
|
|
20
|
+
try {
|
|
21
|
+
const url = new URL(value);
|
|
22
|
+
return (url.protocol === 'https:' ||
|
|
23
|
+
(url.protocol === 'http:' && LOOPBACK_HOSTS.includes(url.hostname.replace(/^\[|\]$/g, ''))));
|
|
24
|
+
}
|
|
25
|
+
catch {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
}, 'issuer must be an https url (http only for localhost)');
|
|
29
|
+
exports.issuerSchema = zod_1.z
|
|
30
|
+
.object({
|
|
31
|
+
issuer: issuerUrlSchema,
|
|
32
|
+
audience: zod_1.z.string().min(1),
|
|
33
|
+
// the claim listing the groups of the caller
|
|
34
|
+
groupsClaim: zod_1.z.string().min(1).default('groups'),
|
|
35
|
+
// the public OAuth client `remote login` signs in with (device authorization
|
|
36
|
+
// grant); without one this issuer can only be used with a token obtained
|
|
37
|
+
// elsewhere, such as a CI job's or an agent's own
|
|
38
|
+
clientId: zod_1.z.string().min(1).optional(),
|
|
39
|
+
// what the login asks for, such as `openid api://hammerkit/.default`
|
|
40
|
+
scope: zod_1.z.string().min(1).default('openid'),
|
|
41
|
+
})
|
|
42
|
+
.strict();
|
|
43
|
+
// What a published build file is called: the project, as a build file author names it.
|
|
44
|
+
exports.definitionName = zod_1.z
|
|
45
|
+
.string()
|
|
46
|
+
.regex(/^[a-z0-9][a-z0-9._-]{0,63}$/i, 'use letters, digits, ".", "_" and "-", starting with a letter or digit');
|
|
47
|
+
const subjectSchema = zod_1.z.object({ iss: zod_1.z.string().min(1), sub: zod_1.z.string().min(1) }).strict();
|
|
48
|
+
const groupSchema = zod_1.z.object({ iss: zod_1.z.string().min(1), name: zod_1.z.string().min(1) }).strict();
|
|
49
|
+
const claimsSchema = zod_1.z.object({ iss: zod_1.z.string().min(1), match: zod_1.z.record(zod_1.z.string()) }).strict();
|
|
50
|
+
// Who: the person or client (`subject`), everyone in a group of an issuer
|
|
51
|
+
// (`group`), or any token of an issuer whose claims match (`claims`, for CI jobs:
|
|
52
|
+
// repository, ref, environment). Only validated token claims are matched.
|
|
53
|
+
const whoShape = {
|
|
54
|
+
subject: subjectSchema.optional(),
|
|
55
|
+
group: groupSchema.optional(),
|
|
56
|
+
claims: claimsSchema.optional(),
|
|
57
|
+
};
|
|
58
|
+
function checkWho(who, ctx) {
|
|
59
|
+
const named = [who.subject, who.group, who.claims].filter((w) => w !== undefined);
|
|
60
|
+
if (named.length !== 1) {
|
|
61
|
+
ctx.addIssue({ code: zod_1.z.ZodIssueCode.custom, message: 'name exactly one of subject, group or claims' });
|
|
62
|
+
}
|
|
63
|
+
if (who.claims && Object.keys(who.claims.match).length === 0) {
|
|
64
|
+
ctx.addIssue({ code: zod_1.z.ZodIssueCode.custom, path: ['claims', 'match'], message: 'match at least one claim' });
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// The service accounts a server knows. A run never names one: the accounts it
|
|
68
|
+
// uses are the ones its secrets are read as, and an entitlement says who may use
|
|
69
|
+
// or only request each.
|
|
70
|
+
const accountNameSchema = zod_1.z
|
|
71
|
+
.string()
|
|
72
|
+
.regex(/^[A-Za-z][A-Za-z0-9_-]*$/, 'a name starts with a letter and holds letters, digits, - and _');
|
|
73
|
+
exports.accountSchema = zod_1.z
|
|
74
|
+
.object({
|
|
75
|
+
description: zod_1.z.string().min(1).max(200).optional(),
|
|
76
|
+
// per secret provider, the environment that makes its command act as this
|
|
77
|
+
// account; the credentials are files and variables of the operator's machine
|
|
78
|
+
providers: build_file_secret_provider_schema_1.buildFileSecretAccountSchema.shape.providers.default({}),
|
|
79
|
+
// where the code of a run using this account may come from; both when left out
|
|
80
|
+
source: zod_1.z
|
|
81
|
+
.array(zod_1.z.enum(['upload', 'ref']))
|
|
82
|
+
.min(1)
|
|
83
|
+
.optional(),
|
|
84
|
+
// the refs (branches or tags, `release/*` for a level) a run using this account
|
|
85
|
+
// may be started from; a commit is not accepted then, it names no branch
|
|
86
|
+
refs: zod_1.z.array(zod_1.z.string().min(1).max(200)).min(1).optional(),
|
|
87
|
+
})
|
|
88
|
+
.strict()
|
|
89
|
+
.superRefine((account, ctx) => {
|
|
90
|
+
if (account.refs && (account.source?.length !== 1 || account.source[0] !== 'ref')) {
|
|
91
|
+
ctx.addIssue({
|
|
92
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
93
|
+
path: ['refs'],
|
|
94
|
+
message: 'refs only restricts runs from a git ref, set source: [ref] too, an upload names no ref',
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
exports.accountLevelSchema = zod_1.z.enum(['use', 'request']);
|
|
99
|
+
// What a caller may do with the build files: run what is published, propose new
|
|
100
|
+
// versions that someone else approves, or publish. Each level includes the lower.
|
|
101
|
+
exports.buildsLevelSchema = zod_1.z.enum(['run', 'propose', 'publish']);
|
|
102
|
+
const repoPatternSchema = zod_1.z.string().refine(repo_1.isRepoPattern, 'expected host/path, such as github.com/corp/*');
|
|
103
|
+
exports.entitlementSchema = zod_1.z
|
|
104
|
+
.object({
|
|
105
|
+
...whoShape,
|
|
106
|
+
// `use`: runs on the account at once and approves the runs others request on it;
|
|
107
|
+
// `request`: runs wait until somebody who may `use` the account approves them
|
|
108
|
+
accounts: zod_1.z.record(accountNameSchema, exports.accountLevelSchema).default({}),
|
|
109
|
+
// the repositories (`host/path` patterns) the caller may run from a git ref
|
|
110
|
+
repos: zod_1.z.array(repoPatternSchema).default([]),
|
|
111
|
+
builds: exports.buildsLevelSchema.optional(),
|
|
112
|
+
})
|
|
113
|
+
.strict()
|
|
114
|
+
.superRefine((entitlement, ctx) => {
|
|
115
|
+
checkWho(entitlement, ctx);
|
|
116
|
+
if (Object.keys(entitlement.accounts).length === 0 && entitlement.builds === undefined) {
|
|
117
|
+
ctx.addIssue({
|
|
118
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
119
|
+
message: 'name accounts to use or request, or a builds level',
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
// A repository the server can read: `github.com/corp/*` for an organisation,
|
|
124
|
+
// `gitlab.corp.com/platform/**` for a group with its subgroups. The credential is a
|
|
125
|
+
// secret of a provider, read as `account`, and handed to git for that fetch only.
|
|
126
|
+
exports.sourceSchema = zod_1.z
|
|
127
|
+
.object({
|
|
128
|
+
match: repoPatternSchema,
|
|
129
|
+
secret: zod_1.z
|
|
130
|
+
.object({
|
|
131
|
+
from: zod_1.z.string().regex(/^[A-Za-z][A-Za-z0-9_-]*:.+$/, 'expected provider:reference'),
|
|
132
|
+
account: accountNameSchema,
|
|
133
|
+
// the user name of the basic authentication the token is sent with
|
|
134
|
+
username: zod_1.z.string().min(1).default('x-access-token'),
|
|
135
|
+
})
|
|
136
|
+
.strict()
|
|
137
|
+
.optional(),
|
|
138
|
+
})
|
|
139
|
+
.strict();
|
|
140
|
+
const duration = zod_1.z.string().refine((value) => {
|
|
141
|
+
try {
|
|
142
|
+
return (0, units_1.parseDuration)(value) > 0;
|
|
143
|
+
}
|
|
144
|
+
catch {
|
|
145
|
+
return false;
|
|
146
|
+
}
|
|
147
|
+
}, 'expected a duration like 30m, 1h or 1d');
|
|
148
|
+
// The server config is written by an operator and reviewed like code, so a typo
|
|
149
|
+
// is an error and not a silently ignored key.
|
|
150
|
+
exports.serverConfigSchema = zod_1.z
|
|
151
|
+
.object({
|
|
152
|
+
listen: zod_1.z
|
|
153
|
+
.object({
|
|
154
|
+
host: zod_1.z.string().min(1).default('127.0.0.1'),
|
|
155
|
+
port: zod_1.z.number().int().min(0).max(65535).default(8480),
|
|
156
|
+
})
|
|
157
|
+
.strict()
|
|
158
|
+
.default({}),
|
|
159
|
+
// paths of a certificate and key
|
|
160
|
+
tls: zod_1.z
|
|
161
|
+
.object({ cert: zod_1.z.string().min(1), key: zod_1.z.string().min(1) })
|
|
162
|
+
.strict()
|
|
163
|
+
.optional(),
|
|
164
|
+
// plain http carries OAuth tokens, so it is only accepted on the loopback
|
|
165
|
+
// interface, or when a proxy in front terminates TLS and this says so
|
|
166
|
+
behindTlsProxy: zod_1.z.boolean().default(false),
|
|
167
|
+
// the OAuth issuers whose tokens the server accepts, and the audience those
|
|
168
|
+
// tokens have to carry
|
|
169
|
+
issuers: zod_1.z.array(exports.issuerSchema).min(1, 'at least one issuer is needed, OAuth is the only way to call the server'),
|
|
170
|
+
// the service accounts, and the commands that read secrets as them; a build file
|
|
171
|
+
// can not declare either, only name an account on its secrets
|
|
172
|
+
accounts: zod_1.z.record(accountNameSchema, exports.accountSchema).default({}),
|
|
173
|
+
secretProviders: build_file_secret_provider_schema_1.buildFileSecretProvidersSchema.default({}),
|
|
174
|
+
// who may use which service account and which build files; default deny
|
|
175
|
+
entitlements: zod_1.z.array(exports.entitlementSchema).default([]),
|
|
176
|
+
approvals: zod_1.z
|
|
177
|
+
.object({
|
|
178
|
+
// how long a run waits for its approval before it is rejected
|
|
179
|
+
timeout: duration.default('1h'),
|
|
180
|
+
// runs waiting at the same time, more are refused
|
|
181
|
+
maxWaiting: zod_1.z.number().int().min(1).default(100),
|
|
182
|
+
// after a version was rejected, who proposed it can not propose that build
|
|
183
|
+
// file again, and nobody that version, for this long
|
|
184
|
+
cooldown: duration.default('10m'),
|
|
185
|
+
})
|
|
186
|
+
.strict()
|
|
187
|
+
.default({}),
|
|
188
|
+
// the repositories a run may get its code from, as patterns of `host/path`, and
|
|
189
|
+
// the secret that reads them: a token in a secret provider, read as a service
|
|
190
|
+
// account of the operator. The caller names a repository and a ref in the request,
|
|
191
|
+
// never a url.
|
|
192
|
+
sources: zod_1.z.array(exports.sourceSchema).default([]),
|
|
193
|
+
// what tasks may run on: containers, and the local runtime that runs commands on
|
|
194
|
+
// this machine as the user of the server. Local tasks read the whole file system of that
|
|
195
|
+
// user, so they have to be allowed by name.
|
|
196
|
+
runtimes: zod_1.z
|
|
197
|
+
.array(zod_1.z.enum(['docker', 'local']))
|
|
198
|
+
.min(1)
|
|
199
|
+
.default(['docker']),
|
|
200
|
+
// where the server keeps what must survive a restart: the published build files
|
|
201
|
+
storage: zod_1.z
|
|
202
|
+
.object({ directory: zod_1.z.string().min(1).optional() })
|
|
203
|
+
.strict()
|
|
204
|
+
.default({}),
|
|
205
|
+
// limits of the run registry
|
|
206
|
+
runs: zod_1.z
|
|
207
|
+
.object({
|
|
208
|
+
// runs that execute at the same time, the others wait in queue
|
|
209
|
+
maxConcurrent: zod_1.z.number().int().min(1).default(4),
|
|
210
|
+
// finished runs that are kept for their state and logs
|
|
211
|
+
keep: zod_1.z.number().int().min(1).default(200),
|
|
212
|
+
// what one caller (issuer and subject) may have at a time, so a single
|
|
213
|
+
// caller can not take the whole machine; over it a request gets 429
|
|
214
|
+
limits: zod_1.z
|
|
215
|
+
.object({
|
|
216
|
+
// runs that wait for an approval
|
|
217
|
+
waiting: zod_1.z.number().int().min(1).default(10),
|
|
218
|
+
// runs that wait in the queue for a free place
|
|
219
|
+
queued: zod_1.z.number().int().min(1).default(20),
|
|
220
|
+
// runs that execute at the same time, the others wait in the queue
|
|
221
|
+
running: zod_1.z.number().int().min(1).default(2),
|
|
222
|
+
})
|
|
223
|
+
.strict()
|
|
224
|
+
.default({}),
|
|
225
|
+
// a run that executes longer is stopped and fails
|
|
226
|
+
maxDuration: duration.default('1h'),
|
|
227
|
+
// log output kept per run; the rest is cut off
|
|
228
|
+
maxLogBytes: zod_1.z
|
|
229
|
+
.number()
|
|
230
|
+
.int()
|
|
231
|
+
.min(1024)
|
|
232
|
+
.default(1024 * 1024),
|
|
233
|
+
// the environment of a run: the variables of the server process that tasks
|
|
234
|
+
// (and the git fetch) can see. Nothing else of the server's environment
|
|
235
|
+
// is passed on, so there is no list built in; an operator names what the
|
|
236
|
+
// tools of the host need, such as PATH and DOCKER_HOST.
|
|
237
|
+
env: zod_1.z
|
|
238
|
+
.object({
|
|
239
|
+
allow: zod_1.z.array(zod_1.z.string().regex(/^[A-Za-z_][A-Za-z0-9_()]*$/)).default([]),
|
|
240
|
+
// fixed values that are set in addition
|
|
241
|
+
set: zod_1.z.record(zod_1.z.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/), zod_1.z.string()).default({}),
|
|
242
|
+
})
|
|
243
|
+
.strict()
|
|
244
|
+
.default({}),
|
|
245
|
+
})
|
|
246
|
+
.strict()
|
|
247
|
+
.default({}),
|
|
248
|
+
backend: zod_1.z
|
|
249
|
+
.object({
|
|
250
|
+
type: zod_1.z.literal('host'),
|
|
251
|
+
// what this machine offers; detected when left out
|
|
252
|
+
platform: exports.serverPlatformSchema.optional(),
|
|
253
|
+
})
|
|
254
|
+
.strict()
|
|
255
|
+
.default({ type: 'host' }),
|
|
256
|
+
})
|
|
257
|
+
.strict()
|
|
258
|
+
.superRefine((config, ctx) => {
|
|
259
|
+
const issuers = config.issuers.map((i) => i.issuer);
|
|
260
|
+
config.entitlements.forEach((entitlement, index) => {
|
|
261
|
+
const iss = (entitlement.subject ?? entitlement.group ?? entitlement.claims)?.iss;
|
|
262
|
+
if (iss && !issuers.includes(iss)) {
|
|
263
|
+
ctx.addIssue({
|
|
264
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
265
|
+
path: ['entitlements', index],
|
|
266
|
+
message: `issuer ${iss} is not one of the accepted issuers`,
|
|
267
|
+
});
|
|
268
|
+
}
|
|
269
|
+
});
|
|
270
|
+
for (const [name, account] of Object.entries(config.accounts)) {
|
|
271
|
+
for (const provider of Object.keys(account.providers)) {
|
|
272
|
+
if (!config.secretProviders[provider]) {
|
|
273
|
+
ctx.addIssue({
|
|
274
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
275
|
+
path: ['accounts', name, 'providers', provider],
|
|
276
|
+
message: `secret provider ${provider} is not declared in secretProviders`,
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
config.entitlements.forEach((entitlement, index) => {
|
|
282
|
+
for (const account of Object.keys(entitlement.accounts)) {
|
|
283
|
+
if (!config.accounts[account]) {
|
|
284
|
+
const known = Object.keys(config.accounts);
|
|
285
|
+
ctx.addIssue({
|
|
286
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
287
|
+
path: ['entitlements', index, 'accounts', account],
|
|
288
|
+
message: `unknown account ${account}, ${known.length > 0 ? `accounts: ${known.join(', ')}` : 'none is defined'}`,
|
|
289
|
+
});
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
config.sources.forEach((source, index) => {
|
|
294
|
+
if (!source.secret) {
|
|
295
|
+
return;
|
|
296
|
+
}
|
|
297
|
+
const { from, account } = source.secret;
|
|
298
|
+
const kind = from.substring(0, from.indexOf(':'));
|
|
299
|
+
const where = ['sources', index, 'secret'];
|
|
300
|
+
if (!config.accounts[account]) {
|
|
301
|
+
ctx.addIssue({
|
|
302
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
303
|
+
path: [...where, 'account'],
|
|
304
|
+
message: `unknown account ${account}`,
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
else if (!config.accounts[account].providers[kind]) {
|
|
308
|
+
ctx.addIssue({
|
|
309
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
310
|
+
path: [...where, 'from'],
|
|
311
|
+
message: `account ${account} has no credentials for the provider ${kind}`,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
if (!config.secretProviders[kind]) {
|
|
315
|
+
ctx.addIssue({
|
|
316
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
317
|
+
path: [...where, 'from'],
|
|
318
|
+
message: `secret provider ${kind} is not declared in secretProviders`,
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
});
|
|
322
|
+
const readers = getSourceAccounts(config);
|
|
323
|
+
config.entitlements.forEach((entitlement, index) => {
|
|
324
|
+
for (const account of Object.keys(entitlement.accounts)) {
|
|
325
|
+
if (readers.includes(account)) {
|
|
326
|
+
ctx.addIssue({
|
|
327
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
328
|
+
path: ['entitlements', index, 'accounts', account],
|
|
329
|
+
message: `the account ${account} reads the sources, no entitlement may name it`,
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
});
|
|
334
|
+
if (!config.tls && !config.behindTlsProxy && !LOOPBACK_HOSTS.includes(config.listen.host)) {
|
|
335
|
+
ctx.addIssue({
|
|
336
|
+
code: zod_1.z.ZodIssueCode.custom,
|
|
337
|
+
path: ['listen', 'host'],
|
|
338
|
+
message: `plain http on ${config.listen.host} would send tokens in the clear, set tls or behindTlsProxy: true`,
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
});
|
|
342
|
+
// The accounts that read repositories: no caller uses them and no build file names them.
|
|
343
|
+
function getSourceAccounts(config) {
|
|
344
|
+
return [...new Set(config.sources.flatMap((source) => (source.secret ? [source.secret.account] : [])))];
|
|
345
|
+
}
|
|
346
|
+
exports.getSourceAccounts = getSourceAccounts;
|
|
347
|
+
async function readServerConfig(environment, path) {
|
|
348
|
+
if (!(await environment.file.exists(path))) {
|
|
349
|
+
throw new Error(`unable to read ${path}: no such file`);
|
|
350
|
+
}
|
|
351
|
+
let raw;
|
|
352
|
+
try {
|
|
353
|
+
raw = (0, yaml_1.parse)(await environment.file.read(path));
|
|
354
|
+
}
|
|
355
|
+
catch (e) {
|
|
356
|
+
throw new Error(`unable to read ${path}: ${(0, log_1.getErrorMessage)(e)}`);
|
|
357
|
+
}
|
|
358
|
+
const result = exports.serverConfigSchema.safeParse(raw ?? {});
|
|
359
|
+
if (!result.success) {
|
|
360
|
+
const issue = result.error.issues[0];
|
|
361
|
+
const where = issue.path.length > 0 ? `${issue.path.join('.')}: ` : '';
|
|
362
|
+
throw new Error(`invalid ${path}: ${where}${issue.message}`);
|
|
363
|
+
}
|
|
364
|
+
return result.data;
|
|
365
|
+
}
|
|
366
|
+
exports.readServerConfig = readServerConfig;
|
|
367
|
+
// The secret providers and accounts of the config as the engine reads them, so a
|
|
368
|
+
// secret of a build file is read as an account of the operator.
|
|
369
|
+
function getSecretCatalog(config) {
|
|
370
|
+
const providers = {};
|
|
371
|
+
for (const [name, provider] of Object.entries(config.secretProviders)) {
|
|
372
|
+
providers[name] = {
|
|
373
|
+
name,
|
|
374
|
+
type: 'command',
|
|
375
|
+
command: provider.command,
|
|
376
|
+
env: provider.env ?? {},
|
|
377
|
+
timeoutMs: (0, units_1.parseDuration)(provider.timeout ?? '30s'),
|
|
378
|
+
declaredIn: 'the server config',
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
const accounts = {};
|
|
382
|
+
for (const [name, account] of Object.entries(config.accounts)) {
|
|
383
|
+
accounts[name] = { name, default: false, providers: account.providers, declaredIn: 'the server config' };
|
|
384
|
+
}
|
|
385
|
+
return { providers, accounts, explicitAccounts: true };
|
|
386
|
+
}
|
|
387
|
+
exports.getSecretCatalog = getSecretCatalog;
|
|
388
|
+
function detectPlatform() {
|
|
389
|
+
const os = { darwin: 'macos', win32: 'windows' }[process.platform] ?? 'linux';
|
|
390
|
+
const arch = process.arch === 'arm64' ? 'arm64' : 'amd64';
|
|
391
|
+
return { os, arch };
|
|
392
|
+
}
|
|
393
|
+
exports.detectPlatform = detectPlatform;
|
|
394
|
+
//# sourceMappingURL=server-config.js.map
|