@servicetitan/startup 34.3.0 → 35.1.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/commands/get-command.d.ts.map +1 -1
- package/dist/cli/commands/get-command.js +2 -0
- package/dist/cli/commands/get-command.js.map +1 -1
- package/dist/cli/commands/mfe-package-publish.d.ts +10 -0
- package/dist/cli/commands/mfe-package-publish.d.ts.map +1 -1
- package/dist/cli/commands/mfe-package-publish.js +12 -0
- package/dist/cli/commands/mfe-package-publish.js.map +1 -1
- package/dist/cli/commands/mfe-package-rollback.d.ts +10 -1
- package/dist/cli/commands/mfe-package-rollback.d.ts.map +1 -1
- package/dist/cli/commands/mfe-package-rollback.js +15 -3
- package/dist/cli/commands/mfe-package-rollback.js.map +1 -1
- package/dist/cli/commands/mfe-publish.d.ts.map +1 -1
- package/dist/cli/commands/mfe-publish.js +10 -4
- package/dist/cli/commands/mfe-publish.js.map +1 -1
- package/dist/cli/commands/mfe-purge-cache.d.ts +57 -0
- package/dist/cli/commands/mfe-purge-cache.d.ts.map +1 -0
- package/dist/cli/commands/mfe-purge-cache.js +101 -0
- package/dist/cli/commands/mfe-purge-cache.js.map +1 -0
- package/dist/cli/commands/{utils.d.ts → utils/build-rollback-tag.d.ts} +1 -2
- package/dist/cli/commands/utils/build-rollback-tag.d.ts.map +1 -0
- package/dist/cli/commands/{utils.js → utils/build-rollback-tag.js} +1 -5
- package/dist/cli/commands/utils/build-rollback-tag.js.map +1 -0
- package/dist/cli/commands/utils/constants.d.ts +2 -0
- package/dist/cli/commands/utils/constants.d.ts.map +1 -0
- package/dist/cli/commands/utils/constants.js +13 -0
- package/dist/cli/commands/utils/constants.js.map +1 -0
- package/dist/cli/commands/utils/index.d.ts +4 -0
- package/dist/cli/commands/utils/index.d.ts.map +1 -0
- package/dist/cli/commands/utils/index.js +22 -0
- package/dist/cli/commands/utils/index.js.map +1 -0
- package/dist/cli/commands/utils/purge-cache.d.ts +6 -0
- package/dist/cli/commands/utils/purge-cache.d.ts.map +1 -0
- package/dist/cli/commands/utils/purge-cache.js +21 -0
- package/dist/cli/commands/utils/purge-cache.js.map +1 -0
- package/dist/cli/utils/cli-os.js +1 -1
- package/dist/cli/utils/cli-os.js.map +1 -1
- package/dist/cypress-config/index.d.ts +2 -0
- package/dist/cypress-config/index.d.ts.map +1 -0
- package/dist/cypress-config/index.js +20 -0
- package/dist/cypress-config/index.js.map +1 -0
- package/dist/cypress-config/webpack-config.d.ts +4 -0
- package/dist/cypress-config/webpack-config.d.ts.map +1 -0
- package/dist/cypress-config/webpack-config.js +76 -0
- package/dist/cypress-config/webpack-config.js.map +1 -0
- package/dist/storybook-config/swc.js +1 -1
- package/dist/storybook-config/swc.js.map +1 -1
- package/dist/utils/get-base-tsconfig.d.ts +2 -0
- package/dist/utils/get-base-tsconfig.d.ts.map +1 -0
- package/dist/utils/get-base-tsconfig.js +21 -0
- package/dist/utils/get-base-tsconfig.js.map +1 -0
- package/dist/utils/get-configuration.d.ts +1 -0
- package/dist/utils/get-configuration.d.ts.map +1 -1
- package/dist/utils/get-configuration.js +1 -0
- package/dist/utils/get-configuration.js.map +1 -1
- package/dist/utils/get-tsconfig-with-fallback.d.ts +6 -0
- package/dist/utils/get-tsconfig-with-fallback.d.ts.map +1 -0
- package/dist/utils/get-tsconfig-with-fallback.js +24 -0
- package/dist/utils/get-tsconfig-with-fallback.js.map +1 -0
- package/dist/utils/index.d.ts +2 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +2 -0
- package/dist/utils/index.js.map +1 -1
- package/package.json +23 -21
- package/src/cli/commands/__tests__/mfe-package-publish.test.ts +27 -1
- package/src/cli/commands/__tests__/mfe-package-rollback.test.ts +31 -0
- package/src/cli/commands/__tests__/mfe-publish.test.ts +4 -2
- package/src/cli/commands/__tests__/mfe-purge-cache.test.ts +141 -0
- package/src/cli/commands/get-command.ts +2 -0
- package/src/cli/commands/mfe-package-publish.ts +11 -1
- package/src/cli/commands/mfe-package-rollback.ts +15 -5
- package/src/cli/commands/mfe-publish.ts +14 -3
- package/src/cli/commands/mfe-purge-cache.ts +75 -0
- package/src/cli/commands/utils/__tests__/purge-cache.test.ts +40 -0
- package/src/cli/commands/{utils.ts → utils/build-rollback-tag.ts} +0 -1
- package/src/cli/commands/utils/constants.ts +1 -0
- package/src/cli/commands/utils/index.ts +3 -0
- package/src/cli/commands/utils/purge-cache.ts +13 -0
- package/src/cli/utils/cli-os.ts +1 -1
- package/src/cypress-config/__tests__/webpack-config.test.ts +124 -0
- package/src/cypress-config/index.ts +1 -0
- package/src/cypress-config/webpack-config.ts +58 -0
- package/src/storybook-config/__tests__/swc.test.ts +11 -0
- package/src/storybook-config/swc.ts +2 -2
- package/src/utils/__tests__/get-tsconfig-with-fallback.test.ts +23 -0
- package/src/utils/__tests__/get-tsconfig.test.ts +4 -4
- package/src/utils/get-base-tsconfig.ts +5 -0
- package/src/utils/get-configuration.ts +1 -0
- package/src/utils/get-tsconfig-with-fallback.ts +12 -0
- package/src/utils/index.ts +2 -0
- package/dist/cli/commands/utils.d.ts.map +0 -1
- package/dist/cli/commands/utils.js.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@servicetitan/startup",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "35.1.0",
|
|
4
4
|
"description": "CLI to create multi-package Lerna projects with TypeScript and React",
|
|
5
5
|
"homepage": "https://docs.st.dev/docs/frontend/uikit/startup",
|
|
6
6
|
"repository": {
|
|
@@ -9,8 +9,8 @@
|
|
|
9
9
|
"directory": "packages/startup"
|
|
10
10
|
},
|
|
11
11
|
"engines": {
|
|
12
|
-
"node": ">=
|
|
13
|
-
"npm": ">=
|
|
12
|
+
"node": ">=22.17",
|
|
13
|
+
"npm": ">=10"
|
|
14
14
|
},
|
|
15
15
|
"sideEffects": false,
|
|
16
16
|
"main": "./dist/index.js",
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
"default": "./dist/index.js",
|
|
21
21
|
"types": "./dist/index.d.ts"
|
|
22
22
|
},
|
|
23
|
+
"./cypress-config": "./dist/cypress-config/index.js",
|
|
23
24
|
"./jest-preset": "./jest/jest-preset.js",
|
|
24
25
|
"./jest-resolver": "./dist/jest/resolver.js",
|
|
25
26
|
"./jest-svg-transformer": "./dist/jest/svg-transformer.js",
|
|
@@ -53,19 +54,19 @@
|
|
|
53
54
|
},
|
|
54
55
|
"dependencies": {
|
|
55
56
|
"@babel/preset-env": "~7.29.0",
|
|
56
|
-
"@jest/core": "~30.
|
|
57
|
-
"@jest/types": "~30.
|
|
57
|
+
"@jest/core": "~30.3.0",
|
|
58
|
+
"@jest/types": "~30.3.0",
|
|
58
59
|
"@jsdevtools/coverage-istanbul-loader": "^3.0.5",
|
|
59
|
-
"@servicetitan/eslint-config": "
|
|
60
|
-
"@servicetitan/install": "
|
|
61
|
-
"@servicetitan/startup-utils": "
|
|
62
|
-
"@servicetitan/stylelint-config": "
|
|
60
|
+
"@servicetitan/eslint-config": "35.1.0",
|
|
61
|
+
"@servicetitan/install": "35.1.0",
|
|
62
|
+
"@servicetitan/startup-utils": "35.1.0",
|
|
63
|
+
"@servicetitan/stylelint-config": "35.1.0",
|
|
63
64
|
"@svgr/webpack": "^8.1.0",
|
|
64
65
|
"@swc/cli": "^0.5.0",
|
|
65
66
|
"@swc/core": "1.15.18",
|
|
66
67
|
"@types/debug": "^4.1.12",
|
|
67
68
|
"@types/jest": "~30.0.0",
|
|
68
|
-
"@vitest/coverage-v8": "^4.0
|
|
69
|
+
"@vitest/coverage-v8": "^4.1.0",
|
|
69
70
|
"chalk": "~4.1.2",
|
|
70
71
|
"cli-table3": "^0.6.5",
|
|
71
72
|
"cpx2": "8.0.0",
|
|
@@ -80,40 +81,41 @@
|
|
|
80
81
|
"html-webpack-plugin": "~5.6.6",
|
|
81
82
|
"html-webpack-tags-plugin": "^3.0.2",
|
|
82
83
|
"identity-obj-proxy": "~3.0.0",
|
|
83
|
-
"jest": "~30.
|
|
84
|
-
"jest-circus": "~30.
|
|
85
|
-
"jest-environment-jsdom": "^30.
|
|
84
|
+
"jest": "~30.3.0",
|
|
85
|
+
"jest-circus": "~30.3.0",
|
|
86
|
+
"jest-environment-jsdom": "^30.3.0",
|
|
86
87
|
"jest-fetch-mock": "~3.0.3",
|
|
87
88
|
"json5": "^2.2.3",
|
|
88
89
|
"lerna": "~9.0.5",
|
|
89
90
|
"less": "~4.5.1",
|
|
90
|
-
"less-loader": "~12.3.
|
|
91
|
+
"less-loader": "~12.3.2",
|
|
91
92
|
"less-plugin-npm-import": "~2.1.0",
|
|
92
93
|
"lodash.kebabcase": "^4.1.1",
|
|
93
94
|
"lodash.memoize": "^4.1.2",
|
|
94
|
-
"memfs": "~4.56.
|
|
95
|
-
"mini-css-extract-plugin": "~2.10.
|
|
95
|
+
"memfs": "~4.56.11",
|
|
96
|
+
"mini-css-extract-plugin": "~2.10.1",
|
|
96
97
|
"moment-locales-webpack-plugin": "~1.2.0",
|
|
97
98
|
"multimatch": "~8.0.0",
|
|
98
99
|
"patch-package": "^8.0.1",
|
|
99
100
|
"portfinder": "~1.0.38",
|
|
100
101
|
"postcss": "~8.5.8",
|
|
101
102
|
"prettier": "~3.8.1",
|
|
102
|
-
"sass": "~1.
|
|
103
|
+
"sass": "~1.98.0",
|
|
103
104
|
"sass-loader": "~16.0.7",
|
|
104
105
|
"semver": "~7.7.4",
|
|
105
106
|
"source-map-loader": "~5.0.0",
|
|
106
107
|
"style-loader": "~4.0.0",
|
|
107
108
|
"stylelint": "~16.26.1",
|
|
109
|
+
"swc-loader": "^0.2.7",
|
|
108
110
|
"terminal-link": "^5.0.0",
|
|
109
|
-
"terser-webpack-plugin": "^5.
|
|
111
|
+
"terser-webpack-plugin": "^5.4.0",
|
|
110
112
|
"ts-jest": "29.4.6",
|
|
111
113
|
"ts-node": "~10.9.2",
|
|
112
114
|
"typed-css-modules": "~0.9.1",
|
|
113
115
|
"typescript": "5.9.3",
|
|
114
|
-
"vitest": "^4.0
|
|
116
|
+
"vitest": "^4.1.0",
|
|
115
117
|
"webpack": "~5.105.4",
|
|
116
|
-
"webpack-assets-manifest": "~6.5.
|
|
118
|
+
"webpack-assets-manifest": "~6.5.1",
|
|
117
119
|
"webpack-bundle-analyzer": "^5.2.0",
|
|
118
120
|
"webpack-dev-server": "~5.2.3",
|
|
119
121
|
"webpack-filter-warnings-plugin": "~1.2.1",
|
|
@@ -144,5 +146,5 @@
|
|
|
144
146
|
"cli": {
|
|
145
147
|
"webpack": false
|
|
146
148
|
},
|
|
147
|
-
"gitHead": "
|
|
149
|
+
"gitHead": "f1d49b43f27789cf5cd4bea3e065cd9e33bf5876"
|
|
148
150
|
}
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
runCommand,
|
|
13
13
|
} from '../../utils';
|
|
14
14
|
import { MFEPackagePublish } from '../mfe-package-publish';
|
|
15
|
-
import { ROLLBACK_TAG_SUFFIX } from '../utils';
|
|
15
|
+
import { purgeCache, ROLLBACK_TAG_SUFFIX } from '../utils';
|
|
16
16
|
|
|
17
17
|
jest.mock('fs', () => fs);
|
|
18
18
|
|
|
@@ -32,6 +32,10 @@ jest.mock('../../utils', () => ({
|
|
|
32
32
|
npmView: jest.fn(),
|
|
33
33
|
runCommand: jest.fn(),
|
|
34
34
|
}));
|
|
35
|
+
jest.mock('../utils', () => ({
|
|
36
|
+
...jest.requireActual('../utils'),
|
|
37
|
+
purgeCache: jest.fn(),
|
|
38
|
+
}));
|
|
35
39
|
|
|
36
40
|
describe(`[startup] ${MFEPackagePublish.name}`, () => {
|
|
37
41
|
const defaultRegistry = 'https://verdaccio.servicetitan.com';
|
|
@@ -127,6 +131,12 @@ describe(`[startup] ${MFEPackagePublish.name}`, () => {
|
|
|
127
131
|
);
|
|
128
132
|
});
|
|
129
133
|
|
|
134
|
+
test('purges cache after publishing', async () => {
|
|
135
|
+
await subject();
|
|
136
|
+
|
|
137
|
+
expect(purgeCache).toHaveBeenCalledWith({ packageName, tag });
|
|
138
|
+
});
|
|
139
|
+
|
|
130
140
|
test('gets current tagged versions before publishing', async () => {
|
|
131
141
|
await subject();
|
|
132
142
|
|
|
@@ -196,6 +206,14 @@ describe(`[startup] ${MFEPackagePublish.name}`, () => {
|
|
|
196
206
|
});
|
|
197
207
|
});
|
|
198
208
|
|
|
209
|
+
function itDoesNotPurgeCache() {
|
|
210
|
+
test('does not purge cache', async () => {
|
|
211
|
+
await subject();
|
|
212
|
+
|
|
213
|
+
expect(purgeCache).not.toHaveBeenCalled();
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
|
|
199
217
|
describe('with "dry" argument', () => {
|
|
200
218
|
beforeEach(() => (args.dry = true));
|
|
201
219
|
|
|
@@ -239,6 +257,8 @@ describe(`[startup] ${MFEPackagePublish.name}`, () => {
|
|
|
239
257
|
`npx startup upload-sourcemaps --dry --releaseVersion=${buildPackageVersion()}`
|
|
240
258
|
);
|
|
241
259
|
});
|
|
260
|
+
|
|
261
|
+
itDoesNotPurgeCache();
|
|
242
262
|
});
|
|
243
263
|
|
|
244
264
|
function itDoesNotUploadSourcemaps() {
|
|
@@ -257,6 +277,12 @@ describe(`[startup] ${MFEPackagePublish.name}`, () => {
|
|
|
257
277
|
itDoesNotUploadSourcemaps();
|
|
258
278
|
});
|
|
259
279
|
|
|
280
|
+
describe('with "purgeCache: false"', () => {
|
|
281
|
+
beforeEach(() => (args.purgeCache = false));
|
|
282
|
+
|
|
283
|
+
itDoesNotPurgeCache();
|
|
284
|
+
});
|
|
285
|
+
|
|
260
286
|
describe('when branch configuration includes "uploadSourcemaps: false"', () => {
|
|
261
287
|
beforeEach(() =>
|
|
262
288
|
vol.fromJSON({
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { log, readJson } from '../../../utils';
|
|
2
2
|
import { npmTagVersion, npmView } from '../../utils';
|
|
3
3
|
import { MFEPackageRollback } from '../mfe-package-rollback';
|
|
4
|
+
import { purgeCache } from '../utils';
|
|
4
5
|
|
|
5
6
|
jest.mock('../../../utils', () => ({
|
|
6
7
|
...jest.requireActual('../../../utils'),
|
|
@@ -16,6 +17,10 @@ jest.mock('../../utils', () => ({
|
|
|
16
17
|
npmTagVersion: jest.fn(),
|
|
17
18
|
npmView: jest.fn(),
|
|
18
19
|
}));
|
|
20
|
+
jest.mock('../utils', () => ({
|
|
21
|
+
...jest.requireActual('../utils'),
|
|
22
|
+
purgeCache: jest.fn(),
|
|
23
|
+
}));
|
|
19
24
|
|
|
20
25
|
describe(`[startup] ${MFEPackageRollback.name}`, () => {
|
|
21
26
|
const registry = 'https://verdaccio.servicetitan.com';
|
|
@@ -98,6 +103,12 @@ describe(`[startup] ${MFEPackageRollback.name}`, () => {
|
|
|
98
103
|
});
|
|
99
104
|
});
|
|
100
105
|
|
|
106
|
+
test('purges cache after rollback', async () => {
|
|
107
|
+
await subject();
|
|
108
|
+
|
|
109
|
+
expect(purgeCache).toHaveBeenCalledWith({ packageName, tag });
|
|
110
|
+
});
|
|
111
|
+
|
|
101
112
|
describe('when package is not found in registry', () => {
|
|
102
113
|
beforeEach(() => {
|
|
103
114
|
jest.mocked(npmView).mockReturnValue(undefined);
|
|
@@ -114,6 +125,14 @@ describe(`[startup] ${MFEPackageRollback.name}`, () => {
|
|
|
114
125
|
itReportsError(`No rollback version found for tag "${tag}" (looking for "${rollbackTag}")`);
|
|
115
126
|
});
|
|
116
127
|
|
|
128
|
+
function itDoesNotPurgeCache() {
|
|
129
|
+
test('does not purge cache', async () => {
|
|
130
|
+
await subject();
|
|
131
|
+
|
|
132
|
+
expect(purgeCache).not.toHaveBeenCalled();
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
|
|
117
136
|
describe('when tag and rollback tag both point to the same version', () => {
|
|
118
137
|
beforeEach(() => {
|
|
119
138
|
packageInfo['dist-tags'] = {
|
|
@@ -130,6 +149,8 @@ describe(`[startup] ${MFEPackageRollback.name}`, () => {
|
|
|
130
149
|
);
|
|
131
150
|
expect(npmTagVersion).not.toHaveBeenCalled();
|
|
132
151
|
});
|
|
152
|
+
|
|
153
|
+
itDoesNotPurgeCache();
|
|
133
154
|
});
|
|
134
155
|
|
|
135
156
|
describe('when tag does not exist', () => {
|
|
@@ -190,5 +211,15 @@ describe(`[startup] ${MFEPackageRollback.name}`, () => {
|
|
|
190
211
|
)
|
|
191
212
|
);
|
|
192
213
|
});
|
|
214
|
+
|
|
215
|
+
itDoesNotPurgeCache();
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
describe('with purgeCache: false', () => {
|
|
219
|
+
beforeEach(() => {
|
|
220
|
+
args.purgeCache = false;
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
itDoesNotPurgeCache();
|
|
193
224
|
});
|
|
194
225
|
});
|
|
@@ -139,11 +139,13 @@ describe(`[startup] ${MFEPublish.name}`, () => {
|
|
|
139
139
|
const testArgs: { name: ArgumentName; value: any; expected?: string | string[] }[] = [
|
|
140
140
|
{ name: 'build', value: true },
|
|
141
141
|
{ name: 'branch', value: 'foo-123' },
|
|
142
|
-
{ name: 'tag', value: 'foo' },
|
|
143
|
-
{ name: 'tag', value: true, expected: [] }, // check ignores obsolete --tag with no name
|
|
144
142
|
{ name: 'dry', value: true, expected: '--dry' },
|
|
145
143
|
{ name: 'force', value: true, expected: '--force' },
|
|
144
|
+
{ name: 'purgeCache', value: true, expected: '--purge-cache' },
|
|
145
|
+
{ name: 'purgeCache', value: false, expected: '--no-purge-cache' },
|
|
146
146
|
{ name: 'registry', value: 'https://foo' },
|
|
147
|
+
{ name: 'tag', value: 'foo' },
|
|
148
|
+
{ name: 'tag', value: true, expected: [] }, // check ignores obsolete --tag with no name
|
|
147
149
|
{ name: 'trackHistory', value: true, expected: '--track-history' },
|
|
148
150
|
{ name: 'trackHistory', value: false, expected: '--no-track-history' },
|
|
149
151
|
{ name: 'uploadSourcemaps', value: true, expected: '--upload-sourcemaps' },
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { log } from '../../../utils';
|
|
2
|
+
import { MFEPurgeCache, PurgeResponse } from '../mfe-purge-cache';
|
|
3
|
+
|
|
4
|
+
jest.mock('../../../utils', () => ({
|
|
5
|
+
...jest.requireActual('../../../utils'),
|
|
6
|
+
log: { debug: jest.fn(), info: jest.fn(), warning: jest.fn() },
|
|
7
|
+
}));
|
|
8
|
+
|
|
9
|
+
describe(`[startup] ${MFEPurgeCache.name}`, () => {
|
|
10
|
+
const host = 'https://unpkg.example.com';
|
|
11
|
+
const token = 'purge-token';
|
|
12
|
+
const tag = 'prod';
|
|
13
|
+
const packageName = '@servicetitan/foo';
|
|
14
|
+
const version = '0.0.0-master.abc1223';
|
|
15
|
+
const location = `/${packageName}@${version}/dist/metadata.json`;
|
|
16
|
+
|
|
17
|
+
let args: ConstructorParameters<typeof MFEPurgeCache>[0];
|
|
18
|
+
let fetchSpy: jest.SpyInstance;
|
|
19
|
+
let fetchResponse: any;
|
|
20
|
+
let responseJson: PurgeResponse;
|
|
21
|
+
|
|
22
|
+
beforeEach(() => {
|
|
23
|
+
process.env.UNPKG_PURGE_TOKEN = token;
|
|
24
|
+
args = { host, packageName, tag };
|
|
25
|
+
fetchResponse = {
|
|
26
|
+
ok: true,
|
|
27
|
+
json: jest.fn().mockImplementation(() => Promise.resolve(responseJson)),
|
|
28
|
+
};
|
|
29
|
+
responseJson = [{ pod: '10.36.110.200', status: 302, cache: 'BYPASS', location }];
|
|
30
|
+
|
|
31
|
+
jest.clearAllMocks();
|
|
32
|
+
jest.spyOn(process.stdout, 'write').mockImplementation(jest.fn()); // suppress error output
|
|
33
|
+
fetchSpy = jest.spyOn(global, 'fetch').mockImplementation(() => fetchResponse);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
afterEach(() => delete process.env.UNPKG_PURGE_TOKEN);
|
|
37
|
+
|
|
38
|
+
const subject = () => new MFEPurgeCache(args).execute();
|
|
39
|
+
|
|
40
|
+
test('sends POST request with X-Purge-Token header', async () => {
|
|
41
|
+
await subject();
|
|
42
|
+
|
|
43
|
+
expect(fetchSpy).toHaveBeenCalledWith(
|
|
44
|
+
`${host}/_purge/${packageName}@${tag}/dist/metadata.json`,
|
|
45
|
+
{ method: 'POST', headers: { 'X-Purge-Token': token } }
|
|
46
|
+
);
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
test('logs progress', async () => {
|
|
50
|
+
await subject();
|
|
51
|
+
|
|
52
|
+
expect(log.info).toHaveBeenCalledWith(`Purging cache for ${packageName}@${tag}`);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('logs the redirect location from the response', async () => {
|
|
56
|
+
await subject();
|
|
57
|
+
|
|
58
|
+
expect(log.info).toHaveBeenCalledWith(
|
|
59
|
+
`Cache purged: ${packageName}@${tag}/dist/metadata.json -> ${location.replace(/^\//, '')}`
|
|
60
|
+
);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe('when request fails', () => {
|
|
64
|
+
beforeEach(() => {
|
|
65
|
+
fetchResponse = { ok: false, status: 403, statusText: 'Forbidden' };
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
test('throws error', () => {
|
|
69
|
+
expect(subject).rejects.toThrow(
|
|
70
|
+
`Purge request failed: ${fetchResponse.status} ${fetchResponse.statusText}`
|
|
71
|
+
);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
function itLogsNullLocation() {
|
|
76
|
+
test('logs null location', async () => {
|
|
77
|
+
await subject();
|
|
78
|
+
|
|
79
|
+
expect(log.info).toHaveBeenCalledWith(expect.stringMatching(/-> null$/));
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
describe('when response location is null', () => {
|
|
84
|
+
beforeEach(() => (responseJson[0].location = null));
|
|
85
|
+
|
|
86
|
+
itLogsNullLocation();
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe('when response is empty', () => {
|
|
90
|
+
beforeEach(() => (responseJson = []));
|
|
91
|
+
|
|
92
|
+
itLogsNullLocation();
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
describe('when response is unexpected', () => {
|
|
96
|
+
beforeEach(() => (responseJson = {} as any));
|
|
97
|
+
|
|
98
|
+
itLogsNullLocation();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
describe('when host has trailing slash', () => {
|
|
102
|
+
beforeEach(() => {
|
|
103
|
+
args.host = `${host}/`;
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
test('strips trailing slash from host', async () => {
|
|
107
|
+
await subject();
|
|
108
|
+
|
|
109
|
+
expect(fetchSpy).toHaveBeenCalledWith(
|
|
110
|
+
`${host}/_purge/${packageName}@${tag}/dist/metadata.json`,
|
|
111
|
+
expect.anything()
|
|
112
|
+
);
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
describe('with --token', () => {
|
|
117
|
+
const cliToken = 'cli-purge-token';
|
|
118
|
+
|
|
119
|
+
beforeEach(() => (args.token = cliToken));
|
|
120
|
+
|
|
121
|
+
test('uses specified token', async () => {
|
|
122
|
+
await subject();
|
|
123
|
+
|
|
124
|
+
expect(fetchSpy).toHaveBeenCalledWith(
|
|
125
|
+
expect.anything(),
|
|
126
|
+
expect.objectContaining({ headers: { 'X-Purge-Token': cliToken } })
|
|
127
|
+
);
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
describe('with no token', () => {
|
|
132
|
+
beforeEach(() => delete process.env.UNPKG_PURGE_TOKEN);
|
|
133
|
+
|
|
134
|
+
test('logs warning and does nothing', async () => {
|
|
135
|
+
await subject();
|
|
136
|
+
|
|
137
|
+
expect(log.warning).toHaveBeenCalledWith('Token is not defined, skipping purge');
|
|
138
|
+
expect(fetchSpy).not.toHaveBeenCalled();
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
});
|
|
@@ -15,6 +15,7 @@ import { MFEPackageClean } from './mfe-package-clean';
|
|
|
15
15
|
import { MFEPackagePublish } from './mfe-package-publish';
|
|
16
16
|
import { MFEPackageRollback } from './mfe-package-rollback';
|
|
17
17
|
import { MFEPublish } from './mfe-publish';
|
|
18
|
+
import { MFEPurgeCache } from './mfe-purge-cache';
|
|
18
19
|
import { PreparePackage } from './prepare-package';
|
|
19
20
|
import { Review } from './review';
|
|
20
21
|
import { RunTask } from './run-task';
|
|
@@ -42,6 +43,7 @@ const commands: Record<CommandName, Newable<Command>> = {
|
|
|
42
43
|
[CommandName['mfe-package-publish']]: MFEPackagePublish,
|
|
43
44
|
[CommandName['mfe-package-rollback']]: MFEPackageRollback,
|
|
44
45
|
[CommandName['mfe-publish']]: MFEPublish,
|
|
46
|
+
[CommandName['mfe-purge-cache']]: MFEPurgeCache,
|
|
45
47
|
[CommandName['prepare-package']]: PreparePackage,
|
|
46
48
|
[CommandName.review]: Review,
|
|
47
49
|
[CommandName.start]: Start,
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
runCommand,
|
|
23
23
|
} from '../utils';
|
|
24
24
|
import { Command, CommandOptions } from './types';
|
|
25
|
-
import { buildRollbackTag,
|
|
25
|
+
import { DEFAULT_MFE_REGISTRY, buildRollbackTag, purgeCache } from './utils';
|
|
26
26
|
|
|
27
27
|
export const mfePackagePublishOptions = {
|
|
28
28
|
branch: {
|
|
@@ -59,6 +59,11 @@ export const mfePackagePublishOptions = {
|
|
|
59
59
|
description: 'Upload source maps to Datadog?',
|
|
60
60
|
defaultDescription: 'true',
|
|
61
61
|
},
|
|
62
|
+
purgeCache: {
|
|
63
|
+
boolean: true,
|
|
64
|
+
description: 'Purge unpkg cache after publishing?',
|
|
65
|
+
defaultDescription: 'true',
|
|
66
|
+
},
|
|
62
67
|
} satisfies CommandOptions;
|
|
63
68
|
|
|
64
69
|
interface PublishData {
|
|
@@ -100,6 +105,11 @@ export class MFEPackagePublish extends Command<typeof mfePackagePublishOptions>
|
|
|
100
105
|
|
|
101
106
|
await this.maybePublishPackage(packageJson, data);
|
|
102
107
|
|
|
108
|
+
const { dry, tag } = data;
|
|
109
|
+
if (this.args.purgeCache !== false && !dry) {
|
|
110
|
+
purgeCache({ packageName: packageJson.name, tag });
|
|
111
|
+
}
|
|
112
|
+
|
|
103
113
|
if (data.uploadSourcemaps) {
|
|
104
114
|
this.uploadSourcemaps(data);
|
|
105
115
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { log, logErrors, readJson } from '../../utils';
|
|
2
2
|
import { DRY_RUN_PREFIX, npmTagVersion, npmView } from '../utils';
|
|
3
3
|
import { Command, CommandOptions } from './types';
|
|
4
|
-
import { buildRollbackTag,
|
|
4
|
+
import { DEFAULT_MFE_REGISTRY, buildRollbackTag, purgeCache } from './utils';
|
|
5
5
|
|
|
6
6
|
export const mfePackageRollbackOptions = {
|
|
7
7
|
dry: {
|
|
@@ -18,6 +18,11 @@ export const mfePackageRollbackOptions = {
|
|
|
18
18
|
description: 'The tag to rollback to its previous version',
|
|
19
19
|
required: true,
|
|
20
20
|
},
|
|
21
|
+
purgeCache: {
|
|
22
|
+
boolean: true,
|
|
23
|
+
description: 'Purge unpkg cache after rolling back?',
|
|
24
|
+
defaultDescription: 'true',
|
|
25
|
+
},
|
|
21
26
|
} satisfies CommandOptions;
|
|
22
27
|
|
|
23
28
|
interface PackageJson {
|
|
@@ -25,8 +30,6 @@ interface PackageJson {
|
|
|
25
30
|
}
|
|
26
31
|
|
|
27
32
|
export class MFEPackageRollback extends Command<typeof mfePackageRollbackOptions> {
|
|
28
|
-
static readonly description = 'Rollback a package to a previous tagged version';
|
|
29
|
-
|
|
30
33
|
static readonly options = mfePackageRollbackOptions;
|
|
31
34
|
|
|
32
35
|
private get dryRunPrefix() {
|
|
@@ -35,11 +38,16 @@ export class MFEPackageRollback extends Command<typeof mfePackageRollbackOptions
|
|
|
35
38
|
|
|
36
39
|
@logErrors
|
|
37
40
|
async execute() {
|
|
38
|
-
|
|
41
|
+
const { dry, tag } = this.args;
|
|
42
|
+
if (dry) {
|
|
39
43
|
log.warning('DRY-RUN MODE ENABLED, WILL NOT PERFORM ROLLBACK');
|
|
40
44
|
}
|
|
41
45
|
|
|
42
|
-
await this.handleRollback();
|
|
46
|
+
const packageName = await this.handleRollback();
|
|
47
|
+
|
|
48
|
+
if (packageName && this.args.purgeCache !== false && !dry) {
|
|
49
|
+
purgeCache({ packageName, tag });
|
|
50
|
+
}
|
|
43
51
|
}
|
|
44
52
|
|
|
45
53
|
private async handleRollback() {
|
|
@@ -96,5 +104,7 @@ export class MFEPackageRollback extends Command<typeof mfePackageRollbackOptions
|
|
|
96
104
|
tag,
|
|
97
105
|
});
|
|
98
106
|
}
|
|
107
|
+
|
|
108
|
+
return packageName;
|
|
99
109
|
}
|
|
100
110
|
}
|
|
@@ -111,17 +111,28 @@ export class MFEPublish extends Command<MFEPublishOptions> {
|
|
|
111
111
|
}
|
|
112
112
|
|
|
113
113
|
getPublishOptions() {
|
|
114
|
-
const {
|
|
115
|
-
|
|
114
|
+
const {
|
|
115
|
+
build,
|
|
116
|
+
branch,
|
|
117
|
+
dry,
|
|
118
|
+
force,
|
|
119
|
+
purgeCache,
|
|
120
|
+
registry,
|
|
121
|
+
tag,
|
|
122
|
+
uploadSourcemaps,
|
|
123
|
+
trackHistory,
|
|
124
|
+
} = this.args;
|
|
116
125
|
return [
|
|
117
126
|
...[branch && `--branch ${branch}`],
|
|
118
127
|
...[build && `--build ${build}`],
|
|
119
128
|
...[dry && '--dry'],
|
|
120
129
|
...[force && '--force'],
|
|
130
|
+
...[purgeCache === true && '--purge-cache'],
|
|
131
|
+
...[purgeCache === false && '--no-purge-cache'],
|
|
121
132
|
...[registry && `--registry ${registry}`],
|
|
133
|
+
...[typeof tag === 'string' && `--tag ${tag}`],
|
|
122
134
|
...[trackHistory === true && '--track-history'],
|
|
123
135
|
...[trackHistory === false && '--no-track-history'],
|
|
124
|
-
...[typeof tag === 'string' && `--tag ${tag}`],
|
|
125
136
|
...[uploadSourcemaps === true && `--upload-sourcemaps`],
|
|
126
137
|
...[uploadSourcemaps === false && `--no-upload-sourcemaps`],
|
|
127
138
|
].filter(item => !!item) as string[];
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { log, logErrors } from '../../utils';
|
|
2
|
+
import { Command, CommandOptions } from './types';
|
|
3
|
+
|
|
4
|
+
const mfePurgeCacheOptions = {
|
|
5
|
+
host: {
|
|
6
|
+
string: true,
|
|
7
|
+
description: 'Host URL to send purge requests to',
|
|
8
|
+
demandOption: true,
|
|
9
|
+
},
|
|
10
|
+
packageName: {
|
|
11
|
+
string: true,
|
|
12
|
+
description: 'Package name to purge',
|
|
13
|
+
demandOption: true,
|
|
14
|
+
},
|
|
15
|
+
tag: {
|
|
16
|
+
string: true,
|
|
17
|
+
description: 'Package tag to purge',
|
|
18
|
+
demandOption: true,
|
|
19
|
+
},
|
|
20
|
+
token: {
|
|
21
|
+
string: true,
|
|
22
|
+
description: 'X-Purge-Token header value',
|
|
23
|
+
defaultDescription: 'process.env.UNPKG_PURGE_TOKEN',
|
|
24
|
+
},
|
|
25
|
+
} satisfies CommandOptions;
|
|
26
|
+
|
|
27
|
+
export type PurgeResponse = {
|
|
28
|
+
pod: string;
|
|
29
|
+
status: number;
|
|
30
|
+
cache: string | null;
|
|
31
|
+
location: string | null;
|
|
32
|
+
}[];
|
|
33
|
+
|
|
34
|
+
export class MFEPurgeCache extends Command<typeof mfePurgeCacheOptions> {
|
|
35
|
+
static readonly options = mfePurgeCacheOptions;
|
|
36
|
+
|
|
37
|
+
@logErrors
|
|
38
|
+
async execute() {
|
|
39
|
+
const { host: rawHost, packageName, tag } = this.args;
|
|
40
|
+
const host = rawHost.replace(/\/$/, '');
|
|
41
|
+
const token = this.args.token ?? process.env.UNPKG_PURGE_TOKEN;
|
|
42
|
+
const packageNameAndTag = `${packageName}@${tag}`;
|
|
43
|
+
const path = `${packageNameAndTag}/dist/metadata.json`;
|
|
44
|
+
|
|
45
|
+
log.info(`Purging cache for ${packageNameAndTag}`);
|
|
46
|
+
|
|
47
|
+
if (!token) {
|
|
48
|
+
log.warning('Token is not defined, skipping purge');
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const response = await fetch(`${host}/_purge/${path}`, {
|
|
53
|
+
method: 'POST',
|
|
54
|
+
headers: { 'X-Purge-Token': token },
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
if (!response.ok) {
|
|
58
|
+
throw new Error(`Purge request failed: ${response.status} ${response.statusText}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const responseJson = await response.json();
|
|
62
|
+
/* istanbul ignore next: debug only */
|
|
63
|
+
log.debug('mfe-purge-cache', () => JSON.stringify(responseJson, null, 2));
|
|
64
|
+
|
|
65
|
+
log.info(`Cache purged: ${path} -> ${this.getLocation(responseJson)}`);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
private getLocation(responseJson: PurgeResponse) {
|
|
69
|
+
let location: string | null = null;
|
|
70
|
+
if (Array.isArray(responseJson)) {
|
|
71
|
+
location = responseJson[0]?.location?.replace(/^\//, '') ?? null;
|
|
72
|
+
}
|
|
73
|
+
return location;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { runCommandOutput } from '../../../utils';
|
|
2
|
+
import { purgeCache } from '../purge-cache';
|
|
3
|
+
|
|
4
|
+
jest.mock('../../../utils', () => ({
|
|
5
|
+
...jest.requireActual('../../../utils'),
|
|
6
|
+
runCommandOutput: jest.fn(),
|
|
7
|
+
}));
|
|
8
|
+
|
|
9
|
+
describe(purgeCache.name, () => {
|
|
10
|
+
let args: Parameters<typeof purgeCache>[0];
|
|
11
|
+
const packageName = '@servicetitan/foo';
|
|
12
|
+
const tag = 'prod';
|
|
13
|
+
|
|
14
|
+
beforeEach(() => {
|
|
15
|
+
jest.clearAllMocks();
|
|
16
|
+
args = { packageName, tag };
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
const subject = () => purgeCache(args);
|
|
20
|
+
|
|
21
|
+
test('runs mfe-purge-cache with host "https://unpkg.servicetitan.com", package name and tag', () => {
|
|
22
|
+
subject();
|
|
23
|
+
|
|
24
|
+
expect(runCommandOutput).toHaveBeenCalledWith(
|
|
25
|
+
`npx startup mfe-purge-cache --host https://unpkg.servicetitan.com --package-name ${packageName} --tag ${tag}`
|
|
26
|
+
);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe('when registry is "https://verdaccio.st.dev"', () => {
|
|
30
|
+
beforeEach(() => (args.registry = 'https://verdaccio.st.dev'));
|
|
31
|
+
|
|
32
|
+
test('runs mfe-purge-cache with host "https://unpkg.st.dev"', () => {
|
|
33
|
+
subject();
|
|
34
|
+
|
|
35
|
+
expect(runCommandOutput).toHaveBeenCalledWith(
|
|
36
|
+
expect.stringContaining(`--host https://unpkg.st.dev`)
|
|
37
|
+
);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const DEFAULT_MFE_REGISTRY = 'https://verdaccio.servicetitan.com';
|