@magland/mochi 0.3.1
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/LICENSE +201 -0
- package/README.md +108 -0
- package/dist/ansi.js +174 -0
- package/dist/api/admin.js +416 -0
- package/dist/api/auth.js +166 -0
- package/dist/api/backup.js +598 -0
- package/dist/api/ci.js +336 -0
- package/dist/api/contents.js +339 -0
- package/dist/api/issues.js +165 -0
- package/dist/api/pulls.js +244 -0
- package/dist/api/releases.js +83 -0
- package/dist/api/repos.js +156 -0
- package/dist/api/write.js +518 -0
- package/dist/api.js +326 -0
- package/dist/assets.js +29 -0
- package/dist/atom.js +32 -0
- package/dist/atomic.js +171 -0
- package/dist/avatar.js +81 -0
- package/dist/browse.js +630 -0
- package/dist/build-info.json +4 -0
- package/dist/ci/actionref.js +86 -0
- package/dist/ci/api.js +829 -0
- package/dist/ci/artifacts.js +201 -0
- package/dist/ci/dispatch.js +30 -0
- package/dist/ci/engine.js +1321 -0
- package/dist/ci/expr.js +526 -0
- package/dist/ci/manual.js +199 -0
- package/dist/ci/present.js +82 -0
- package/dist/ci/protocol.js +6 -0
- package/dist/ci/runners.js +256 -0
- package/dist/ci/runs.js +208 -0
- package/dist/ci/trigger.js +28 -0
- package/dist/ci/views.js +441 -0
- package/dist/ci/wake.js +194 -0
- package/dist/ci/web.js +617 -0
- package/dist/ci/workflow.js +436 -0
- package/dist/cli/admin-cmd.js +324 -0
- package/dist/cli/api-cmd.js +128 -0
- package/dist/cli/backup-cmd.js +1500 -0
- package/dist/cli/exit.js +69 -0
- package/dist/cli/input.js +64 -0
- package/dist/cli/issue-cmd.js +243 -0
- package/dist/cli/output.js +93 -0
- package/dist/cli/parse.js +317 -0
- package/dist/cli/pr-cmd.js +289 -0
- package/dist/cli/release-cmd.js +171 -0
- package/dist/cli/repo-cmd.js +763 -0
- package/dist/cli/repo.js +101 -0
- package/dist/cli/run-cmd.js +438 -0
- package/dist/cli/target.js +54 -0
- package/dist/cli-api.js +84 -0
- package/dist/compare.js +111 -0
- package/dist/config.js +212 -0
- package/dist/credentials.js +235 -0
- package/dist/deploy-cli.js +859 -0
- package/dist/deploy-runner-cli.js +592 -0
- package/dist/diff.js +171 -0
- package/dist/discussion.js +253 -0
- package/dist/egress.js +559 -0
- package/dist/filecache.js +68 -0
- package/dist/find.js +162 -0
- package/dist/forms.js +737 -0
- package/dist/git.js +547 -0
- package/dist/githttp.js +428 -0
- package/dist/html.js +87 -0
- package/dist/icons.js +101 -0
- package/dist/import-cli.js +316 -0
- package/dist/index.js +752 -0
- package/dist/issues.js +308 -0
- package/dist/issueweb.js +447 -0
- package/dist/job-cli.js +197 -0
- package/dist/jobtoken.js +96 -0
- package/dist/languages.js +383 -0
- package/dist/layout.js +100 -0
- package/dist/lfs.js +438 -0
- package/dist/lfsstore.js +425 -0
- package/dist/limit.js +259 -0
- package/dist/logo.js +61 -0
- package/dist/markdown.js +382 -0
- package/dist/migrate.js +334 -0
- package/dist/multipart.js +90 -0
- package/dist/ops.js +869 -0
- package/dist/pagescript.js +465 -0
- package/dist/perms.js +370 -0
- package/dist/pointer.js +55 -0
- package/dist/profile.js +106 -0
- package/dist/pulls.js +320 -0
- package/dist/pullweb.js +461 -0
- package/dist/redirects.js +455 -0
- package/dist/releases.js +435 -0
- package/dist/render.js +233 -0
- package/dist/runner/actions.js +448 -0
- package/dist/runner/client.js +428 -0
- package/dist/runner/context.js +247 -0
- package/dist/runner/docker.js +197 -0
- package/dist/runner/externals.js +175 -0
- package/dist/runner/job.js +290 -0
- package/dist/runner/manual-run.js +272 -0
- package/dist/runner/overrides.js +554 -0
- package/dist/runner/steps.js +571 -0
- package/dist/runner/wake.js +84 -0
- package/dist/runner-cli.js +405 -0
- package/dist/scan.js +231 -0
- package/dist/server.js +424 -0
- package/dist/session.js +267 -0
- package/dist/site.js +259 -0
- package/dist/siteshost.js +94 -0
- package/dist/source.js +90 -0
- package/dist/style.js +1295 -0
- package/dist/themes.js +369 -0
- package/dist/vault.js +442 -0
- package/dist/version.js +88 -0
- package/dist/views.js +1007 -0
- package/dist/web.js +182 -0
- package/dist/webops.js +1402 -0
- package/package.json +71 -0
|
@@ -0,0 +1,436 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.WorkflowError = void 0;
|
|
37
|
+
exports.parseWorkflow = parseWorkflow;
|
|
38
|
+
exports.filterPatternToRegExp = filterPatternToRegExp;
|
|
39
|
+
exports.matchFilterPatterns = matchFilterPatterns;
|
|
40
|
+
exports.pushMatches = pushMatches;
|
|
41
|
+
const YAML = __importStar(require("yaml"));
|
|
42
|
+
// Parsing and normalizing workflow files (.github/workflows/*.yml and
|
|
43
|
+
// .mochi/workflows/*.yml). The result is a plain-data Workflow that the
|
|
44
|
+
// planner turns into a run. Parsing is strict about shapes it understands and
|
|
45
|
+
// forgiving about keys it does not: unknown top-level or job keys are
|
|
46
|
+
// ignored, so a workflow using a feature we have not implemented degrades to
|
|
47
|
+
// a clear failure at the point where the feature would matter, not a parse
|
|
48
|
+
// error that hides the rest of the file.
|
|
49
|
+
class WorkflowError extends Error {
|
|
50
|
+
}
|
|
51
|
+
exports.WorkflowError = WorkflowError;
|
|
52
|
+
function isObj(v) {
|
|
53
|
+
return typeof v === 'object' && v !== null && !Array.isArray(v);
|
|
54
|
+
}
|
|
55
|
+
function asScalarString(v) {
|
|
56
|
+
if (v === undefined || v === null)
|
|
57
|
+
return undefined;
|
|
58
|
+
if (typeof v === 'string')
|
|
59
|
+
return v;
|
|
60
|
+
if (typeof v === 'number' || typeof v === 'boolean')
|
|
61
|
+
return String(v);
|
|
62
|
+
return undefined;
|
|
63
|
+
}
|
|
64
|
+
function stringList(v) {
|
|
65
|
+
if (v === undefined)
|
|
66
|
+
return undefined;
|
|
67
|
+
if (typeof v === 'string')
|
|
68
|
+
return [v];
|
|
69
|
+
if (Array.isArray(v))
|
|
70
|
+
return v.map((x) => asScalarString(x) ?? '').filter((s) => s !== '');
|
|
71
|
+
return undefined;
|
|
72
|
+
}
|
|
73
|
+
function envMap(v, where) {
|
|
74
|
+
if (v === undefined)
|
|
75
|
+
return {};
|
|
76
|
+
if (!isObj(v))
|
|
77
|
+
throw new WorkflowError(`${where}: env must be a map`);
|
|
78
|
+
const out = {};
|
|
79
|
+
for (const [k, val] of Object.entries(v)) {
|
|
80
|
+
const s = asScalarString(val);
|
|
81
|
+
if (s === undefined)
|
|
82
|
+
throw new WorkflowError(`${where}: env value for ${k} must be a scalar`);
|
|
83
|
+
out[k] = s;
|
|
84
|
+
}
|
|
85
|
+
return out;
|
|
86
|
+
}
|
|
87
|
+
function parsePushFilter(v) {
|
|
88
|
+
if (v === null || v === undefined)
|
|
89
|
+
return {};
|
|
90
|
+
if (!isObj(v))
|
|
91
|
+
return {};
|
|
92
|
+
return {
|
|
93
|
+
branches: stringList(v.branches),
|
|
94
|
+
branchesIgnore: stringList(v['branches-ignore']),
|
|
95
|
+
tags: stringList(v.tags),
|
|
96
|
+
tagsIgnore: stringList(v['tags-ignore']),
|
|
97
|
+
paths: stringList(v.paths),
|
|
98
|
+
pathsIgnore: stringList(v['paths-ignore']),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
function parseDispatchInputs(v) {
|
|
102
|
+
const out = {};
|
|
103
|
+
if (!isObj(v))
|
|
104
|
+
return out;
|
|
105
|
+
const inputs = v.inputs;
|
|
106
|
+
if (!isObj(inputs))
|
|
107
|
+
return out;
|
|
108
|
+
for (const [name, def] of Object.entries(inputs)) {
|
|
109
|
+
const d = isObj(def) ? def : {};
|
|
110
|
+
const type = typeof d.type === 'string' ? d.type : 'string';
|
|
111
|
+
out[name] = {
|
|
112
|
+
description: asScalarString(d.description),
|
|
113
|
+
required: d.required === true,
|
|
114
|
+
default: typeof d.default === 'boolean' || typeof d.default === 'number' ? d.default : asScalarString(d.default),
|
|
115
|
+
type: type === 'boolean' || type === 'choice' || type === 'number' || type === 'environment' ? type : 'string',
|
|
116
|
+
options: stringList(d.options),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
return out;
|
|
120
|
+
}
|
|
121
|
+
function parseTriggers(v) {
|
|
122
|
+
const t = { others: [] };
|
|
123
|
+
const add = (event, config) => {
|
|
124
|
+
if (event === 'push')
|
|
125
|
+
t.push = parsePushFilter(config);
|
|
126
|
+
else if (event === 'workflow_dispatch')
|
|
127
|
+
t.workflowDispatch = { inputs: parseDispatchInputs(config) };
|
|
128
|
+
else
|
|
129
|
+
t.others.push(event);
|
|
130
|
+
};
|
|
131
|
+
if (typeof v === 'string') {
|
|
132
|
+
add(v, null);
|
|
133
|
+
}
|
|
134
|
+
else if (Array.isArray(v)) {
|
|
135
|
+
for (const e of v) {
|
|
136
|
+
if (typeof e === 'string')
|
|
137
|
+
add(e, null);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
else if (isObj(v)) {
|
|
141
|
+
for (const [e, c] of Object.entries(v))
|
|
142
|
+
add(e, c);
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
throw new WorkflowError('the workflow has no usable "on:" triggers');
|
|
146
|
+
}
|
|
147
|
+
return t;
|
|
148
|
+
}
|
|
149
|
+
function parseConcurrency(v) {
|
|
150
|
+
if (v === undefined)
|
|
151
|
+
return undefined;
|
|
152
|
+
if (typeof v === 'string')
|
|
153
|
+
return { group: v, cancelInProgress: 'false' };
|
|
154
|
+
if (isObj(v)) {
|
|
155
|
+
const group = asScalarString(v.group);
|
|
156
|
+
if (!group)
|
|
157
|
+
throw new WorkflowError('concurrency needs a group');
|
|
158
|
+
return { group, cancelInProgress: asScalarString(v['cancel-in-progress']) ?? 'false' };
|
|
159
|
+
}
|
|
160
|
+
throw new WorkflowError('invalid concurrency');
|
|
161
|
+
}
|
|
162
|
+
function parseStep(v, where) {
|
|
163
|
+
if (!isObj(v))
|
|
164
|
+
throw new WorkflowError(`${where}: each step must be a map`);
|
|
165
|
+
const step = { env: envMap(v.env, where) };
|
|
166
|
+
if (v.id !== undefined)
|
|
167
|
+
step.id = asScalarString(v.id);
|
|
168
|
+
if (v.name !== undefined)
|
|
169
|
+
step.name = asScalarString(v.name);
|
|
170
|
+
if (v.if !== undefined)
|
|
171
|
+
step.if = asScalarString(v.if);
|
|
172
|
+
if (v.run !== undefined)
|
|
173
|
+
step.run = asScalarString(v.run);
|
|
174
|
+
if (v.uses !== undefined)
|
|
175
|
+
step.uses = asScalarString(v.uses);
|
|
176
|
+
if (v.shell !== undefined)
|
|
177
|
+
step.shell = asScalarString(v.shell);
|
|
178
|
+
if (v['working-directory'] !== undefined)
|
|
179
|
+
step.workingDirectory = asScalarString(v['working-directory']);
|
|
180
|
+
if (v['continue-on-error'] !== undefined)
|
|
181
|
+
step.continueOnError = asScalarString(v['continue-on-error']);
|
|
182
|
+
if (v['timeout-minutes'] !== undefined)
|
|
183
|
+
step.timeoutMinutes = asScalarString(v['timeout-minutes']);
|
|
184
|
+
if (isObj(v.with)) {
|
|
185
|
+
step.with = {};
|
|
186
|
+
for (const [k, val] of Object.entries(v.with)) {
|
|
187
|
+
const s = asScalarString(val);
|
|
188
|
+
if (s !== undefined)
|
|
189
|
+
step.with[k] = s;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (!step.run && !step.uses)
|
|
193
|
+
throw new WorkflowError(`${where}: a step needs either "run" or "uses"`);
|
|
194
|
+
if (step.run && step.uses)
|
|
195
|
+
throw new WorkflowError(`${where}: a step cannot have both "run" and "uses"`);
|
|
196
|
+
return step;
|
|
197
|
+
}
|
|
198
|
+
// Job ids become path segments under <repo>.runs/<n>/jobs/, so this is a
|
|
199
|
+
// containment check and not only a syntax one: a workflow is repository
|
|
200
|
+
// content, and a key like "../../x" must never reach a filesystem path.
|
|
201
|
+
// GitHub's own rule for job ids is the same shape.
|
|
202
|
+
const JOB_KEY = /^[A-Za-z_][A-Za-z0-9_-]*$/;
|
|
203
|
+
function parseJob(key, v) {
|
|
204
|
+
if (!JOB_KEY.test(key) || key.length > 100) {
|
|
205
|
+
throw new WorkflowError(`job id "${key.slice(0, 40)}" is not valid: it must start with a letter or underscore and contain only letters, digits, dashes, and underscores`);
|
|
206
|
+
}
|
|
207
|
+
if (!isObj(v))
|
|
208
|
+
throw new WorkflowError(`job ${key} must be a map`);
|
|
209
|
+
const job = {
|
|
210
|
+
key,
|
|
211
|
+
needs: stringList(v.needs) ?? [],
|
|
212
|
+
runsOn: [],
|
|
213
|
+
env: envMap(v.env, `job ${key}`),
|
|
214
|
+
steps: [],
|
|
215
|
+
outputs: {},
|
|
216
|
+
};
|
|
217
|
+
if (v.name !== undefined)
|
|
218
|
+
job.name = asScalarString(v.name);
|
|
219
|
+
if (v.if !== undefined)
|
|
220
|
+
job.if = asScalarString(v.if);
|
|
221
|
+
if (v['timeout-minutes'] !== undefined)
|
|
222
|
+
job.timeoutMinutes = asScalarString(v['timeout-minutes']);
|
|
223
|
+
if (v['continue-on-error'] !== undefined)
|
|
224
|
+
job.continueOnError = asScalarString(v['continue-on-error']);
|
|
225
|
+
if (v.uses !== undefined) {
|
|
226
|
+
job.unsupported = 'reusable workflows (jobs.<id>.uses) are not supported yet';
|
|
227
|
+
return job;
|
|
228
|
+
}
|
|
229
|
+
if (v.container !== undefined) {
|
|
230
|
+
job.unsupported = 'container jobs (jobs.<id>.container) are not supported yet';
|
|
231
|
+
return job;
|
|
232
|
+
}
|
|
233
|
+
if (v.services !== undefined) {
|
|
234
|
+
job.unsupported = 'service containers (jobs.<id>.services) are not supported yet';
|
|
235
|
+
return job;
|
|
236
|
+
}
|
|
237
|
+
const runsOn = stringList(v['runs-on']);
|
|
238
|
+
if (!runsOn || runsOn.length === 0) {
|
|
239
|
+
// runs-on may also be {group, labels}; take the labels if so.
|
|
240
|
+
const ro = v['runs-on'];
|
|
241
|
+
const labels = isObj(ro) ? stringList(ro.labels) : undefined;
|
|
242
|
+
if (!labels || labels.length === 0)
|
|
243
|
+
throw new WorkflowError(`job ${key} has no runs-on`);
|
|
244
|
+
job.runsOn = labels;
|
|
245
|
+
}
|
|
246
|
+
else {
|
|
247
|
+
job.runsOn = runsOn;
|
|
248
|
+
}
|
|
249
|
+
if (isObj(v.defaults) && isObj(v.defaults.run)) {
|
|
250
|
+
job.defaults = {
|
|
251
|
+
shell: asScalarString(v.defaults.run.shell),
|
|
252
|
+
workingDirectory: asScalarString(v.defaults.run['working-directory']),
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
if (isObj(v.strategy)) {
|
|
256
|
+
job.strategy = {
|
|
257
|
+
matrix: v.strategy.matrix,
|
|
258
|
+
failFast: v.strategy['fail-fast'] !== false && v.strategy['fail-fast'] !== 'false',
|
|
259
|
+
maxParallel: typeof v.strategy['max-parallel'] === 'number' ? v.strategy['max-parallel'] : undefined,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
if (isObj(v.outputs)) {
|
|
263
|
+
for (const [k, val] of Object.entries(v.outputs)) {
|
|
264
|
+
const s = asScalarString(val);
|
|
265
|
+
if (s !== undefined)
|
|
266
|
+
job.outputs[k] = s;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
if (!Array.isArray(v.steps) || v.steps.length === 0) {
|
|
270
|
+
throw new WorkflowError(`job ${key} has no steps`);
|
|
271
|
+
}
|
|
272
|
+
job.steps = v.steps.map((s, i) => parseStep(s, `job ${key}, step ${i + 1}`));
|
|
273
|
+
return job;
|
|
274
|
+
}
|
|
275
|
+
function parseWorkflow(source) {
|
|
276
|
+
let doc;
|
|
277
|
+
try {
|
|
278
|
+
doc = YAML.parse(source);
|
|
279
|
+
}
|
|
280
|
+
catch (e) {
|
|
281
|
+
throw new WorkflowError(`YAML parse error: ${e instanceof Error ? e.message : String(e)}`);
|
|
282
|
+
}
|
|
283
|
+
if (!isObj(doc))
|
|
284
|
+
throw new WorkflowError('the workflow file is not a YAML map');
|
|
285
|
+
// YAML 1.1 reads a bare `on:` key as boolean true; the yaml package in core
|
|
286
|
+
// schema keeps it as the string 'on' only when quoted. Handle both spellings.
|
|
287
|
+
const onValue = 'on' in doc ? doc.on : doc['true'];
|
|
288
|
+
if (onValue === undefined)
|
|
289
|
+
throw new WorkflowError('the workflow has no "on:" triggers');
|
|
290
|
+
const jobsRaw = doc.jobs;
|
|
291
|
+
if (!isObj(jobsRaw) || Object.keys(jobsRaw).length === 0) {
|
|
292
|
+
throw new WorkflowError('the workflow has no jobs');
|
|
293
|
+
}
|
|
294
|
+
const jobs = Object.entries(jobsRaw).map(([k, v]) => parseJob(k, v));
|
|
295
|
+
const keys = new Set(jobs.map((j) => j.key));
|
|
296
|
+
for (const j of jobs) {
|
|
297
|
+
for (const n of j.needs) {
|
|
298
|
+
if (!keys.has(n))
|
|
299
|
+
throw new WorkflowError(`job ${j.key} needs unknown job ${n}`);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
// There is no secrets store, deliberately: workflows here run without
|
|
303
|
+
// credentials. A reference to one would evaluate to an empty string, and an
|
|
304
|
+
// empty credential fails somewhere far from its cause -- a push that is
|
|
305
|
+
// refused, an API that 401s mid-deploy -- so the reference is refused here,
|
|
306
|
+
// where the message can say what is actually going on.
|
|
307
|
+
const secretRef = source.match(/\$\{\{[^}]*\bsecrets\s*\.\s*([A-Za-z0-9_-]+)/);
|
|
308
|
+
if (secretRef) {
|
|
309
|
+
throw new WorkflowError(`the workflow references secrets.${secretRef[1]}, and this vault has no secrets: workflows run without ` +
|
|
310
|
+
`credentials, so the value would silently be an empty string. Remove the reference, or run this job ` +
|
|
311
|
+
`somewhere that holds the secret`);
|
|
312
|
+
}
|
|
313
|
+
return {
|
|
314
|
+
name: asScalarString(doc.name) ?? null,
|
|
315
|
+
on: parseTriggers(onValue),
|
|
316
|
+
env: envMap(doc.env, 'workflow'),
|
|
317
|
+
concurrency: parseConcurrency(doc.concurrency),
|
|
318
|
+
jobs,
|
|
319
|
+
};
|
|
320
|
+
}
|
|
321
|
+
// ---- filter-pattern matching (branches:, tags:, paths:) ----
|
|
322
|
+
// GitHub filter patterns: * matches within a path segment, ** across
|
|
323
|
+
// segments, ? one character, [] ranges, and a leading ! negates (applied in
|
|
324
|
+
// order, so a later pattern can re-include).
|
|
325
|
+
function filterPatternToRegExp(pattern) {
|
|
326
|
+
let rx = '';
|
|
327
|
+
let i = 0;
|
|
328
|
+
while (i < pattern.length) {
|
|
329
|
+
const c = pattern[i];
|
|
330
|
+
if (c === '*') {
|
|
331
|
+
if (pattern[i + 1] === '*') {
|
|
332
|
+
// `**/` spans zero or more directories, so that dist/**/*.js matches
|
|
333
|
+
// dist/a.js as well as dist/sub/b.js. A bare `**` matches anything.
|
|
334
|
+
if (pattern[i + 2] === '/') {
|
|
335
|
+
rx += '(?:.*/)?';
|
|
336
|
+
i += 3;
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
rx += '.*';
|
|
340
|
+
i += 2;
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
else {
|
|
344
|
+
rx += '[^/]*';
|
|
345
|
+
i++;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
else if (c === '?') {
|
|
349
|
+
rx += '[^/]';
|
|
350
|
+
i++;
|
|
351
|
+
}
|
|
352
|
+
else if (c === '[') {
|
|
353
|
+
const end = pattern.indexOf(']', i + 1);
|
|
354
|
+
if (end === -1) {
|
|
355
|
+
rx += '\\[';
|
|
356
|
+
i++;
|
|
357
|
+
}
|
|
358
|
+
else {
|
|
359
|
+
rx += `[${pattern.slice(i + 1, end).replace(/\\/g, '\\\\')}]`;
|
|
360
|
+
i = end + 1;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
else {
|
|
364
|
+
rx += c.replace(/[.+^${}()|[\]\\]/g, '\\$&');
|
|
365
|
+
i++;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
return new RegExp(`^${rx}$`);
|
|
369
|
+
}
|
|
370
|
+
function matchFilterPatterns(patterns, target) {
|
|
371
|
+
let matched = false;
|
|
372
|
+
let sawPositive = false;
|
|
373
|
+
for (const p of patterns) {
|
|
374
|
+
if (p.startsWith('!')) {
|
|
375
|
+
if (filterPatternToRegExp(p.slice(1)).test(target))
|
|
376
|
+
matched = false;
|
|
377
|
+
}
|
|
378
|
+
else {
|
|
379
|
+
sawPositive = true;
|
|
380
|
+
if (filterPatternToRegExp(p).test(target))
|
|
381
|
+
matched = true;
|
|
382
|
+
}
|
|
383
|
+
}
|
|
384
|
+
// A list of only negations implicitly starts from "everything matches".
|
|
385
|
+
if (!sawPositive) {
|
|
386
|
+
matched = true;
|
|
387
|
+
for (const p of patterns) {
|
|
388
|
+
if (p.startsWith('!') && filterPatternToRegExp(p.slice(1)).test(target))
|
|
389
|
+
matched = false;
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
return matched;
|
|
393
|
+
}
|
|
394
|
+
// Whether a push to `ref` (refs/heads/x or refs/tags/x) fires this
|
|
395
|
+
// workflow's push trigger. `changedPaths` is null when unknown (a brand-new
|
|
396
|
+
// branch), in which case a paths filter is treated as matched, as on GitHub.
|
|
397
|
+
function pushMatches(f, ref, changedPaths) {
|
|
398
|
+
const isTag = ref.startsWith('refs/tags/');
|
|
399
|
+
const short = ref.replace(/^refs\/(heads|tags)\//, '');
|
|
400
|
+
if (isTag) {
|
|
401
|
+
// With branch filters and no tag filters, tag pushes do not fire.
|
|
402
|
+
if (!f.tags && !f.tagsIgnore) {
|
|
403
|
+
if (f.branches || f.branchesIgnore)
|
|
404
|
+
return false;
|
|
405
|
+
}
|
|
406
|
+
else {
|
|
407
|
+
if (f.tags && !matchFilterPatterns(f.tags, short))
|
|
408
|
+
return false;
|
|
409
|
+
if (f.tagsIgnore && matchFilterPatterns(f.tagsIgnore, short))
|
|
410
|
+
return false;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
else {
|
|
414
|
+
if (!f.branches && !f.branchesIgnore) {
|
|
415
|
+
if (f.tags || f.tagsIgnore)
|
|
416
|
+
return false;
|
|
417
|
+
}
|
|
418
|
+
else {
|
|
419
|
+
if (f.branches && !matchFilterPatterns(f.branches, short))
|
|
420
|
+
return false;
|
|
421
|
+
if (f.branchesIgnore && matchFilterPatterns(f.branchesIgnore, short))
|
|
422
|
+
return false;
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
if ((f.paths || f.pathsIgnore) && changedPaths !== null) {
|
|
426
|
+
const considered = f.paths
|
|
427
|
+
? changedPaths.filter((p) => matchFilterPatterns(f.paths, p))
|
|
428
|
+
: changedPaths;
|
|
429
|
+
const surviving = f.pathsIgnore
|
|
430
|
+
? considered.filter((p) => !matchFilterPatterns(f.pathsIgnore, p))
|
|
431
|
+
: considered;
|
|
432
|
+
if (surviving.length === 0)
|
|
433
|
+
return false;
|
|
434
|
+
}
|
|
435
|
+
return true;
|
|
436
|
+
}
|