@adobe/helix-deploy 4.13.0 → 4.15.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/CHANGELOG.md +56 -0
- package/README.md +1 -1
- package/index.js +1 -1
- package/package.json +20 -15
- package/src/ActionBuilder.js +70 -28
- package/src/BaseConfig.js +30 -1
- package/src/DevelopmentServer.js +10 -4
- package/src/{BaseBundler.js → bundler/BaseBundler.js} +13 -4
- package/src/bundler/EdgeBundler.js +118 -0
- package/src/{RollupBundler.js → bundler/RollupBundler.js} +2 -2
- package/src/{Bundler.js → bundler/WebpackBundler.js} +13 -8
- package/src/cli.js +4 -0
- package/src/deploy/BaseDeployer.js +5 -0
- package/src/deploy/CloudflareConfig.js +71 -0
- package/src/deploy/CloudflareDeployer.js +145 -0
- package/src/deploy/ComputeAtEdgeConfig.js +93 -0
- package/src/deploy/ComputeAtEdgeDeployer.js +190 -0
- package/src/deploy/GoogleDeployer.js +15 -19
- package/src/gateway/FastlyGateway.js +245 -166
- package/src/template/cloudflare-adapter.js +62 -0
- package/src/template/fastly-adapter.js +99 -0
- package/src/template/{index.js → node-index.js} +0 -1
- package/src/template/{index.mjs → node-index.mjs} +0 -0
- package/src/template/polyfills/helix-fetch.js +19 -0
- package/src/template/serviceworker-index.js +24 -0
|
@@ -17,7 +17,7 @@ const chalk = require('chalk');
|
|
|
17
17
|
const BaseBundler = require('./BaseBundler.js');
|
|
18
18
|
|
|
19
19
|
/**
|
|
20
|
-
*
|
|
20
|
+
* Webpack based bundler
|
|
21
21
|
*/
|
|
22
22
|
module.exports = class WebpackBundler extends BaseBundler {
|
|
23
23
|
async init() {
|
|
@@ -32,12 +32,13 @@ module.exports = class WebpackBundler extends BaseBundler {
|
|
|
32
32
|
target: 'node',
|
|
33
33
|
mode: 'development',
|
|
34
34
|
// the universal adapter is the entry point
|
|
35
|
-
entry: cfg.adapterFile || path.resolve(__dirname, 'template', 'index.js'),
|
|
35
|
+
entry: cfg.adapterFile || path.resolve(__dirname, '..', 'template', 'node-index.js'),
|
|
36
36
|
output: {
|
|
37
37
|
path: cfg.cwd,
|
|
38
38
|
filename: path.relative(cfg.cwd, cfg.bundle),
|
|
39
39
|
library: 'main',
|
|
40
40
|
libraryTarget: 'umd',
|
|
41
|
+
globalObject: 'globalThis',
|
|
41
42
|
},
|
|
42
43
|
devtool: false,
|
|
43
44
|
externals: [
|
|
@@ -88,17 +89,14 @@ module.exports = class WebpackBundler extends BaseBundler {
|
|
|
88
89
|
return opts;
|
|
89
90
|
}
|
|
90
91
|
|
|
91
|
-
async
|
|
92
|
+
async createWebpackBundle(arch) {
|
|
92
93
|
const { cfg } = this;
|
|
93
|
-
if (!cfg.bundle) {
|
|
94
|
-
throw Error('bundle path is undefined');
|
|
95
|
-
}
|
|
96
94
|
if (!cfg.depFile) {
|
|
97
95
|
throw Error('dependencies info path is undefined');
|
|
98
96
|
}
|
|
99
97
|
const m = cfg.minify ? 'minified ' : '';
|
|
100
98
|
if (!cfg.progressHandler) {
|
|
101
|
-
cfg.log.info(`--: creating ${m}bundle using webpack ...`);
|
|
99
|
+
cfg.log.info(`--: creating ${arch} ${m}bundle using webpack ...`);
|
|
102
100
|
}
|
|
103
101
|
const config = await this.getWebpackConfig();
|
|
104
102
|
const compiler = webpack(config);
|
|
@@ -120,11 +118,18 @@ module.exports = class WebpackBundler extends BaseBundler {
|
|
|
120
118
|
// write dependencies info file
|
|
121
119
|
await fse.writeJson(cfg.depFile, cfg.dependencies, { spaces: 2 });
|
|
122
120
|
if (!cfg.progressHandler) {
|
|
123
|
-
cfg.log.info(chalk`{green ok:} created bundle {yellow ${config.output.filename}}`);
|
|
121
|
+
cfg.log.info(chalk`{green ok:} created ${arch} bundle {yellow ${config.output.filename}}`);
|
|
124
122
|
}
|
|
125
123
|
return stats;
|
|
126
124
|
}
|
|
127
125
|
|
|
126
|
+
async createBundle() {
|
|
127
|
+
if (!this.cfg.bundle) {
|
|
128
|
+
throw Error('bundle path is undefined');
|
|
129
|
+
}
|
|
130
|
+
return this.createWebpackBundle('node');
|
|
131
|
+
}
|
|
132
|
+
|
|
128
133
|
/**
|
|
129
134
|
* Resolves the dependencies by chunk. eg:
|
|
130
135
|
*
|
package/src/cli.js
CHANGED
|
@@ -20,6 +20,8 @@ const OpenWhiskDeployer = require('./deploy/OpenWhiskDeployer');
|
|
|
20
20
|
const AWSDeployer = require('./deploy/AWSDeployer');
|
|
21
21
|
const AzureDeployer = require('./deploy/AzureDeployer');
|
|
22
22
|
const GoogleDeployer = require('./deploy/GoogleDeployer');
|
|
23
|
+
const CloudflareDeployer = require('./deploy/CloudflareDeployer');
|
|
24
|
+
const ComputeAtEdgeDeployer = require('./deploy/ComputeAtEdgeDeployer');
|
|
23
25
|
const FastlyGateway = require('./gateway/FastlyGateway');
|
|
24
26
|
|
|
25
27
|
const PLUGINS = [
|
|
@@ -27,6 +29,8 @@ const PLUGINS = [
|
|
|
27
29
|
AWSDeployer,
|
|
28
30
|
AzureDeployer,
|
|
29
31
|
GoogleDeployer,
|
|
32
|
+
CloudflareDeployer,
|
|
33
|
+
ComputeAtEdgeDeployer,
|
|
30
34
|
FastlyGateway,
|
|
31
35
|
];
|
|
32
36
|
|
|
@@ -0,0 +1,71 @@
|
|
|
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
|
+
class CloudflareConfig {
|
|
13
|
+
constructor() {
|
|
14
|
+
Object.assign(this, {});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
configure(argv) {
|
|
18
|
+
return this
|
|
19
|
+
.withEmail(argv.cloudflareEmail)
|
|
20
|
+
.withAuth(argv.cloudflareAuth)
|
|
21
|
+
.withTestDomain(argv.cloudflareTestDomain)
|
|
22
|
+
.withAccountID(argv.cloudflareAccountId);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
withAccountID(value) {
|
|
26
|
+
this.accountID = value;
|
|
27
|
+
return this;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
withEmail(value) {
|
|
31
|
+
this.email = value;
|
|
32
|
+
return this;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
withTestDomain(value) {
|
|
36
|
+
this.testDomain = value;
|
|
37
|
+
return this;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
withAuth(value) {
|
|
41
|
+
this.auth = value;
|
|
42
|
+
return this;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
static yarg(yargs) {
|
|
46
|
+
return yargs
|
|
47
|
+
.group(['cloudflare-account-id', 'cloudflare-auth', 'cloudflare-email', 'cloudflare-test-domain'], 'Cloudflare Workers Deployment Options')
|
|
48
|
+
.option('cloudflare-account-id', {
|
|
49
|
+
description: 'the Cloudflare account ID to deploy to',
|
|
50
|
+
type: 'string',
|
|
51
|
+
default: '',
|
|
52
|
+
})
|
|
53
|
+
.option('cloudflare-email', {
|
|
54
|
+
description: 'the Cloudflare email address belonging to the authentication token',
|
|
55
|
+
type: 'string',
|
|
56
|
+
default: '',
|
|
57
|
+
})
|
|
58
|
+
.option('cloudflare-test-domain', {
|
|
59
|
+
description: 'the *.workers.dev subdomain to use for testing deployed scripts',
|
|
60
|
+
type: 'string',
|
|
61
|
+
default: '',
|
|
62
|
+
})
|
|
63
|
+
.option('cloudflare-auth', {
|
|
64
|
+
description: 'the Cloudflare API token from https://dash.cloudflare.com/profile/api-tokens',
|
|
65
|
+
type: 'string',
|
|
66
|
+
default: '',
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
module.exports = CloudflareConfig;
|
|
@@ -0,0 +1,145 @@
|
|
|
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
|
+
const path = require('path');
|
|
13
|
+
const fs = require('fs');
|
|
14
|
+
const FormData = require('form-data');
|
|
15
|
+
const BaseDeployer = require('./BaseDeployer');
|
|
16
|
+
const CloudflareConfig = require('./CloudflareConfig');
|
|
17
|
+
|
|
18
|
+
class CloudflareDeployer extends BaseDeployer {
|
|
19
|
+
constructor(baseConfig, config) {
|
|
20
|
+
super(baseConfig);
|
|
21
|
+
Object.assign(this, {
|
|
22
|
+
id: 'cloudflare',
|
|
23
|
+
name: 'Cloudflare',
|
|
24
|
+
_cfg: config,
|
|
25
|
+
noGatewayBackend: true,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
ready() {
|
|
30
|
+
return !!this._cfg.auth && !!this._cfg.accountID && !!this.cfg.edgeBundle;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
validate() {
|
|
34
|
+
if (!this.ready()) {
|
|
35
|
+
throw new Error('Cloudflare target needs email, token, and account ID');
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
get fullFunctionName() {
|
|
40
|
+
return `${this.cfg.packageName}--${this.cfg.name}`
|
|
41
|
+
.replace(/\./g, '_')
|
|
42
|
+
.replace('@', '_');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async deploy() {
|
|
46
|
+
const body = fs.readFileSync(path.relative(this.cfg.cwd, this.cfg.edgeBundle));
|
|
47
|
+
const { id } = await this.createKVNamespace(`${this.cfg.packageName}--secrets`);
|
|
48
|
+
|
|
49
|
+
const metadata = {
|
|
50
|
+
body_part: 'script',
|
|
51
|
+
bindings: [
|
|
52
|
+
...Object.entries(this.cfg.params).map(([key, value]) => ({
|
|
53
|
+
name: key,
|
|
54
|
+
type: 'secret_text',
|
|
55
|
+
text: value,
|
|
56
|
+
})),
|
|
57
|
+
{
|
|
58
|
+
name: 'PACKAGE',
|
|
59
|
+
namespace_id: id,
|
|
60
|
+
type: 'kv_namespace',
|
|
61
|
+
},
|
|
62
|
+
],
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
// what https://api.cloudflare.com/#worker-script-upload-worker won't tell you:
|
|
66
|
+
// you can use multipart/formdata to set metadata according to
|
|
67
|
+
// https://community.cloudflare.com/t/bind-kv-and-workers-via-api/221391
|
|
68
|
+
const form = new FormData();
|
|
69
|
+
form.append('script', body, {
|
|
70
|
+
contentType: 'application/javascript',
|
|
71
|
+
});
|
|
72
|
+
form.append('metadata', JSON.stringify(metadata), {
|
|
73
|
+
contentType: 'application/json',
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
const res = await this.fetch(`https://api.cloudflare.com/client/v4/accounts/${this._cfg.accountID}/workers/scripts/${this.fullFunctionName}`, {
|
|
77
|
+
method: 'PUT',
|
|
78
|
+
headers: form.getHeaders({
|
|
79
|
+
Authorization: `Bearer ${this._cfg.auth}`,
|
|
80
|
+
}),
|
|
81
|
+
body: form.getBuffer(),
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
if (!res.ok) {
|
|
85
|
+
const { errors } = await res.json();
|
|
86
|
+
throw new Error(`Unable to upload worker to Cloudflare: ${errors[0].message}`);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
await this.updatePackageParams(id, this.cfg.packageParams);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async updatePackageParams(id, params) {
|
|
93
|
+
const kvlist = Object.entries(params).map(([key, value]) => ({
|
|
94
|
+
key, value,
|
|
95
|
+
}));
|
|
96
|
+
|
|
97
|
+
const res = await this.fetch(`https://api.cloudflare.com/client/v4/accounts/${this._cfg.accountID}/storage/kv/namespaces/${id}/bulk`, {
|
|
98
|
+
method: 'PUT',
|
|
99
|
+
headers: {
|
|
100
|
+
Authorization: `Bearer ${this._cfg.auth}`,
|
|
101
|
+
'content-type': 'application/json',
|
|
102
|
+
},
|
|
103
|
+
body: JSON.stringify(kvlist),
|
|
104
|
+
});
|
|
105
|
+
return res.ok;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async createKVNamespace(name) {
|
|
109
|
+
const postres = await this.fetch(`https://api.cloudflare.com/client/v4/accounts/${this._cfg.accountID}/storage/kv/namespaces`, {
|
|
110
|
+
method: 'POST',
|
|
111
|
+
headers: {
|
|
112
|
+
Authorization: `Bearer ${this._cfg.auth}`,
|
|
113
|
+
'content-type': 'application/json',
|
|
114
|
+
},
|
|
115
|
+
body: {
|
|
116
|
+
title: name,
|
|
117
|
+
},
|
|
118
|
+
});
|
|
119
|
+
let { result } = await postres.json();
|
|
120
|
+
if (!result) {
|
|
121
|
+
const listres = await this.fetch(`https://api.cloudflare.com/client/v4/accounts/${this._cfg.accountID}/storage/kv/namespaces`, {
|
|
122
|
+
method: 'GET',
|
|
123
|
+
headers: {
|
|
124
|
+
Authorization: `Bearer ${this._cfg.auth}`,
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
const { result: results } = await listres.json();
|
|
128
|
+
result = results.find((r) => r.title === name);
|
|
129
|
+
}
|
|
130
|
+
return result;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async test() {
|
|
134
|
+
return this._cfg.testDomain
|
|
135
|
+
? this.testRequest({
|
|
136
|
+
url: `https://${this.fullFunctionName}.${this._cfg.testDomain}.workers.dev`,
|
|
137
|
+
idHeader: 'CF-RAY',
|
|
138
|
+
retry404: 0,
|
|
139
|
+
})
|
|
140
|
+
: undefined;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
CloudflareDeployer.Config = CloudflareConfig;
|
|
145
|
+
module.exports = CloudflareDeployer;
|
|
@@ -0,0 +1,93 @@
|
|
|
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
|
+
class ComputeAtEdgeConfig {
|
|
13
|
+
constructor() {
|
|
14
|
+
Object.assign(this, {});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
configure(argv) {
|
|
18
|
+
return this
|
|
19
|
+
.withServiceID(argv.computeServiceId)
|
|
20
|
+
.withAuth(argv.fastlyAuth)
|
|
21
|
+
.withCoralogixToken(argv.coralogixToken)
|
|
22
|
+
.withFastlyGateway(argv.fastlyGateway)
|
|
23
|
+
.withComputeDomain(argv.computeTestDomain)
|
|
24
|
+
.withCoralogixApp(argv.computeCoralogixApp);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
withServiceID(value) {
|
|
28
|
+
this.service = value;
|
|
29
|
+
return this;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
withAuth(value) {
|
|
33
|
+
this.auth = value;
|
|
34
|
+
return this;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
withCoralogixToken(value) {
|
|
38
|
+
this.coralogixToken = value;
|
|
39
|
+
return this;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
withCoralogixApp(value) {
|
|
43
|
+
this.coralogixApp = value;
|
|
44
|
+
return this;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
withFastlyGateway(value) {
|
|
48
|
+
this.fastlyGateway = value;
|
|
49
|
+
return this;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
withComputeDomain(value) {
|
|
53
|
+
this.testDomain = value;
|
|
54
|
+
return this;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static yarg(yargs) {
|
|
58
|
+
return yargs
|
|
59
|
+
.group(['compute-service-id', 'compute-domain', 'fastly-auth', 'coralogix-token', 'compute-coralogix-app'], 'Fastly Compute@Edge Options')
|
|
60
|
+
.option('compute-service-id', {
|
|
61
|
+
description: 'the Fastly Service to deploy the action to',
|
|
62
|
+
type: 'string',
|
|
63
|
+
default: '',
|
|
64
|
+
})
|
|
65
|
+
.option('compute-test-domain', {
|
|
66
|
+
description: 'the domain name of the Compute@Edge service (used for testing)',
|
|
67
|
+
type: 'string',
|
|
68
|
+
default: '',
|
|
69
|
+
})
|
|
70
|
+
.option('fastly-auth', {
|
|
71
|
+
description: 'the Fastly token',
|
|
72
|
+
type: 'string',
|
|
73
|
+
default: '',
|
|
74
|
+
})
|
|
75
|
+
.option('coralogix-token', {
|
|
76
|
+
description: 'the Coralogix token (to enable logging)',
|
|
77
|
+
type: 'string',
|
|
78
|
+
default: '',
|
|
79
|
+
})
|
|
80
|
+
.option('fastly-gateway', {
|
|
81
|
+
description: 'the hostname of the Fastly gateway for package params',
|
|
82
|
+
type: 'string',
|
|
83
|
+
default: '',
|
|
84
|
+
})
|
|
85
|
+
.option('compute-coralogix-app', {
|
|
86
|
+
description: 'the Application name',
|
|
87
|
+
type: 'string',
|
|
88
|
+
default: 'fastly-compute',
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
module.exports = ComputeAtEdgeConfig;
|
|
@@ -0,0 +1,190 @@
|
|
|
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
|
+
const { fork } = require('child_process');
|
|
13
|
+
const path = require('path');
|
|
14
|
+
const fs = require('fs/promises');
|
|
15
|
+
const tar = require('tar');
|
|
16
|
+
const getStream = require('get-stream');
|
|
17
|
+
const Fastly = require('@adobe/fastly-native-promises');
|
|
18
|
+
const BaseDeployer = require('./BaseDeployer');
|
|
19
|
+
const ComputeAtEdgeConfig = require('./ComputeAtEdgeConfig');
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* The class ComputeAtEdgeDeployer deploys to Fastly's Compute(at)Edge (WASM) runtime.
|
|
23
|
+
* It should be seen as a functional equivalent to the CloudflareDeployer
|
|
24
|
+
* and not confused with the FastlyGateway (which only routes requests, but
|
|
25
|
+
* does not handle them.)
|
|
26
|
+
*/
|
|
27
|
+
class ComputeAtEdgeDeployer extends BaseDeployer {
|
|
28
|
+
constructor(baseConfig, config) {
|
|
29
|
+
super(baseConfig);
|
|
30
|
+
Object.assign(this, {
|
|
31
|
+
id: 'c@e',
|
|
32
|
+
name: 'Fastly Compute@Edge',
|
|
33
|
+
_cfg: config,
|
|
34
|
+
_fastly: null,
|
|
35
|
+
noGatewayBackend: true,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
ready() {
|
|
40
|
+
return !!this._cfg.service && !!this._cfg.auth && !!this.cfg.edgeBundle;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
validate() {
|
|
44
|
+
if (!this.ready()) {
|
|
45
|
+
throw new Error('Compute@Edge target needs token and service ID');
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
init() {
|
|
50
|
+
if (this.ready() && !this._fastly) {
|
|
51
|
+
this._fastly = Fastly(this._cfg.auth, this._cfg.service, 60000);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
get log() {
|
|
56
|
+
return this.cfg.log;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
*
|
|
61
|
+
* @returns
|
|
62
|
+
*/
|
|
63
|
+
async bundle() {
|
|
64
|
+
const bundleDir = path.dirname(this.cfg.edgeBundle);
|
|
65
|
+
this.log.debug(`Creating fastly.toml in ${bundleDir}`);
|
|
66
|
+
fs.writeFile(path.resolve(bundleDir, 'fastly.toml'), `
|
|
67
|
+
# This file describes a Fastly Compute@Edge package. To learn more visit:
|
|
68
|
+
# https://developer.fastly.com/reference/fastly-toml/
|
|
69
|
+
|
|
70
|
+
authors = ["Helix Deploy"]
|
|
71
|
+
description = "Test Project"
|
|
72
|
+
language = "javascript"
|
|
73
|
+
manifest_version = 2
|
|
74
|
+
name = "Test"
|
|
75
|
+
service_id = ""
|
|
76
|
+
`);
|
|
77
|
+
|
|
78
|
+
return new Promise((resolve, reject) => {
|
|
79
|
+
const child = fork(
|
|
80
|
+
path.resolve(
|
|
81
|
+
__dirname,
|
|
82
|
+
'..',
|
|
83
|
+
'..',
|
|
84
|
+
'node_modules',
|
|
85
|
+
'@fastly',
|
|
86
|
+
'js-compute',
|
|
87
|
+
'js-compute-runtime-cli.js',
|
|
88
|
+
),
|
|
89
|
+
[this.cfg.edgeBundle, 'bin/main.wasm'],
|
|
90
|
+
{
|
|
91
|
+
cwd: bundleDir,
|
|
92
|
+
},
|
|
93
|
+
);
|
|
94
|
+
child.on('data', (data) => resolve(data));
|
|
95
|
+
child.on('error', (err) => reject(err));
|
|
96
|
+
child.on('close', (err) => {
|
|
97
|
+
if (err) {
|
|
98
|
+
// non-zero status code
|
|
99
|
+
reject(err);
|
|
100
|
+
} else {
|
|
101
|
+
this.log.debug(`Created WASM bundle of script and interpreter in ${bundleDir}/bin/main.wasm`);
|
|
102
|
+
const stream = tar.c({
|
|
103
|
+
gzip: true,
|
|
104
|
+
// sync: true,
|
|
105
|
+
cwd: bundleDir,
|
|
106
|
+
prefix: 'Test',
|
|
107
|
+
// file: path.resolve(bundleDir, 'fastly-bundle.tar.gz')
|
|
108
|
+
}, ['bin/main.wasm', 'fastly.toml']);
|
|
109
|
+
// this.log.debug(`Created tar file in ${bundleDir}/fastly-bundle.tar.gz`);
|
|
110
|
+
resolve(getStream.buffer(stream));
|
|
111
|
+
}
|
|
112
|
+
});
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
async deploy() {
|
|
117
|
+
const buf = await this.bundle();
|
|
118
|
+
|
|
119
|
+
this.init();
|
|
120
|
+
|
|
121
|
+
await this._fastly.transact(async (version) => {
|
|
122
|
+
await this._fastly.writePackage(version, buf);
|
|
123
|
+
|
|
124
|
+
await this._fastly.writeDictionary(version, 'secrets', {
|
|
125
|
+
name: 'secrets',
|
|
126
|
+
write_only: 'true',
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
const host = this._cfg.fastlyGateway;
|
|
130
|
+
console.log('Host', host);
|
|
131
|
+
const backend = {
|
|
132
|
+
hostname: host,
|
|
133
|
+
ssl_cert_hostname: host,
|
|
134
|
+
ssl_sni_hostname: host,
|
|
135
|
+
address: host,
|
|
136
|
+
override_host: host,
|
|
137
|
+
name: 'gateway',
|
|
138
|
+
error_threshold: 0,
|
|
139
|
+
first_byte_timeout: 60000,
|
|
140
|
+
weight: 100,
|
|
141
|
+
connect_timeout: 5000,
|
|
142
|
+
port: 443,
|
|
143
|
+
between_bytes_timeout: 10000,
|
|
144
|
+
shield: '', // 'bwi-va-us',
|
|
145
|
+
max_conn: 200,
|
|
146
|
+
use_ssl: true,
|
|
147
|
+
};
|
|
148
|
+
await this._fastly.writeBackend(version, 'gateway', backend);
|
|
149
|
+
}, true);
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
async updatePackage() {
|
|
153
|
+
this.log.info('--: updating app (gateway) config ...');
|
|
154
|
+
|
|
155
|
+
this.init();
|
|
156
|
+
|
|
157
|
+
const functionparams = Object
|
|
158
|
+
.entries(this.cfg.params)
|
|
159
|
+
.map(([key, value]) => ({
|
|
160
|
+
item_key: key,
|
|
161
|
+
item_value: value,
|
|
162
|
+
op: 'update',
|
|
163
|
+
}));
|
|
164
|
+
|
|
165
|
+
await this._fastly.bulkUpdateDictItems(undefined, 'secrets', ...functionparams);
|
|
166
|
+
await this._fastly.updateDictItem(undefined, 'secrets', '_token', this.cfg.packageToken);
|
|
167
|
+
console.log('package', `https://${this._cfg.fastlyGateway}/${this.cfg.packageName}/`);
|
|
168
|
+
await this._fastly.updateDictItem(undefined, 'secrets', '_package', `https://${this._cfg.fastlyGateway}/${this.cfg.packageName}/`);
|
|
169
|
+
|
|
170
|
+
this._fastly.discard();
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
get fullFunctionName() {
|
|
174
|
+
return `${this.cfg.packageName}--${this.cfg.name}`
|
|
175
|
+
.replace(/\./g, '_')
|
|
176
|
+
.replace('@', '_');
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async test() {
|
|
180
|
+
return this._cfg.testDomain
|
|
181
|
+
? this.testRequest({
|
|
182
|
+
url: `https://${this._cfg.testDomain}.edgecompute.app`,
|
|
183
|
+
retry404: 0,
|
|
184
|
+
})
|
|
185
|
+
: undefined;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
ComputeAtEdgeDeployer.Config = ComputeAtEdgeConfig;
|
|
190
|
+
module.exports = ComputeAtEdgeDeployer;
|