@adobe/helix-deploy 13.5.7 → 14.0.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/CHANGELOG.md CHANGED
@@ -1,3 +1,22 @@
1
+ # [14.0.0](https://github.com/adobe/helix-deploy/compare/v13.5.8...v14.0.0) (2026-04-14)
2
+
3
+
4
+ ### Bug Fixes
5
+
6
+ * move google deployer to plugin ([#914](https://github.com/adobe/helix-deploy/issues/914)) ([015a73b](https://github.com/adobe/helix-deploy/commit/015a73b875fd7ab05c7c83402ba62be6c0d42649))
7
+
8
+
9
+ ### BREAKING CHANGES
10
+
11
+ * deploying to GCF will require an additional --plugin argument
12
+
13
+ ## [13.5.8](https://github.com/adobe/helix-deploy/compare/v13.5.7...v13.5.8) (2026-04-13)
14
+
15
+
16
+ ### Bug Fixes
17
+
18
+ * **deps:** update external fixes to v3.1020.0 ([#912](https://github.com/adobe/helix-deploy/issues/912)) ([3103a8b](https://github.com/adobe/helix-deploy/commit/3103a8bfbb21497cbbfedf4edec7043a33db836a))
19
+
1
20
  ## [13.5.7](https://github.com/adobe/helix-deploy/compare/v13.5.6...v13.5.7) (2026-04-08)
2
21
 
3
22
 
package/index.js CHANGED
@@ -14,3 +14,4 @@ export { default as BaseBundler } from './src/bundler/BaseBundler.js';
14
14
  export { default as BaseConfig } from './src/BaseConfig.js';
15
15
  export { default as BaseDeployer } from './src/deploy/BaseDeployer.js';
16
16
  export { default as CLI } from './src/cli.js';
17
+ export { filterActions } from './src/utils.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/helix-deploy",
3
- "version": "13.5.7",
3
+ "version": "14.0.0",
4
4
  "description": "Library and Commandline Tools to build and deploy OpenWhisk Actions",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/adobe/helix-deploy#readme",
@@ -39,15 +39,12 @@
39
39
  "dependencies": {
40
40
  "@adobe/fetch": "4.3.0",
41
41
  "@adobe/helix-shared-process-queue": "3.1.8",
42
- "@aws-sdk/client-apigatewayv2": "3.1014.0",
43
- "@aws-sdk/client-lambda": "3.1014.0",
44
- "@aws-sdk/client-s3": "3.1014.0",
45
- "@aws-sdk/client-secrets-manager": "3.1014.0",
46
- "@aws-sdk/client-ssm": "3.1014.0",
47
- "@aws-sdk/client-sts": "3.1014.0",
48
- "@google-cloud/functions": "4.2.1",
49
- "@google-cloud/secret-manager": "6.1.1",
50
- "@google-cloud/storage": "7.19.0",
42
+ "@aws-sdk/client-apigatewayv2": "3.1020.0",
43
+ "@aws-sdk/client-lambda": "3.1020.0",
44
+ "@aws-sdk/client-s3": "3.1020.0",
45
+ "@aws-sdk/client-secrets-manager": "3.1020.0",
46
+ "@aws-sdk/client-ssm": "3.1020.0",
47
+ "@aws-sdk/client-sts": "3.1020.0",
51
48
  "archiver": "7.0.1",
52
49
  "chalk-template": "1.1.2",
53
50
  "dotenv": "17.3.1",
@@ -290,6 +290,18 @@ export default class ActionBuilder {
290
290
  }
291
291
  this.bundlers.push(...bundler);
292
292
  });
293
+
294
+ // collect archive dependencies from all registered deployers and inject into bundlers
295
+ const archiveDependencies = Object.values(this._deployers).reduce((deps, deployer) => {
296
+ if (typeof deployer.getArchiveDependencies === 'function') {
297
+ Object.assign(deps, deployer.getArchiveDependencies());
298
+ }
299
+ return deps;
300
+ }, {});
301
+ for (const bundler of this.bundlers) {
302
+ bundler.archiveDependencies = archiveDependencies;
303
+ }
304
+
293
305
  for (const bundler of this.bundlers) {
294
306
  await bundler.init();
295
307
  }
package/src/BaseConfig.js CHANGED
@@ -707,7 +707,7 @@ export default class BaseConfig {
707
707
 
708
708
  .group(['target', 'hints'], 'Deploy Options')
709
709
  .option('target', {
710
- description: 'Select target(s) for test, deploy, update-package actions (wsk,aws,google,auto)',
710
+ description: 'Select target(s) for test, deploy, update-package actions (wsk,aws,auto) or plugin targets (e.g. google via @adobe/helix-deploy-plugin-google)',
711
711
  type: 'string',
712
712
  default: ['auto'],
713
713
  array: true,
@@ -16,7 +16,6 @@ import chalk from 'chalk-template';
16
16
  import archiver from 'archiver';
17
17
  import semver from 'semver';
18
18
  import { validateBundle } from '../utils.js';
19
- import pkgJson from '../package.cjs';
20
19
 
21
20
  // eslint-disable-next-line no-underscore-dangle
22
21
  const __dirname = path.resolve(fileURLToPath(import.meta.url), '..');
@@ -43,6 +42,7 @@ export default class BaseBundler {
43
42
  cfg,
44
43
  arch: '',
45
44
  type: '',
45
+ archiveDependencies: {},
46
46
  });
47
47
  }
48
48
 
@@ -113,11 +113,7 @@ export default class BaseBundler {
113
113
  type: cfg.esm ? 'module' : 'script',
114
114
  license: 'Apache-2.0',
115
115
  dependencies: {
116
- // google cloud installs these dependencies at deploy time
117
- // all other environments ignore them – this allows us to
118
- // avoid bundling something that only google needs
119
- '@google-cloud/secret-manager': pkgJson.dependencies['@google-cloud/secret-manager'],
120
- '@google-cloud/storage': pkgJson.dependencies['@google-cloud/storage'],
116
+ ...this.archiveDependencies,
121
117
  },
122
118
  };
123
119
  archive.pipe(output);
package/src/cli.js CHANGED
@@ -19,14 +19,12 @@ import escalade from 'escalade/sync';
19
19
  import BaseConfig from './BaseConfig.js';
20
20
  import OpenWhiskDeployer from './deploy/OpenWhiskDeployer.js';
21
21
  import AWSDeployer from './deploy/AWSDeployer.js';
22
- import GoogleDeployer from './deploy/GoogleDeployer.js';
23
22
  import ActionBuilder from './ActionBuilder.js';
24
23
  import ESBuildBundler from './bundler/ESBuildBundler.js';
25
24
 
26
25
  const PLUGINS = [
27
26
  OpenWhiskDeployer,
28
27
  AWSDeployer,
29
- GoogleDeployer,
30
28
  ESBuildBundler,
31
29
  ];
32
30
 
@@ -138,6 +138,16 @@ export default class BaseDeployer {
138
138
  }
139
139
  }
140
140
 
141
+ /**
142
+ * Returns additional dependencies to include in the archive's package.json.
143
+ * Deployers can override this to inject runtime dependencies needed by the target platform.
144
+ * @returns {object} an object mapping package names to version strings
145
+ */
146
+ // eslint-disable-next-line class-methods-use-this
147
+ getArchiveDependencies() {
148
+ return {};
149
+ }
150
+
141
151
  /**
142
152
  * Can be used by deployers to run additional tasks, eg. custom commands.
143
153
  */
@@ -1,71 +0,0 @@
1
- /*
2
- * Copyright 2021 Adobe. All rights reserved.
3
- * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
- * you may not use this file except in compliance with the License. You may obtain a copy
5
- * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
- *
7
- * Unless required by applicable law or agreed to in writing, software distributed under
8
- * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
- * OF ANY KIND, either express or implied. See the License for the specific language
10
- * governing permissions and limitations under the License.
11
- */
12
- export default class GoogleConfig {
13
- constructor() {
14
- Object.assign(this, {
15
- appName: '',
16
- });
17
- }
18
-
19
- configure(argv) {
20
- return this
21
- .withProjectID(argv.googleProjectId)
22
- .withKeyFile(argv.googleKeyFile)
23
- .withRegion(argv.googleRegion)
24
- .withEmail(argv.googleEmail);
25
- }
26
-
27
- withProjectID(value) {
28
- this.projectID = value;
29
- return this;
30
- }
31
-
32
- withKeyFile(value) {
33
- this.keyFile = value;
34
- return this;
35
- }
36
-
37
- withEmail(value) {
38
- this.email = value;
39
- return this;
40
- }
41
-
42
- withRegion(value) {
43
- this.region = value;
44
- return this;
45
- }
46
-
47
- static yarg(yargs) {
48
- return yargs
49
- .group(['google-project-id', 'google-key-file', 'google-email'], 'Google Deployment Options')
50
- .option('google-email', {
51
- description: 'the Google account email address. Required when using a .pem or .p12 credential file',
52
- type: 'string',
53
- default: '',
54
- })
55
- .option('google-key-file', {
56
- description: 'full path to the a .json, .pem, or .p12 key downloaded from the Google Developers Console',
57
- type: 'string',
58
- default: '',
59
- })
60
- .option('google-project-id', {
61
- description: 'the Google Cloud project to deploy to. Optional when the key file is a JSON file',
62
- type: 'string',
63
- default: '',
64
- })
65
- .option('google-region', {
66
- description: 'the Google Cloud region to deploy in',
67
- type: 'string',
68
- default: '',
69
- });
70
- }
71
- }
@@ -1,352 +0,0 @@
1
- /*
2
- * Copyright 2021 Adobe. All rights reserved.
3
- * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4
- * you may not use this file except in compliance with the License. You may obtain a copy
5
- * of the License at http://www.apache.org/licenses/LICENSE-2.0
6
- *
7
- * Unless required by applicable law or agreed to in writing, software distributed under
8
- * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9
- * OF ANY KIND, either express or implied. See the License for the specific language
10
- * governing permissions and limitations under the License.
11
- */
12
- import { CloudFunctionsServiceClient } from '@google-cloud/functions';
13
- import { SecretManagerServiceClient } from '@google-cloud/secret-manager';
14
- import path from 'path';
15
- import fs from 'fs';
16
- import semver from 'semver';
17
- import chalk from 'chalk-template';
18
- import BaseDeployer from './BaseDeployer.js';
19
- import GoogleConfig from './GoogleConfig.js';
20
- import { filterActions } from '../utils.js';
21
-
22
- export default class GoogleDeployer extends BaseDeployer {
23
- constructor(baseConfig, config) {
24
- super(baseConfig);
25
- Object.assign(this, {
26
- id: 'google',
27
- name: 'Google',
28
- _cfg: config,
29
- _client: null,
30
- _keyFilename: '',
31
- });
32
- }
33
-
34
- ready() {
35
- if (this._cfg.keyFile) {
36
- this._keyFilename = path.resolve(process.cwd(), this._cfg.keyFile);
37
- if (!fs.existsSync(this._keyFilename)) {
38
- return false;
39
- }
40
- }
41
- return !!this._cfg.projectID && !!this._cfg.keyFile;
42
- }
43
-
44
- validate() {
45
- if (!this.ready()) {
46
- throw new Error('Google target needs key file, email, and project ID');
47
- }
48
- }
49
-
50
- async init() {
51
- if (this.ready() && !this._client) {
52
- try {
53
- this._client = new CloudFunctionsServiceClient({
54
- email: this._cfg.email,
55
- keyFilename: this._keyFilename,
56
- projectId: this._cfg.projectID,
57
- });
58
- this._secretclient = new SecretManagerServiceClient({
59
- email: this._cfg.email,
60
- keyFilename: this._keyFilename,
61
- projectId: this._cfg.projectID,
62
- });
63
- } catch (e) {
64
- this.log.error(chalk`{red error:} Unable to authenticate with Google: ${e.message}`);
65
- throw e;
66
- }
67
- }
68
- }
69
-
70
- async updatePackage() {
71
- this.log.info('--: updating app (package) parameters ...');
72
- // Create the secret with automation replication.
73
- const secretId = `helix-deploy--${this.cfg.packageName.replace(/\./g, '_')}`;
74
- const parent = `projects/${this._cfg.projectID}`;
75
- let secret;
76
-
77
- try {
78
- [secret] = await this._secretclient.createSecret({
79
- parent,
80
- secret: {
81
- name: secretId,
82
- replication: {
83
- automatic: {},
84
- },
85
- },
86
- secretId,
87
- });
88
- this.log.info(`--: Created secret ${secret.name}`);
89
- } catch {
90
- this.log.info('--: Using existing secret');
91
- [secret] = await this._secretclient.getSecret({
92
- name: `${parent}/secrets/${secretId}`,
93
- });
94
- }
95
-
96
- // Add a version with a payload onto the secret.
97
- const [version] = await this._secretclient.addSecretVersion({
98
- parent: secret.name,
99
- payload: {
100
- data: Buffer.from(JSON.stringify(this.cfg.packageParams), 'utf8'),
101
- },
102
- });
103
-
104
- this.log.info(chalk`{green ok}: Added secret version ${version.name}`);
105
-
106
- /*
107
- const [retversion] = await this._secretclient.accessSecretVersion({
108
- name: `${parent}/secrets/${secretId}/versions/latest`,
109
- });
110
- const payload = JSON.parse(retversion.payload.data.toString());
111
-
112
- this.log.info(payload);
113
- */
114
- }
115
-
116
- async uploadZIP() {
117
- const [{ uploadUrl }] = await this._client.generateUploadUrl({
118
- parent: `projects/${this._cfg.projectID}/locations/${this._cfg.region}`,
119
- });
120
-
121
- const body = fs.createReadStream(this.cfg.zipFile);
122
-
123
- // upload
124
- await this.fetch(uploadUrl, {
125
- method: 'PUT',
126
- headers: {
127
- 'Content-Type': 'application/zip',
128
- 'x-goog-content-length-range': '0,104857600',
129
- },
130
- body,
131
- });
132
-
133
- this._uploadURL = uploadUrl;
134
- }
135
-
136
- get fullFunctionName() {
137
- return `${this.cfg.packageName}--${this.cfg.name}`
138
- .replace(/\./g, '_')
139
- .replace('@', '_');
140
- }
141
-
142
- async createFunction() {
143
- const name = `projects/${this._cfg.projectID}/locations/${this._cfg.region}/functions/${this.fullFunctionName}`;
144
- let exists = false;
145
-
146
- try {
147
- await this._client.getFunction({
148
- name,
149
- });
150
- exists = true;
151
- } catch {
152
- exists = false;
153
- }
154
-
155
- try {
156
- const func = {
157
- name,
158
- serviceAccountEmail: this._cfg.email,
159
- description: this.cfg.pkgJson.description,
160
- entryPoint: 'google',
161
- runtime: `nodejs${this.cfg.nodeVersion}`,
162
- // timeout: `${Math.floor(this.cfg.timeout / 1000)}s`,
163
- availableMemoryMb: this.cfg.memory,
164
- labels: {
165
- /*
166
- * Each resource can have multiple labels, up to a maximum of 64.
167
- * - Each label must be a key-value pair.
168
- * - Keys have a minimum length of 1 character and a maximum length of 63
169
- * characters, and cannot be empty. Values can be empty, and have a maximum
170
- * length of 63 characters.
171
- * - Keys and values can contain only lowercase letters, numeric characters,
172
- * underscores, and dashes. All characters must use UTF-8 encoding, and
173
- * international characters are allowed.
174
- * - The key portion of a label must be unique. However, you can use the same key
175
- * with multiple resources.
176
- * - Keys must start with a lowercase letter or international character.
177
- */
178
- // not worth the effort, I think
179
- pkgversion: `${encodeURIComponent(this.cfg.version.replace(/\./g, '_'))}`,
180
- // dependencies: this.cfg.dependencies.main
181
- // .map((dep) => `${dep.name}:${dep.version}`).join(','),
182
- // repository: encodeURIComponent(this.cfg.gitUrl).replace(/%/g, '_'),
183
- // git: `${this.cfg.gitOrigin}#${this.cfg.gitRef}`,
184
- updated: `${this.cfg.updatedAt}`,
185
- },
186
- environmentVariables: this.cfg.params,
187
- httpsTrigger: {},
188
- sourceUploadUrl: this._uploadURL,
189
- };
190
-
191
- if (exists) {
192
- const [op] = await this._client.updateFunction({
193
- // location: `projects/${this._cfg.projectID}/locations/${this._cfg.region}`,
194
- function: func,
195
- });
196
-
197
- this.log.info('--: updating existing function');
198
- const [res] = await op.promise();
199
- this._function = res;
200
- this.log.info(chalk`{green ok:} function deployed`);
201
- } else {
202
- const [op] = await this._client.createFunction({
203
- location: `projects/${this._cfg.projectID}/locations/${this._cfg.region}`,
204
- function: func,
205
- });
206
-
207
- this.log.info('--: creating function, please wait (Google deployments are slow).');
208
- const [res] = await op.promise();
209
- this._function = res;
210
- this.log.info(chalk`{green ok:} function deployed`);
211
- }
212
-
213
- this.log.info('--: enabling unauthenticated requests');
214
- await this._client.setIamPolicy({
215
- resource: name,
216
- policy: {
217
- bindings: [
218
- {
219
- role: 'roles/cloudfunctions.invoker',
220
- members: [
221
- 'allUsers',
222
- ],
223
- },
224
- ],
225
- },
226
- });
227
- } catch (err) {
228
- this.log.error(chalk`{red error:} bad request: ${err.metadata.internalRepr?.get('google.rpc.badrequest-bin')?.toString()}`);
229
- this.log.error(chalk`{red error:} details: ${err.metadata.internalRepr?.get('grpc-status-details-bin')?.toString()}`);
230
- throw err;
231
- }
232
-
233
- this._functionURL = this._function.httpsTrigger.url;
234
- }
235
-
236
- async deploy() {
237
- try {
238
- await this.uploadZIP();
239
- await this.createFunction();
240
- } catch (err) {
241
- const message = err.metadata ? err.metadata.get('grpc-status-details-bin')[0].toString() : err.message;
242
- this.log.error(chalk`{red error:} Unable to deploy Google Cloud function: ${message}`, err.metadata);
243
- throw err;
244
- }
245
- }
246
-
247
- get host() {
248
- return `${this._cfg.region}-${this._cfg.projectID}.cloudfunctions.net`;
249
- }
250
-
251
- // eslint-disable-next-line class-methods-use-this
252
- get urlVCL() {
253
- return '"/" + var.package + "--" + var.action + regsuball(var._version, "\\.", "_") + var.rest';
254
- }
255
-
256
- get basePath() {
257
- return `/${this.fullFunctionName}`;
258
- }
259
-
260
- async test() {
261
- let url = this._functionURL;
262
- if (!url) {
263
- url = `https://${this._cfg.region}-${this._cfg.projectID}.cloudfunctions.net/${this.fullFunctionName}`;
264
- }
265
- return this.testRequest({
266
- url,
267
- idHeader: 'X-Cloud-Trace-Context',
268
- retry404: 1,
269
- });
270
- }
271
-
272
- async cleanup() {
273
- if (!this._client) {
274
- return;
275
- }
276
-
277
- try {
278
- const [allfns] = await this._client.listFunctions({
279
- parent: `projects/${this._cfg.projectID}/locations/${this._cfg.region}`,
280
- });
281
-
282
- const versionspec = {};
283
- const sver = semver.parse(this.cfg.version);
284
- if (sver) {
285
- versionspec.patchVersion = sver.patch;
286
- versionspec.minorVersion = sver.minor;
287
- versionspec.majorVersion = sver.major;
288
- }
289
- await Promise.all(GoogleDeployer.filterFunctions(
290
- allfns,
291
- this.cfg.baseName,
292
- Date.now(),
293
- {
294
- ciAge: this.cfg.cleanupCiAge,
295
- patchAge: this.cfg.cleanupPatchAge,
296
- minorAge: this.cfg.cleanupMinorAge,
297
- majorAge: this.cfg.cleanupMajorAge,
298
- ciNum: this.cfg.cleanupCiNum,
299
- patchNum: this.cfg.cleanupPatchNum,
300
- minorNum: this.cfg.cleanupMinorNum,
301
- majorNum: this.cfg.cleanupMajorNum,
302
- },
303
- versionspec,
304
- ).map((fn) => {
305
- this.log.info(`--: Cleaning up outdated function '${fn.fqName}`);
306
-
307
- return this._client.deleteFunction({
308
- name: fn.fqName,
309
- });
310
- }));
311
- } catch (e) {
312
- this.log.error(chalk`{red error:} Cleanup failed, proceeding anyway.`, e);
313
- }
314
- }
315
-
316
- static filterFunctions(fns, name, now, rangespec, versionspec) {
317
- const namedfns = fns.map((fn) => {
318
- const re = /(ci\d+)|(\d+_\d+_\d+)$/;
319
-
320
- const updated = fn.labels && fn.labels.updated
321
- ? fn.labels.updated
322
- : fn.updateTime.seconds;
323
-
324
- const versionstr = fn.labels && fn.labels.pkgversion
325
- ? fn.labels.pkgversion
326
- : fn.name.match(re) && fn.name.match(re)[0];
327
-
328
- const version = {};
329
- if (versionstr && versionstr.startsWith('ci')) {
330
- version.ci = versionstr.substr(2);
331
- } else if (versionstr) {
332
- const cleanversionstr = versionstr
333
- .replace(/^(\d+)[-_](\d+)[-_](\d+)[-_]/, '$1.$2.$3-')
334
- .replace(/^(\d+)[-_](\d+)[-_](\d+)/, '$1.$2.$3');
335
- const ver = semver.parse(cleanversionstr);
336
- version.major = ver.major;
337
- version.minor = ver.minor;
338
- version.patch = ver.patch;
339
- }
340
- return {
341
- fqName: fn.name,
342
- name: fn.name.replace(/^.*--/, '').replace(re, '').replace(/_$/, ''),
343
- updated: new Date(Number(updated)),
344
- version,
345
- };
346
- }).filter((fn) => fn.name === name);
347
-
348
- return filterActions(namedfns, now, rangespec, versionspec);
349
- }
350
- }
351
-
352
- GoogleDeployer.Config = GoogleConfig;