@docupace/pipeline 1.0.7-7.micro-cli11 → 1.0.7-alpha.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/.prettierrc.cjs +12 -12
- package/bin/next-changeset-validate.sh +8 -8
- package/eslint.config.js +26 -49
- package/next/changeset.sh +75 -75
- package/next/copy.sh +34 -34
- package/next/deploy.sh +40 -40
- package/next/execute.sh +117 -123
- package/next/sonar.sh +23 -23
- package/package.json +7 -23
- package/src/changeset/cli.js +281 -237
- package/src/changeset/validate.js +47 -91
- package/src/jenkins/setParams.groovy +59 -58
- package/src/next/cli.js +15 -111
- package/src/next/config.js +72 -137
- package/src/next/copy.js +30 -61
- package/src/next/deploy.js +314 -633
- package/src/next/jenkins.js +88 -123
- package/src/next/pipline.js +115 -114
- package/src/next/publish.js +48 -71
- package/src/next/pull.js +7 -7
- package/src/next/sonar.js +23 -23
- package/README.md +0 -136
- package/bin/azuread_update_callbacks_for_app.sh +0 -58
- package/bitbucket-pipelines.yml +0 -60
- package/jest.config.js +0 -15
- package/src/auth0/alias.js +0 -79
- package/src/auth0/alias.test.js +0 -172
- package/src/auth0/cli.js +0 -82
- package/src/auth0/configure.js +0 -902
- package/src/auth0/configure.test.js +0 -167
- package/src/auth0/export.js +0 -48
- package/src/auth0/export.test.js +0 -103
- package/src/auth0/metadata.js +0 -141
- package/src/auth0/metadata.test.js +0 -186
- package/src/auth0/vault.js +0 -60
- package/src/auth0/vault.test.js +0 -134
- package/src/changeset/changelog-wiki.js +0 -328
- package/src/changeset/changeset-config.js +0 -34
- package/src/changeset/create.js +0 -215
- package/src/changeset/package-util.js +0 -87
- package/src/changeset/util.js +0 -43
- package/src/changeset/version.js +0 -102
- package/src/lib/merge-driver.js +0 -204
- package/src/merge/cli-merge-all.js +0 -507
- package/src/merge/cli-merge.js +0 -492
- package/src/merge/cli-resolve.js +0 -399
- package/src/merge/cli.js +0 -38
- package/src/merge/merge-changelog.js +0 -87
- package/src/merge/merge-package.js +0 -106
- package/src/merge/merge-types.js +0 -82
- package/src/merge/pr.js +0 -231
- package/src/micro/cli.js +0 -51
- package/src/micro/ms-deploy.js +0 -451
- package/src/micro/ms-publish.js +0 -186
- package/src/next/alias.js +0 -53
- package/src/next/apache.js +0 -81
- package/src/next/connect.js +0 -226
- package/src/next/git-to-semver.js +0 -79
- package/src/next/haproxy.js +0 -76
- package/src/next/jira.js +0 -84
- package/src/next/logger.js +0 -17
- package/src/next/test.js +0 -5
- package/src/next/util.js +0 -93
- package/src/next/vault.js +0 -45
- package/terraform/apply.sh +0 -37
- package/terraform/init.sh +0 -72
- package/terraform/plan.sh +0 -76
- package/terraform/validate.sh +0 -17
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
// @jest-environment node
|
|
2
|
-
import { jest } from '@jest/globals';
|
|
3
|
-
|
|
4
|
-
// ─── Mock All Required Modules ───────────────────────────────────────────────
|
|
5
|
-
|
|
6
|
-
jest.unstable_mockModule('fs-extra', () => ({
|
|
7
|
-
default: {
|
|
8
|
-
promises: {
|
|
9
|
-
mkdir: jest.fn(),
|
|
10
|
-
},
|
|
11
|
-
writeFile: jest.fn(),
|
|
12
|
-
ensureDir: jest.fn(),
|
|
13
|
-
},
|
|
14
|
-
writeFile: jest.fn(),
|
|
15
|
-
ensureDir: jest.fn(),
|
|
16
|
-
}));
|
|
17
|
-
|
|
18
|
-
jest.unstable_mockModule('axios', () => ({
|
|
19
|
-
default: {
|
|
20
|
-
post: jest.fn(),
|
|
21
|
-
get: jest.fn(),
|
|
22
|
-
},
|
|
23
|
-
}));
|
|
24
|
-
|
|
25
|
-
jest.unstable_mockModule('crypto', () => ({
|
|
26
|
-
randomBytes: () => ({
|
|
27
|
-
toString: () => 'mockBase64==',
|
|
28
|
-
}),
|
|
29
|
-
}));
|
|
30
|
-
|
|
31
|
-
jest.unstable_mockModule('../next/deploy.js', () => ({
|
|
32
|
-
cloneRepository: jest.fn(),
|
|
33
|
-
cloneEnvRepository: jest.fn(),
|
|
34
|
-
pushDeployConfig: jest.fn(),
|
|
35
|
-
readDeployConfig: jest.fn(),
|
|
36
|
-
}));
|
|
37
|
-
|
|
38
|
-
jest.unstable_mockModule('./vault.js', () => ({
|
|
39
|
-
getVaultSecrets: jest.fn(),
|
|
40
|
-
updateVaultSecrets: jest.fn(),
|
|
41
|
-
}));
|
|
42
|
-
|
|
43
|
-
jest.unstable_mockModule('../next/config.js', () => ({
|
|
44
|
-
coreSiteEnvMap: { dev: 'dev-env' },
|
|
45
|
-
environmentConfig: {
|
|
46
|
-
dev: {
|
|
47
|
-
newUrl: (site) => `dev.${site}.com`,
|
|
48
|
-
oldUrl: (site) => `legacy.${site}.com`,
|
|
49
|
-
ccUrl: (site) => `cc.${site}.com`,
|
|
50
|
-
docupaceHost: () => 'docupace.com',
|
|
51
|
-
},
|
|
52
|
-
},
|
|
53
|
-
getVaultSecretsConfigs: jest.fn(),
|
|
54
|
-
}));
|
|
55
|
-
|
|
56
|
-
// ─── Global References ───────────────────────────────────────────────────────
|
|
57
|
-
|
|
58
|
-
let fs, axios, deploy, vault, config;
|
|
59
|
-
let getAuth0AuthToken, cleanAndWritePropertiesFile, updateCoreSiteConfig;
|
|
60
|
-
|
|
61
|
-
const originalProcessExit = process.exit;
|
|
62
|
-
|
|
63
|
-
beforeAll(async () => {
|
|
64
|
-
// Mock process.exit to prevent killing the test runner
|
|
65
|
-
process.exit = jest.fn();
|
|
66
|
-
|
|
67
|
-
// Suppress noisy error output
|
|
68
|
-
jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
69
|
-
jest.spyOn(console, 'log').mockImplementation(() => {});
|
|
70
|
-
|
|
71
|
-
// Import after mocks
|
|
72
|
-
({ default: fs } = await import('fs-extra'));
|
|
73
|
-
({ default: axios } = await import('axios'));
|
|
74
|
-
deploy = await import('../next/deploy.js');
|
|
75
|
-
vault = await import('./vault.js');
|
|
76
|
-
config = await import('../next/config.js');
|
|
77
|
-
|
|
78
|
-
const mod = await import('./configure.js');
|
|
79
|
-
({ getAuth0AuthToken, cleanAndWritePropertiesFile, updateCoreSiteConfig } =
|
|
80
|
-
mod);
|
|
81
|
-
});
|
|
82
|
-
|
|
83
|
-
afterAll(() => {
|
|
84
|
-
// Restore
|
|
85
|
-
process.exit = originalProcessExit;
|
|
86
|
-
console.error.mockRestore();
|
|
87
|
-
console.log.mockRestore();
|
|
88
|
-
});
|
|
89
|
-
|
|
90
|
-
// ─── Tests ───────────────────────────────────────────────────────────────────
|
|
91
|
-
|
|
92
|
-
describe('Deployment Automation Module (ESM)', () => {
|
|
93
|
-
describe('getAuth0AuthToken', () => {
|
|
94
|
-
it('should return access token on success', async () => {
|
|
95
|
-
axios.post.mockResolvedValue({ data: { access_token: 'mock-token' } });
|
|
96
|
-
const token = await getAuth0AuthToken('auth.domain', 'cid', 'secret');
|
|
97
|
-
expect(token).toBe('mock-token');
|
|
98
|
-
});
|
|
99
|
-
|
|
100
|
-
it('should return undefined on failure', async () => {
|
|
101
|
-
axios.post.mockRejectedValue({ message: 'fail' });
|
|
102
|
-
const token = await getAuth0AuthToken('auth.domain', 'cid', 'secret');
|
|
103
|
-
expect(token).toBeUndefined();
|
|
104
|
-
expect(console.error).toHaveBeenCalledWith(
|
|
105
|
-
'Error fetching Auth0 token:',
|
|
106
|
-
'fail',
|
|
107
|
-
);
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
|
|
111
|
-
describe('cleanAndWritePropertiesFile', () => {
|
|
112
|
-
it('writes cleaned file content', async () => {
|
|
113
|
-
fs.writeFile.mockResolvedValue();
|
|
114
|
-
await cleanAndWritePropertiesFile('/tmp/test.properties', 'a=1\n\nb=2\n');
|
|
115
|
-
expect(fs.writeFile).toHaveBeenCalledWith(
|
|
116
|
-
'/tmp/test.properties',
|
|
117
|
-
'a=1\nb=2',
|
|
118
|
-
'utf8',
|
|
119
|
-
);
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
it('calls process.exit on write error', async () => {
|
|
123
|
-
fs.writeFile.mockRejectedValue(new Error('disk full'));
|
|
124
|
-
await cleanAndWritePropertiesFile('/bad/path', 'x=y');
|
|
125
|
-
expect(process.exit).toHaveBeenCalledWith(1);
|
|
126
|
-
});
|
|
127
|
-
});
|
|
128
|
-
|
|
129
|
-
describe('updateCoreSiteConfig', () => {
|
|
130
|
-
it('updates config when S3 is enabled', async () => {
|
|
131
|
-
config.getVaultSecretsConfigs.mockReturnValue({
|
|
132
|
-
awsSecretsPath: 'aws/path',
|
|
133
|
-
});
|
|
134
|
-
|
|
135
|
-
deploy.cloneRepository.mockResolvedValue('/repo/path');
|
|
136
|
-
|
|
137
|
-
vault.getVaultSecrets.mockResolvedValue({
|
|
138
|
-
AWS_ACCESS_KEY_ID: 'abc',
|
|
139
|
-
AWS_SECRET_ACCESS_KEY: 'def',
|
|
140
|
-
AWS_REGION: 'us-west-2',
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
fs.ensureDir.mockResolvedValue();
|
|
144
|
-
fs.writeFile.mockResolvedValue();
|
|
145
|
-
deploy.pushDeployConfig.mockResolvedValue();
|
|
146
|
-
|
|
147
|
-
await updateCoreSiteConfig('mysite', 'dev', true, false);
|
|
148
|
-
|
|
149
|
-
expect(fs.ensureDir).toHaveBeenCalledWith('/repo/path/MYSITE');
|
|
150
|
-
expect(fs.writeFile).toHaveBeenCalled();
|
|
151
|
-
expect(deploy.pushDeployConfig).toHaveBeenCalled();
|
|
152
|
-
});
|
|
153
|
-
|
|
154
|
-
it('calls process.exit if cloneRepository fails', async () => {
|
|
155
|
-
config.getVaultSecretsConfigs.mockReturnValue({
|
|
156
|
-
awsSecretsPath: 'aws/path',
|
|
157
|
-
});
|
|
158
|
-
|
|
159
|
-
deploy.cloneRepository.mockRejectedValue(new Error('bad clone'));
|
|
160
|
-
|
|
161
|
-
await updateCoreSiteConfig('mysite', 'dev', true, false);
|
|
162
|
-
|
|
163
|
-
expect(process.exit).toHaveBeenCalledWith(1);
|
|
164
|
-
expect(console.error).toHaveBeenCalledWith('Error:', 'bad clone');
|
|
165
|
-
});
|
|
166
|
-
});
|
|
167
|
-
});
|
package/src/auth0/export.js
DELETED
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { getVaultSecrets } from './vault.js';
|
|
2
|
-
import { getVaultSecretsConfigs } from '../next/config.js';
|
|
3
|
-
import { dump } from 'auth0-deploy-cli';
|
|
4
|
-
|
|
5
|
-
export async function exportConf(site, env, auth0Tenant) {
|
|
6
|
-
try {
|
|
7
|
-
console.log(
|
|
8
|
-
`Exporting Auth0 configuration for site: ${site} and env: ${env}`,
|
|
9
|
-
);
|
|
10
|
-
|
|
11
|
-
const siteCommonSecretsPath =
|
|
12
|
-
getVaultSecretsConfigs(site).siteCommonSecretsPath;
|
|
13
|
-
console.log(`siteCommonSecretsPath => ${siteCommonSecretsPath}`);
|
|
14
|
-
const siteCommonSecrets = await getVaultSecrets(siteCommonSecretsPath);
|
|
15
|
-
|
|
16
|
-
let auth0Domain;
|
|
17
|
-
if (auth0Tenant === '') {
|
|
18
|
-
auth0Domain = `docupace-${env}-${site}.us.auth0.com`;
|
|
19
|
-
} else {
|
|
20
|
-
auth0Domain = `${auth0Tenant}.us.auth0.com`;
|
|
21
|
-
}
|
|
22
|
-
const auth0ClientId = siteCommonSecrets.AUTH0_CLIENT_ID;
|
|
23
|
-
const auth0ClientSecret = siteCommonSecrets.AUTH0_CLIENT_SECRET;
|
|
24
|
-
|
|
25
|
-
const config = {
|
|
26
|
-
config: {
|
|
27
|
-
AUTH0_DOMAIN: auth0Domain,
|
|
28
|
-
AUTH0_CLIENT_ID: auth0ClientId,
|
|
29
|
-
AUTH0_CLIENT_SECRET: auth0ClientSecret,
|
|
30
|
-
},
|
|
31
|
-
format: 'directory',
|
|
32
|
-
output_folder: `./auth0-${site}-export`,
|
|
33
|
-
export_ids: false,
|
|
34
|
-
env: false,
|
|
35
|
-
debug: true,
|
|
36
|
-
};
|
|
37
|
-
|
|
38
|
-
console.log(`Starting Auth0 ${site} configuration export...`);
|
|
39
|
-
await dump(config);
|
|
40
|
-
console.log(`Auth0 ${site} configuration export completed successfully!`);
|
|
41
|
-
} catch (error) {
|
|
42
|
-
console.error(
|
|
43
|
-
`Error during Auth0 ${site} configuration export:`,
|
|
44
|
-
error.message,
|
|
45
|
-
);
|
|
46
|
-
process.exit(1);
|
|
47
|
-
}
|
|
48
|
-
}
|
package/src/auth0/export.test.js
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
// exportConf.test.js
|
|
2
|
-
import { jest } from '@jest/globals';
|
|
3
|
-
|
|
4
|
-
// Mock modules using ESM-safe methods
|
|
5
|
-
jest.unstable_mockModule('./vault.js', () => ({
|
|
6
|
-
getVaultSecrets: jest.fn(),
|
|
7
|
-
}));
|
|
8
|
-
|
|
9
|
-
jest.unstable_mockModule('../next/config.js', () => ({
|
|
10
|
-
getVaultSecretsConfigs: jest.fn(),
|
|
11
|
-
}));
|
|
12
|
-
|
|
13
|
-
jest.unstable_mockModule('auth0-deploy-cli', () => ({
|
|
14
|
-
dump: jest.fn(),
|
|
15
|
-
}));
|
|
16
|
-
|
|
17
|
-
// Import the actual function and mocked dependencies after mocks are in place
|
|
18
|
-
const { exportConf } = await import('./export.js');
|
|
19
|
-
const { getVaultSecrets } = await import('./vault.js');
|
|
20
|
-
const { getVaultSecretsConfigs } = await import('../next/config.js');
|
|
21
|
-
const { dump } = await import('auth0-deploy-cli');
|
|
22
|
-
|
|
23
|
-
describe('exportConf', () => {
|
|
24
|
-
const site = 'example';
|
|
25
|
-
const env = 'dev';
|
|
26
|
-
const auth0Tenant = '';
|
|
27
|
-
|
|
28
|
-
const mockSecrets = {
|
|
29
|
-
AUTH0_CLIENT_ID: 'test-client-id',
|
|
30
|
-
AUTH0_CLIENT_SECRET: 'test-client-secret',
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
const mockPath = 'vault/path/to/secrets';
|
|
34
|
-
|
|
35
|
-
beforeEach(() => {
|
|
36
|
-
jest.clearAllMocks();
|
|
37
|
-
|
|
38
|
-
getVaultSecretsConfigs.mockReturnValue({
|
|
39
|
-
siteCommonSecretsPath: mockPath,
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
getVaultSecrets.mockResolvedValue(mockSecrets);
|
|
43
|
-
|
|
44
|
-
dump.mockResolvedValue(undefined);
|
|
45
|
-
});
|
|
46
|
-
|
|
47
|
-
it('should export Auth0 configuration with default tenant format', async () => {
|
|
48
|
-
const logSpy = jest.spyOn(console, 'log').mockImplementation(() => {});
|
|
49
|
-
await exportConf(site, env, auth0Tenant);
|
|
50
|
-
|
|
51
|
-
expect(getVaultSecretsConfigs).toHaveBeenCalledWith(site);
|
|
52
|
-
expect(getVaultSecrets).toHaveBeenCalledWith(mockPath);
|
|
53
|
-
expect(dump).toHaveBeenCalledWith(
|
|
54
|
-
expect.objectContaining({
|
|
55
|
-
config: {
|
|
56
|
-
AUTH0_DOMAIN: 'docupace-dev-example.us.auth0.com',
|
|
57
|
-
AUTH0_CLIENT_ID: 'test-client-id',
|
|
58
|
-
AUTH0_CLIENT_SECRET: 'test-client-secret',
|
|
59
|
-
},
|
|
60
|
-
}),
|
|
61
|
-
);
|
|
62
|
-
|
|
63
|
-
logSpy.mockRestore();
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
it('should export Auth0 configuration with custom tenant', async () => {
|
|
67
|
-
const customTenant = 'custom-tenant';
|
|
68
|
-
await exportConf(site, env, customTenant);
|
|
69
|
-
|
|
70
|
-
expect(dump).toHaveBeenCalledWith(
|
|
71
|
-
expect.objectContaining({
|
|
72
|
-
config: {
|
|
73
|
-
AUTH0_DOMAIN: 'custom-tenant.us.auth0.com',
|
|
74
|
-
AUTH0_CLIENT_ID: 'test-client-id',
|
|
75
|
-
AUTH0_CLIENT_SECRET: 'test-client-secret',
|
|
76
|
-
},
|
|
77
|
-
}),
|
|
78
|
-
);
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it('should log error and exit process on failure', async () => {
|
|
82
|
-
getVaultSecrets.mockRejectedValue(new Error('Vault error'));
|
|
83
|
-
|
|
84
|
-
const errorSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
85
|
-
const exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => {
|
|
86
|
-
throw new Error('process.exit');
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
await expect(exportConf(site, env, auth0Tenant)).rejects.toThrow(
|
|
90
|
-
'process.exit',
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
expect(errorSpy).toHaveBeenCalledWith(
|
|
94
|
-
expect.stringContaining(
|
|
95
|
-
'Error during Auth0 example configuration export:',
|
|
96
|
-
),
|
|
97
|
-
'Vault error',
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
errorSpy.mockRestore();
|
|
101
|
-
exitSpy.mockRestore();
|
|
102
|
-
});
|
|
103
|
-
});
|
package/src/auth0/metadata.js
DELETED
|
@@ -1,141 +0,0 @@
|
|
|
1
|
-
import fs from 'fs-extra';
|
|
2
|
-
import axios from 'axios';
|
|
3
|
-
import * as path from 'node:path'; // ESM-compatible import
|
|
4
|
-
import { cloneRepository, pushDeployConfig } from '../next/deploy.js';
|
|
5
|
-
import { getAuth0AuthToken } from './configure.js';
|
|
6
|
-
import { getVaultSecrets } from './vault.js';
|
|
7
|
-
import { getVaultSecretsConfigs } from '../next/config.js';
|
|
8
|
-
|
|
9
|
-
export async function getAuth0Metadata(site, env, auth0Tenant, jiraTicket) {
|
|
10
|
-
try {
|
|
11
|
-
const connectDeployRepoPath = await cloneRepository('connect-deploy');
|
|
12
|
-
|
|
13
|
-
const envMap = {
|
|
14
|
-
dev: 'dev',
|
|
15
|
-
preprod: 'qa',
|
|
16
|
-
stage: 'stage',
|
|
17
|
-
uat: 'uat',
|
|
18
|
-
'prod-www3': 'prod',
|
|
19
|
-
'prod-lax': 'prod',
|
|
20
|
-
'prod-las': 'prod',
|
|
21
|
-
};
|
|
22
|
-
|
|
23
|
-
process.chdir(`${connectDeployRepoPath}/envs/${env}/${site}`);
|
|
24
|
-
|
|
25
|
-
const configPath = path.join('conf', 'application.conf');
|
|
26
|
-
const appConf = await fs.readFile(configPath, 'utf-8');
|
|
27
|
-
|
|
28
|
-
const connectContext = appConf.match(
|
|
29
|
-
/application\.context\s*=\s*"(.*?)"/,
|
|
30
|
-
)?.[1];
|
|
31
|
-
if (!connectContext) {
|
|
32
|
-
console.error('connectContext not found in application.conf. Exiting...');
|
|
33
|
-
process.exit(1);
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const siteHost = appConf.match(
|
|
37
|
-
/docupace\.baseUrlClient\s*=\s*"?https?:\/\/([^/]+)"?/,
|
|
38
|
-
)?.[1];
|
|
39
|
-
if (!siteHost) {
|
|
40
|
-
console.error('siteHost not found in application.conf. Exiting...');
|
|
41
|
-
process.exit(1);
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const connectEndpoint = `https://${siteHost}${connectContext}`;
|
|
45
|
-
console.log('CONNECT_ENDPOINT:', connectEndpoint);
|
|
46
|
-
|
|
47
|
-
console.log('Requesting token from Auth0...');
|
|
48
|
-
const siteCommonSecretsPath =
|
|
49
|
-
getVaultSecretsConfigs(site).siteCommonSecretsPath;
|
|
50
|
-
console.log(`siteCommonSecretsPath => ${siteCommonSecretsPath}`);
|
|
51
|
-
|
|
52
|
-
const siteCommonSecrets = await getVaultSecrets(siteCommonSecretsPath);
|
|
53
|
-
|
|
54
|
-
const auth0Domain =
|
|
55
|
-
auth0Tenant === ''
|
|
56
|
-
? `docupace-${envMap[env]}-${site}.us.auth0.com`
|
|
57
|
-
: `${auth0Tenant}.us.auth0.com`;
|
|
58
|
-
|
|
59
|
-
const auth0ClientId = siteCommonSecrets.AUTH0_CLIENT_ID;
|
|
60
|
-
const auth0ClientSecret = siteCommonSecrets.AUTH0_CLIENT_SECRET;
|
|
61
|
-
|
|
62
|
-
const auth0Token = await getAuth0AuthToken(
|
|
63
|
-
auth0Domain,
|
|
64
|
-
auth0ClientId,
|
|
65
|
-
auth0ClientSecret,
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
const auth0Response = await axios.get(
|
|
69
|
-
`https://${auth0Domain}/api/v2/clients`,
|
|
70
|
-
{
|
|
71
|
-
headers: { authorization: `Bearer ${auth0Token}` },
|
|
72
|
-
},
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
const connectClient = auth0Response.data.find(
|
|
76
|
-
(client) => client.name === 'docupace-connect',
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
if (!connectClient) {
|
|
80
|
-
console.error('Client "docupace-connect" not found.');
|
|
81
|
-
process.exit(1);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const clientId = connectClient.client_id;
|
|
85
|
-
console.log('Found client_id:', clientId);
|
|
86
|
-
|
|
87
|
-
const metadataRes = await axios.get(
|
|
88
|
-
`https://${auth0Domain}/samlp/metadata/${clientId}`,
|
|
89
|
-
{ responseType: 'text' },
|
|
90
|
-
);
|
|
91
|
-
|
|
92
|
-
const metadata = metadataRes.data;
|
|
93
|
-
|
|
94
|
-
if (metadata.length < 2000) {
|
|
95
|
-
console.error(
|
|
96
|
-
'Downloaded metadata looks incorrect (too small). Exiting...',
|
|
97
|
-
);
|
|
98
|
-
process.exit(1);
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
await fs.ensureDir('conf/saml');
|
|
102
|
-
await fs.writeFile('conf/saml/auth0.xml', metadata);
|
|
103
|
-
console.log('SAML metadata written to conf/saml/auth0.xml');
|
|
104
|
-
|
|
105
|
-
let newConfig = appConf;
|
|
106
|
-
|
|
107
|
-
if (/useSAML\s*=/.test(appConf)) {
|
|
108
|
-
newConfig = newConfig.replace(/useSAML\s*=.*/, 'useSAML=true');
|
|
109
|
-
} else {
|
|
110
|
-
newConfig += '\nuseSAML=true';
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
const replacements = {
|
|
114
|
-
'saml.clients.auth0.idpMetadataPath': '${samlEnvPath}"/auth0.xml"',
|
|
115
|
-
'saml.clients.auth0.spEntityId': `"${connectEndpoint}"`,
|
|
116
|
-
'saml.clients.auth0.redirectFormInput': '"RelayState"',
|
|
117
|
-
'saml.clients.auth0.idpLoginUrl': `"https://${auth0Domain}/v2/logout?client_id=${clientId}&returnTo=${connectEndpoint}"`,
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
for (const [key, value] of Object.entries(replacements)) {
|
|
121
|
-
const pattern = new RegExp(`${key}\\s*=.*`, 'g');
|
|
122
|
-
if (pattern.test(newConfig)) {
|
|
123
|
-
newConfig = newConfig.replace(pattern, `${key}=${value}`);
|
|
124
|
-
} else {
|
|
125
|
-
newConfig += `\n${key}=${value}`;
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
await fs.writeFile(configPath, newConfig);
|
|
130
|
-
console.log('The application.conf has been updated.');
|
|
131
|
-
|
|
132
|
-
const commitMessage = jiraTicket
|
|
133
|
-
? `${jiraTicket} Auth0 SAML metadata update for site: (${site}) in env: (${env})`
|
|
134
|
-
: `Auth0 SAML metadata update for site: (${site}) in env: (${env})`;
|
|
135
|
-
console.log('Commit message:', commitMessage);
|
|
136
|
-
await pushDeployConfig(connectDeployRepoPath, commitMessage);
|
|
137
|
-
} catch (error) {
|
|
138
|
-
console.error('Error:', error.message);
|
|
139
|
-
process.exit(1);
|
|
140
|
-
}
|
|
141
|
-
}
|
|
@@ -1,186 +0,0 @@
|
|
|
1
|
-
import { jest } from '@jest/globals';
|
|
2
|
-
|
|
3
|
-
// ----- Mocks (ESM-safe) -----
|
|
4
|
-
jest.unstable_mockModule('fs-extra', () => ({
|
|
5
|
-
default: {
|
|
6
|
-
readFile: jest.fn(),
|
|
7
|
-
writeFile: jest.fn(),
|
|
8
|
-
ensureDir: jest.fn(),
|
|
9
|
-
},
|
|
10
|
-
}));
|
|
11
|
-
|
|
12
|
-
jest.unstable_mockModule('axios', () => ({
|
|
13
|
-
default: {
|
|
14
|
-
get: jest.fn(),
|
|
15
|
-
},
|
|
16
|
-
}));
|
|
17
|
-
|
|
18
|
-
jest.unstable_mockModule('path', () => ({
|
|
19
|
-
join: jest.fn((...args) => args.join('/')),
|
|
20
|
-
}));
|
|
21
|
-
|
|
22
|
-
jest.unstable_mockModule('../next/deploy.js', () => ({
|
|
23
|
-
cloneRepository: jest.fn(),
|
|
24
|
-
pushDeployConfig: jest.fn(),
|
|
25
|
-
}));
|
|
26
|
-
|
|
27
|
-
jest.unstable_mockModule('./configure.js', () => ({
|
|
28
|
-
getAuth0AuthToken: jest.fn(),
|
|
29
|
-
}));
|
|
30
|
-
|
|
31
|
-
jest.unstable_mockModule('./vault.js', () => ({
|
|
32
|
-
getVaultSecrets: jest.fn(),
|
|
33
|
-
}));
|
|
34
|
-
|
|
35
|
-
jest.unstable_mockModule('../next/config.js', () => ({
|
|
36
|
-
getVaultSecretsConfigs: jest.fn(),
|
|
37
|
-
}));
|
|
38
|
-
|
|
39
|
-
// ----- Imports AFTER mocks -----
|
|
40
|
-
const { getAuth0Metadata } = await import('./metadata.js');
|
|
41
|
-
const fs = (await import('fs-extra')).default;
|
|
42
|
-
const axios = (await import('axios')).default;
|
|
43
|
-
const { cloneRepository, pushDeployConfig } = await import('../next/deploy.js');
|
|
44
|
-
const { getAuth0AuthToken } = await import('./configure.js');
|
|
45
|
-
const { getVaultSecrets } = await import('./vault.js');
|
|
46
|
-
const { getVaultSecretsConfigs } = await import('../next/config.js');
|
|
47
|
-
|
|
48
|
-
// ----- Shared Setup -----
|
|
49
|
-
beforeAll(() => {
|
|
50
|
-
jest.spyOn(console, 'log').mockImplementation(() => {});
|
|
51
|
-
jest.spyOn(console, 'error').mockImplementation(() => {});
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
afterAll(() => {
|
|
55
|
-
console.log.mockRestore();
|
|
56
|
-
console.error.mockRestore();
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
describe('getAuth0Metadata', () => {
|
|
60
|
-
const site = 'example';
|
|
61
|
-
const env = 'dev';
|
|
62
|
-
const auth0Tenant = '';
|
|
63
|
-
const fakeRepoPath = '/tmp/repo';
|
|
64
|
-
const fakeAppConf = `
|
|
65
|
-
application.context = "/connect"
|
|
66
|
-
docupace.baseUrlClient = "https://example.docupace.com"
|
|
67
|
-
`;
|
|
68
|
-
|
|
69
|
-
const mockSecrets = {
|
|
70
|
-
AUTH0_CLIENT_ID: 'client-id',
|
|
71
|
-
AUTH0_CLIENT_SECRET: 'client-secret',
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
const auth0Token = 'fake-token';
|
|
75
|
-
const clientId = 'abc123';
|
|
76
|
-
const fakeMetadata = '<xml>'.padEnd(2500, 'a') + '</xml>';
|
|
77
|
-
|
|
78
|
-
let originalChdir;
|
|
79
|
-
let exitSpy;
|
|
80
|
-
|
|
81
|
-
beforeEach(() => {
|
|
82
|
-
jest.clearAllMocks();
|
|
83
|
-
axios.get.mockReset();
|
|
84
|
-
|
|
85
|
-
cloneRepository.mockResolvedValue(fakeRepoPath);
|
|
86
|
-
getVaultSecretsConfigs.mockReturnValue({
|
|
87
|
-
siteCommonSecretsPath: 'vault/path',
|
|
88
|
-
});
|
|
89
|
-
getVaultSecrets.mockResolvedValue(mockSecrets);
|
|
90
|
-
getAuth0AuthToken.mockResolvedValue(auth0Token);
|
|
91
|
-
fs.readFile.mockResolvedValue(fakeAppConf);
|
|
92
|
-
fs.writeFile.mockResolvedValue();
|
|
93
|
-
fs.ensureDir.mockResolvedValue();
|
|
94
|
-
pushDeployConfig.mockResolvedValue();
|
|
95
|
-
|
|
96
|
-
originalChdir = process.chdir;
|
|
97
|
-
process.chdir = jest.fn();
|
|
98
|
-
|
|
99
|
-
exitSpy = jest.spyOn(process, 'exit').mockImplementation(() => {});
|
|
100
|
-
});
|
|
101
|
-
|
|
102
|
-
afterEach(() => {
|
|
103
|
-
process.chdir = originalChdir;
|
|
104
|
-
exitSpy.mockRestore();
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
it('should download and write metadata, update config, and push changes', async () => {
|
|
108
|
-
axios.get
|
|
109
|
-
.mockResolvedValueOnce({
|
|
110
|
-
data: [{ name: 'docupace-connect', client_id: clientId }],
|
|
111
|
-
}) // clients
|
|
112
|
-
.mockResolvedValueOnce({ data: fakeMetadata }); // metadata
|
|
113
|
-
|
|
114
|
-
await getAuth0Metadata(site, env, auth0Tenant);
|
|
115
|
-
|
|
116
|
-
expect(cloneRepository).toHaveBeenCalledWith('connect-deploy');
|
|
117
|
-
expect(fs.readFile).toHaveBeenCalledWith('conf/application.conf', 'utf-8');
|
|
118
|
-
expect(fs.ensureDir).toHaveBeenCalledWith('conf/saml');
|
|
119
|
-
expect(fs.writeFile).toHaveBeenCalledWith(
|
|
120
|
-
'conf/saml/auth0.xml',
|
|
121
|
-
expect.stringContaining('<xml'),
|
|
122
|
-
);
|
|
123
|
-
expect(fs.writeFile).toHaveBeenCalledWith(
|
|
124
|
-
'conf/application.conf',
|
|
125
|
-
expect.stringContaining('useSAML=true'),
|
|
126
|
-
);
|
|
127
|
-
expect(pushDeployConfig).toHaveBeenCalled();
|
|
128
|
-
});
|
|
129
|
-
|
|
130
|
-
it('should exit if docupace-connect client is not found', async () => {
|
|
131
|
-
axios.get.mockResolvedValueOnce({ data: [] }); // empty clients
|
|
132
|
-
|
|
133
|
-
await getAuth0Metadata(site, env, auth0Tenant);
|
|
134
|
-
|
|
135
|
-
expect(process.exit).toHaveBeenCalledWith(1);
|
|
136
|
-
expect(console.error).toHaveBeenCalledWith(
|
|
137
|
-
'Client "docupace-connect" not found.',
|
|
138
|
-
);
|
|
139
|
-
});
|
|
140
|
-
|
|
141
|
-
it('should exit if metadata is too small', async () => {
|
|
142
|
-
axios.get
|
|
143
|
-
.mockResolvedValueOnce({
|
|
144
|
-
data: [{ name: 'docupace-connect', client_id: clientId }],
|
|
145
|
-
}) // clients
|
|
146
|
-
.mockResolvedValueOnce({ data: '<small>' }); // small metadata
|
|
147
|
-
|
|
148
|
-
await getAuth0Metadata(site, env, auth0Tenant);
|
|
149
|
-
|
|
150
|
-
expect(process.exit).toHaveBeenCalledWith(1);
|
|
151
|
-
expect(console.error).toHaveBeenCalledWith(
|
|
152
|
-
'Downloaded metadata looks incorrect (too small). Exiting...',
|
|
153
|
-
);
|
|
154
|
-
});
|
|
155
|
-
|
|
156
|
-
it('should exit if connectContext is missing', async () => {
|
|
157
|
-
fs.readFile.mockResolvedValue(`docupace.baseUrlClient = "https://abc.com"`); // no context
|
|
158
|
-
|
|
159
|
-
await getAuth0Metadata(site, env, auth0Tenant);
|
|
160
|
-
|
|
161
|
-
expect(process.exit).toHaveBeenCalledWith(1);
|
|
162
|
-
expect(console.error).toHaveBeenCalledWith(
|
|
163
|
-
'connectContext not found in application.conf. Exiting...',
|
|
164
|
-
);
|
|
165
|
-
});
|
|
166
|
-
|
|
167
|
-
it('should exit if siteHost is missing', async () => {
|
|
168
|
-
fs.readFile.mockResolvedValue(`application.context = "/abc"`); // no host
|
|
169
|
-
|
|
170
|
-
await getAuth0Metadata(site, env, auth0Tenant);
|
|
171
|
-
|
|
172
|
-
expect(process.exit).toHaveBeenCalledWith(1);
|
|
173
|
-
expect(console.error).toHaveBeenCalledWith(
|
|
174
|
-
'siteHost not found in application.conf. Exiting...',
|
|
175
|
-
);
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
it('should exit on unknown error', async () => {
|
|
179
|
-
getVaultSecrets.mockRejectedValue(new Error('boom'));
|
|
180
|
-
|
|
181
|
-
await getAuth0Metadata(site, env, auth0Tenant);
|
|
182
|
-
|
|
183
|
-
expect(process.exit).toHaveBeenCalledWith(1);
|
|
184
|
-
expect(console.error).toHaveBeenCalledWith('Error:', 'boom');
|
|
185
|
-
});
|
|
186
|
-
});
|