@servicetitan/startup 31.6.0 → 32.0.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/dist/cli/commands/init.d.ts +2 -1
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +24 -43
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/install.d.ts +4 -0
- package/dist/cli/commands/install.d.ts.map +1 -1
- package/dist/cli/commands/install.js +91 -3
- package/dist/cli/commands/install.js.map +1 -1
- package/dist/cli/commands/mfe-package-clean.d.ts.map +1 -1
- package/dist/cli/commands/mfe-package-clean.js +5 -7
- package/dist/cli/commands/mfe-package-clean.js.map +1 -1
- package/dist/cli/commands/mfe-package-publish.d.ts.map +1 -1
- package/dist/cli/commands/mfe-package-publish.js +11 -15
- package/dist/cli/commands/mfe-package-publish.js.map +1 -1
- package/dist/cli/commands/upload-sourcemaps.d.ts.map +1 -1
- package/dist/cli/commands/upload-sourcemaps.js +1 -1
- package/dist/cli/commands/upload-sourcemaps.js.map +1 -1
- package/dist/cli/index.js +1 -2
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/utils/cli-git.d.ts +11 -2
- package/dist/cli/utils/cli-git.d.ts.map +1 -1
- package/dist/cli/utils/cli-git.js +60 -4
- package/dist/cli/utils/cli-git.js.map +1 -1
- package/dist/cli/utils/cli-os.d.ts.map +1 -1
- package/dist/cli/utils/cli-os.js +3 -0
- package/dist/cli/utils/cli-os.js.map +1 -1
- package/dist/cli/utils/index.d.ts +6 -0
- package/dist/cli/utils/index.d.ts.map +1 -1
- package/dist/cli/utils/index.js +6 -0
- package/dist/cli/utils/index.js.map +1 -1
- package/dist/cli/utils/is-ci.d.ts +2 -0
- package/dist/cli/utils/is-ci.d.ts.map +1 -0
- package/dist/cli/utils/is-ci.js +15 -0
- package/dist/cli/utils/is-ci.js.map +1 -0
- package/dist/cli/utils/lerna-exec.d.ts.map +1 -1
- package/dist/cli/utils/lerna-exec.js +2 -1
- package/dist/cli/utils/lerna-exec.js.map +1 -1
- package/dist/utils/get-branch-configs.d.ts +1 -1
- package/dist/utils/get-branch-configs.d.ts.map +1 -1
- package/dist/utils/get-branch-configs.js +2 -2
- package/dist/utils/get-branch-configs.js.map +1 -1
- package/dist/utils/index.d.ts +1 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +1 -0
- package/dist/utils/index.js.map +1 -1
- package/package.json +6 -6
- package/src/cli/commands/__tests__/init.test.ts +21 -87
- package/src/cli/commands/__tests__/install.test.ts +174 -12
- package/src/cli/commands/__tests__/mfe-package-clean.test.ts +3 -6
- package/src/cli/commands/__tests__/mfe-package-publish.test.ts +6 -8
- package/src/cli/commands/__tests__/upload-sourcemaps.test.ts +7 -3
- package/src/cli/commands/init.ts +17 -37
- package/src/cli/commands/install.ts +95 -6
- package/src/cli/commands/mfe-package-clean.ts +2 -4
- package/src/cli/commands/mfe-package-publish.ts +18 -6
- package/src/cli/commands/upload-sourcemaps.ts +2 -2
- package/src/cli/index.ts +1 -2
- package/src/cli/utils/__tests__/cli-git.test.ts +142 -6
- package/src/cli/utils/__tests__/eslint.test.ts +3 -2
- package/src/cli/utils/__tests__/is-ci.test.ts +40 -0
- package/src/cli/utils/__tests__/lerna-exec.test.ts +6 -3
- package/src/cli/utils/cli-git.ts +55 -5
- package/src/cli/utils/cli-os.ts +4 -1
- package/src/cli/utils/index.ts +6 -0
- package/src/cli/utils/is-ci.ts +3 -0
- package/src/cli/utils/lerna-exec.ts +2 -1
- package/src/utils/get-branch-configs.ts +1 -1
- package/src/utils/index.ts +1 -0
|
@@ -2,40 +2,38 @@ import { fs, vol } from 'memfs';
|
|
|
2
2
|
import { mkdirSync, rmSync } from 'fs';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
|
|
5
|
+
import { log } from '../../../utils';
|
|
6
|
+
import { gitCloneRepo, gitIsReachable } from '../../utils';
|
|
5
7
|
import { Init } from '../init';
|
|
6
|
-
import { runCommand, runCommandOutput } from '../../utils/cli-os';
|
|
7
8
|
|
|
8
9
|
jest.mock('fs', () => fs);
|
|
9
|
-
jest.mock('../../utils
|
|
10
|
+
jest.mock('../../utils', () => ({
|
|
11
|
+
gitCloneRepo: jest.fn(),
|
|
12
|
+
gitIsReachable: jest.fn(),
|
|
13
|
+
}));
|
|
10
14
|
jest.mock('../../../utils', () => ({
|
|
11
|
-
log: {
|
|
15
|
+
log: { debug: jest.fn(), error: jest.fn(), info: jest.fn() },
|
|
12
16
|
}));
|
|
13
17
|
|
|
14
|
-
const webUrl = 'https://github.com/servicetitan/frontend-example.git';
|
|
15
|
-
const sshUrl = 'git@github.com:servicetitan/frontend-example.git';
|
|
16
|
-
|
|
17
18
|
describe(`[startup] ${Init.name}`, () => {
|
|
18
19
|
let args: ConstructorParameters<typeof Init>[0];
|
|
19
20
|
|
|
20
21
|
beforeEach(() => {
|
|
21
22
|
args = {};
|
|
22
23
|
vol.reset();
|
|
23
|
-
jest.
|
|
24
|
-
jest.mocked(runCommand).mockImplementation(jest.fn());
|
|
25
|
-
jest.mocked(runCommandOutput).mockImplementation(jest.fn());
|
|
24
|
+
jest.mocked(gitCloneRepo).mockResolvedValue(true);
|
|
26
25
|
jest.spyOn(fs, 'mkdirSync').mockImplementation(jest.fn());
|
|
27
26
|
jest.spyOn(fs, 'rmSync').mockImplementation(jest.fn());
|
|
27
|
+
jest.spyOn(log, 'error').mockImplementation(jest.fn()); // suppress error output
|
|
28
28
|
});
|
|
29
29
|
|
|
30
30
|
const subject = async () => new Init(args).execute();
|
|
31
31
|
|
|
32
|
-
test(
|
|
32
|
+
test('clones "frontend-example" to current directory', async () => {
|
|
33
33
|
const cwd = path.resolve('.');
|
|
34
34
|
await subject();
|
|
35
35
|
|
|
36
|
-
expect(
|
|
37
|
-
quiet: true,
|
|
38
|
-
});
|
|
36
|
+
expect(gitCloneRepo).toHaveBeenCalledWith({ destination: cwd, name: 'frontend-example' });
|
|
39
37
|
expect(rmSync).toHaveBeenCalledWith(path.join(cwd, '.git'), {
|
|
40
38
|
recursive: true,
|
|
41
39
|
force: true,
|
|
@@ -44,59 +42,18 @@ describe(`[startup] ${Init.name}`, () => {
|
|
|
44
42
|
expect(rmSync).toHaveBeenCalledWith(path.join(cwd, 'package-lock.json'));
|
|
45
43
|
});
|
|
46
44
|
|
|
47
|
-
describe(
|
|
48
|
-
beforeEach(() => jest.mocked(
|
|
49
|
-
|
|
50
|
-
test(`clones ${sshUrl} to current directory`, async () => {
|
|
51
|
-
await subject();
|
|
52
|
-
|
|
53
|
-
expect(runCommand).toHaveBeenCalledWith(
|
|
54
|
-
`git clone -q ${sshUrl} ${path.resolve('.')}`,
|
|
55
|
-
expect.anything()
|
|
56
|
-
);
|
|
57
|
-
});
|
|
45
|
+
describe('when cloning fails', () => {
|
|
46
|
+
beforeEach(() => jest.mocked(gitCloneRepo).mockResolvedValue(false));
|
|
58
47
|
|
|
59
|
-
describe(
|
|
48
|
+
describe('when repo is not reachable', () => {
|
|
60
49
|
beforeEach(() => {
|
|
61
|
-
jest.mocked(
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
test(`checks if ${webUrl} is reachable`, async () => {
|
|
65
|
-
await subject();
|
|
66
|
-
|
|
67
|
-
expect(runCommandOutput).toHaveBeenCalledWith(`git ls-remote -qt ${webUrl}`, {
|
|
68
|
-
quiet: true,
|
|
69
|
-
});
|
|
50
|
+
jest.mocked(gitIsReachable).mockReturnValue(false);
|
|
70
51
|
});
|
|
71
52
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
|
|
79
|
-
test(`checks if ${sshUrl} is reachable`, async () => {
|
|
80
|
-
await subject();
|
|
81
|
-
|
|
82
|
-
expect(runCommandOutput).toHaveBeenCalledWith(`git ls-remote -qt ${sshUrl}`, {
|
|
83
|
-
quiet: true,
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
describe(`when ${sshUrl} is also unreachable`, () => {
|
|
88
|
-
beforeEach(() =>
|
|
89
|
-
jest.mocked(runCommandOutput).mockImplementation(() => {
|
|
90
|
-
throw new Error('Oops');
|
|
91
|
-
})
|
|
92
|
-
);
|
|
93
|
-
|
|
94
|
-
test('raises error', async () => {
|
|
95
|
-
await expect(subject()).rejects.toThrow(
|
|
96
|
-
/could not read servicetitan\/frontend-example repository/
|
|
97
|
-
);
|
|
98
|
-
});
|
|
99
|
-
});
|
|
53
|
+
test('raises error', async () => {
|
|
54
|
+
await expect(subject()).rejects.toThrow(
|
|
55
|
+
/could not read servicetitan\/frontend-example repository/
|
|
56
|
+
);
|
|
100
57
|
});
|
|
101
58
|
});
|
|
102
59
|
});
|
|
@@ -104,15 +61,13 @@ describe(`[startup] ${Init.name}`, () => {
|
|
|
104
61
|
describe('with an output location', () => {
|
|
105
62
|
beforeEach(() => (args.output = 'foo/bar'));
|
|
106
63
|
|
|
107
|
-
test(
|
|
64
|
+
test('clones repo to output location', async () => {
|
|
108
65
|
const destination = path.resolve(args.output!);
|
|
109
66
|
|
|
110
67
|
await subject();
|
|
111
68
|
|
|
112
69
|
expect(mkdirSync).toHaveBeenCalledWith(destination, { recursive: true });
|
|
113
|
-
expect(
|
|
114
|
-
quiet: true,
|
|
115
|
-
});
|
|
70
|
+
expect(gitCloneRepo).toHaveBeenCalledWith(expect.objectContaining({ destination }));
|
|
116
71
|
});
|
|
117
72
|
|
|
118
73
|
describe('when output location is a file', () => {
|
|
@@ -131,25 +86,4 @@ describe(`[startup] ${Init.name}`, () => {
|
|
|
131
86
|
});
|
|
132
87
|
});
|
|
133
88
|
});
|
|
134
|
-
|
|
135
|
-
describe('when running in CI environment with GITHUB_TOKEN', () => {
|
|
136
|
-
const originalEnv = process.env;
|
|
137
|
-
const token = 'foo-bar';
|
|
138
|
-
|
|
139
|
-
beforeEach(() => {
|
|
140
|
-
process.env.CI = 'true';
|
|
141
|
-
process.env.GITHUB_TOKEN = token;
|
|
142
|
-
});
|
|
143
|
-
afterEach(() => (process.env = originalEnv));
|
|
144
|
-
|
|
145
|
-
test(`adds token to ${webUrl}`, async () => {
|
|
146
|
-
await subject();
|
|
147
|
-
|
|
148
|
-
const urlWithToken = webUrl.replace('github.com', `oauth2:${token}@github.com`);
|
|
149
|
-
expect(runCommand).toHaveBeenCalledWith(
|
|
150
|
-
`git clone -q ${urlWithToken} ${path.resolve('.')}`,
|
|
151
|
-
expect.anything()
|
|
152
|
-
);
|
|
153
|
-
});
|
|
154
|
-
});
|
|
155
89
|
});
|
|
@@ -1,33 +1,92 @@
|
|
|
1
|
+
import { vol, fs } from 'memfs';
|
|
1
2
|
import { execSync } from 'child_process';
|
|
2
|
-
import { log } from '../../../utils';
|
|
3
|
+
import { getStartupVersion, log } from '../../../utils';
|
|
4
|
+
import { gitCloneRepo, isCI } from '../../utils';
|
|
3
5
|
|
|
4
6
|
import { Install } from '../install';
|
|
5
7
|
|
|
8
|
+
jest.mock('fs', () => fs);
|
|
6
9
|
jest.mock('child_process', () => ({ execSync: jest.fn() }));
|
|
10
|
+
jest.mock('../../utils', () => ({
|
|
11
|
+
...jest.requireActual('../../utils'),
|
|
12
|
+
gitCloneRepo: jest.fn(),
|
|
13
|
+
isCI: jest.fn(),
|
|
14
|
+
}));
|
|
7
15
|
jest.mock('../../../utils', () => ({
|
|
8
16
|
...jest.requireActual('../../../utils'),
|
|
9
|
-
|
|
17
|
+
getStartupVersion: jest.fn(),
|
|
18
|
+
log: { debug: jest.fn(), info: jest.fn(), warning: jest.fn() }, // suppress log output
|
|
10
19
|
}));
|
|
11
20
|
|
|
12
21
|
describe(`${Install.name}`, () => {
|
|
13
|
-
const
|
|
14
|
-
const
|
|
22
|
+
const mockNpmToken = 'npm_Foo';
|
|
23
|
+
const npmOptions = ['--audit=false', '--fund=false', '--legacy-peer-deps'];
|
|
24
|
+
const startupVersion = '1.2.3';
|
|
25
|
+
const tempDirPath = 'tempDirPath';
|
|
15
26
|
let args: ConstructorParameters<typeof Install>[0];
|
|
16
27
|
|
|
28
|
+
function volFromJSON(overrides?: Record<string, string>) {
|
|
29
|
+
vol.fromJSON({
|
|
30
|
+
'.npmrc': '',
|
|
31
|
+
// Mock cloned Github repo with .npm.json containing readOnlyToken
|
|
32
|
+
[`${tempDirPath}/.npm.json`]: JSON.stringify({
|
|
33
|
+
readOnlyToken: Buffer.from(mockNpmToken).toString('base64'),
|
|
34
|
+
}),
|
|
35
|
+
...overrides,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
17
39
|
beforeEach(() => {
|
|
18
|
-
process.env = {};
|
|
19
40
|
args = undefined;
|
|
20
41
|
jest.clearAllMocks();
|
|
42
|
+
jest.mocked(gitCloneRepo).mockResolvedValue(true);
|
|
43
|
+
jest.mocked(getStartupVersion).mockReturnValue(startupVersion);
|
|
44
|
+
jest.mocked(isCI).mockReturnValue(false);
|
|
45
|
+
jest.spyOn(fs, 'mkdtempSync').mockImplementation(() => tempDirPath);
|
|
46
|
+
volFromJSON();
|
|
21
47
|
});
|
|
22
48
|
|
|
23
|
-
afterEach(() => (
|
|
49
|
+
afterEach(() => vol.reset());
|
|
24
50
|
|
|
25
51
|
const subject = async () => new Install(args).execute();
|
|
26
52
|
|
|
27
|
-
test(
|
|
53
|
+
test('clones "frontend-dev-config" repo to temp directory', async () => {
|
|
54
|
+
await subject();
|
|
55
|
+
|
|
56
|
+
expect(gitCloneRepo).toHaveBeenCalledWith({
|
|
57
|
+
destination: tempDirPath,
|
|
58
|
+
name: 'frontend-dev-config',
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('configures NPM token with value from cloned repo', async () => {
|
|
63
|
+
await subject();
|
|
64
|
+
|
|
65
|
+
expect(execSync).toHaveBeenCalledWith(
|
|
66
|
+
`npm config set "//registry.npmjs.org/:_authToken"="${mockNpmToken}"`
|
|
67
|
+
);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test('deletes NPM token from project .npmrc', async () => {
|
|
71
|
+
await subject();
|
|
72
|
+
|
|
73
|
+
expect(execSync).toHaveBeenCalledWith(
|
|
74
|
+
`npm config delete --location=project "//registry.npmjs.org/:_authToken"`
|
|
75
|
+
);
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
test('removes temp directory', async () => {
|
|
79
|
+
const rmSpy = jest.spyOn(fs, 'rmSync');
|
|
80
|
+
|
|
28
81
|
await subject();
|
|
29
82
|
|
|
30
|
-
expect(
|
|
83
|
+
expect(rmSpy).toHaveBeenCalledWith(tempDirPath, { recursive: true, force: true });
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
test(`runs npm i ${npmOptions.join(' ')}`, async () => {
|
|
87
|
+
await subject();
|
|
88
|
+
|
|
89
|
+
expect(execSync).toHaveBeenCalledWith(`npm i ${npmOptions.join(' ')}`, {
|
|
31
90
|
stdio: 'inherit',
|
|
32
91
|
});
|
|
33
92
|
});
|
|
@@ -35,19 +94,72 @@ describe(`${Install.name}`, () => {
|
|
|
35
94
|
test('logs progress', async () => {
|
|
36
95
|
await subject();
|
|
37
96
|
|
|
38
|
-
|
|
97
|
+
[`startup cli v${startupVersion}`, 'Configuring NPM token', 'Running npm'].forEach(
|
|
98
|
+
message => {
|
|
99
|
+
expect(log.info).toHaveBeenCalledWith(expect.stringMatching(message));
|
|
100
|
+
}
|
|
101
|
+
);
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
function itDoesNotConfigureNpmToken() {
|
|
105
|
+
test('does not configure NPM token', async () => {
|
|
106
|
+
await subject();
|
|
107
|
+
|
|
108
|
+
expect(execSync).not.toHaveBeenCalledWith(expect.stringMatching(/npm config/));
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function itDoesNotDeleteProjectToken() {
|
|
113
|
+
test('does not delete project token', async () => {
|
|
114
|
+
await subject();
|
|
115
|
+
|
|
116
|
+
expect(execSync).not.toHaveBeenCalledWith(expect.stringMatching(/npm config delete/));
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
describe('when .npmrc uses an environment variable', () => {
|
|
121
|
+
beforeEach(() => {
|
|
122
|
+
volFromJSON({
|
|
123
|
+
'.npmrc': [
|
|
124
|
+
// eslint-disable-next-line no-template-curly-in-string
|
|
125
|
+
'#//registry.npmjs.org/:_authToken=${NPM_READONLY_TOKEN}', // should ignore this comment
|
|
126
|
+
// eslint-disable-next-line no-template-curly-in-string
|
|
127
|
+
'//registry.npmjs.org/:_authToken=${ST_NPM_READONLY_TOKEN}',
|
|
128
|
+
].join('\n'),
|
|
129
|
+
});
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
test('runs npm i with environment variable', async () => {
|
|
133
|
+
await subject();
|
|
134
|
+
|
|
135
|
+
expect(execSync).toHaveBeenCalledWith(`npm i ${npmOptions.join(' ')}`, {
|
|
136
|
+
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
137
|
+
env: expect.objectContaining({ ST_NPM_READONLY_TOKEN: mockNpmToken }),
|
|
138
|
+
stdio: 'inherit',
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
itDoesNotDeleteProjectToken();
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
describe('with no .npmrc', () => {
|
|
146
|
+
beforeEach(() => fs.rmSync('.npmrc'));
|
|
147
|
+
|
|
148
|
+
itDoesNotDeleteProjectToken();
|
|
39
149
|
});
|
|
40
150
|
|
|
41
151
|
describe('when in CI environment', () => {
|
|
42
|
-
beforeEach(() => (
|
|
152
|
+
beforeEach(() => jest.mocked(isCI).mockReturnValue(true));
|
|
43
153
|
|
|
44
154
|
test('runs npm ci', async () => {
|
|
45
155
|
await subject();
|
|
46
156
|
|
|
47
|
-
expect(execSync).toHaveBeenCalledWith(`npm ci ${
|
|
157
|
+
expect(execSync).toHaveBeenCalledWith(`npm ci ${npmOptions.join(' ')}`, {
|
|
48
158
|
stdio: 'inherit',
|
|
49
159
|
});
|
|
50
160
|
});
|
|
161
|
+
|
|
162
|
+
itDoesNotConfigureNpmToken();
|
|
51
163
|
});
|
|
52
164
|
|
|
53
165
|
describe('with --quite', () => {
|
|
@@ -61,7 +173,7 @@ describe(`${Install.name}`, () => {
|
|
|
61
173
|
});
|
|
62
174
|
|
|
63
175
|
describe('with --fix', () => {
|
|
64
|
-
const fixOptions = [...
|
|
176
|
+
const fixOptions = [...npmOptions, '--package-lock-only', '--prefer-dedupe'];
|
|
65
177
|
|
|
66
178
|
beforeEach(() => (args = { fix: true }));
|
|
67
179
|
|
|
@@ -73,5 +185,55 @@ describe(`${Install.name}`, () => {
|
|
|
73
185
|
expect.anything()
|
|
74
186
|
);
|
|
75
187
|
});
|
|
188
|
+
|
|
189
|
+
itDoesNotConfigureNpmToken();
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
describe('with --no-token', () => {
|
|
193
|
+
beforeEach(() => (args = { token: false }));
|
|
194
|
+
|
|
195
|
+
itDoesNotConfigureNpmToken();
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
function itLogsError(message: string | RegExp) {
|
|
199
|
+
test('logs error', async () => {
|
|
200
|
+
await subject();
|
|
201
|
+
|
|
202
|
+
expect(log.warning).toHaveBeenCalledWith(expect.stringMatching(message));
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
describe('when error occurs fetching token', () => {
|
|
207
|
+
beforeEach(() => jest.mocked(gitCloneRepo).mockResolvedValue(false));
|
|
208
|
+
|
|
209
|
+
itLogsError(/could not clone servicetitan\/frontend-dev-config/);
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
describe('when .npm.json is not an object', () => {
|
|
213
|
+
beforeEach(() => volFromJSON({ [`${tempDirPath}/.npm.json`]: JSON.stringify('') }));
|
|
214
|
+
|
|
215
|
+
itLogsError(/is not an object/);
|
|
216
|
+
});
|
|
217
|
+
|
|
218
|
+
describe('when .npm.json omits readOnlyToken', () => {
|
|
219
|
+
beforeEach(() => volFromJSON({ [`${tempDirPath}/.npm.json`]: JSON.stringify({}) }));
|
|
220
|
+
|
|
221
|
+
itLogsError(/does not contain auth token/);
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
describe('when readOnlyToken is blank', () => {
|
|
225
|
+
beforeEach(() =>
|
|
226
|
+
volFromJSON({ [`${tempDirPath}/.npm.json`]: JSON.stringify({ readOnlyToken: '' }) })
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
itLogsError(/does not contain auth token/);
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
describe('when readOnlyToken is not a string', () => {
|
|
233
|
+
beforeEach(() =>
|
|
234
|
+
volFromJSON({ [`${tempDirPath}/.npm.json`]: JSON.stringify({ readOnlyToken: {} }) })
|
|
235
|
+
);
|
|
236
|
+
|
|
237
|
+
itLogsError(/token is not a string/);
|
|
76
238
|
});
|
|
77
239
|
});
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { fs, vol } from 'memfs';
|
|
2
2
|
import { isWebComponent, log } from '../../../utils';
|
|
3
|
-
import { gitGetBranch } from '../../utils
|
|
4
|
-
import { Version, npmGetPackageVersionsDetails, npmUnpublish } from '../../utils/cli-npm';
|
|
3
|
+
import { gitGetBranch, npmGetPackageVersionsDetails, npmUnpublish, Version } from '../../utils';
|
|
5
4
|
|
|
6
5
|
import { MFEPackageClean } from '../mfe-package-clean';
|
|
7
6
|
|
|
@@ -11,13 +10,11 @@ jest.mock('../../../utils', () => ({
|
|
|
11
10
|
isWebComponent: jest.fn(),
|
|
12
11
|
log: { error: jest.fn(), info: jest.fn() },
|
|
13
12
|
}));
|
|
14
|
-
jest.mock('../../utils
|
|
13
|
+
jest.mock('../../utils', () => ({
|
|
14
|
+
gitGetBranch: jest.fn(),
|
|
15
15
|
npmGetPackageVersionsDetails: jest.fn(),
|
|
16
16
|
npmUnpublish: jest.fn(),
|
|
17
17
|
}));
|
|
18
|
-
jest.mock('../../utils/cli-git', () => ({
|
|
19
|
-
gitGetBranch: jest.fn(),
|
|
20
|
-
}));
|
|
21
18
|
|
|
22
19
|
const DEFAULT_VERSIONS_TO_KEEP = 5;
|
|
23
20
|
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import { fs, vol } from 'memfs';
|
|
2
2
|
import path from 'path';
|
|
3
3
|
import { isWebComponent, log, readJson, WebComponentBranchConfigs } from '../../../utils';
|
|
4
|
-
import { gitGetBranch, gitGetCommitHash } from '../../utils/cli-git';
|
|
5
4
|
import {
|
|
5
|
+
gitGetBranch,
|
|
6
|
+
gitGetCommitHash,
|
|
6
7
|
npmGetPackageVersions,
|
|
7
8
|
npmPackageSet,
|
|
8
9
|
npmPublish,
|
|
9
10
|
npmTagVersion,
|
|
10
|
-
|
|
11
|
-
|
|
11
|
+
runCommand,
|
|
12
|
+
} from '../../utils';
|
|
12
13
|
import { MFEPackagePublish } from '../mfe-package-publish';
|
|
13
14
|
|
|
14
15
|
jest.mock('fs', () => fs);
|
|
@@ -17,17 +18,14 @@ jest.mock('../../../utils', () => ({
|
|
|
17
18
|
isWebComponent: jest.fn(),
|
|
18
19
|
log: { info: jest.fn(), warning: jest.fn() },
|
|
19
20
|
}));
|
|
20
|
-
jest.mock('../../utils
|
|
21
|
+
jest.mock('../../utils', () => ({
|
|
22
|
+
...jest.requireActual('../../utils'),
|
|
21
23
|
gitGetBranch: jest.fn(),
|
|
22
24
|
gitGetCommitHash: jest.fn(),
|
|
23
|
-
}));
|
|
24
|
-
jest.mock('../../utils/cli-npm', () => ({
|
|
25
25
|
npmGetPackageVersions: jest.fn(),
|
|
26
26
|
npmPackageSet: jest.fn(),
|
|
27
27
|
npmPublish: jest.fn(),
|
|
28
28
|
npmTagVersion: jest.fn(),
|
|
29
|
-
}));
|
|
30
|
-
jest.mock('../../utils/cli-os', () => ({
|
|
31
29
|
runCommand: jest.fn(),
|
|
32
30
|
}));
|
|
33
31
|
|
|
@@ -2,11 +2,16 @@ import { execSync } from 'child_process';
|
|
|
2
2
|
import { vol, fs } from 'memfs';
|
|
3
3
|
import { inspect } from 'node:util';
|
|
4
4
|
import path from 'path';
|
|
5
|
+
import { isCI } from '../../utils';
|
|
5
6
|
import { log } from '../../../utils';
|
|
6
7
|
import { UploadSourcemaps } from '../upload-sourcemaps';
|
|
7
8
|
|
|
8
9
|
jest.mock('child_process', () => ({ execSync: jest.fn() }));
|
|
9
10
|
jest.mock('fs', () => fs);
|
|
11
|
+
jest.mock('../../utils', () => ({
|
|
12
|
+
...jest.requireActual('../../utils'),
|
|
13
|
+
isCI: jest.fn(),
|
|
14
|
+
}));
|
|
10
15
|
jest.mock('../../../utils', () => ({
|
|
11
16
|
...jest.requireActual('../../../utils'),
|
|
12
17
|
log: { info: jest.fn(), warning: jest.fn() },
|
|
@@ -33,6 +38,7 @@ describe(`[startup] ${UploadSourcemaps.name}`, () => {
|
|
|
33
38
|
|
|
34
39
|
beforeEach(() => {
|
|
35
40
|
jest.clearAllMocks();
|
|
41
|
+
jest.mocked(isCI).mockReturnValue(false);
|
|
36
42
|
|
|
37
43
|
// eslint-disable-next-line @typescript-eslint/naming-convention
|
|
38
44
|
process.env = { DATADOG_API_KEY: 'datadog-api-key' };
|
|
@@ -91,9 +97,7 @@ describe(`[startup] ${UploadSourcemaps.name}`, () => {
|
|
|
91
97
|
itThrowsError('DATADOG_API_KEY environment variable is not set');
|
|
92
98
|
|
|
93
99
|
describe('when in CI environment', () => {
|
|
94
|
-
beforeEach(() => (
|
|
95
|
-
|
|
96
|
-
afterEach(() => delete process.env.CI);
|
|
100
|
+
beforeEach(() => jest.mocked(isCI).mockReturnValue(true));
|
|
97
101
|
|
|
98
102
|
test('logs warning', async () => {
|
|
99
103
|
await subject();
|
package/src/cli/commands/init.ts
CHANGED
|
@@ -2,15 +2,14 @@ import fs from 'fs';
|
|
|
2
2
|
import path from 'path';
|
|
3
3
|
|
|
4
4
|
import { log, logErrors } from '../../utils';
|
|
5
|
-
import {
|
|
6
|
-
import { Command } from './';
|
|
5
|
+
import { gitCloneRepo, gitIsReachable } from '../utils';
|
|
6
|
+
import { Command } from './types';
|
|
7
7
|
|
|
8
8
|
interface Args {
|
|
9
9
|
output?: string;
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
const
|
|
13
|
-
const sshUrl = 'git@github.com:servicetitan/frontend-example.git';
|
|
12
|
+
const REPO_NAME = 'frontend-example';
|
|
14
13
|
|
|
15
14
|
export class Init implements Command {
|
|
16
15
|
constructor(private readonly args: Args) {}
|
|
@@ -30,44 +29,25 @@ export class Init implements Command {
|
|
|
30
29
|
throw new Error(`${destination} is not an empty directory`);
|
|
31
30
|
}
|
|
32
31
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
webUrl.replace('github.com', `oauth2:${process.env.GITHUB_TOKEN}@github.com`)
|
|
37
|
-
);
|
|
32
|
+
if (await this.cloneRepo(destination)) {
|
|
33
|
+
log.info(`copied example project to ${destination}`);
|
|
34
|
+
return;
|
|
38
35
|
}
|
|
39
36
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
if (await cloneRepo(url, destination)) {
|
|
43
|
-
log.info(`copied example project to ${destination}`);
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
if (!gitUrls.some(isReachable)) {
|
|
49
|
-
throw new Error('could not read servicetitan/frontend-example repository');
|
|
37
|
+
if (!gitIsReachable({ name: REPO_NAME })) {
|
|
38
|
+
throw new Error(`could not read servicetitan/${REPO_NAME} repository`);
|
|
50
39
|
}
|
|
51
40
|
}
|
|
52
|
-
}
|
|
53
41
|
|
|
54
|
-
async
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
}
|
|
60
|
-
fs.rmSync(path.join(destination, '.git'), { recursive: true, force: true });
|
|
61
|
-
fs.rmSync(path.join(destination, '.github', 'CODEOWNERS'));
|
|
62
|
-
fs.rmSync(path.join(destination, 'package-lock.json'));
|
|
63
|
-
return true;
|
|
64
|
-
}
|
|
42
|
+
async cloneRepo(destination: string) {
|
|
43
|
+
const ok = await gitCloneRepo({ destination, name: REPO_NAME });
|
|
44
|
+
if (!ok) {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
65
47
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
return false;
|
|
48
|
+
fs.rmSync(path.join(destination, '.git'), { recursive: true, force: true });
|
|
49
|
+
fs.rmSync(path.join(destination, '.github', 'CODEOWNERS'));
|
|
50
|
+
fs.rmSync(path.join(destination, 'package-lock.json'));
|
|
51
|
+
return true;
|
|
71
52
|
}
|
|
72
|
-
return true;
|
|
73
53
|
}
|