@gitlab/eslint-plugin 21.2.1 → 21.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/.changeset/README.md +8 -0
- package/.changeset/config.json +11 -0
- package/.changeset/release_plan.json +25 -0
- package/.tool-versions +2 -1
- package/CHANGELOG-old.md +799 -0
- package/CHANGELOG.md +7 -793
- package/docs/rules/no-hardcoded-urls.md +27 -0
- package/docs/rules/vue-no-hardcoded-urls.md +30 -0
- package/docs/rules.md +2 -0
- package/lefthook.yml +3 -1
- package/lib/index.js +10 -0
- package/lib/rules/no-hardcoded-urls.js +71 -0
- package/lib/rules/vue-no-hardcoded-urls.js +77 -0
- package/lib/utils/hardcoded-urls.js +127 -0
- package/package.json +4 -26
- package/scripts/check_lockfile_stability +19 -0
- package/scripts/ensure_updated_files +11 -0
- package/scripts/publish_npm_package.mjs +250 -0
- package/scripts/shared.mjs +34 -0
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { readdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
|
+
import { join, relative } from 'node:path';
|
|
4
|
+
import { styleText } from 'node:util';
|
|
5
|
+
import defaultChangelogFunctions from '@changesets/cli/changelog';
|
|
6
|
+
import { ROOT, printDiagnostics, run } from './shared.mjs';
|
|
7
|
+
|
|
8
|
+
const { env } = process;
|
|
9
|
+
|
|
10
|
+
const CHANGESET_DIR = join(ROOT, '.changeset');
|
|
11
|
+
const CHANGESET_BIN = join(ROOT, 'node_modules', '.bin', 'changeset');
|
|
12
|
+
|
|
13
|
+
// Changesets fails if this is an absolute path.
|
|
14
|
+
const CHANGESET_RELEASE_PLAN_FILE = relative(
|
|
15
|
+
process.cwd(),
|
|
16
|
+
join(CHANGESET_DIR, 'release_plan.json'),
|
|
17
|
+
);
|
|
18
|
+
|
|
19
|
+
function isEnvironmentOkay() {
|
|
20
|
+
const messages = [];
|
|
21
|
+
|
|
22
|
+
if (!env.CI) {
|
|
23
|
+
messages.push('This script should only be run in CI.');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
if (env.DRY_RUN) {
|
|
27
|
+
if (!env.CI_MERGE_REQUEST_IID) {
|
|
28
|
+
messages.push('This script should only run in merge request pipelines.');
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (!env.GITLAB_TOKEN_MR) {
|
|
32
|
+
messages.push('GITLAB_TOKEN_MR is not defined.');
|
|
33
|
+
}
|
|
34
|
+
} else {
|
|
35
|
+
if (!env.CI_COMMIT_BRANCH) {
|
|
36
|
+
messages.push('This script should only run in branch pipelines.');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
if (env.CI_COMMIT_BRANCH !== env.CI_DEFAULT_BRANCH) {
|
|
40
|
+
messages.push(
|
|
41
|
+
`This script should only run on pipelines for the default branch: CI_COMMIT_BRANCH=${env.CI_COMMIT_BRANCH}, CI_DEFAULT_BRANCH=${env.CI_DEFAULT_BRANCH}`,
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!env.GITLAB_TOKEN) {
|
|
46
|
+
messages.push('GITLAB_TOKEN is not defined.');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (!env.NPM_ID_TOKEN) {
|
|
50
|
+
messages.push(
|
|
51
|
+
'NPM_ID_TOKEN is not defined. Is trusted publishing set up correctly? See https://docs.npmjs.com/trusted-publishers',
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
messages.forEach((message) => {
|
|
57
|
+
console.warn(message);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
return messages.length === 0;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function gitUrl() {
|
|
64
|
+
const token = env.GITLAB_TOKEN || env.GITLAB_TOKEN_MR;
|
|
65
|
+
const projectUrl = env.CI_MERGE_REQUEST_SOURCE_PROJECT_PATH || env.CI_PROJECT_PATH;
|
|
66
|
+
return `https://gitlab-bot:${token}@gitlab.com/${projectUrl}.git`;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Changesets needs git to be checked out on the branch, not on a detached
|
|
71
|
+
* commit sha. In other words, HEAD must point to the branch name, not its
|
|
72
|
+
* commit sha.
|
|
73
|
+
*
|
|
74
|
+
* For dry runs, there also needs to exist a local branch for the default
|
|
75
|
+
* branch so that it can compare the source branch against it.
|
|
76
|
+
*/
|
|
77
|
+
function ensureBranches() {
|
|
78
|
+
run('git', ['remote', 'set-url', 'origin', gitUrl()]);
|
|
79
|
+
|
|
80
|
+
if (env.DRY_RUN) {
|
|
81
|
+
const branch = env.CI_MERGE_REQUEST_SOURCE_BRANCH_NAME;
|
|
82
|
+
run('git', ['fetch', 'origin', env.CI_DEFAULT_BRANCH, branch]);
|
|
83
|
+
run('git', ['branch', env.CI_DEFAULT_BRANCH, `origin/${env.CI_DEFAULT_BRANCH}`]);
|
|
84
|
+
run('git', ['checkout', '-b', branch, `origin/${branch}`]);
|
|
85
|
+
} else {
|
|
86
|
+
run('git', ['fetch', 'origin', env.CI_DEFAULT_BRANCH]);
|
|
87
|
+
run('git', ['checkout', '-b', env.CI_DEFAULT_BRANCH, `origin/${env.CI_DEFAULT_BRANCH}`]);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function getReleasePlan() {
|
|
92
|
+
const changesetFiles = readdirSync(CHANGESET_DIR).filter(
|
|
93
|
+
(name) => name.endsWith('.md') && name !== 'README.md',
|
|
94
|
+
);
|
|
95
|
+
|
|
96
|
+
if (changesetFiles.length > 0) {
|
|
97
|
+
console.log(`Found changeset files:\n${changesetFiles.join('\n')}`);
|
|
98
|
+
} else {
|
|
99
|
+
// Create an empty release plan file anyway, to avoid a spurious warning in
|
|
100
|
+
// job log about missing artifacts.
|
|
101
|
+
writeFileSync(CHANGESET_RELEASE_PLAN_FILE, '');
|
|
102
|
+
console.log('No changesets found.');
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// We know there are changeset files, so we expect this command to succeed.
|
|
107
|
+
run(CHANGESET_BIN, ['status', `--output=${CHANGESET_RELEASE_PLAN_FILE}`]);
|
|
108
|
+
|
|
109
|
+
const releasePlan = JSON.parse(readFileSync(CHANGESET_RELEASE_PLAN_FILE, 'utf8'));
|
|
110
|
+
|
|
111
|
+
console.log('Changesets release plan:', releasePlan);
|
|
112
|
+
|
|
113
|
+
return releasePlan;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Pretty-print the release plan JSON.
|
|
118
|
+
* @param {object} releasePlan The release plan.
|
|
119
|
+
*/
|
|
120
|
+
function printReleasePlan({ releases }) {
|
|
121
|
+
const releaseLines = releases.map(
|
|
122
|
+
({ name, type, oldVersion, newVersion }) =>
|
|
123
|
+
`${styleText('bold', name)} ${styleText('yellow', oldVersion)} ⟶ ${styleText(
|
|
124
|
+
'green',
|
|
125
|
+
newVersion,
|
|
126
|
+
)} (${type})`,
|
|
127
|
+
);
|
|
128
|
+
console.log(releaseLines.join('\n'));
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function releaseSection(type, lines) {
|
|
132
|
+
const typeCapitalized = `${type.charAt(0).toUpperCase()}${type.slice(1)}`;
|
|
133
|
+
|
|
134
|
+
return `### ${typeCapitalized} changes\n\n${lines.join('\n')}`;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
async function createRelease({ tag, description }) {
|
|
138
|
+
const url = `${env.CI_API_V4_URL}/projects/${env.CI_PROJECT_ID}/releases`;
|
|
139
|
+
|
|
140
|
+
console.log(`Creating release via ${url}...`);
|
|
141
|
+
|
|
142
|
+
const response = await fetch(url, {
|
|
143
|
+
method: 'POST',
|
|
144
|
+
headers: { 'content-type': 'application/json', 'private-token': env.GITLAB_TOKEN },
|
|
145
|
+
body: JSON.stringify({ tag_name: tag, description }),
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
if (!response.ok) {
|
|
149
|
+
const responseBody = JSON.stringify(await response.json(), null, 2);
|
|
150
|
+
|
|
151
|
+
throw new Error(
|
|
152
|
+
`Failed to create release for tag ${tag}: ${response.status}: ${response.statusText}, body: ${responseBody}`,
|
|
153
|
+
);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
console.log(`Successfully created release at ${url}/${encodeURIComponent(tag)}`);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
async function createReleases({ changesets, releases }) {
|
|
160
|
+
for (const release of releases) {
|
|
161
|
+
const releaseDescriptionObject = {
|
|
162
|
+
major: [],
|
|
163
|
+
minor: [],
|
|
164
|
+
patch: [],
|
|
165
|
+
};
|
|
166
|
+
|
|
167
|
+
for (const changeset of changesets) {
|
|
168
|
+
const { type } =
|
|
169
|
+
changeset.releases.find((rel) => rel.name === release.name && rel.type !== 'none') ?? {};
|
|
170
|
+
|
|
171
|
+
if (type) {
|
|
172
|
+
releaseDescriptionObject[type].push(
|
|
173
|
+
// eslint-disable-next-line no-await-in-loop
|
|
174
|
+
await defaultChangelogFunctions.getReleaseLine(changeset, type),
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const description = Object.entries(releaseDescriptionObject)
|
|
180
|
+
.filter(([, lines]) => lines.length > 0)
|
|
181
|
+
.map(([type, lines]) => releaseSection(type, lines))
|
|
182
|
+
.join('\n\n');
|
|
183
|
+
|
|
184
|
+
// eslint-disable-next-line no-await-in-loop
|
|
185
|
+
await createRelease({ tag: `v${release.newVersion}`, description });
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
function gitCommit({ message = 'Update packages for release [skip ci]' } = {}) {
|
|
190
|
+
run('git', ['config', '--global', 'user.email', 'gitlab-bot@gitlab.com']);
|
|
191
|
+
run('git', ['config', '--global', 'user.name', 'GitLab Bot']);
|
|
192
|
+
|
|
193
|
+
// Stage modified and deleted files only.
|
|
194
|
+
run('git', ['add', '--update']);
|
|
195
|
+
|
|
196
|
+
run('git', ['status']);
|
|
197
|
+
run('git', ['commit', '-m', message]);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function gitPush() {
|
|
201
|
+
const refspec = `HEAD:${env.CI_COMMIT_BRANCH}`;
|
|
202
|
+
|
|
203
|
+
run('git', ['push', '--follow-tags', 'origin', refspec]);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function publish() {
|
|
207
|
+
run(CHANGESET_BIN, ['publish']);
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
async function main() {
|
|
211
|
+
if (!isEnvironmentOkay()) {
|
|
212
|
+
process.exitCode = 1;
|
|
213
|
+
return;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
ensureBranches();
|
|
217
|
+
|
|
218
|
+
const releasePlan = getReleasePlan();
|
|
219
|
+
if (!releasePlan || releasePlan.releases.length === 0) {
|
|
220
|
+
console.log('Nothing to publish.');
|
|
221
|
+
return;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Process changeset files and update package changelogs
|
|
225
|
+
run(CHANGESET_BIN, ['version']);
|
|
226
|
+
|
|
227
|
+
if (env.DRY_RUN) {
|
|
228
|
+
console.log('Diff of changes that would be made if this were on the default branch:');
|
|
229
|
+
run('git', ['diff', '--color=always']);
|
|
230
|
+
console.log('The following packages would be released:');
|
|
231
|
+
printReleasePlan(releasePlan);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
gitCommit();
|
|
236
|
+
|
|
237
|
+
publish();
|
|
238
|
+
|
|
239
|
+
gitPush();
|
|
240
|
+
|
|
241
|
+
await createReleases(releasePlan);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
try {
|
|
245
|
+
await main();
|
|
246
|
+
} catch (error) {
|
|
247
|
+
process.exitCode = 1;
|
|
248
|
+
printDiagnostics();
|
|
249
|
+
console.error('Unhandled error (see above for diagnostics):', error);
|
|
250
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { spawnSync } from 'node:child_process';
|
|
2
|
+
import { join, resolve } from 'node:path';
|
|
3
|
+
|
|
4
|
+
export const ROOT = resolve(import.meta.dirname, '..');
|
|
5
|
+
const CHANGESET_DIR = join(ROOT, '.changeset');
|
|
6
|
+
|
|
7
|
+
export function run(executable, args = [], { throwOnFailure = true, ...options } = {}) {
|
|
8
|
+
const commandLine = `${executable} ${args.join(' ')}`;
|
|
9
|
+
console.log(`Running: ${commandLine}`);
|
|
10
|
+
|
|
11
|
+
const child = spawnSync(executable, args, {
|
|
12
|
+
stdio: 'inherit',
|
|
13
|
+
encoding: 'utf8',
|
|
14
|
+
...options,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
if (child.status === 0) {
|
|
18
|
+
console.log(`Success: "${commandLine}"`);
|
|
19
|
+
} else {
|
|
20
|
+
const message = `Failure: "${commandLine}" exited with:\n code=${child.status}\n signal=${child.signal}\n error=${child.error}`;
|
|
21
|
+
if (throwOnFailure) throw new Error(message);
|
|
22
|
+
else console.warn(message);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
return child;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function printDiagnostics() {
|
|
29
|
+
console.log('Beginning diagnostics:');
|
|
30
|
+
run('npm', ['config', 'ls'], { throwOnFailure: false });
|
|
31
|
+
run('git', ['status', '--porcelain=v2', '--branch'], { throwOnFailure: false });
|
|
32
|
+
run('git', ['diff'], { throwOnFailure: false });
|
|
33
|
+
run('ls', ['-la', CHANGESET_DIR], { throwOnFailure: false });
|
|
34
|
+
}
|