mythix 3.0.0 → 4.0.2
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/LICENSE +1 -1
- package/README.md +27 -30
- package/{src → lib}/application.d.ts +25 -39
- package/{src/application.js → lib/application.mjs} +110 -135
- package/lib/cli/cli-utils.d.ts +21 -0
- package/lib/cli/cli-utils.mjs +319 -0
- package/lib/cli/command-base.d.ts +33 -0
- package/lib/cli/command-base.mjs +118 -0
- package/lib/cli/command-executor.mjs +14 -0
- package/lib/cli/commands/deploy-command.mjs +1010 -0
- package/lib/cli/commands/generators/generate-command.mjs +117 -0
- package/lib/cli/commands/generators/index.mjs +2 -0
- package/{src/cli/generators/migration-generator.js → lib/cli/commands/generators/migration-generator.mjs} +11 -13
- package/lib/cli/commands/index.mjs +22 -0
- package/lib/cli/commands/migrate-command.mjs +184 -0
- package/lib/cli/commands/routes-command.mjs +40 -0
- package/lib/cli/commands/serve-command.mjs +63 -0
- package/lib/cli/commands/shell-command.mjs +95 -0
- package/lib/cli/index.mjs +3 -0
- package/{src → lib}/controllers/controller-base.d.ts +1 -1
- package/{src/controllers/controller-base.js → lib/controllers/controller-base.mjs} +3 -9
- package/{src → lib}/controllers/controller-module.d.ts +3 -3
- package/lib/controllers/controller-module.mjs +70 -0
- package/lib/controllers/generate-client-api-interface-spec.mjs +44 -0
- package/{src → lib}/controllers/generate-client-api-interface.d.ts +1 -0
- package/{src/controllers/generate-client-api-interface.js → lib/controllers/generate-client-api-interface.mjs} +39 -16
- package/{src → lib}/controllers/index.d.ts +2 -3
- package/lib/controllers/index.mjs +4 -0
- package/lib/controllers/routes/index.mjs +4 -0
- package/{src/controllers/routes/route-capture.js → lib/controllers/routes/route-capture.mjs} +2 -8
- package/{src/controllers/routes/route-endpoint.js → lib/controllers/routes/route-endpoint.mjs} +8 -8
- package/{src/controllers/routes/route-scope-base.js → lib/controllers/routes/route-scope-base.mjs} +19 -15
- package/{src/controllers/routes/route-scope.js → lib/controllers/routes/route-scope.mjs} +10 -12
- package/{src/http-server/http-errors.js → lib/http/http-errors.mjs} +9 -20
- package/{src/utils/http-interface.js → lib/http/http-interface.mjs} +6 -14
- package/{src/http-server → lib/http}/http-server-module.d.ts +2 -2
- package/{src/http-server/http-server-module.js → lib/http/http-server-module.mjs} +7 -11
- package/{src/http-server/http-server.js → lib/http/http-server.mjs} +17 -21
- package/lib/http/index.d.ts +4 -0
- package/lib/http/index.mjs +5 -0
- package/lib/index.d.ts +30 -0
- package/lib/index.mjs +31 -0
- package/lib/logger-spec.mjs +121 -0
- package/{src → lib}/logger.d.ts +1 -3
- package/{src/logger.js → lib/logger.mjs} +10 -24
- package/lib/models/index.d.ts +2 -0
- package/lib/models/index.mjs +2 -0
- package/{src/models/migration-model.js → lib/models/migration-model.mjs} +3 -11
- package/lib/models/model.d.ts +10 -0
- package/lib/models/model.mjs +28 -0
- package/{src → lib}/modules/database-module.d.ts +2 -2
- package/{src/modules/database-module.js → lib/modules/database-module.mjs} +19 -14
- package/lib/modules/index.d.ts +2 -0
- package/lib/modules/index.mjs +2 -0
- package/{src/modules/base-module.d.ts → lib/modules/module-base.d.ts} +3 -10
- package/{src/modules/base-module.js → lib/modules/module-base.mjs} +16 -6
- package/lib/tasks/index.d.ts +2 -0
- package/lib/tasks/index.mjs +2 -0
- package/{src → lib}/tasks/task-base.d.ts +4 -14
- package/lib/tasks/task-base.mjs +73 -0
- package/{src → lib}/tasks/task-module.d.ts +3 -3
- package/{src/tasks/task-module.js → lib/tasks/task-module.mjs} +43 -98
- package/{src/utils/config-utils.js → lib/utils/config-utils.mjs} +2 -10
- package/lib/utils/crypto-utils-spec.mjs +24 -0
- package/{src/utils/crypto-utils.js → lib/utils/crypto-utils.mjs} +16 -38
- package/lib/utils/file-utils-spec.mjs +10 -0
- package/{src/utils/file-utils.js → lib/utils/file-utils.mjs} +4 -11
- package/{src/utils/http-utils.js → lib/utils/http-utils.mjs} +3 -10
- package/{src → lib}/utils/index.d.ts +1 -1
- package/lib/utils/index.mjs +6 -0
- package/lib/utils/mime-utils-spec.mjs +171 -0
- package/{src/utils/mime-utils.js → lib/utils/mime-utils.mjs} +5 -14
- package/{src/utils/test-utils.js → lib/utils/test-utils.mjs} +14 -42
- package/package.json +5 -5
- package/src/cli/cli-utils.d.ts +0 -80
- package/src/cli/cli-utils.js +0 -547
- package/src/cli/command-executor.js +0 -31
- package/src/cli/deploy-command.js +0 -1010
- package/src/cli/generators/generate-command.js +0 -149
- package/src/cli/index.js +0 -5
- package/src/cli/migrate-command.js +0 -181
- package/src/cli/routes-command.js +0 -40
- package/src/cli/serve-command.js +0 -63
- package/src/cli/shell-command.js +0 -96
- package/src/controllers/controller-module.js +0 -126
- package/src/controllers/controller-utils.d.ts +0 -19
- package/src/controllers/controller-utils.js +0 -24
- package/src/controllers/index.js +0 -19
- package/src/controllers/routes/index.js +0 -31
- package/src/http-server/index.d.ts +0 -3
- package/src/http-server/index.js +0 -16
- package/src/index.d.ts +0 -49
- package/src/index.js +0 -47
- package/src/models/index.d.ts +0 -4
- package/src/models/index.js +0 -17
- package/src/models/model-module.d.ts +0 -9
- package/src/models/model-module.js +0 -130
- package/src/models/model-utils.d.ts +0 -20
- package/src/models/model-utils.js +0 -46
- package/src/models/model.d.ts +0 -20
- package/src/models/model.js +0 -65
- package/src/modules/file-watcher-module.d.ts +0 -13
- package/src/modules/file-watcher-module.js +0 -220
- package/src/modules/index.d.ts +0 -3
- package/src/modules/index.js +0 -11
- package/src/tasks/index.d.ts +0 -3
- package/src/tasks/index.js +0 -11
- package/src/tasks/task-base.js +0 -117
- package/src/tasks/task-utils.d.ts +0 -46
- package/src/tasks/task-utils.js +0 -130
- package/src/utils/index.js +0 -25
- /package/{src → lib}/cli/index.d.ts +0 -0
- /package/{src → lib}/controllers/routes/index.d.ts +0 -0
- /package/{src → lib}/controllers/routes/route-capture.d.ts +0 -0
- /package/{src → lib}/controllers/routes/route-endpoint.d.ts +0 -0
- /package/{src → lib}/controllers/routes/route-scope-base.d.ts +0 -0
- /package/{src → lib}/controllers/routes/route-scope.d.ts +0 -0
- /package/{src/http-server → lib/http}/http-errors.d.ts +0 -0
- /package/{src/utils → lib/http}/http-interface.d.ts +0 -0
- /package/{src/http-server → lib/http}/http-server.d.ts +0 -0
- /package/{src → lib}/interfaces/common.ts +0 -0
- /package/{src → lib}/models/migration-model.d.ts +0 -0
- /package/{src → lib}/utils/config-utils.d.ts +0 -0
- /package/{src → lib}/utils/crypto-utils.d.ts +0 -0
- /package/{src → lib}/utils/file-utils.d.ts +0 -0
- /package/{src → lib}/utils/http-utils.d.ts +0 -0
- /package/{src → lib}/utils/mime-utils.d.ts +0 -0
- /package/{src → lib}/utils/test-utils.d.ts +0 -0
|
@@ -1,1010 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/* global process */
|
|
4
|
-
|
|
5
|
-
const Nife = require('nife');
|
|
6
|
-
const Path = require('path');
|
|
7
|
-
const OS = require('os');
|
|
8
|
-
const FileSystem = require('fs');
|
|
9
|
-
const { URL } = require('url');
|
|
10
|
-
const micromatch = require('micromatch');
|
|
11
|
-
const { defineCommand } = require('./cli-utils');
|
|
12
|
-
const { Logger } = require('../logger');
|
|
13
|
-
const { walkDir } = require('../utils/file-utils');
|
|
14
|
-
|
|
15
|
-
const DEFAULT_SSH_PORT = 22;
|
|
16
|
-
const LATEST_DEPLOY_VERSIONS_TO_KEEP = 5;
|
|
17
|
-
|
|
18
|
-
module.exports = defineCommand('deploy', ({ Parent }) => {
|
|
19
|
-
return class DeployCommand extends Parent {
|
|
20
|
-
static applicationConfig = { database: false, logger: { level: Logger.LEVEL_ERROR } };
|
|
21
|
-
|
|
22
|
-
static commandArguments() {
|
|
23
|
-
return {
|
|
24
|
-
help: {
|
|
25
|
-
'@usage': 'mythix-cli deploy {target}',
|
|
26
|
-
'@title': 'Deploy your application to the specified target',
|
|
27
|
-
'--dry-run': 'Show what would be deployed without actually deploying',
|
|
28
|
-
'--no-cleanup': 'File prep in the temporary file location will not be cleaned up after processing',
|
|
29
|
-
'--direct': 'Skip source control, and deploy the project as-is',
|
|
30
|
-
'--branch': 'Specify branch to deploy (overrides configuration for specified environment)',
|
|
31
|
-
},
|
|
32
|
-
runner: ({ $, Types }) => {
|
|
33
|
-
$('--dry-run', Types.BOOLEAN());
|
|
34
|
-
$('--no-cleanup', Types.BOOLEAN());
|
|
35
|
-
$('--direct', Types.BOOLEAN());
|
|
36
|
-
$('--branch', Types.STRING());
|
|
37
|
-
|
|
38
|
-
return $(
|
|
39
|
-
/[\w-]+/,
|
|
40
|
-
({ store }, parsedResult) => {
|
|
41
|
-
store({ target: parsedResult.value });
|
|
42
|
-
return true;
|
|
43
|
-
},
|
|
44
|
-
{
|
|
45
|
-
formatParsedResult: (result) => {
|
|
46
|
-
return {
|
|
47
|
-
value: result[0],
|
|
48
|
-
};
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
);
|
|
52
|
-
},
|
|
53
|
-
};
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
joinUnixPath(...args) {
|
|
57
|
-
return args.join('/').replace(/\/{2,}/g, '/');
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
async spawnCommand(dryRun, command, args, options) {
|
|
61
|
-
if (dryRun) {
|
|
62
|
-
console.log(` (would run)$ ${command} ${args.join(' ')}`);
|
|
63
|
-
return 0;
|
|
64
|
-
} else {
|
|
65
|
-
console.log(` (running)$ ${command} ${args.join(' ')}`);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
try {
|
|
69
|
-
return await super.spawnCommand(command, args, options);
|
|
70
|
-
} catch (error) {
|
|
71
|
-
console.error('Error with command: ', { dryRun, command, args, options }, error);
|
|
72
|
-
if (error.stderr)
|
|
73
|
-
console.error(error.stderr);
|
|
74
|
-
|
|
75
|
-
if (error instanceof Error)
|
|
76
|
-
throw error;
|
|
77
|
-
|
|
78
|
-
throw new Error(error.error);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
mkdirSync(dryRun, path) {
|
|
83
|
-
if (!dryRun) {
|
|
84
|
-
console.log(` (running)$ mkdir -p ${path}`);
|
|
85
|
-
try {
|
|
86
|
-
FileSystem.mkdirSync(path, { recursive: true });
|
|
87
|
-
} catch (error) {}
|
|
88
|
-
} else {
|
|
89
|
-
console.log(` (would run)$ mkdir -p ${path}`);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
getRevisionNumber() {
|
|
94
|
-
let date = new Date();
|
|
95
|
-
return date.toISOString().replace(/\.[^.]+$/, '').replace(/\D/g, '');
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
stripPostfixSlashFromPath(path) {
|
|
99
|
-
return path.replace(/[/\\]+$/, '');
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
collectFilesToDeploy(rootPath, config) {
|
|
103
|
-
const filterFunc = (context) => {
|
|
104
|
-
const matchesAnyPattern = (patterns) => {
|
|
105
|
-
for (let i = 0, il = patterns.length; i < il; i++) {
|
|
106
|
-
let pattern = patterns[i];
|
|
107
|
-
let result;
|
|
108
|
-
|
|
109
|
-
if (typeof pattern === 'function')
|
|
110
|
-
result = pattern(context);
|
|
111
|
-
else if (pattern instanceof RegExp)
|
|
112
|
-
result = pattern.test(relativeFileName);
|
|
113
|
-
else if (Nife.instanceOf(pattern, 'string'))
|
|
114
|
-
result = micromatch.isMatch(relativeFileName, pattern);
|
|
115
|
-
|
|
116
|
-
if (result)
|
|
117
|
-
return true;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
return false;
|
|
121
|
-
};
|
|
122
|
-
|
|
123
|
-
let {
|
|
124
|
-
relativeFileName,
|
|
125
|
-
stats,
|
|
126
|
-
} = context;
|
|
127
|
-
|
|
128
|
-
let shouldIncludeFile = true;
|
|
129
|
-
|
|
130
|
-
// First check gitignore patterns
|
|
131
|
-
// if that was requested
|
|
132
|
-
if (useGitIgnore && matchesAnyPattern(useGitIgnore))
|
|
133
|
-
shouldIncludeFile = false;
|
|
134
|
-
|
|
135
|
-
if (Nife.isNotEmpty(include) && !stats.isDirectory())
|
|
136
|
-
shouldIncludeFile = matchesAnyPattern(include);
|
|
137
|
-
|
|
138
|
-
if (Nife.isNotEmpty(exclude) && matchesAnyPattern(exclude))
|
|
139
|
-
shouldIncludeFile = false;
|
|
140
|
-
|
|
141
|
-
return shouldIncludeFile;
|
|
142
|
-
};
|
|
143
|
-
|
|
144
|
-
let {
|
|
145
|
-
git,
|
|
146
|
-
include,
|
|
147
|
-
exclude,
|
|
148
|
-
} = config;
|
|
149
|
-
|
|
150
|
-
let useGitIgnore = (git || {}).useGitIgnore;
|
|
151
|
-
|
|
152
|
-
if (Nife.isEmpty(include)) {
|
|
153
|
-
include = [
|
|
154
|
-
'app/**',
|
|
155
|
-
'package.json',
|
|
156
|
-
];
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
if (Nife.isEmpty(exclude)) {
|
|
160
|
-
exclude = [
|
|
161
|
-
'**/node_modules/**',
|
|
162
|
-
'.git',
|
|
163
|
-
];
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
return walkDir(rootPath, {
|
|
167
|
-
filter: (fullFileName, fileName, stats) => {
|
|
168
|
-
let relativeFileName = fullFileName.substring(rootPath.length).replace(/^[/\\]/, '');
|
|
169
|
-
|
|
170
|
-
return filterFunc({
|
|
171
|
-
relativeFileName,
|
|
172
|
-
fullFileName,
|
|
173
|
-
fileName,
|
|
174
|
-
stats,
|
|
175
|
-
});
|
|
176
|
-
},
|
|
177
|
-
});
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
async getRepositoryURL(remoteName) {
|
|
181
|
-
let result = await this.spawnCommand(false, 'git', [ 'remote', '-v' ]);
|
|
182
|
-
result = result.stdout.split(/\r\n|\n|\r/g).filter((line) => {
|
|
183
|
-
return (/\(fetch\)$/).test(line);
|
|
184
|
-
}).map((line) => {
|
|
185
|
-
let parts = line.trim().split(/\s+/);
|
|
186
|
-
|
|
187
|
-
return {
|
|
188
|
-
remoteName: parts[0],
|
|
189
|
-
url: parts[1],
|
|
190
|
-
};
|
|
191
|
-
});
|
|
192
|
-
|
|
193
|
-
if (result.length === 0) {
|
|
194
|
-
throw new Error('Unable to locate your repository URL. Please specify a "git: { repository: "{url}" }" in your deployment config.');
|
|
195
|
-
} else if (result.length === 1) {
|
|
196
|
-
return result[0].url;
|
|
197
|
-
} else {
|
|
198
|
-
let remotes = Nife.toLookup('remoteName', result);
|
|
199
|
-
let remote = remotes[remoteName];
|
|
200
|
-
let url = (remote && remote.url);
|
|
201
|
-
|
|
202
|
-
if (Nife.isEmpty(url))
|
|
203
|
-
throw new Error('Unable to locate your repository URL. You have multiple remotes setup in your git configuration. Please specify a "git: { remoteName: "{name}" }", or the repository directly ("git: { repository: "{url}" }") in your deployment config.');
|
|
204
|
-
|
|
205
|
-
return url;
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
async cleanup(dryRun, deployConfig) {
|
|
210
|
-
let { tempLocation } = deployConfig;
|
|
211
|
-
let removeCommand;
|
|
212
|
-
|
|
213
|
-
tempLocation = this.stripPostfixSlashFromPath(tempLocation);
|
|
214
|
-
|
|
215
|
-
if (process.platform === 'win32') {
|
|
216
|
-
removeCommand = {
|
|
217
|
-
command: 'rmdir',
|
|
218
|
-
args: [
|
|
219
|
-
tempLocation,
|
|
220
|
-
'/Q',
|
|
221
|
-
'/S',
|
|
222
|
-
],
|
|
223
|
-
};
|
|
224
|
-
} else {
|
|
225
|
-
removeCommand = {
|
|
226
|
-
command: 'rm',
|
|
227
|
-
args: [
|
|
228
|
-
'-fr',
|
|
229
|
-
tempLocation,
|
|
230
|
-
],
|
|
231
|
-
};
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
await this.spawnCommand(false, removeCommand.command, removeCommand.args);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
async cloneProject(deployConfig) {
|
|
238
|
-
let {
|
|
239
|
-
git,
|
|
240
|
-
directDeploy,
|
|
241
|
-
tempLocation,
|
|
242
|
-
rootPath,
|
|
243
|
-
} = deployConfig;
|
|
244
|
-
|
|
245
|
-
let {
|
|
246
|
-
branch,
|
|
247
|
-
repository,
|
|
248
|
-
} = (git || {});
|
|
249
|
-
|
|
250
|
-
rootPath = this.stripPostfixSlashFromPath(rootPath);
|
|
251
|
-
|
|
252
|
-
let targetPath = Path.join(tempLocation, 'project');
|
|
253
|
-
|
|
254
|
-
if (directDeploy || !repository) {
|
|
255
|
-
let copyCommand;
|
|
256
|
-
|
|
257
|
-
if (process.platform === 'win32') {
|
|
258
|
-
copyCommand = {
|
|
259
|
-
command: 'xcopy',
|
|
260
|
-
args: [
|
|
261
|
-
rootPath,
|
|
262
|
-
`${targetPath}${Path.sep}`,
|
|
263
|
-
'/H',
|
|
264
|
-
'/E',
|
|
265
|
-
'/Y',
|
|
266
|
-
'/K',
|
|
267
|
-
'/I',
|
|
268
|
-
],
|
|
269
|
-
};
|
|
270
|
-
} else {
|
|
271
|
-
copyCommand = {
|
|
272
|
-
command: 'cp',
|
|
273
|
-
args: [
|
|
274
|
-
'-a',
|
|
275
|
-
rootPath,
|
|
276
|
-
targetPath,
|
|
277
|
-
],
|
|
278
|
-
};
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
await this.spawnCommand(false, copyCommand.command, copyCommand.args);
|
|
282
|
-
} else {
|
|
283
|
-
let args = [
|
|
284
|
-
'clone',
|
|
285
|
-
repository,
|
|
286
|
-
];
|
|
287
|
-
|
|
288
|
-
if (branch) {
|
|
289
|
-
args.push('-b');
|
|
290
|
-
args.push(branch);
|
|
291
|
-
}
|
|
292
|
-
|
|
293
|
-
args.push(targetPath);
|
|
294
|
-
|
|
295
|
-
await this.spawnCommand(
|
|
296
|
-
false,
|
|
297
|
-
'git',
|
|
298
|
-
args,
|
|
299
|
-
);
|
|
300
|
-
}
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
async postCloneProject() {
|
|
304
|
-
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
async copyFile(dryRun, sourceFilePath, targetFilePath) {
|
|
308
|
-
let targetDir = Path.dirname(targetFilePath);
|
|
309
|
-
if (!FileSystem.existsSync(targetDir))
|
|
310
|
-
await this.mkdirSync(false, targetDir);
|
|
311
|
-
|
|
312
|
-
console.log(` (running)$ cp ${sourceFilePath} ${targetFilePath}`);
|
|
313
|
-
FileSystem.copyFileSync(sourceFilePath, targetFilePath);
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
formatOutputPath(context, deployConfig) {
|
|
317
|
-
let { formatOutputPath } = deployConfig;
|
|
318
|
-
if (typeof formatOutputPath === 'function')
|
|
319
|
-
return formatOutputPath.call(this, context, deployConfig);
|
|
320
|
-
|
|
321
|
-
return context.targetPath;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
async copyProjectFilesToDeployFolder(deployConfig) {
|
|
325
|
-
let {
|
|
326
|
-
tempLocation,
|
|
327
|
-
version,
|
|
328
|
-
dryRun,
|
|
329
|
-
} = deployConfig;
|
|
330
|
-
|
|
331
|
-
let projectLocation = Path.join(tempLocation, 'project');
|
|
332
|
-
let deployFolderLocation = Path.join(tempLocation, ('' + version));
|
|
333
|
-
|
|
334
|
-
let filesToDeploy = this.collectFilesToDeploy(projectLocation, deployConfig);
|
|
335
|
-
if (Nife.isEmpty(filesToDeploy)) {
|
|
336
|
-
console.error('Nothing to deploy');
|
|
337
|
-
return 1;
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
for (let i = 0, il = filesToDeploy.length; i < il; i++) {
|
|
341
|
-
let sourcePath = filesToDeploy[i];
|
|
342
|
-
let relativeFilePath = sourcePath.substring(projectLocation.length).replace(/^[/\\]+/, '');
|
|
343
|
-
let targetPath = this.formatOutputPath({
|
|
344
|
-
targetPath: Path.join(deployFolderLocation, relativeFilePath),
|
|
345
|
-
outputPath: projectLocation,
|
|
346
|
-
deployPath: deployFolderLocation,
|
|
347
|
-
relativeFilePath,
|
|
348
|
-
sourcePath,
|
|
349
|
-
}, deployConfig);
|
|
350
|
-
|
|
351
|
-
await this.copyFile(dryRun, sourcePath, targetPath);
|
|
352
|
-
}
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
async installModulesForApp(target, deployConfig) {
|
|
356
|
-
let {
|
|
357
|
-
tempLocation,
|
|
358
|
-
version,
|
|
359
|
-
installModulesCommand,
|
|
360
|
-
} = deployConfig;
|
|
361
|
-
|
|
362
|
-
let {
|
|
363
|
-
remoteLocation,
|
|
364
|
-
} = target;
|
|
365
|
-
|
|
366
|
-
let deployLocation = Path.join(tempLocation, ('' + version));
|
|
367
|
-
|
|
368
|
-
if (installModulesCommand == null) {
|
|
369
|
-
let yarnLockLocation = Path.join(deployLocation, 'yarn.lock');
|
|
370
|
-
if (FileSystem.existsSync(yarnLockLocation))
|
|
371
|
-
installModulesCommand = { command: 'bash', args: [ '--login', '-c', '"yarn --prod"' ] };
|
|
372
|
-
else
|
|
373
|
-
installModulesCommand = { command: 'bash', args: [ '--login', '-c', '"npm i --omit=dev"' ] };
|
|
374
|
-
} else if (installModulesCommand !== false && Nife.isEmpty(typeof installModulesCommand !== 'function' && Nife.isEmpty(installModulesCommand.command))) {
|
|
375
|
-
throw new Error('You specified a "installModulesCommand" in your deploy config, but no "command" property found... you need to specify "installModulesCommand" as an object "{ installModulesCommand: { command: "npm", args: [ "i" ] } }", or as a function.');
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
if (typeof installModulesCommand === 'function') {
|
|
379
|
-
await installModulesCommand(deployConfig);
|
|
380
|
-
} else if (installModulesCommand) {
|
|
381
|
-
await this.executeRemoteCommands(target, deployConfig, [
|
|
382
|
-
{ sudo: false, command: 'cd', args: [ `"${remoteLocation}"` ]},
|
|
383
|
-
installModulesCommand,
|
|
384
|
-
]);
|
|
385
|
-
}
|
|
386
|
-
}
|
|
387
|
-
|
|
388
|
-
// eslint-disable-next-line no-unused-vars
|
|
389
|
-
async prepProjectPreDeploy(deployConfig) {
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
async archiveProject(deployConfig) {
|
|
393
|
-
let {
|
|
394
|
-
dryRun,
|
|
395
|
-
tempLocation,
|
|
396
|
-
version,
|
|
397
|
-
} = deployConfig;
|
|
398
|
-
|
|
399
|
-
let archiveLocation = deployConfig.archiveLocation = Path.join(tempLocation, `${version}.tar.gz`);
|
|
400
|
-
|
|
401
|
-
await this.spawnCommand(
|
|
402
|
-
dryRun,
|
|
403
|
-
'tar',
|
|
404
|
-
[
|
|
405
|
-
'-czf',
|
|
406
|
-
archiveLocation,
|
|
407
|
-
`.${Path.sep}${version}`,
|
|
408
|
-
],
|
|
409
|
-
{
|
|
410
|
-
cwd: tempLocation,
|
|
411
|
-
env: {
|
|
412
|
-
pwd: tempLocation,
|
|
413
|
-
},
|
|
414
|
-
},
|
|
415
|
-
);
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
getTargetInfo(target) {
|
|
419
|
-
let {
|
|
420
|
-
uri,
|
|
421
|
-
ssh,
|
|
422
|
-
scp,
|
|
423
|
-
restartService,
|
|
424
|
-
} = target;
|
|
425
|
-
|
|
426
|
-
if (!ssh)
|
|
427
|
-
ssh = { command: 'ssh' };
|
|
428
|
-
|
|
429
|
-
if (!scp)
|
|
430
|
-
scp = { command: 'scp' };
|
|
431
|
-
|
|
432
|
-
if (Nife.isEmpty(ssh.command))
|
|
433
|
-
ssh.command = 'ssh';
|
|
434
|
-
|
|
435
|
-
if (Nife.isEmpty(scp.command))
|
|
436
|
-
scp.command = 'scp';
|
|
437
|
-
|
|
438
|
-
let port = target.port || uri.port;
|
|
439
|
-
if (!port) {
|
|
440
|
-
port = DEFAULT_SSH_PORT;
|
|
441
|
-
} else {
|
|
442
|
-
port = parseInt(port, 10);
|
|
443
|
-
if (!isFinite(port))
|
|
444
|
-
port = DEFAULT_SSH_PORT;
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
return {
|
|
448
|
-
origin: uri.origin,
|
|
449
|
-
protocol: (uri.protocol || '').replace(/:+$/, ''),
|
|
450
|
-
username: uri.username,
|
|
451
|
-
password: uri.password,
|
|
452
|
-
host: uri.host,
|
|
453
|
-
hostname: uri.hostname,
|
|
454
|
-
pathname: uri.pathname,
|
|
455
|
-
...target,
|
|
456
|
-
ssh,
|
|
457
|
-
scp,
|
|
458
|
-
restartService,
|
|
459
|
-
port,
|
|
460
|
-
};
|
|
461
|
-
}
|
|
462
|
-
|
|
463
|
-
substituteURLParams(target, value) {
|
|
464
|
-
let substitutionMap = {
|
|
465
|
-
TARGET_ORIGIN: target.origin,
|
|
466
|
-
TARGET_PROTOCOL: target.protocol,
|
|
467
|
-
TARGET_USERNAME: target.username,
|
|
468
|
-
TARGET_PASSWORD: target.password,
|
|
469
|
-
TARGET_HOST: target.host,
|
|
470
|
-
TARGET_HOSTNAME: target.hostname,
|
|
471
|
-
TARGET_PORT: target.port,
|
|
472
|
-
TARGET_PATHNAME: this.stripPostfixSlashFromPath(target.pathname),
|
|
473
|
-
TARGET_DEPLOY_PATH: target.remoteLocation,
|
|
474
|
-
};
|
|
475
|
-
|
|
476
|
-
let keys = Object.keys(substitutionMap);
|
|
477
|
-
let re = new RegExp(`\\$\\{(${keys.join('|')})\\}`, 'g');
|
|
478
|
-
|
|
479
|
-
return ('' + value).replace(re, (m, name) => {
|
|
480
|
-
return substitutionMap[name];
|
|
481
|
-
});
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
sudo(...args) {
|
|
485
|
-
return (args.some((value) => (value === false))) ? '' : 'sudo ';
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
async executeRemoteCommands(target, deployConfig, commands, options) {
|
|
489
|
-
let {
|
|
490
|
-
dryRun,
|
|
491
|
-
} = deployConfig;
|
|
492
|
-
|
|
493
|
-
let {
|
|
494
|
-
hostname,
|
|
495
|
-
username,
|
|
496
|
-
port,
|
|
497
|
-
ssh,
|
|
498
|
-
} = target;
|
|
499
|
-
|
|
500
|
-
let {
|
|
501
|
-
command,
|
|
502
|
-
args,
|
|
503
|
-
} = ssh;
|
|
504
|
-
|
|
505
|
-
if (Nife.isEmpty(args)) {
|
|
506
|
-
if (port !== DEFAULT_SSH_PORT)
|
|
507
|
-
args = [ '-p', port ];
|
|
508
|
-
else
|
|
509
|
-
args = [];
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
let commandString = commands.map((command) => {
|
|
513
|
-
if (!command)
|
|
514
|
-
return;
|
|
515
|
-
|
|
516
|
-
if (Nife.isEmpty(command.args))
|
|
517
|
-
return `${this.sudo(deployConfig)}${command.command}`;
|
|
518
|
-
|
|
519
|
-
const escapeArgument = (arg) => {
|
|
520
|
-
if (arg.indexOf('\'') < 0)
|
|
521
|
-
return arg;
|
|
522
|
-
|
|
523
|
-
return `'${this.substituteURLParams(target, arg).replace(/'/g, '\'')}'`;
|
|
524
|
-
};
|
|
525
|
-
|
|
526
|
-
return `${this.sudo(deployConfig, command.sudo)}${command.command} ${command.args.map(escapeArgument).join(' ')}`;
|
|
527
|
-
}).filter(Boolean).join(' && ');
|
|
528
|
-
|
|
529
|
-
if (options && Nife.isNotEmpty(options.sshArgs))
|
|
530
|
-
args = args.concat(options.sshArgs);
|
|
531
|
-
else
|
|
532
|
-
args = args.concat([ '-t', '-q' ]);
|
|
533
|
-
|
|
534
|
-
if (Nife.isNotEmpty(args))
|
|
535
|
-
args = args.map((arg) => this.substituteURLParams(target, arg));
|
|
536
|
-
|
|
537
|
-
return await this.spawnCommand(
|
|
538
|
-
dryRun,
|
|
539
|
-
command,
|
|
540
|
-
args.concat([
|
|
541
|
-
(Nife.isNotEmpty(username)) ? `'${username}@${hostname}'` : `'${hostname}'`,
|
|
542
|
-
`'${commandString}'`,
|
|
543
|
-
]),
|
|
544
|
-
options,
|
|
545
|
-
);
|
|
546
|
-
}
|
|
547
|
-
|
|
548
|
-
async iterateDeployTargets(deployConfig, callback, options) {
|
|
549
|
-
const invokeCallback = (_target, index) => {
|
|
550
|
-
let target = this.getTargetInfo(_target);
|
|
551
|
-
return callback({
|
|
552
|
-
...target,
|
|
553
|
-
index,
|
|
554
|
-
remoteLocation: this.joinUnixPath(decodeURIComponent(target.pathname), ('' + deployConfig.version)),
|
|
555
|
-
}, deployConfig);
|
|
556
|
-
};
|
|
557
|
-
|
|
558
|
-
let targets = deployConfig.targets;
|
|
559
|
-
let parallelDeploy = (options && typeof options.parallelDeploy === 'boolean') ? options.parallelDeploy : deployConfig.parallelDeploy;
|
|
560
|
-
|
|
561
|
-
if (parallelDeploy !== false) {
|
|
562
|
-
let promises = [];
|
|
563
|
-
|
|
564
|
-
for (let i = 0, il = targets.length; i < il; i++) {
|
|
565
|
-
let target = targets[i];
|
|
566
|
-
promises.push(invokeCallback(target, i));
|
|
567
|
-
}
|
|
568
|
-
|
|
569
|
-
return await Promise.all(promises);
|
|
570
|
-
} else {
|
|
571
|
-
let results = [];
|
|
572
|
-
|
|
573
|
-
for (let i = 0, il = targets.length; i < il; i++) {
|
|
574
|
-
let target = targets[i];
|
|
575
|
-
results.push(await invokeCallback(target, i));
|
|
576
|
-
}
|
|
577
|
-
|
|
578
|
-
return results;
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
async allRemotesPreDeploy(deployConfig) {
|
|
583
|
-
return await this.iterateDeployTargets(deployConfig, this.remotePreDeploy.bind(this));
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
async remotePreDeploy(target, deployConfig) {
|
|
587
|
-
if (typeof target.preDeploy === 'function')
|
|
588
|
-
return await target.preDeploy.call(this, target, deployConfig);
|
|
589
|
-
|
|
590
|
-
let {
|
|
591
|
-
relativeConfigPath,
|
|
592
|
-
} = deployConfig;
|
|
593
|
-
|
|
594
|
-
await this.executeRemoteCommands(target, deployConfig, [
|
|
595
|
-
{ command: 'mkdir', args: [ '-p', this.joinUnixPath(decodeURIComponent(target.pathname)) ] },
|
|
596
|
-
(relativeConfigPath) && { command: 'mkdir', args: [ '-p', this.joinUnixPath(decodeURIComponent(target.pathname), 'shared') ] },
|
|
597
|
-
], { ignoreExitCode: true });
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
async allRemotesDeploy(deployConfig) {
|
|
601
|
-
return await this.iterateDeployTargets(deployConfig, this.remoteDeploy.bind(this));
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
async copyFileToRemote(target, deployConfig, _filePath, options) {
|
|
605
|
-
let filePath = _filePath;
|
|
606
|
-
let fileName = Path.basename(filePath);
|
|
607
|
-
|
|
608
|
-
let {
|
|
609
|
-
dryRun,
|
|
610
|
-
tempLocation,
|
|
611
|
-
} = deployConfig;
|
|
612
|
-
|
|
613
|
-
let {
|
|
614
|
-
hostname,
|
|
615
|
-
username,
|
|
616
|
-
port,
|
|
617
|
-
scp,
|
|
618
|
-
} = target;
|
|
619
|
-
|
|
620
|
-
let {
|
|
621
|
-
command,
|
|
622
|
-
args,
|
|
623
|
-
} = scp;
|
|
624
|
-
|
|
625
|
-
if (Nife.isEmpty(args)) {
|
|
626
|
-
if (port !== DEFAULT_SSH_PORT)
|
|
627
|
-
args = [ '-P', port ];
|
|
628
|
-
else
|
|
629
|
-
args = [];
|
|
630
|
-
}
|
|
631
|
-
|
|
632
|
-
if (!dryRun && options && typeof options.substituteContent === 'function') {
|
|
633
|
-
let content = FileSystem.readFileSync(filePath, 'utf8');
|
|
634
|
-
content = options.substituteContent.call(this, content);
|
|
635
|
-
|
|
636
|
-
await this.mkdirSync(false, tempLocation);
|
|
637
|
-
|
|
638
|
-
filePath = Path.join(tempLocation, fileName);
|
|
639
|
-
FileSystem.writeFileSync(filePath, content, 'utf8');
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
await this.spawnCommand(
|
|
643
|
-
dryRun,
|
|
644
|
-
command,
|
|
645
|
-
args.concat([
|
|
646
|
-
`'${filePath}'`,
|
|
647
|
-
(Nife.isNotEmpty(username)) ? `'${username}@${hostname}:/tmp/'` : `'${hostname}:/tmp/'`,
|
|
648
|
-
]),
|
|
649
|
-
);
|
|
650
|
-
|
|
651
|
-
return `/tmp/${fileName}`;
|
|
652
|
-
}
|
|
653
|
-
|
|
654
|
-
async copyArchiveToRemote(target, deployConfig) {
|
|
655
|
-
let {
|
|
656
|
-
archiveLocation,
|
|
657
|
-
} = deployConfig;
|
|
658
|
-
|
|
659
|
-
if (!deployConfig.archiveLocation)
|
|
660
|
-
throw new Error('Unable to find the location of the archive to send to the remote server');
|
|
661
|
-
|
|
662
|
-
let remoteArchiveLocation = await this.copyFileToRemote(target, deployConfig, archiveLocation);
|
|
663
|
-
|
|
664
|
-
let archiveFileName = Path.basename(archiveLocation);
|
|
665
|
-
let sourceLocation = remoteArchiveLocation;
|
|
666
|
-
let targetLocation = `"${decodeURIComponent(target.pathname)}/"`;
|
|
667
|
-
|
|
668
|
-
await this.executeRemoteCommands(target, deployConfig, [
|
|
669
|
-
{ command: 'cp', args: [ sourceLocation, targetLocation ] },
|
|
670
|
-
{ command: 'rm -f', args: [ `"/tmp/${archiveFileName}"` ] },
|
|
671
|
-
{ command: 'cd', args: [ targetLocation ], sudo: false },
|
|
672
|
-
{ command: 'tar', args: [ '-xf', `"./${archiveFileName}"` ] },
|
|
673
|
-
{ command: 'rm -f', args: [ `"./${archiveFileName}"` ] },
|
|
674
|
-
]);
|
|
675
|
-
}
|
|
676
|
-
|
|
677
|
-
async remoteDeploy(target, deployConfig) {
|
|
678
|
-
if (typeof target.deploy === 'function')
|
|
679
|
-
return await target.deploy.call(this, target, deployConfig);
|
|
680
|
-
|
|
681
|
-
await this.copyArchiveToRemote(target, deployConfig);
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
async allRemotesPostDeploy(deployConfig) {
|
|
685
|
-
return await this.iterateDeployTargets(deployConfig, this.remotePostDeploy.bind(this));
|
|
686
|
-
}
|
|
687
|
-
|
|
688
|
-
async remotePostDeploy(target, deployConfig) {
|
|
689
|
-
if (typeof target.postDeploy === 'function')
|
|
690
|
-
return await target.postDeploy.call(this, target, deployConfig);
|
|
691
|
-
|
|
692
|
-
let {
|
|
693
|
-
relativeConfigPath,
|
|
694
|
-
} = deployConfig;
|
|
695
|
-
|
|
696
|
-
if (relativeConfigPath) {
|
|
697
|
-
let deployLocation = this.joinUnixPath(decodeURIComponent(target.pathname), '' + deployConfig.version);
|
|
698
|
-
let targetConfigPath = this.joinUnixPath(decodeURIComponent(target.pathname), 'shared', 'config');
|
|
699
|
-
let sourceConfigPath = this.joinUnixPath(deployLocation, relativeConfigPath);
|
|
700
|
-
let nodeModulesLink = this.joinUnixPath(targetConfigPath, 'node_modules');
|
|
701
|
-
|
|
702
|
-
// Ensure shared/config exists
|
|
703
|
-
await this.executeRemoteCommands(target, deployConfig, [
|
|
704
|
-
{ command: 'test', args: [ '!', '-d', `"${targetConfigPath}"`, '&&', `${this.sudo(deployConfig)}cp -a "${sourceConfigPath}" "${targetConfigPath}"`, '|| true' ] },
|
|
705
|
-
]);
|
|
706
|
-
|
|
707
|
-
// Ensure shared/config/node_modules symlink exists
|
|
708
|
-
await this.executeRemoteCommands(target, deployConfig, [
|
|
709
|
-
{ command: 'test', args: [ '!', '-e', `"${nodeModulesLink}"`, '&&', `{ cd "${targetConfigPath}"; ${this.sudo(deployConfig)}ln -s "../../current/node_modules" "node_modules"; }`, '|| true' ] },
|
|
710
|
-
]);
|
|
711
|
-
|
|
712
|
-
// Remove app/config and symlink to ../shared/app/config
|
|
713
|
-
await this.executeRemoteCommands(target, deployConfig, [
|
|
714
|
-
{ command: 'rm', args: [ '-fr', `"${sourceConfigPath}"` ] },
|
|
715
|
-
{ command: 'ln', args: [ '-s', `"${targetConfigPath}"`, `"${sourceConfigPath}"` ] },
|
|
716
|
-
]);
|
|
717
|
-
}
|
|
718
|
-
|
|
719
|
-
await this.installModulesForApp(target, deployConfig);
|
|
720
|
-
}
|
|
721
|
-
|
|
722
|
-
async allRemotesFinalizeDeploy(deployConfig) {
|
|
723
|
-
return await this.iterateDeployTargets(
|
|
724
|
-
deployConfig,
|
|
725
|
-
this.remoteFinalizeDeploy.bind(this),
|
|
726
|
-
{ parallelDeploy: false },
|
|
727
|
-
);
|
|
728
|
-
}
|
|
729
|
-
|
|
730
|
-
async cleanupOldDeployVersions(target, deployConfig) {
|
|
731
|
-
let deployLocation = decodeURIComponent(target.pathname);
|
|
732
|
-
|
|
733
|
-
let result = await this.executeRemoteCommands(target, deployConfig, [
|
|
734
|
-
{ command: 'ls', args: [ '-1', `"${deployLocation}"`, '|', 'grep', '-P', '"\\d+"' ] },
|
|
735
|
-
]);
|
|
736
|
-
|
|
737
|
-
let versions = (result.stdout || '').split(/\n+/g).map((part) => part.trim()).filter(Boolean).sort();
|
|
738
|
-
let versionsToRemove = versions.slice(0, -LATEST_DEPLOY_VERSIONS_TO_KEEP);
|
|
739
|
-
|
|
740
|
-
if (Nife.isNotEmpty(versionsToRemove)) {
|
|
741
|
-
versionsToRemove = versionsToRemove.map((part) => `"${this.joinUnixPath(deployLocation, part)}"`);
|
|
742
|
-
|
|
743
|
-
await this.executeRemoteCommands(target, deployConfig, [
|
|
744
|
-
{ command: 'rm', args: [ '-fr' ].concat(versionsToRemove) },
|
|
745
|
-
]);
|
|
746
|
-
}
|
|
747
|
-
}
|
|
748
|
-
|
|
749
|
-
async remoteFinalizeDeploy(target, deployConfig) {
|
|
750
|
-
if (typeof target.finalizeDeploy === 'function')
|
|
751
|
-
return await target.finalizeDeploy.call(this, target, deployConfig);
|
|
752
|
-
|
|
753
|
-
// Finally, upon success, swap the "current" symlink to
|
|
754
|
-
// point to the new deploy
|
|
755
|
-
let deployLocation = this.joinUnixPath(decodeURIComponent(target.pathname), '' + deployConfig.version);
|
|
756
|
-
let currentLinkLocation = this.joinUnixPath(decodeURIComponent(target.pathname), 'current');
|
|
757
|
-
await this.executeRemoteCommands(target, deployConfig, [
|
|
758
|
-
{ command: 'rm', args: [ '-f', `"${currentLinkLocation}"` ] },
|
|
759
|
-
{ command: 'ln', args: [ '-s', `"${deployLocation}"`, `"${currentLinkLocation}"` ] },
|
|
760
|
-
]);
|
|
761
|
-
|
|
762
|
-
if (target.index === 0 && deployConfig.migrations !== false) {
|
|
763
|
-
let targetLocation = `"${decodeURIComponent(target.pathname)}/current"`;
|
|
764
|
-
|
|
765
|
-
let serviceUser = target.serviceUser || target.uri.username;
|
|
766
|
-
let serviceGroup = target.serviceGroup || serviceUser;
|
|
767
|
-
|
|
768
|
-
await this.executeRemoteCommands(target, deployConfig, [
|
|
769
|
-
{ sudo: false, command: 'cd', args: [ targetLocation ] },
|
|
770
|
-
{ sudo: false, command: 'sudo', args: [ '-u', serviceUser, '-g', serviceGroup, `NODE_ENV=${deployConfig.target} mythix-cli migrate` ] },
|
|
771
|
-
]);
|
|
772
|
-
}
|
|
773
|
-
|
|
774
|
-
// Cleanup old deploy versions
|
|
775
|
-
await this.cleanupOldDeployVersions(target, deployConfig);
|
|
776
|
-
|
|
777
|
-
if (target.restartService !== false && Nife.isNotEmpty(target.restartService)) {
|
|
778
|
-
await this.executeRemoteCommands(target, deployConfig, [
|
|
779
|
-
target.restartService,
|
|
780
|
-
]);
|
|
781
|
-
} else if (target.restartService !== false) {
|
|
782
|
-
console.log(` !!!NOTICE!!! "restartService" command is not defined on your deploy "targets[${target.index}]". Your service will not be automatically restarted. Please make sure to manually restart your application service on this remote target.`);
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
|
|
786
|
-
async loadDeployConfig(args) {
|
|
787
|
-
let application = this.getApplication();
|
|
788
|
-
let appOptions = application.getOptions();
|
|
789
|
-
let appName = application.getApplicationName() || 'mythix';
|
|
790
|
-
let configPath;
|
|
791
|
-
let deployConfig;
|
|
792
|
-
|
|
793
|
-
try {
|
|
794
|
-
configPath = require.resolve(Path.resolve(process.cwd(), '.mythix-deploy'));
|
|
795
|
-
deployConfig = require(configPath);
|
|
796
|
-
if (deployConfig.__esModule)
|
|
797
|
-
deployConfig = deployConfig['default'];
|
|
798
|
-
|
|
799
|
-
if (!Object.prototype.hasOwnProperty.call(deployConfig, args.target)) {
|
|
800
|
-
console.error(`The specified deploy target "${args.target}" doesn't exist in the deploy config "${configPath}"`);
|
|
801
|
-
return 1;
|
|
802
|
-
}
|
|
803
|
-
|
|
804
|
-
deployConfig = deployConfig[args.target];
|
|
805
|
-
|
|
806
|
-
if (Nife.isEmpty(deployConfig.targets)) {
|
|
807
|
-
console.error('"targets" is empty in your deploy config. "targets" is required, and must specify at least one remote target to deploy to.');
|
|
808
|
-
return 1;
|
|
809
|
-
}
|
|
810
|
-
|
|
811
|
-
if (!Nife.instanceOf(deployConfig.targets, 'array')) {
|
|
812
|
-
console.error('"targets" is not an array. "targets" in your deploy config must be an array of remote targets.');
|
|
813
|
-
return 1;
|
|
814
|
-
}
|
|
815
|
-
|
|
816
|
-
deployConfig.target = args.target;
|
|
817
|
-
|
|
818
|
-
deployConfig.targets = deployConfig.targets.filter(Boolean).map((target) => {
|
|
819
|
-
if (Nife.instanceOf(target, 'string')) {
|
|
820
|
-
return {
|
|
821
|
-
rawURI: ('' + target),
|
|
822
|
-
uri: new URL('' + target),
|
|
823
|
-
};
|
|
824
|
-
}
|
|
825
|
-
|
|
826
|
-
if (!Nife.instanceOf(target, 'object'))
|
|
827
|
-
throw new Error(`Bad value detected in "targets" in deploy config: "${target}". All targets must either be a string (URI), or an object with a "uri" property.`);
|
|
828
|
-
|
|
829
|
-
return {
|
|
830
|
-
...target,
|
|
831
|
-
rawURI: ('' + target.uri),
|
|
832
|
-
uri: new URL('' + target.uri),
|
|
833
|
-
};
|
|
834
|
-
});
|
|
835
|
-
|
|
836
|
-
deployConfig.targets.forEach((target, index) => {
|
|
837
|
-
let { uri, rawURI } = target;
|
|
838
|
-
if (Nife.isEmpty(uri.hostname))
|
|
839
|
-
throw new Error(`Bad "uri" specified for "targets[${index}]": "${rawURI}". No "hostname" found.`);
|
|
840
|
-
|
|
841
|
-
if (Nife.isEmpty(uri.pathname))
|
|
842
|
-
throw new Error(`Bad "uri" specified for "targets[${index}]": "${rawURI}". No "pathname" found.`);
|
|
843
|
-
});
|
|
844
|
-
|
|
845
|
-
deployConfig.directDeploy = args.direct;
|
|
846
|
-
deployConfig.branchOverride = (Nife.isNotEmpty(args.branch)) ? args.branch : undefined;
|
|
847
|
-
deployConfig.dryRun = args.dryRun;
|
|
848
|
-
deployConfig.version = this.getRevisionNumber();
|
|
849
|
-
|
|
850
|
-
if (!deployConfig.tempLocation)
|
|
851
|
-
deployConfig.tempLocation = Path.join(OS.tmpdir(), 'mythix-cli', appName, deployConfig.version);
|
|
852
|
-
|
|
853
|
-
if (!deployConfig.rootPath)
|
|
854
|
-
deployConfig.rootPath = Path.dirname(configPath);
|
|
855
|
-
|
|
856
|
-
if (appOptions.configPath)
|
|
857
|
-
deployConfig.relativeConfigPath = appOptions.configPath.substring(deployConfig.rootPath.length).replace(/^[/\\.]+/, '').replace(/[/\\]+$/, '');
|
|
858
|
-
|
|
859
|
-
let { git } = deployConfig;
|
|
860
|
-
if (git.useGitIgnore) {
|
|
861
|
-
let gitIgnorePath = Path.resolve(deployConfig.rootPath, '.gitignore');
|
|
862
|
-
try {
|
|
863
|
-
let useGitIgnore = FileSystem.readFileSync(gitIgnorePath, 'utf8');
|
|
864
|
-
|
|
865
|
-
useGitIgnore = useGitIgnore.split(/(\r\n|\r|\n)+/g).filter((line) => {
|
|
866
|
-
if ((/^\s*$/).test(line))
|
|
867
|
-
return false;
|
|
868
|
-
|
|
869
|
-
if ((/^\s*#/).test(line))
|
|
870
|
-
return false;
|
|
871
|
-
|
|
872
|
-
return line.trim();
|
|
873
|
-
});
|
|
874
|
-
|
|
875
|
-
git.useGitIgnore = useGitIgnore;
|
|
876
|
-
} catch (error) {
|
|
877
|
-
if (error.code === 'ENOENT') {
|
|
878
|
-
console.error(`"git: { useGitIgnore: true }" was set in the deploy configuration, but unable to find "${gitIgnorePath}"`);
|
|
879
|
-
return 1;
|
|
880
|
-
}
|
|
881
|
-
|
|
882
|
-
console.error(error);
|
|
883
|
-
return 1;
|
|
884
|
-
}
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
return deployConfig;
|
|
888
|
-
} catch (error) {
|
|
889
|
-
if (error.code === 'MODULE_NOT_FOUND') {
|
|
890
|
-
if (!configPath)
|
|
891
|
-
configPath = Path.resolve(process.cwd(), '.mythix-deploy.js');
|
|
892
|
-
|
|
893
|
-
throw new Error(`Unable to find deployment config "${configPath}"`);
|
|
894
|
-
}
|
|
895
|
-
|
|
896
|
-
throw error;
|
|
897
|
-
}
|
|
898
|
-
}
|
|
899
|
-
|
|
900
|
-
log(str, ...args) {
|
|
901
|
-
console.log(` (log)! ${str}`, ...args);
|
|
902
|
-
}
|
|
903
|
-
|
|
904
|
-
async execute(args) {
|
|
905
|
-
let application = this.getApplication();
|
|
906
|
-
let appName = application.getApplicationName() || 'mythix';
|
|
907
|
-
let deployConfig;
|
|
908
|
-
|
|
909
|
-
try {
|
|
910
|
-
deployConfig = await this.loadDeployConfig(args);
|
|
911
|
-
} catch (error) {
|
|
912
|
-
console.error(error);
|
|
913
|
-
return 1;
|
|
914
|
-
}
|
|
915
|
-
|
|
916
|
-
let dryRun = args.dryRun;
|
|
917
|
-
let {
|
|
918
|
-
git,
|
|
919
|
-
tempLocation,
|
|
920
|
-
} = deployConfig;
|
|
921
|
-
|
|
922
|
-
let {
|
|
923
|
-
branch,
|
|
924
|
-
remoteName,
|
|
925
|
-
repository,
|
|
926
|
-
} = (git || {});
|
|
927
|
-
|
|
928
|
-
if (dryRun) {
|
|
929
|
-
console.log('|-------------- DRY RUN --------------|');
|
|
930
|
-
console.log('| 1. Temporary files will be created |');
|
|
931
|
-
console.log('| 2. Repository will be cloned |');
|
|
932
|
-
console.log('| 3. Files will be archived |');
|
|
933
|
-
console.log('| 4. Will verify SSH connections |');
|
|
934
|
-
console.log('| 5. Will *NOT* actually deploy app |');
|
|
935
|
-
console.log('| 6. Will *NOT* migrate database |');
|
|
936
|
-
console.log('|-------------------------------------|');
|
|
937
|
-
console.log('');
|
|
938
|
-
}
|
|
939
|
-
|
|
940
|
-
console.log(`-------- Deploy ${deployConfig.version} --------`);
|
|
941
|
-
console.log(` Application name: ${appName}`);
|
|
942
|
-
console.log(` Application config location: ${(deployConfig.relativeConfigPath) ? `./${deployConfig.relativeConfigPath}` : '<none>'}`);
|
|
943
|
-
console.log(` Temporary file location: ${deployConfig.tempLocation}`);
|
|
944
|
-
console.log(` Project file location: ${deployConfig.rootPath}`);
|
|
945
|
-
console.log('');
|
|
946
|
-
console.log(' Targets:');
|
|
947
|
-
|
|
948
|
-
let targets = deployConfig.targets;
|
|
949
|
-
for (let i = 0, il = targets.length; i < il; i++) {
|
|
950
|
-
let target = targets[i];
|
|
951
|
-
console.log(` -> ${target.uri}`);
|
|
952
|
-
}
|
|
953
|
-
|
|
954
|
-
if (!deployConfig.directDeploy && Nife.isEmpty(branch)) {
|
|
955
|
-
console.log('');
|
|
956
|
-
console.log(' !!!WARNING!!! "branch" is not set in your deploy configuration. The current file structure will be deployed "as-is". It is highly recommended that you set a "branch" in your deploy configuration.');
|
|
957
|
-
} else if (deployConfig.directDeploy) {
|
|
958
|
-
console.log('');
|
|
959
|
-
console.log(' !!!NOTICE!!! "--direct" argument was specified. Deploying project as-is, bypassing source-control.');
|
|
960
|
-
}
|
|
961
|
-
|
|
962
|
-
if (!deployConfig.directDeploy && !Nife.isEmpty(branch) && deployConfig.branchOverride) {
|
|
963
|
-
console.log('');
|
|
964
|
-
console.log(` !!!NOTICE!!! "--branch" argument was specified. Overriding config branch "${branch}" and using branch "${deployConfig.branchOverride}" instead.`);
|
|
965
|
-
|
|
966
|
-
branch = deployConfig.branchOverride;
|
|
967
|
-
if (git)
|
|
968
|
-
git.branch = deployConfig.branchOverride;
|
|
969
|
-
}
|
|
970
|
-
|
|
971
|
-
console.log('');
|
|
972
|
-
|
|
973
|
-
console.log(' Command log:');
|
|
974
|
-
|
|
975
|
-
try {
|
|
976
|
-
this.mkdirSync(false, tempLocation);
|
|
977
|
-
this.mkdirSync(false, Path.join(tempLocation, deployConfig.version));
|
|
978
|
-
|
|
979
|
-
if (!deployConfig.directDeploy && (Nife.isNotEmpty(branch) && Nife.isEmpty(repository))) {
|
|
980
|
-
repository = await this.getRepositoryURL(remoteName);
|
|
981
|
-
if (git)
|
|
982
|
-
git.repository = repository;
|
|
983
|
-
}
|
|
984
|
-
|
|
985
|
-
await this.cloneProject(deployConfig);
|
|
986
|
-
await this.postCloneProject(deployConfig);
|
|
987
|
-
await this.copyProjectFilesToDeployFolder(deployConfig);
|
|
988
|
-
await this.prepProjectPreDeploy(deployConfig);
|
|
989
|
-
await this.archiveProject(deployConfig);
|
|
990
|
-
await this.allRemotesPreDeploy(deployConfig);
|
|
991
|
-
await this.allRemotesDeploy(deployConfig);
|
|
992
|
-
await this.allRemotesPostDeploy(deployConfig);
|
|
993
|
-
await this.allRemotesFinalizeDeploy(deployConfig);
|
|
994
|
-
|
|
995
|
-
console.log(`Deployment of version "${deployConfig.version}" completed successfully!`);
|
|
996
|
-
} catch (error) {
|
|
997
|
-
console.error(error);
|
|
998
|
-
return 1;
|
|
999
|
-
} finally {
|
|
1000
|
-
if (!args.noCleanup) {
|
|
1001
|
-
try {
|
|
1002
|
-
await this.cleanup(dryRun, deployConfig);
|
|
1003
|
-
} catch (error) {
|
|
1004
|
-
console.error('Error while attempting to clean up after operation: ', error);
|
|
1005
|
-
}
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
}
|
|
1009
|
-
};
|
|
1010
|
-
});
|