@webqit/webflo 0.11.61-0 → 1.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/.gitignore +7 -7
- package/LICENSE +20 -20
- package/README.md +2079 -2074
- package/docker/Dockerfile +42 -42
- package/docker/README.md +91 -91
- package/docker/package.json +2 -2
- package/package.json +80 -81
- package/src/{Context.js → AbstractContext.js} +71 -79
- package/src/config-pi/deployment/Env.js +68 -68
- package/src/config-pi/deployment/Layout.js +63 -63
- package/src/config-pi/deployment/Origins.js +139 -139
- package/src/config-pi/deployment/Proxy.js +74 -74
- package/src/config-pi/deployment/index.js +17 -17
- package/src/config-pi/index.js +15 -15
- package/src/config-pi/runtime/Client.js +116 -98
- package/src/config-pi/runtime/Server.js +125 -125
- package/src/config-pi/runtime/client/Worker.js +109 -134
- package/src/config-pi/runtime/client/index.js +11 -11
- package/src/config-pi/runtime/index.js +17 -17
- package/src/config-pi/runtime/server/Headers.js +74 -74
- package/src/config-pi/runtime/server/Redirects.js +69 -69
- package/src/config-pi/runtime/server/index.js +13 -13
- package/src/config-pi/static/Manifest.js +319 -319
- package/src/config-pi/static/Ssg.js +49 -49
- package/src/config-pi/static/index.js +13 -13
- package/src/deployment-pi/index.js +10 -10
- package/src/deployment-pi/origins/index.js +216 -216
- package/src/index.js +11 -19
- package/src/runtime-pi/HttpEvent.js +126 -106
- package/src/runtime-pi/HttpUser.js +126 -0
- package/src/runtime-pi/MessagingOverBroadcast.js +9 -0
- package/src/runtime-pi/MessagingOverChannel.js +85 -0
- package/src/runtime-pi/MessagingOverSocket.js +106 -0
- package/src/runtime-pi/MultiportMessagingAPI.js +81 -0
- package/src/runtime-pi/WebfloCookieStorage.js +27 -0
- package/src/runtime-pi/WebfloEventTarget.js +39 -0
- package/src/runtime-pi/WebfloMessageEvent.js +58 -0
- package/src/runtime-pi/WebfloMessagingAPI.js +69 -0
- package/src/runtime-pi/{Router.js → WebfloRouter.js} +99 -130
- package/src/runtime-pi/WebfloRuntime.js +52 -0
- package/src/runtime-pi/WebfloStorage.js +109 -0
- package/src/runtime-pi/client/ClientMessaging.js +5 -0
- package/src/runtime-pi/client/Context.js +3 -7
- package/src/runtime-pi/client/CookieStorage.js +17 -0
- package/src/runtime-pi/client/Router.js +38 -48
- package/src/runtime-pi/client/SessionStorage.js +33 -0
- package/src/runtime-pi/client/Url.js +156 -205
- package/src/runtime-pi/client/WebfloClient.js +544 -0
- package/src/runtime-pi/client/WebfloRootClient1.js +179 -0
- package/src/runtime-pi/client/WebfloRootClient2.js +109 -0
- package/src/runtime-pi/client/WebfloSubClient.js +165 -0
- package/src/runtime-pi/client/Workport.js +118 -178
- package/src/runtime-pi/client/generate.js +480 -471
- package/src/runtime-pi/client/index.js +16 -21
- package/src/runtime-pi/client/worker/ClientMessaging.js +5 -0
- package/src/runtime-pi/client/worker/Context.js +3 -7
- package/src/runtime-pi/client/worker/CookieStorage.js +17 -0
- package/src/runtime-pi/client/worker/SessionStorage.js +13 -0
- package/src/runtime-pi/client/worker/WebfloWorker.js +294 -0
- package/src/runtime-pi/client/worker/Workport.js +17 -85
- package/src/runtime-pi/client/worker/index.js +10 -21
- package/src/runtime-pi/index.js +6 -13
- package/src/runtime-pi/server/ClientMessaging.js +18 -0
- package/src/runtime-pi/server/ClientMessagingRegistry.js +57 -0
- package/src/runtime-pi/server/Context.js +11 -15
- package/src/runtime-pi/server/CookieStorage.js +17 -0
- package/src/runtime-pi/server/Router.js +93 -159
- package/src/runtime-pi/server/SessionStorage.js +53 -0
- package/src/runtime-pi/server/WebfloServer.js +755 -0
- package/src/runtime-pi/server/index.js +10 -21
- package/src/runtime-pi/util-http.js +322 -86
- package/src/runtime-pi/util-url.js +146 -146
- package/src/runtime-pi/xURL.js +108 -105
- package/src/runtime-pi/xfetch.js +22 -22
- package/src/services-pi/cert/http-auth-hook.js +22 -22
- package/src/services-pi/cert/http-cleanup-hook.js +22 -22
- package/src/services-pi/cert/index.js +79 -79
- package/src/services-pi/index.js +8 -8
- package/src/static-pi/index.js +10 -10
- package/src/webflo.js +30 -30
- package/test/index.test.js +26 -26
- package/test/site/package.json +9 -9
- package/test/site/public/bundle.html +5 -5
- package/test/site/public/bundle.html.json +3 -3
- package/test/site/public/bundle.js +2 -2
- package/test/site/public/bundle.webflo.js +15 -15
- package/test/site/public/index.html +29 -29
- package/test/site/public/index1.html +34 -34
- package/test/site/public/page-2/bundle.html +4 -4
- package/test/site/public/page-2/bundle.js +2 -2
- package/test/site/public/page-2/index.html +45 -45
- package/test/site/public/page-2/main.html +2 -2
- package/test/site/public/page-4/subpage/bundle.js +2 -2
- package/test/site/public/page-4/subpage/index.html +30 -30
- package/test/site/public/sparoots.json +4 -4
- package/test/site/public/worker.js +3 -3
- package/test/site/server/index.js +15 -15
- package/src/runtime-pi/Application.js +0 -29
- package/src/runtime-pi/Cookies.js +0 -82
- package/src/runtime-pi/Runtime.js +0 -21
- package/src/runtime-pi/client/Application.js +0 -100
- package/src/runtime-pi/client/Runtime.js +0 -332
- package/src/runtime-pi/client/createStorage.js +0 -57
- package/src/runtime-pi/client/oohtml/full.js +0 -7
- package/src/runtime-pi/client/oohtml/namespacing.js +0 -7
- package/src/runtime-pi/client/oohtml/scripting.js +0 -8
- package/src/runtime-pi/client/oohtml/templating.js +0 -8
- package/src/runtime-pi/client/worker/Application.js +0 -44
- package/src/runtime-pi/client/worker/Runtime.js +0 -269
- package/src/runtime-pi/server/Application.js +0 -116
- package/src/runtime-pi/server/Runtime.js +0 -557
- package/src/runtime-pi/xFormData.js +0 -24
- package/src/runtime-pi/xHeaders.js +0 -146
- package/src/runtime-pi/xRequest.js +0 -46
- package/src/runtime-pi/xRequestHeaders.js +0 -109
- package/src/runtime-pi/xResponse.js +0 -33
- package/src/runtime-pi/xResponseHeaders.js +0 -117
- package/src/runtime-pi/xxHttpMessage.js +0 -102
|
@@ -1,49 +1,49 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* imports
|
|
4
|
-
*/
|
|
5
|
-
import { Dotfile } from '@webqit/backpack';
|
|
6
|
-
|
|
7
|
-
export default class Ssg extends Dotfile {
|
|
8
|
-
|
|
9
|
-
// Base name
|
|
10
|
-
get name() {
|
|
11
|
-
return 'ssg';
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
// @desc
|
|
15
|
-
static get ['@desc']() {
|
|
16
|
-
return 'Server-Side Generation (SSG) config.';
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
// Defaults merger
|
|
20
|
-
withDefaults(config) {
|
|
21
|
-
return this.merge({
|
|
22
|
-
entries: [],
|
|
23
|
-
}, config, 'patch');
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Questions generator
|
|
27
|
-
getSchema(config, choices = {}) {
|
|
28
|
-
// Questions
|
|
29
|
-
return [
|
|
30
|
-
{
|
|
31
|
-
name: 'entries',
|
|
32
|
-
type: 'recursive',
|
|
33
|
-
controls: {
|
|
34
|
-
name: 'page',
|
|
35
|
-
},
|
|
36
|
-
initial: config.entries,
|
|
37
|
-
schema: [
|
|
38
|
-
{
|
|
39
|
-
name: 'url',
|
|
40
|
-
type: 'text',
|
|
41
|
-
message: 'Page URL',
|
|
42
|
-
validation: ['important'],
|
|
43
|
-
},
|
|
44
|
-
],
|
|
45
|
-
},
|
|
46
|
-
|
|
47
|
-
];
|
|
48
|
-
}
|
|
49
|
-
}
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* imports
|
|
4
|
+
*/
|
|
5
|
+
import { Dotfile } from '@webqit/backpack';
|
|
6
|
+
|
|
7
|
+
export default class Ssg extends Dotfile {
|
|
8
|
+
|
|
9
|
+
// Base name
|
|
10
|
+
get name() {
|
|
11
|
+
return 'ssg';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// @desc
|
|
15
|
+
static get ['@desc']() {
|
|
16
|
+
return 'Server-Side Generation (SSG) config.';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Defaults merger
|
|
20
|
+
withDefaults(config) {
|
|
21
|
+
return this.merge({
|
|
22
|
+
entries: [],
|
|
23
|
+
}, config, 'patch');
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
// Questions generator
|
|
27
|
+
getSchema(config, choices = {}) {
|
|
28
|
+
// Questions
|
|
29
|
+
return [
|
|
30
|
+
{
|
|
31
|
+
name: 'entries',
|
|
32
|
+
type: 'recursive',
|
|
33
|
+
controls: {
|
|
34
|
+
name: 'page',
|
|
35
|
+
},
|
|
36
|
+
initial: config.entries,
|
|
37
|
+
schema: [
|
|
38
|
+
{
|
|
39
|
+
name: 'url',
|
|
40
|
+
type: 'text',
|
|
41
|
+
message: 'Page URL',
|
|
42
|
+
validation: ['important'],
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
|
|
47
|
+
];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* @imports
|
|
4
|
-
*/
|
|
5
|
-
import Manifest from './Manifest.js';
|
|
6
|
-
import Ssg from './Ssg.js';
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* @exports
|
|
10
|
-
*/
|
|
11
|
-
export {
|
|
12
|
-
Manifest,
|
|
13
|
-
Ssg,
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* @imports
|
|
4
|
+
*/
|
|
5
|
+
import Manifest from './Manifest.js';
|
|
6
|
+
import Ssg from './Ssg.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* @exports
|
|
10
|
+
*/
|
|
11
|
+
export {
|
|
12
|
+
Manifest,
|
|
13
|
+
Ssg,
|
|
14
14
|
}
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* imports
|
|
4
|
-
*/
|
|
5
|
-
import * as origins from './origins/index.js';
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* @exports
|
|
9
|
-
*/
|
|
10
|
-
export { origins }
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* imports
|
|
4
|
+
*/
|
|
5
|
+
import * as origins from './origins/index.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @exports
|
|
9
|
+
*/
|
|
10
|
+
export { origins }
|
|
@@ -1,217 +1,217 @@
|
|
|
1
|
-
|
|
2
|
-
/**
|
|
3
|
-
* @imports
|
|
4
|
-
*/
|
|
5
|
-
import Fs from 'fs';
|
|
6
|
-
import Path from 'path';
|
|
7
|
-
import SimpleGit from 'simple-git';
|
|
8
|
-
import { spawn } from 'child_process';
|
|
9
|
-
import { _any } from '@webqit/util/arr/index.js';
|
|
10
|
-
import { _beforeLast } from '@webqit/util/str/index.js';
|
|
11
|
-
import { _isObject, _isNumeric } from '@webqit/util/js/index.js';
|
|
12
|
-
import Webhooks from '@octokit/webhooks';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* @desc
|
|
16
|
-
*/
|
|
17
|
-
export const desc = {
|
|
18
|
-
deploy: 'Deploy project from a remote origin.',
|
|
19
|
-
};
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* @deploy
|
|
23
|
-
*/
|
|
24
|
-
export async function deploy(origin) {
|
|
25
|
-
const cx = this || {};
|
|
26
|
-
if (!cx.config.deployment?.Origins) {
|
|
27
|
-
throw new Error(`The Origins configurator "config.deployment.Origins" is required in context.`);
|
|
28
|
-
}
|
|
29
|
-
if (!_isObject(origin)) {
|
|
30
|
-
if (!origin) {
|
|
31
|
-
throw new Error(`Please provide a repository name.`);
|
|
32
|
-
}
|
|
33
|
-
if (origin.includes('/')) {
|
|
34
|
-
if (!origin.startsWith('https://') || !origin.endsWith('.git')) {
|
|
35
|
-
throw new Error(`Cannot deploy ${origin}: A valid https git repository is expected.`);
|
|
36
|
-
}
|
|
37
|
-
var urlSplit = _beforeLast(origin, '.git').split('/');
|
|
38
|
-
var [ repo, branch ] = urlSplit.splice(-2).join('/').split(':');
|
|
39
|
-
origin = {
|
|
40
|
-
repo,
|
|
41
|
-
branch: branch || 'master',
|
|
42
|
-
host: urlSplit.pop(),
|
|
43
|
-
url: origin,
|
|
44
|
-
tag: repo.replace('/', '-'),
|
|
45
|
-
};
|
|
46
|
-
} else {
|
|
47
|
-
const matches = await (new cx.config.deployment.Origins(cx)).match(origin);
|
|
48
|
-
if (matches.length > 1) {
|
|
49
|
-
throw new Error(`Cannot deploy ${origin}: Multiple deploy settings found.`);
|
|
50
|
-
}
|
|
51
|
-
if (!matches.length) {
|
|
52
|
-
throw new Error(`Cannot deploy ${origin}: No deploy settings found.`);
|
|
53
|
-
}
|
|
54
|
-
origin = matches[0];
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
// ---------------
|
|
58
|
-
const accessToken = origin.access_token;
|
|
59
|
-
const isDeployPathSet = origin.deploy_path;
|
|
60
|
-
origin.deploy_path = Path.join(cx.CWD || '', origin.deploy_path || '.');
|
|
61
|
-
// ---------------
|
|
62
|
-
// Instance
|
|
63
|
-
const git = SimpleGit();
|
|
64
|
-
// Before calling git.init()
|
|
65
|
-
var isNewDeployPath = !Fs.existsSync((origin.deploy_path || '') + '/.git');
|
|
66
|
-
if (isDeployPathSet) {
|
|
67
|
-
if (!Fs.existsSync(origin.deploy_path)) {
|
|
68
|
-
Fs.mkdirSync(origin.deploy_path, { recursive: true });
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
await git.cwd(origin.deploy_path);
|
|
72
|
-
// Must come after git.cwd()
|
|
73
|
-
await git.init();
|
|
74
|
-
|
|
75
|
-
const hosts = {
|
|
76
|
-
github: `https://${ accessToken ? accessToken + '@' : '' }github.com`,
|
|
77
|
-
bitbucket: `https://${ accessToken ? accessToken + '@' : '' }bitbucket.org`,
|
|
78
|
-
};
|
|
79
|
-
const url = origin.url || hosts[origin.host] + '/' + origin.repo + '.git';
|
|
80
|
-
|
|
81
|
-
// Deployment
|
|
82
|
-
const pull = async () => {
|
|
83
|
-
let waiting;
|
|
84
|
-
if (cx.logger) {
|
|
85
|
-
cx.logger.log('');
|
|
86
|
-
waiting = cx.logger.waiting(cx.logger.f`Deploying ${origin.tag}`);
|
|
87
|
-
waiting.start();
|
|
88
|
-
}
|
|
89
|
-
await git.reset('hard');
|
|
90
|
-
return git.pull(origin.tag, origin.branch)
|
|
91
|
-
.then(() => {
|
|
92
|
-
if (cx.logger) {
|
|
93
|
-
waiting.stop();
|
|
94
|
-
cx.logger.log('');
|
|
95
|
-
var _date = (new Date).toUTCString();
|
|
96
|
-
cx.logger.success(cx.logger.f`[${cx.logger.style.comment(_date)}][AUTODEPLOY] Successfully deployed: ${origin.tag + '@' + origin.branch} - ${url} to ${origin.deploy_path}!`);
|
|
97
|
-
cx.logger.success(cx.logger.f`[${cx.logger.style.comment(_date)}][AUTODEPLOY] On-deploy script: ${origin.ondeploy}!`);
|
|
98
|
-
}
|
|
99
|
-
if ((origin.ondeploy || '').trim()) {
|
|
100
|
-
const run = cmd => new Promise((resolve, reject) => {
|
|
101
|
-
cmd = cmd.split(' ').map(a => a.trim()).filter(a => a);
|
|
102
|
-
const child = spawn(cmd.shift(), cmd, {
|
|
103
|
-
cwd: origin.deploy_path,
|
|
104
|
-
stdio: [ process.stdin, process.stdout, process.stderr ],
|
|
105
|
-
});
|
|
106
|
-
|
|
107
|
-
child.on('error', error => {
|
|
108
|
-
cx.logger && cx.logger.error(error);
|
|
109
|
-
reject(error);
|
|
110
|
-
});
|
|
111
|
-
|
|
112
|
-
child.on('exit', async exitCode => {
|
|
113
|
-
resolve(exitCode);
|
|
114
|
-
cx.logger && cx.logger.log('');
|
|
115
|
-
});
|
|
116
|
-
});
|
|
117
|
-
return origin.ondeploy.split('&&').map(cmd => cmd.trim()).reduce(
|
|
118
|
-
async (prev, cmd) => (await prev) === 0 ? run(cmd) : prev
|
|
119
|
-
, 0);
|
|
120
|
-
}
|
|
121
|
-
return 0;
|
|
122
|
-
}).catch(err => {
|
|
123
|
-
if (cx.logger) {
|
|
124
|
-
waiting.stop();
|
|
125
|
-
cx.logger.error(err);
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
};
|
|
129
|
-
|
|
130
|
-
// Remote layout
|
|
131
|
-
return git.getRemotes().then(remotes => {
|
|
132
|
-
if (!_any(remotes, remote => remote.name === origin.tag)
|
|
133
|
-
// But if the folder was deleted and created anew,
|
|
134
|
-
// the above would still hold true, so we detect that here
|
|
135
|
-
|| isNewDeployPath) {
|
|
136
|
-
return git.addRemote(origin.tag, url)
|
|
137
|
-
.then(() => {
|
|
138
|
-
if (cx.logger) {
|
|
139
|
-
cx.logger.log('');
|
|
140
|
-
cx.logger.info(cx.logger.f`Added new origin - ${origin.tag}: ${url}`);
|
|
141
|
-
}
|
|
142
|
-
return pull();
|
|
143
|
-
})
|
|
144
|
-
.catch(err => cx.logger && cx.logger.error(err));
|
|
145
|
-
} else {
|
|
146
|
-
return pull();
|
|
147
|
-
}
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
/**
|
|
153
|
-
* @hook
|
|
154
|
-
*/
|
|
155
|
-
export async function webhook(httpEvent, router, next) {
|
|
156
|
-
const cx = this || {};
|
|
157
|
-
if (!cx.config.deployment?.Origins) {
|
|
158
|
-
throw new Error(`The Origins configurator "config.deployment.Origins" is required in context.`);
|
|
159
|
-
}
|
|
160
|
-
const webhookEventHandler = Webhooks.createEventHandler();
|
|
161
|
-
if (httpEvent.request.headers.has('user-agent') && httpEvent.request.headers.get('user-agent').startsWith('GitHub-Hookshot/')) {
|
|
162
|
-
const payload = await httpEvent.request.json();
|
|
163
|
-
const matches = (await (new cx.config.deployment.Origins(cx)).match(payload.repository.full_name)).filter(o => o.autodeploy);
|
|
164
|
-
var deployParams;
|
|
165
|
-
if (!(deployParams = matches[0])) {
|
|
166
|
-
return next();
|
|
167
|
-
}
|
|
168
|
-
if (matches.length > 1) {
|
|
169
|
-
throw new Error(`Failed deploy attempt (${payload.repository.full_name}): Multiple deploy settings found.`);
|
|
170
|
-
}
|
|
171
|
-
if (!deployParams.autodeploy_secret) {
|
|
172
|
-
throw new Error(`Failed deploy attempt (${payload.repository.full_name}): The deploy settings do not contain a secret.`);
|
|
173
|
-
}
|
|
174
|
-
if (!Webhooks.verify(deployParams.autodeploy_secret, payload, httpEvent.request.headers.get('x-hub-signature'))) {
|
|
175
|
-
throw new Error(`Failed deploy attempt (${payload.repository.full_name}): Signature mismatch.`);
|
|
176
|
-
}
|
|
177
|
-
if (payload.repository.disabled || payload.repository.archived) {
|
|
178
|
-
throw new Error(`Failed deploy attempt (${payload.repository.full_name}): Repository disabled or archived.`);
|
|
179
|
-
}
|
|
180
|
-
return new Promise(resolve => {
|
|
181
|
-
webhookEventHandler.on('push', async ({ name, payload }) => {
|
|
182
|
-
if (cx.logger) {
|
|
183
|
-
cx.logger.log('---------------------------');
|
|
184
|
-
cx.logger.log('');
|
|
185
|
-
}
|
|
186
|
-
var exitCode = await router.route('deploy', httpEvent, payload, _payload => {
|
|
187
|
-
return deploy.call(cx, deployParams);
|
|
188
|
-
});
|
|
189
|
-
if (deployParams.ondeploy_autoexit) {
|
|
190
|
-
if (cx.logger) {
|
|
191
|
-
cx.logger.log('');
|
|
192
|
-
var _date = (new Date).toUTCString();
|
|
193
|
-
cx.logger.success(cx.logger.f`[${cx.logger.style.comment(_date)}][AUTODEPLOY] Exiting(${exitCode})...`);
|
|
194
|
-
cx.logger.log('');
|
|
195
|
-
}
|
|
196
|
-
setTimeout(() => {
|
|
197
|
-
process.exit(exitCode);
|
|
198
|
-
}, _isNumeric(deployParams.ondeploy_autoexit) ? parseInt(deployParams.ondeploy_autoexit) : 0);
|
|
199
|
-
}
|
|
200
|
-
resolve(
|
|
201
|
-
new
|
|
202
|
-
);
|
|
203
|
-
if (cx.logger) {
|
|
204
|
-
cx.logger.log('');
|
|
205
|
-
cx.logger.log('---------------------------');
|
|
206
|
-
}
|
|
207
|
-
});
|
|
208
|
-
webhookEventHandler.receive({
|
|
209
|
-
id: httpEvent.request.headers.get('x-github-delivery'),
|
|
210
|
-
name: httpEvent.request.headers.get('x-github-event'),
|
|
211
|
-
payload: payload /* JSON object */,
|
|
212
|
-
});
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
return next();
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* @imports
|
|
4
|
+
*/
|
|
5
|
+
import Fs from 'fs';
|
|
6
|
+
import Path from 'path';
|
|
7
|
+
import SimpleGit from 'simple-git';
|
|
8
|
+
import { spawn } from 'child_process';
|
|
9
|
+
import { _any } from '@webqit/util/arr/index.js';
|
|
10
|
+
import { _beforeLast } from '@webqit/util/str/index.js';
|
|
11
|
+
import { _isObject, _isNumeric } from '@webqit/util/js/index.js';
|
|
12
|
+
import Webhooks from '@octokit/webhooks';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* @desc
|
|
16
|
+
*/
|
|
17
|
+
export const desc = {
|
|
18
|
+
deploy: 'Deploy project from a remote origin.',
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @deploy
|
|
23
|
+
*/
|
|
24
|
+
export async function deploy(origin) {
|
|
25
|
+
const cx = this || {};
|
|
26
|
+
if (!cx.config.deployment?.Origins) {
|
|
27
|
+
throw new Error(`The Origins configurator "config.deployment.Origins" is required in context.`);
|
|
28
|
+
}
|
|
29
|
+
if (!_isObject(origin)) {
|
|
30
|
+
if (!origin) {
|
|
31
|
+
throw new Error(`Please provide a repository name.`);
|
|
32
|
+
}
|
|
33
|
+
if (origin.includes('/')) {
|
|
34
|
+
if (!origin.startsWith('https://') || !origin.endsWith('.git')) {
|
|
35
|
+
throw new Error(`Cannot deploy ${origin}: A valid https git repository is expected.`);
|
|
36
|
+
}
|
|
37
|
+
var urlSplit = _beforeLast(origin, '.git').split('/');
|
|
38
|
+
var [ repo, branch ] = urlSplit.splice(-2).join('/').split(':');
|
|
39
|
+
origin = {
|
|
40
|
+
repo,
|
|
41
|
+
branch: branch || 'master',
|
|
42
|
+
host: urlSplit.pop(),
|
|
43
|
+
url: origin,
|
|
44
|
+
tag: repo.replace('/', '-'),
|
|
45
|
+
};
|
|
46
|
+
} else {
|
|
47
|
+
const matches = await (new cx.config.deployment.Origins(cx)).match(origin);
|
|
48
|
+
if (matches.length > 1) {
|
|
49
|
+
throw new Error(`Cannot deploy ${origin}: Multiple deploy settings found.`);
|
|
50
|
+
}
|
|
51
|
+
if (!matches.length) {
|
|
52
|
+
throw new Error(`Cannot deploy ${origin}: No deploy settings found.`);
|
|
53
|
+
}
|
|
54
|
+
origin = matches[0];
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// ---------------
|
|
58
|
+
const accessToken = origin.access_token;
|
|
59
|
+
const isDeployPathSet = origin.deploy_path;
|
|
60
|
+
origin.deploy_path = Path.join(cx.CWD || '', origin.deploy_path || '.');
|
|
61
|
+
// ---------------
|
|
62
|
+
// Instance
|
|
63
|
+
const git = SimpleGit();
|
|
64
|
+
// Before calling git.init()
|
|
65
|
+
var isNewDeployPath = !Fs.existsSync((origin.deploy_path || '') + '/.git');
|
|
66
|
+
if (isDeployPathSet) {
|
|
67
|
+
if (!Fs.existsSync(origin.deploy_path)) {
|
|
68
|
+
Fs.mkdirSync(origin.deploy_path, { recursive: true });
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
await git.cwd(origin.deploy_path);
|
|
72
|
+
// Must come after git.cwd()
|
|
73
|
+
await git.init();
|
|
74
|
+
|
|
75
|
+
const hosts = {
|
|
76
|
+
github: `https://${ accessToken ? accessToken + '@' : '' }github.com`,
|
|
77
|
+
bitbucket: `https://${ accessToken ? accessToken + '@' : '' }bitbucket.org`,
|
|
78
|
+
};
|
|
79
|
+
const url = origin.url || hosts[origin.host] + '/' + origin.repo + '.git';
|
|
80
|
+
|
|
81
|
+
// Deployment
|
|
82
|
+
const pull = async () => {
|
|
83
|
+
let waiting;
|
|
84
|
+
if (cx.logger) {
|
|
85
|
+
cx.logger.log('');
|
|
86
|
+
waiting = cx.logger.waiting(cx.logger.f`Deploying ${origin.tag}`);
|
|
87
|
+
waiting.start();
|
|
88
|
+
}
|
|
89
|
+
await git.reset('hard');
|
|
90
|
+
return git.pull(origin.tag, origin.branch)
|
|
91
|
+
.then(() => {
|
|
92
|
+
if (cx.logger) {
|
|
93
|
+
waiting.stop();
|
|
94
|
+
cx.logger.log('');
|
|
95
|
+
var _date = (new Date).toUTCString();
|
|
96
|
+
cx.logger.success(cx.logger.f`[${cx.logger.style.comment(_date)}][AUTODEPLOY] Successfully deployed: ${origin.tag + '@' + origin.branch} - ${url} to ${origin.deploy_path}!`);
|
|
97
|
+
cx.logger.success(cx.logger.f`[${cx.logger.style.comment(_date)}][AUTODEPLOY] On-deploy script: ${origin.ondeploy}!`);
|
|
98
|
+
}
|
|
99
|
+
if ((origin.ondeploy || '').trim()) {
|
|
100
|
+
const run = cmd => new Promise((resolve, reject) => {
|
|
101
|
+
cmd = cmd.split(' ').map(a => a.trim()).filter(a => a);
|
|
102
|
+
const child = spawn(cmd.shift(), cmd, {
|
|
103
|
+
cwd: origin.deploy_path,
|
|
104
|
+
stdio: [ process.stdin, process.stdout, process.stderr ],
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
child.on('error', error => {
|
|
108
|
+
cx.logger && cx.logger.error(error);
|
|
109
|
+
reject(error);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
child.on('exit', async exitCode => {
|
|
113
|
+
resolve(exitCode);
|
|
114
|
+
cx.logger && cx.logger.log('');
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
return origin.ondeploy.split('&&').map(cmd => cmd.trim()).reduce(
|
|
118
|
+
async (prev, cmd) => (await prev) === 0 ? run(cmd) : prev
|
|
119
|
+
, 0);
|
|
120
|
+
}
|
|
121
|
+
return 0;
|
|
122
|
+
}).catch(err => {
|
|
123
|
+
if (cx.logger) {
|
|
124
|
+
waiting.stop();
|
|
125
|
+
cx.logger.error(err);
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
// Remote layout
|
|
131
|
+
return git.getRemotes().then(remotes => {
|
|
132
|
+
if (!_any(remotes, remote => remote.name === origin.tag)
|
|
133
|
+
// But if the folder was deleted and created anew,
|
|
134
|
+
// the above would still hold true, so we detect that here
|
|
135
|
+
|| isNewDeployPath) {
|
|
136
|
+
return git.addRemote(origin.tag, url)
|
|
137
|
+
.then(() => {
|
|
138
|
+
if (cx.logger) {
|
|
139
|
+
cx.logger.log('');
|
|
140
|
+
cx.logger.info(cx.logger.f`Added new origin - ${origin.tag}: ${url}`);
|
|
141
|
+
}
|
|
142
|
+
return pull();
|
|
143
|
+
})
|
|
144
|
+
.catch(err => cx.logger && cx.logger.error(err));
|
|
145
|
+
} else {
|
|
146
|
+
return pull();
|
|
147
|
+
}
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* @hook
|
|
154
|
+
*/
|
|
155
|
+
export async function webhook(httpEvent, router, next) {
|
|
156
|
+
const cx = this || {};
|
|
157
|
+
if (!cx.config.deployment?.Origins) {
|
|
158
|
+
throw new Error(`The Origins configurator "config.deployment.Origins" is required in context.`);
|
|
159
|
+
}
|
|
160
|
+
const webhookEventHandler = Webhooks.createEventHandler();
|
|
161
|
+
if (httpEvent.request.headers.has('user-agent') && httpEvent.request.headers.get('user-agent').startsWith('GitHub-Hookshot/')) {
|
|
162
|
+
const payload = await httpEvent.request.json();
|
|
163
|
+
const matches = (await (new cx.config.deployment.Origins(cx)).match(payload.repository.full_name)).filter(o => o.autodeploy);
|
|
164
|
+
var deployParams;
|
|
165
|
+
if (!(deployParams = matches[0])) {
|
|
166
|
+
return next();
|
|
167
|
+
}
|
|
168
|
+
if (matches.length > 1) {
|
|
169
|
+
throw new Error(`Failed deploy attempt (${payload.repository.full_name}): Multiple deploy settings found.`);
|
|
170
|
+
}
|
|
171
|
+
if (!deployParams.autodeploy_secret) {
|
|
172
|
+
throw new Error(`Failed deploy attempt (${payload.repository.full_name}): The deploy settings do not contain a secret.`);
|
|
173
|
+
}
|
|
174
|
+
if (!Webhooks.verify(deployParams.autodeploy_secret, payload, httpEvent.request.headers.get('x-hub-signature'))) {
|
|
175
|
+
throw new Error(`Failed deploy attempt (${payload.repository.full_name}): Signature mismatch.`);
|
|
176
|
+
}
|
|
177
|
+
if (payload.repository.disabled || payload.repository.archived) {
|
|
178
|
+
throw new Error(`Failed deploy attempt (${payload.repository.full_name}): Repository disabled or archived.`);
|
|
179
|
+
}
|
|
180
|
+
return new Promise(resolve => {
|
|
181
|
+
webhookEventHandler.on('push', async ({ name, payload }) => {
|
|
182
|
+
if (cx.logger) {
|
|
183
|
+
cx.logger.log('---------------------------');
|
|
184
|
+
cx.logger.log('');
|
|
185
|
+
}
|
|
186
|
+
var exitCode = await router.route('deploy', httpEvent, payload, _payload => {
|
|
187
|
+
return deploy.call(cx, deployParams);
|
|
188
|
+
});
|
|
189
|
+
if (deployParams.ondeploy_autoexit) {
|
|
190
|
+
if (cx.logger) {
|
|
191
|
+
cx.logger.log('');
|
|
192
|
+
var _date = (new Date).toUTCString();
|
|
193
|
+
cx.logger.success(cx.logger.f`[${cx.logger.style.comment(_date)}][AUTODEPLOY] Exiting(${exitCode})...`);
|
|
194
|
+
cx.logger.log('');
|
|
195
|
+
}
|
|
196
|
+
setTimeout(() => {
|
|
197
|
+
process.exit(exitCode);
|
|
198
|
+
}, _isNumeric(deployParams.ondeploy_autoexit) ? parseInt(deployParams.ondeploy_autoexit) : 0);
|
|
199
|
+
}
|
|
200
|
+
resolve(
|
|
201
|
+
new Response(`Deployment ${ exitCode === 0 ? 'success' : 'error: ' + exitCode }!`, { status: exitCode === 0 ? 200 : 500 })
|
|
202
|
+
);
|
|
203
|
+
if (cx.logger) {
|
|
204
|
+
cx.logger.log('');
|
|
205
|
+
cx.logger.log('---------------------------');
|
|
206
|
+
}
|
|
207
|
+
});
|
|
208
|
+
webhookEventHandler.receive({
|
|
209
|
+
id: httpEvent.request.headers.get('x-github-delivery'),
|
|
210
|
+
name: httpEvent.request.headers.get('x-github-event'),
|
|
211
|
+
payload: payload /* JSON object */,
|
|
212
|
+
});
|
|
213
|
+
});
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
return next();
|
|
217
217
|
}
|
package/src/index.js
CHANGED
|
@@ -1,20 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
*
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
* @exports
|
|
13
|
-
*/
|
|
14
|
-
export {
|
|
15
|
-
config,
|
|
16
|
-
deployment,
|
|
17
|
-
runtime,
|
|
18
|
-
services,
|
|
19
|
-
Context,
|
|
1
|
+
import * as config from './config-pi/index.js';
|
|
2
|
+
import * as deployment from './deployment-pi/index.js';
|
|
3
|
+
import * as runtime from './runtime-pi/index.js';
|
|
4
|
+
import * as services from './services-pi/index.js';
|
|
5
|
+
|
|
6
|
+
export { AbstractContext as Context } from './AbstractContext.js';
|
|
7
|
+
export {
|
|
8
|
+
config,
|
|
9
|
+
deployment,
|
|
10
|
+
runtime,
|
|
11
|
+
services,
|
|
20
12
|
}
|