zapier-platform-cli 18.0.1 → 18.0.6
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/package.json +20 -22
- package/src/generators/templates/README.template.md +1 -1
- package/src/generators/templates/gitignore +3 -0
- package/src/oclif/commands/build.js +7 -6
- package/src/oclif/commands/push.js +4 -4
- package/src/utils/build.js +19 -9
- package/src/utils/decompress.js +121 -0
- package/src/utils/misc.js +1 -1
- package/src/utils/package-manager.js +31 -14
- package/oclif.manifest.json +0 -2559
- package/src/generators/templates/search-or-create/.gitignore +0 -6
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "zapier-platform-cli",
|
|
3
|
-
"version": "18.0.
|
|
3
|
+
"version": "18.0.6",
|
|
4
4
|
"description": "The CLI for managing integrations in Zapier Developer Platform.",
|
|
5
5
|
"repository": "zapier/zapier-platform",
|
|
6
6
|
"homepage": "https://platform.zapier.com/",
|
|
@@ -20,24 +20,6 @@
|
|
|
20
20
|
"engines": {
|
|
21
21
|
"node": ">=18.20"
|
|
22
22
|
},
|
|
23
|
-
"scripts": {
|
|
24
|
-
"docs": "ZAPIER_BASE_ENDPOINT='' node scripts/docs.js",
|
|
25
|
-
"preversion": "git pull && yarn validate",
|
|
26
|
-
"prepack": "oclif manifest",
|
|
27
|
-
"postpack": "rimraf oclif.manifest.json",
|
|
28
|
-
"precommit": "yarn docs && git add docs",
|
|
29
|
-
"version": "yarn docs && git add docs/*",
|
|
30
|
-
"postversion": "git push && git push --tags",
|
|
31
|
-
"lint": "eslint src",
|
|
32
|
-
"lint:fix": "eslint --fix src",
|
|
33
|
-
"test": "cross-env NODE_ENV=test mocha -t 200s --recursive src/tests --exit",
|
|
34
|
-
"test:debug": "cross-env NODE_ENV=test node inspect ../../node_modules/.bin/mocha -t 200s --recursive src/tests --exit",
|
|
35
|
-
"smoke-test": "cross-env NODE_ENV=test mocha -t 6m --recursive src/smoke-tests --exit",
|
|
36
|
-
"smoke-test:debug": "cross-env NODE_ENV=test node inspect ../../node_modules/.bin/mocha -t 6m --recursive src/smoke-tests --exit",
|
|
37
|
-
"validate-templates": "./scripts/validate-app-templates.js",
|
|
38
|
-
"set-template-versions": "./scripts/set-app-template-versions.js",
|
|
39
|
-
"validate": "yarn test && yarn smoke-test && yarn lint"
|
|
40
|
-
},
|
|
41
23
|
"dependencies": {
|
|
42
24
|
"@oclif/core": "4.5.2",
|
|
43
25
|
"@oclif/plugin-autocomplete": "3.2.34",
|
|
@@ -50,7 +32,7 @@
|
|
|
50
32
|
"cli-table3": "0.6.5",
|
|
51
33
|
"colors": "1.4.0",
|
|
52
34
|
"debug": "4.4.1",
|
|
53
|
-
"decompress": "4.
|
|
35
|
+
"decompress-unzip": "4.0.1",
|
|
54
36
|
"dotenv": "17.2.1",
|
|
55
37
|
"esbuild": "0.25.8",
|
|
56
38
|
"fs-extra": "11.3.1",
|
|
@@ -71,7 +53,7 @@
|
|
|
71
53
|
"semver": "7.7.2",
|
|
72
54
|
"string-length": "4.0.2",
|
|
73
55
|
"through2": "4.0.2",
|
|
74
|
-
"tmp": "0.2.
|
|
56
|
+
"tmp": "0.2.5",
|
|
75
57
|
"traverse": "0.6.11",
|
|
76
58
|
"update-notifier": "7.3.1",
|
|
77
59
|
"yeoman-environment": "4.4.3",
|
|
@@ -136,5 +118,21 @@
|
|
|
136
118
|
"description": "Add, remove, or get invited users of your integration."
|
|
137
119
|
}
|
|
138
120
|
}
|
|
121
|
+
},
|
|
122
|
+
"scripts": {
|
|
123
|
+
"build-docs": "ZAPIER_BASE_ENDPOINT='' node scripts/docs.js",
|
|
124
|
+
"preversion": "git pull && pnpm validate",
|
|
125
|
+
"precommit": "pnpm build-docs && git add docs",
|
|
126
|
+
"version": "pnpm build-docs && git add docs/*",
|
|
127
|
+
"postversion": "git push && git push --tags",
|
|
128
|
+
"lint": "eslint src",
|
|
129
|
+
"lint:fix": "eslint --fix src",
|
|
130
|
+
"test": "cross-env NODE_ENV=test mocha -t 200s --recursive src/tests --exit",
|
|
131
|
+
"test:debug": "cross-env NODE_ENV=test node inspect ../../node_modules/.bin/mocha -t 200s --recursive src/tests --exit",
|
|
132
|
+
"smoke-test": "cross-env NODE_ENV=test mocha -t 6m --recursive src/smoke-tests --exit",
|
|
133
|
+
"smoke-test:debug": "cross-env NODE_ENV=test node inspect ../../node_modules/.bin/mocha -t 6m --recursive src/smoke-tests --exit",
|
|
134
|
+
"validate-templates": "./scripts/validate-app-templates.js",
|
|
135
|
+
"set-template-versions": "./scripts/set-app-template-versions.js",
|
|
136
|
+
"validate": "pnpm test && pnpm smoke-test && pnpm lint"
|
|
139
137
|
}
|
|
140
|
-
}
|
|
138
|
+
}
|
|
@@ -12,11 +12,11 @@ const colors = require('colors/safe');
|
|
|
12
12
|
|
|
13
13
|
class BuildCommand extends BaseCommand {
|
|
14
14
|
async perform() {
|
|
15
|
-
const
|
|
15
|
+
const skipDepInstall = this.flags['skip-dep-install'];
|
|
16
16
|
await buildAndOrUpload(
|
|
17
17
|
{ build: true },
|
|
18
18
|
{
|
|
19
|
-
|
|
19
|
+
skipDepInstall,
|
|
20
20
|
disableDependencyDetection: this.flags['disable-dependency-detection'],
|
|
21
21
|
skipValidation: this.flags['skip-validation'],
|
|
22
22
|
},
|
|
@@ -27,9 +27,9 @@ class BuildCommand extends BaseCommand {
|
|
|
27
27
|
`Now you can upload them with the ${colors.bold.underline('zapier upload')} command.`,
|
|
28
28
|
);
|
|
29
29
|
|
|
30
|
-
if (!
|
|
30
|
+
if (!skipDepInstall) {
|
|
31
31
|
this.log(
|
|
32
|
-
`\nTip: Try ${colors.bold.underline('zapier build --skip-
|
|
32
|
+
`\nTip: Try ${colors.bold.underline('zapier build --skip-dep-install')} for faster builds.`,
|
|
33
33
|
);
|
|
34
34
|
}
|
|
35
35
|
}
|
|
@@ -40,9 +40,10 @@ BuildCommand.flags = buildFlags({
|
|
|
40
40
|
'disable-dependency-detection': Flags.boolean({
|
|
41
41
|
description: `Disable "smart" file inclusion. By default, Zapier only includes files that are required by your entry point (\`index.js\` by default). If you (or your dependencies) require files dynamically (such as with \`require(someVar)\`), then you may see "Cannot find module" errors. Disabling this may make your \`build.zip\` too large. If that's the case, try using the \`includeInBuild\` option in your \`${CURRENT_APP_FILE}\`. See the docs about \`includeInBuild\` for more info.`,
|
|
42
42
|
}),
|
|
43
|
-
'skip-
|
|
43
|
+
'skip-dep-install': Flags.boolean({
|
|
44
|
+
aliases: ['skip-npm-install'],
|
|
44
45
|
description:
|
|
45
|
-
'
|
|
46
|
+
'[alias: --skip-npm-install]\nSkips installing a fresh copy of dependencies for shorter build time. Helpful for using yarn, pnpm, or local copies of dependencies.',
|
|
46
47
|
}),
|
|
47
48
|
'skip-validation': Flags.boolean({
|
|
48
49
|
description:
|
|
@@ -8,7 +8,7 @@ const BuildCommand = require('./build');
|
|
|
8
8
|
const { buildAndOrUpload } = require('../../utils/build');
|
|
9
9
|
class PushCommand extends ZapierBaseCommand {
|
|
10
10
|
async perform() {
|
|
11
|
-
const
|
|
11
|
+
const skipDepInstall = this.flags['skip-dep-install'];
|
|
12
12
|
|
|
13
13
|
const snapshotLabel = this.flags.snapshot;
|
|
14
14
|
if (snapshotLabel && snapshotLabel.length > 12) {
|
|
@@ -22,7 +22,7 @@ class PushCommand extends ZapierBaseCommand {
|
|
|
22
22
|
await buildAndOrUpload(
|
|
23
23
|
{ build: true, upload: true },
|
|
24
24
|
{
|
|
25
|
-
|
|
25
|
+
skipDepInstall,
|
|
26
26
|
disableDependencyDetection: this.flags['disable-dependency-detection'],
|
|
27
27
|
skipValidation: this.flags['skip-validation'],
|
|
28
28
|
overwritePartnerChanges: this.flags['overwrite-partner-changes'],
|
|
@@ -33,9 +33,9 @@ class PushCommand extends ZapierBaseCommand {
|
|
|
33
33
|
`\nPush complete! Built ${BUILD_PATH} and ${SOURCE_PATH} and uploaded them to Zapier.`,
|
|
34
34
|
);
|
|
35
35
|
|
|
36
|
-
if (!
|
|
36
|
+
if (!skipDepInstall) {
|
|
37
37
|
this.log(
|
|
38
|
-
`\nTip: Try ${colors.bold.underline('zapier push --skip-
|
|
38
|
+
`\nTip: Try ${colors.bold.underline('zapier push --skip-dep-install')} for faster builds.`,
|
|
39
39
|
);
|
|
40
40
|
}
|
|
41
41
|
}
|
package/src/utils/build.js
CHANGED
|
@@ -12,7 +12,7 @@ const colors = require('colors/safe');
|
|
|
12
12
|
const esbuild = require('esbuild');
|
|
13
13
|
const fse = require('fs-extra');
|
|
14
14
|
const { createUpdateNotifier } = require('./esm-wrapper');
|
|
15
|
-
const decompress = require('decompress');
|
|
15
|
+
const decompress = require('./decompress');
|
|
16
16
|
|
|
17
17
|
const {
|
|
18
18
|
BUILD_DIR,
|
|
@@ -46,6 +46,7 @@ const { findCorePackageDir, isWindows, runCommand } = require('./misc');
|
|
|
46
46
|
const { isBlocklisted, respectGitIgnore } = require('./ignore');
|
|
47
47
|
const { localAppCommand } = require('./local');
|
|
48
48
|
const { throwForInvalidVersion } = require('./version');
|
|
49
|
+
const { getPackageManager } = require('./package-manager');
|
|
49
50
|
|
|
50
51
|
const debug = require('debug')('zapier:build');
|
|
51
52
|
|
|
@@ -592,7 +593,7 @@ const testBuildZip = async (zipPath) => {
|
|
|
592
593
|
|
|
593
594
|
const _buildFunc = async (
|
|
594
595
|
{
|
|
595
|
-
|
|
596
|
+
skipDepInstall = false,
|
|
596
597
|
disableDependencyDetection = false,
|
|
597
598
|
skipValidation = false,
|
|
598
599
|
printProgress = true,
|
|
@@ -609,7 +610,7 @@ const _buildFunc = async (
|
|
|
609
610
|
const appDir = process.cwd();
|
|
610
611
|
|
|
611
612
|
let workingDir;
|
|
612
|
-
if (
|
|
613
|
+
if (skipDepInstall) {
|
|
613
614
|
workingDir = appDir;
|
|
614
615
|
debug('Building in app directory: ', workingDir);
|
|
615
616
|
} else {
|
|
@@ -628,16 +629,25 @@ const _buildFunc = async (
|
|
|
628
629
|
const buildDir = path.join(appDir, BUILD_DIR);
|
|
629
630
|
await fse.ensureDir(buildDir);
|
|
630
631
|
|
|
631
|
-
if (!
|
|
632
|
+
if (!skipDepInstall) {
|
|
632
633
|
maybeStartSpinner('Copying project to temp directory');
|
|
633
634
|
const copyFilter = (src) => !src.endsWith('.zip');
|
|
634
635
|
await copyDir(appDir, workingDir, { filter: copyFilter });
|
|
635
636
|
|
|
636
637
|
maybeEndSpinner();
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
638
|
+
const packageManager = await getPackageManager();
|
|
639
|
+
|
|
640
|
+
maybeStartSpinner(
|
|
641
|
+
`Installing project dependencies using ${packageManager.executable}`,
|
|
642
|
+
);
|
|
643
|
+
|
|
644
|
+
const output = await runCommand(
|
|
645
|
+
packageManager.executable,
|
|
646
|
+
['install', '--production'],
|
|
647
|
+
{
|
|
648
|
+
cwd: workingDir,
|
|
649
|
+
},
|
|
650
|
+
);
|
|
641
651
|
|
|
642
652
|
// `npm install` may fail silently without returning a non-zero exit code,
|
|
643
653
|
// need to check further here
|
|
@@ -744,7 +754,7 @@ const _buildFunc = async (
|
|
|
744
754
|
|
|
745
755
|
maybeEndSpinner();
|
|
746
756
|
|
|
747
|
-
if (
|
|
757
|
+
if (skipDepInstall) {
|
|
748
758
|
maybeStartSpinner('Cleaning up temp files');
|
|
749
759
|
await deleteZapierWrapper(workingDir);
|
|
750
760
|
fs.rmSync(path.join(workingDir, 'definition.json'));
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// This is a modernized version of the decompress package.
|
|
2
|
+
// Instead of letting decompress import many of its plugins, such as
|
|
3
|
+
// decompress-unzip, decompress-tar, etc, we extracted only the unzip part,
|
|
4
|
+
// so we only depend on decompress-unzip.
|
|
5
|
+
// Original source: https://github.com/kevva/decompress/blob/84a8c104/index.js
|
|
6
|
+
|
|
7
|
+
const fsP = require('node:fs/promises');
|
|
8
|
+
const path = require('node:path');
|
|
9
|
+
|
|
10
|
+
const decompressUnzip = require('decompress-unzip');
|
|
11
|
+
|
|
12
|
+
const safeMakeDir = async (dir, realOutputPath) => {
|
|
13
|
+
let resolvedPathToCheck;
|
|
14
|
+
|
|
15
|
+
try {
|
|
16
|
+
resolvedPathToCheck = await fsP.realpath(dir);
|
|
17
|
+
} catch (error) {
|
|
18
|
+
const parent = path.dirname(dir);
|
|
19
|
+
resolvedPathToCheck = await safeMakeDir(parent, realOutputPath);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Security check for zip slip vulnerability
|
|
23
|
+
if (!resolvedPathToCheck.startsWith(realOutputPath)) {
|
|
24
|
+
throw new Error('Refusing to create a directory outside the output path.');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
await fsP.mkdir(dir, { recursive: true });
|
|
28
|
+
return await fsP.realpath(dir);
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const preventWritingThroughSymlink = async (destination, realOutputPath) => {
|
|
32
|
+
let symlinkPointsTo;
|
|
33
|
+
try {
|
|
34
|
+
symlinkPointsTo = await fsP.readlink(destination);
|
|
35
|
+
} catch (error) {
|
|
36
|
+
// Either no file exists, or it's not a symlink. In either case, this is
|
|
37
|
+
// not an escape we need to worry about in this phase.
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (symlinkPointsTo) {
|
|
41
|
+
throw new Error('Refusing to write into a symlink');
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const extractItem = async (item, realOutputPath) => {
|
|
46
|
+
const dest = path.join(realOutputPath, item.path);
|
|
47
|
+
const mode = item.mode & ~process.umask();
|
|
48
|
+
const now = new Date();
|
|
49
|
+
|
|
50
|
+
if (item.type === 'directory') {
|
|
51
|
+
await safeMakeDir(dest, realOutputPath);
|
|
52
|
+
await fsP.utimes(dest, now, item.mtime);
|
|
53
|
+
return item;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
await safeMakeDir(path.dirname(dest), realOutputPath);
|
|
57
|
+
|
|
58
|
+
if (item.type === 'file') {
|
|
59
|
+
await preventWritingThroughSymlink(dest, realOutputPath);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const realDestinationDir = await fsP.realpath(path.dirname(dest));
|
|
63
|
+
if (!realDestinationDir.startsWith(realOutputPath)) {
|
|
64
|
+
throw new Error(
|
|
65
|
+
'Refusing to write outside output directory: ' + realDestinationDir,
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if (item.type === 'symlink') {
|
|
70
|
+
// Security check to block symlinks pointing outside output directory,
|
|
71
|
+
// like evil_link -> ../../../../../etc/passwd. We don't throw an error
|
|
72
|
+
// here, just skip creating the symlink, because there are known zip files
|
|
73
|
+
// containing such symlinks (such as .editorconfig) that are not essential
|
|
74
|
+
// to the integration.
|
|
75
|
+
const absTargetPath = path.resolve(realDestinationDir, item.linkname);
|
|
76
|
+
const relTargetPath = path.relative(realOutputPath, absTargetPath);
|
|
77
|
+
if (!relTargetPath.startsWith('..')) {
|
|
78
|
+
// Windows will have issues with the following line, since creating a
|
|
79
|
+
// symlink on Windows requires Administrator privilege. But that's fine
|
|
80
|
+
// because we only run decompress on Windows in CI tests, which do run
|
|
81
|
+
// with Administrator privilege.
|
|
82
|
+
await fsP.symlink(item.linkname, dest);
|
|
83
|
+
}
|
|
84
|
+
} else {
|
|
85
|
+
await fsP.writeFile(dest, item.data, { mode });
|
|
86
|
+
await fsP.utimes(dest, now, item.mtime);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return item;
|
|
90
|
+
};
|
|
91
|
+
|
|
92
|
+
const decompress = async (input, output) => {
|
|
93
|
+
if (typeof input !== 'string') {
|
|
94
|
+
throw new TypeError('input must be a file path (string)');
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
if (typeof output !== 'string') {
|
|
98
|
+
throw new TypeError('output must be a directory path (string)');
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
let fd, buf;
|
|
102
|
+
try {
|
|
103
|
+
fd = await fsP.open(input);
|
|
104
|
+
buf = await fd.readFile();
|
|
105
|
+
} finally {
|
|
106
|
+
await fd?.close();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// Ensure output directory exists
|
|
110
|
+
await fsP.mkdir(output, { recursive: true });
|
|
111
|
+
const realOutputPath = await fsP.realpath(output);
|
|
112
|
+
|
|
113
|
+
const items = await decompressUnzip()(buf);
|
|
114
|
+
return await Promise.all(
|
|
115
|
+
items.map(async (x) => {
|
|
116
|
+
return await extractItem(x, realOutputPath);
|
|
117
|
+
}),
|
|
118
|
+
);
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
module.exports = decompress;
|
package/src/utils/misc.js
CHANGED
|
@@ -1,39 +1,56 @@
|
|
|
1
|
-
const { pathExists } = require('fs-extra');
|
|
1
|
+
const { pathExists, readFile } = require('fs-extra');
|
|
2
2
|
const path = require('path');
|
|
3
3
|
|
|
4
|
-
const packageManagers =
|
|
5
|
-
{
|
|
4
|
+
const packageManagers = {
|
|
5
|
+
npm: {
|
|
6
6
|
lockFile: 'package-lock.json',
|
|
7
7
|
forceFlag: undefined,
|
|
8
8
|
executable: 'npm',
|
|
9
9
|
useDoubleHyphenBeforeArgs: true,
|
|
10
10
|
},
|
|
11
|
-
{
|
|
11
|
+
yarn: {
|
|
12
12
|
lockFile: 'yarn.lock',
|
|
13
13
|
forceFlag: 'yarn',
|
|
14
14
|
executable: 'yarn',
|
|
15
15
|
useDoubleHyphenBeforeArgs: false, // yarn gives a warning if we include `--`
|
|
16
16
|
},
|
|
17
|
-
{
|
|
17
|
+
pnpm: {
|
|
18
18
|
lockFile: 'pnpm-lock.yaml',
|
|
19
19
|
forceFlag: 'pnpm',
|
|
20
20
|
executable: 'pnpm',
|
|
21
21
|
useDoubleHyphenBeforeArgs: true,
|
|
22
22
|
},
|
|
23
|
-
|
|
23
|
+
bun: {
|
|
24
|
+
lockFile: 'bun.lock',
|
|
25
|
+
forceFlag: 'bun',
|
|
26
|
+
executable: 'bun',
|
|
27
|
+
useDoubleHyphenBeforeArgs: false,
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const defaultPackageManager = packageManagers.npm;
|
|
32
|
+
|
|
33
|
+
const getPackageManager = async (flags = {}) => {
|
|
34
|
+
for (const config of Object.values(packageManagers)) {
|
|
35
|
+
if (config.forceFlag && flags[config.forceFlag]) {
|
|
36
|
+
return config;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
24
39
|
|
|
25
|
-
const
|
|
40
|
+
const packageJsonPath = path.join(process.cwd(), 'package.json');
|
|
41
|
+
if (await pathExists(packageJsonPath)) {
|
|
42
|
+
const packageJson = JSON.parse(await readFile(packageJsonPath, 'utf-8'));
|
|
26
43
|
|
|
27
|
-
const
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
44
|
+
for (const man of Object.keys(packageManagers)) {
|
|
45
|
+
if (packageJson.packageManager?.startsWith(man)) {
|
|
46
|
+
return packageManagers[man];
|
|
47
|
+
}
|
|
31
48
|
}
|
|
32
49
|
}
|
|
33
50
|
|
|
34
|
-
for (const
|
|
35
|
-
if (await pathExists(path.join(process.cwd(),
|
|
36
|
-
return
|
|
51
|
+
for (const config of Object.values(packageManagers)) {
|
|
52
|
+
if (await pathExists(path.join(process.cwd(), config.lockFile))) {
|
|
53
|
+
return config;
|
|
37
54
|
}
|
|
38
55
|
}
|
|
39
56
|
|