@ibm-cloud/cd-tools 1.15.16 → 1.15.18
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/cmd/copy-toolchain.js +5 -1
- package/cmd/utils/import-terraform.js +8 -3
- package/cmd/utils/terraform.js +14 -6
- package/cmd/utils/utils.js +7 -0
- package/package.json +3 -2
- package/test/README.md +8 -2
- package/test/copy-toolchain/tf-import.test.js +2 -2
- package/test/data/test-toolchains.js +10 -20
package/cmd/copy-toolchain.js
CHANGED
|
@@ -86,7 +86,11 @@ async function main(options) {
|
|
|
86
86
|
|
|
87
87
|
logger.setVerbosity(verbosity);
|
|
88
88
|
if (LOG_DUMP) logger.createLogStream(`${LOGS_DIR}/copy-toolchain-${TIME_SUFFIX}.log`);
|
|
89
|
-
|
|
89
|
+
|
|
90
|
+
// redact apikey option in logs
|
|
91
|
+
const printOptions = options;
|
|
92
|
+
printOptions.apikey ? printOptions.apikey = '<API KEY>' : delete printOptions.apikey;
|
|
93
|
+
logger.dump(`Options: ${JSON.stringify(printOptions)}\n`);
|
|
90
94
|
|
|
91
95
|
let bearer;
|
|
92
96
|
let sourceToolchainId;
|
|
@@ -14,7 +14,7 @@ import { jsonToTf } from 'json-to-tf';
|
|
|
14
14
|
|
|
15
15
|
import { getPipelineData, getToolchainTools } from './requests.js';
|
|
16
16
|
import { runTerraformPlanGenerate, setTerraformEnv } from './terraform.js';
|
|
17
|
-
import { getRandChars, isSecretReference, normalizeName } from './utils.js';
|
|
17
|
+
import { escapeReservedChars, getRandChars, isSecretReference, normalizeName } from './utils.js';
|
|
18
18
|
import { logger } from './logger.js';
|
|
19
19
|
|
|
20
20
|
import { SECRET_KEYS_MAP, SUPPORTED_TOOLS_MAP } from '../../config.js';
|
|
@@ -263,7 +263,7 @@ export async function importTerraform(token, apiKey, region, toolchainId, toolch
|
|
|
263
263
|
if (propValue.startsWith(START_INDICATOR) && propValue.endsWith(END_INDICATOR)) {
|
|
264
264
|
// skip substitution for jsonencode case, don't want to mangle it
|
|
265
265
|
} else {
|
|
266
|
-
newValue =
|
|
266
|
+
newValue = escapeReservedChars(propValue);
|
|
267
267
|
}
|
|
268
268
|
newTfFileObj['resource'][key][k]['value'] = newValue;
|
|
269
269
|
}
|
|
@@ -314,7 +314,12 @@ export async function importTerraform(token, apiKey, region, toolchainId, toolch
|
|
|
314
314
|
for (const [key, value] of Object.entries(generatedFileJson['resource'])) {
|
|
315
315
|
for (const [k, v] of Object.entries(value)) {
|
|
316
316
|
if (key === 'ibm_cd_tekton_pipeline_definition' || key === 'ibm_cd_tekton_pipeline_trigger') {
|
|
317
|
-
|
|
317
|
+
// escape filter, which may contain reserved characters
|
|
318
|
+
if (key === 'ibm_cd_tekton_pipeline_trigger' && v[0]['filter']) {
|
|
319
|
+
newTfFileObj['resource'][key][k]['filter'] = escapeReservedChars(v[0]['filter']);
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
if (key === 'ibm_cd_tekton_pipeline_trigger' && !v[0]['source']) continue; // skip triggers without source, which aren't tied to a repo
|
|
318
323
|
try {
|
|
319
324
|
const thisUrl = newTfFileObj['resource'][key][k]['source'][0]['properties'][0]['url'];
|
|
320
325
|
|
package/cmd/utils/terraform.js
CHANGED
|
@@ -16,7 +16,7 @@ import { jsonToTf } from 'json-to-tf';
|
|
|
16
16
|
|
|
17
17
|
import { validateToolchainId, validateGritUrl } from './validate.js';
|
|
18
18
|
import { logger, LOG_STAGES } from './logger.js';
|
|
19
|
-
import { getRandChars, promptUserInput, replaceUrlRegion } from './utils.js';
|
|
19
|
+
import { escapeReservedChars, getRandChars, promptUserInput, replaceUrlRegion } from './utils.js';
|
|
20
20
|
|
|
21
21
|
const CLOUD_PLATFORM = process.env['IBMCLOUD_PLATFORM_DOMAIN'] || 'cloud.ibm.com';
|
|
22
22
|
const DEV_MODE = CLOUD_PLATFORM !== 'cloud.ibm.com';
|
|
@@ -31,7 +31,7 @@ const writeFilePromise = promisify(fs.writeFile)
|
|
|
31
31
|
async function execPromise(command, options) {
|
|
32
32
|
try {
|
|
33
33
|
const exec = promisify(child_process.exec);
|
|
34
|
-
const { stdout, _ } = await exec(command, options);
|
|
34
|
+
const { stdout, _ } = await exec(command, {...options, maxBuffer: 10 * 1024 * 1024}); // set max buffer to 10MB
|
|
35
35
|
return stdout.trim();
|
|
36
36
|
} catch (err) {
|
|
37
37
|
throw new Error(`Command failed: ${command} \n${err.stderr || err.stdout}`);
|
|
@@ -258,8 +258,16 @@ async function setupTerraformFiles(config) {
|
|
|
258
258
|
// by default, disable triggers
|
|
259
259
|
if (disableTriggers && newTfFileObj['resource']['ibm_cd_tekton_pipeline_trigger']) {
|
|
260
260
|
for (const key of Object.keys(newTfFileObj['resource']['ibm_cd_tekton_pipeline_trigger'])) {
|
|
261
|
-
|
|
262
|
-
newTfFileObj['resource']['ibm_cd_tekton_pipeline_trigger'][key]['
|
|
261
|
+
// escape filter, which may contain reserved characters
|
|
262
|
+
const filterVal = newTfFileObj['resource']['ibm_cd_tekton_pipeline_trigger'][key]['filter'];
|
|
263
|
+
if (filterVal) {
|
|
264
|
+
newTfFileObj['resource']['ibm_cd_tekton_pipeline_trigger'][key]['filter'] = escapeReservedChars(filterVal);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// disable all triggers (timed, git, webhooks) except manual triggers
|
|
268
|
+
if (newTfFileObj['resource']['ibm_cd_tekton_pipeline_trigger'][key]['type'] != 'manual') {
|
|
269
|
+
newTfFileObj['resource']['ibm_cd_tekton_pipeline_trigger'][key]['enabled'] = false;
|
|
270
|
+
}
|
|
263
271
|
}
|
|
264
272
|
}
|
|
265
273
|
|
|
@@ -360,7 +368,7 @@ async function setupTerraformFiles(config) {
|
|
|
360
368
|
if (thisValue.startsWith(START_INDICATOR) && thisValue.endsWith(END_INDICATOR)) {
|
|
361
369
|
// skip newline substitution for jsonencode case, don't want to mangle it
|
|
362
370
|
} else {
|
|
363
|
-
newTfFileObj['resource']['ibm_cd_tekton_pipeline_property'][k]['value'] = thisValue
|
|
371
|
+
newTfFileObj['resource']['ibm_cd_tekton_pipeline_property'][k]['value'] = escapeReservedChars(thisValue);
|
|
364
372
|
}
|
|
365
373
|
}
|
|
366
374
|
} catch (err) {
|
|
@@ -388,7 +396,7 @@ async function setupTerraformFiles(config) {
|
|
|
388
396
|
if (thisValue.startsWith(START_INDICATOR) && thisValue.endsWith(END_INDICATOR)) {
|
|
389
397
|
// skip newline substitution for jsonencode case, don't want to mangle it
|
|
390
398
|
} else {
|
|
391
|
-
newTfFileObj['resource']['ibm_cd_tekton_pipeline_trigger_property'][k]['value'] = thisValue
|
|
399
|
+
newTfFileObj['resource']['ibm_cd_tekton_pipeline_trigger_property'][k]['value'] = escapeReservedChars(thisValue);
|
|
392
400
|
}
|
|
393
401
|
}
|
|
394
402
|
} catch (err) {
|
package/cmd/utils/utils.js
CHANGED
|
@@ -223,4 +223,11 @@ export function normalizeName(str) {
|
|
|
223
223
|
}
|
|
224
224
|
|
|
225
225
|
return newStr.toLowerCase();
|
|
226
|
+
};
|
|
227
|
+
|
|
228
|
+
export function escapeReservedChars(str) {
|
|
229
|
+
if (!str || typeof str !== 'string') {
|
|
230
|
+
return str;
|
|
231
|
+
}
|
|
232
|
+
return str.replace(/\\/g, '\\\\').replace(/\n/g, '\\n').replace(/\r/g, '\\r').replace(/"/g, '\\"');
|
|
226
233
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ibm-cloud/cd-tools",
|
|
3
|
-
"version": "1.15.
|
|
3
|
+
"version": "1.15.18",
|
|
4
4
|
"description": "Tools and utilities for the IBM Cloud Continuous Delivery service and resources",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"node-pty": "^1.0.0"
|
|
41
41
|
},
|
|
42
42
|
"overrides": {
|
|
43
|
-
"serialize-javascript": "^7.0.
|
|
43
|
+
"serialize-javascript": "^7.0.5",
|
|
44
|
+
"diff": "^8.0.0"
|
|
44
45
|
}
|
|
45
46
|
}
|
package/test/README.md
CHANGED
|
@@ -13,14 +13,20 @@ Before running tests, ensure that you have completed the setup steps in the main
|
|
|
13
13
|
```bash
|
|
14
14
|
npm install
|
|
15
15
|
```
|
|
16
|
-
3. **
|
|
16
|
+
3. **Fix node-pty executable**
|
|
17
|
+
|
|
18
|
+
macOS only: There is a bug in one of the executables used by `node-pty`, the prebuilt binary is missing the executable flag, causing errors like `Error: posix_spawnp failed.`. You can fix this manually:
|
|
19
|
+
```bash
|
|
20
|
+
chmod +x node_modules/node-pty/prebuilds/darwin-arm64/spawn-helper
|
|
21
|
+
```
|
|
22
|
+
4. **Test configuration**
|
|
17
23
|
|
|
18
24
|
Before running tests, create a local configuration file:
|
|
19
25
|
```bash
|
|
20
26
|
cp test/config/local.template.json test/config/local.json
|
|
21
27
|
```
|
|
22
28
|
Then open `test/config/local.json` and replace all placeholder values with your local or test environment settings.
|
|
23
|
-
|
|
29
|
+
5. **Running the tests**
|
|
24
30
|
|
|
25
31
|
To execute the test suite:
|
|
26
32
|
```bash
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Licensed Materials - Property of IBM
|
|
3
|
-
* (c) Copyright IBM Corporation 2025. All Rights Reserved.
|
|
3
|
+
* (c) Copyright IBM Corporation 2025, 2026. All Rights Reserved.
|
|
4
4
|
*
|
|
5
5
|
* Note to U.S. Government Users Restricted Rights:
|
|
6
6
|
* Use, duplication or disclosure restricted by GSA ADP Schedule
|
|
@@ -93,7 +93,7 @@ describe('copy-toolchain: Test import-terraform output', function () {
|
|
|
93
93
|
ibm_cd_tekton_pipeline_trigger: 1,
|
|
94
94
|
ibm_cd_tekton_pipeline_trigger_property: 1,
|
|
95
95
|
ibm_cd_toolchain: 1,
|
|
96
|
-
|
|
96
|
+
ibm_cd_toolchain_tool_cos: 1,
|
|
97
97
|
ibm_cd_toolchain_tool_devopsinsights: 1,
|
|
98
98
|
ibm_cd_toolchain_tool_hostedgit: 1,
|
|
99
99
|
ibm_cd_toolchain_tool_pipeline: 1,
|
|
@@ -15,38 +15,28 @@ export const TEST_TOOLCHAINS = {
|
|
|
15
15
|
},
|
|
16
16
|
'misconfigured': {
|
|
17
17
|
name: 'KEEP-MISCONFIGURED-TOOLCHAIN',
|
|
18
|
-
crn: 'crn:v1:bluemix:public:toolchain:eu-
|
|
19
|
-
region: 'eu-
|
|
18
|
+
crn: 'crn:v1:bluemix:public:toolchain:eu-gb:a/9e8559fac61ee9fc74d3e595fa75d147:128b55a7-56c3-4cc0-87f7-abb24ff0fc6a::',
|
|
19
|
+
region: 'eu-gb'
|
|
20
20
|
},
|
|
21
21
|
'1pl-ghe-cc': {
|
|
22
22
|
name: 'KEEP-1PL-GHE-CC',
|
|
23
|
-
crn: 'crn:v1:bluemix:public:toolchain:eu-
|
|
24
|
-
region: 'eu-
|
|
23
|
+
crn: 'crn:v1:bluemix:public:toolchain:eu-de:a/9e8559fac61ee9fc74d3e595fa75d147:6d75fa57-4eb0-4654-a664-58233ee2aad2::',
|
|
24
|
+
region: 'eu-de'
|
|
25
25
|
},
|
|
26
26
|
'1pl-ghe-cd': {
|
|
27
27
|
name: 'KEEP-1PL-GHE-CD',
|
|
28
|
-
crn: 'crn:v1:bluemix:public:toolchain:eu-
|
|
29
|
-
region: 'eu-
|
|
28
|
+
crn: 'crn:v1:bluemix:public:toolchain:eu-de:a/9e8559fac61ee9fc74d3e595fa75d147:34df7ab1-bb87-4c34-bf72-1e83d04b0999::',
|
|
29
|
+
region: 'eu-de'
|
|
30
30
|
},
|
|
31
31
|
'1pl-ghe-ci': {
|
|
32
32
|
name: 'KEEP-1PL-GHE-CI',
|
|
33
|
-
crn: 'crn:v1:bluemix:public:toolchain:eu-
|
|
34
|
-
region: 'eu-
|
|
35
|
-
},
|
|
36
|
-
'devsecops-grit-cc': {
|
|
37
|
-
name: 'KEEP-DevSecOps-GRIT-CC',
|
|
38
|
-
crn: 'crn:v1:bluemix:public:toolchain:eu-es:a/9e8559fac61ee9fc74d3e595fa75d147:920f6a94-4c1b-412b-b95c-baf823958744::',
|
|
39
|
-
region: 'eu-es'
|
|
40
|
-
},
|
|
41
|
-
'devsecops-grit-cd': {
|
|
42
|
-
name: 'KEEP-DevSecOps-GRIT-CD',
|
|
43
|
-
crn: 'crn:v1:bluemix:public:toolchain:eu-es:a/9e8559fac61ee9fc74d3e595fa75d147:8618565f-08fa-4cac-9250-029cac7b41ba::',
|
|
44
|
-
region: 'eu-es'
|
|
33
|
+
crn: 'crn:v1:bluemix:public:toolchain:eu-de:a/9e8559fac61ee9fc74d3e595fa75d147:7769bb82-0ef5-4b5d-92c1-5089ac7ff385::',
|
|
34
|
+
region: 'eu-de'
|
|
45
35
|
},
|
|
46
36
|
'devsecops-grit-ci': {
|
|
47
37
|
name: 'KEEP-DevSecOps-GRIT-CI',
|
|
48
|
-
crn: 'crn:v1:bluemix:public:toolchain:eu-
|
|
49
|
-
region: 'eu-
|
|
38
|
+
crn: 'crn:v1:bluemix:public:toolchain:eu-de:a/9e8559fac61ee9fc74d3e595fa75d147:1befe39f-e278-439b-a59a-fb16f14119c3::',
|
|
39
|
+
region: 'eu-de'
|
|
50
40
|
},
|
|
51
41
|
'single-pl': {
|
|
52
42
|
name: 'KEEP-SINGLE-PIPELINE-TOOLCHAIN',
|