@ossy/deployment-tools 0.0.25 → 0.0.27
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/dist/index.js +55835 -18407
- package/dist/licenses.txt +2 -1240
- package/package.json +8 -4
- package/src/aws-credentials-client.ts +32 -0
- package/src/{caddy-client.js → caddy-client.ts} +26 -19
- package/src/ci-rest-api.ts +25 -0
- package/src/cli-commands/deploy-handler.ts +32 -0
- package/src/cli-commands/index.ts +23 -0
- package/src/cli-commands/start-handler.ts +24 -0
- package/src/cli-commands/status-handler.ts +7 -0
- package/src/cli-commands/stop-handler.ts +7 -0
- package/src/deployment-platform-client.ts +140 -0
- package/src/deployment-queue-client.ts +73 -0
- package/src/docker-client.ts +80 -0
- package/src/index.ts +7 -0
- package/src/log/index.ts +4 -0
- package/src/types/index.ts +65 -0
- package/tsconfig.json +9 -0
- package/src/aws-credentials-client.js +0 -24
- package/src/config.js +0 -22
- package/src/container-manager-client.js +0 -115
- package/src/container-manager-server.js +0 -91
- package/src/deployment-queue-client.js +0 -51
- package/src/docker-client.js +0 -80
- package/src/index.js +0 -79
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import * as core from '@actions/core'
|
|
2
|
-
import { STSClient, AssumeRoleWithWebIdentityCommand } from '@aws-sdk/client-sts'
|
|
3
|
-
|
|
4
|
-
export class AwsCredentialsClient {
|
|
5
|
-
|
|
6
|
-
static getTemporaryCredentials(awsAccountId, region, roleToAssume = 'github-ci-role') {
|
|
7
|
-
const stsClient = new STSClient({ region })
|
|
8
|
-
|
|
9
|
-
return core.getIDToken('sts.amazonaws.com')
|
|
10
|
-
.then(webIdentityToken => stsClient.send(new AssumeRoleWithWebIdentityCommand({
|
|
11
|
-
RoleArn: `arn:aws:iam::${awsAccountId}:role/${roleToAssume}`,
|
|
12
|
-
RoleSessionName: 'GitHubActions',
|
|
13
|
-
DurationSeconds: 15 * 60,
|
|
14
|
-
WebIdentityToken: webIdentityToken
|
|
15
|
-
})))
|
|
16
|
-
.then(responseData => ({
|
|
17
|
-
accessKeyId: responseData.Credentials.AccessKeyId,
|
|
18
|
-
secretAccessKey: responseData.Credentials.SecretAccessKey,
|
|
19
|
-
sessionToken: responseData.Credentials.SessionToken
|
|
20
|
-
}))
|
|
21
|
-
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
}
|
package/src/config.js
DELETED
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
export const resolveConfiguration = (deploymentPlatform = {}) => {
|
|
2
|
-
const platformName = deploymentPlatform.name || process.env.DEPLOYMENT_TOOLS_PLATFORM_NAME
|
|
3
|
-
const domain = deploymentPlatform.domain || process.env.DEPLOYMENT_TOOLS_DOMAIN || 'localhost'
|
|
4
|
-
const environmentName = process.env.DEPLOYMENT_TOOLS_ENVIRONMENT_NAME || 'dev'
|
|
5
|
-
const ciSubDomain = deploymentPlatform.ciSubDomain || process.env.DEPLOYMENT_TOOLS_CI_SUB_DOMAIN || 'ci'
|
|
6
|
-
const port = process.env.DEPLOYMENT_TOOLS_PORT || 3000
|
|
7
|
-
const awsAccountId = deploymentPlatform.awsAccountId || process.env.AWS_ACCOUNT_ID
|
|
8
|
-
const awsRegion = deploymentPlatform.awsRegion || process.env.AWS_REGION
|
|
9
|
-
|
|
10
|
-
const deploymentQueueUrl = `https://sqs.${awsRegion}.amazonaws.com/${awsAccountId}/${platformName}-${environmentName}`
|
|
11
|
-
|
|
12
|
-
return {
|
|
13
|
-
platformName,
|
|
14
|
-
domain,
|
|
15
|
-
environmentName,
|
|
16
|
-
ciSubDomain,
|
|
17
|
-
port,
|
|
18
|
-
awsAccountId,
|
|
19
|
-
awsRegion,
|
|
20
|
-
deploymentQueueUrl
|
|
21
|
-
}
|
|
22
|
-
}
|
|
@@ -1,115 +0,0 @@
|
|
|
1
|
-
import { resolve } from 'path'
|
|
2
|
-
import { readFileSync } from 'fs'
|
|
3
|
-
import { AwsCredentialsClient } from './aws-credentials-client.js'
|
|
4
|
-
import { DeploymentQueueClient } from './deployment-queue-client.js'
|
|
5
|
-
|
|
6
|
-
export class ContainerManagerClient {
|
|
7
|
-
|
|
8
|
-
//eslint-disable-next-line max-params
|
|
9
|
-
static deploy(
|
|
10
|
-
username,
|
|
11
|
-
authentication,
|
|
12
|
-
targetEnvironment = 'local-dev',
|
|
13
|
-
pathToDeploymentPlatforms,
|
|
14
|
-
pathToOssyFile
|
|
15
|
-
) {
|
|
16
|
-
const containerManagerClient = new ContainerManagerClient()
|
|
17
|
-
return containerManagerClient.deploy(
|
|
18
|
-
username,
|
|
19
|
-
authentication,
|
|
20
|
-
targetEnvironment,
|
|
21
|
-
pathToDeploymentPlatforms,
|
|
22
|
-
pathToOssyFile
|
|
23
|
-
)
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
constructor() {}
|
|
27
|
-
|
|
28
|
-
getDeployments(pathToOssyFile) {
|
|
29
|
-
const ossyfile = JSON.parse(readFileSync(resolve(pathToOssyFile), 'utf8'))
|
|
30
|
-
return Promise.resolve(ossyfile.deployments || [])
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
getDeploymentPlatforms(pathToDeploymentPlatforms) {
|
|
34
|
-
|
|
35
|
-
const localDevPlatform = {
|
|
36
|
-
name: 'dev',
|
|
37
|
-
domain: 'localhost',
|
|
38
|
-
supportedDeploymentTypes: ['CONTAINER']
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
if (!pathToDeploymentPlatforms) return [localDevPlatform]
|
|
42
|
-
|
|
43
|
-
const deploymentPlatforms = JSON.parse(readFileSync(resolve(pathToDeploymentPlatforms), 'utf8'))
|
|
44
|
-
return Promise.resolve(deploymentPlatforms || [localDevPlatform])
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
getDeploymentTargetURL(targetEnvironment, deploymentPlatform, deployment) {
|
|
48
|
-
let domain = targetEnvironment !== 'prod'
|
|
49
|
-
? `${targetEnvironment}.`
|
|
50
|
-
: ''
|
|
51
|
-
|
|
52
|
-
domain = deployment.subdomain
|
|
53
|
-
? `${domain}${deployment.subdomain}.${deploymentPlatform.domain}`
|
|
54
|
-
: `${domain}${deploymentPlatform.domain}`
|
|
55
|
-
|
|
56
|
-
return domain
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
getEnvironmentVariables(targetEnvironment, deployment) {
|
|
60
|
-
const envs = deployment.env || {}
|
|
61
|
-
return {
|
|
62
|
-
...(envs.shared || {}),
|
|
63
|
-
...(envs[targetEnvironment] || {})
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
//eslint-disable-next-line max-params
|
|
68
|
-
deploy(
|
|
69
|
-
username,
|
|
70
|
-
authentication,
|
|
71
|
-
targetEnvironment,
|
|
72
|
-
pathToDeploymentPlatforms,
|
|
73
|
-
pathToOssyFile
|
|
74
|
-
) {
|
|
75
|
-
return Promise.all([
|
|
76
|
-
this.getDeploymentPlatforms(pathToDeploymentPlatforms),
|
|
77
|
-
this.getDeployments(pathToOssyFile)
|
|
78
|
-
])
|
|
79
|
-
.then(([platforms, deployments]) => {
|
|
80
|
-
deployments.map(deployment => {
|
|
81
|
-
const platform = platforms.find(platform => platform.name === deployment.targetDeploymentPlatform)
|
|
82
|
-
|
|
83
|
-
if (!platform) {
|
|
84
|
-
return Promise.reject(
|
|
85
|
-
`Could not find a deployment platform with the name ${deployment.targetDeploymentPlatform}`
|
|
86
|
-
)
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0
|
|
90
|
-
|
|
91
|
-
const body = {
|
|
92
|
-
image: deployment.image,
|
|
93
|
-
hostPort: deployment.hostPort,
|
|
94
|
-
containerPort: deployment.containerPort,
|
|
95
|
-
domain: this.getDeploymentTargetURL(targetEnvironment, platform, deployment),
|
|
96
|
-
env: this.getEnvironmentVariables(targetEnvironment, deployment),
|
|
97
|
-
registry: deployment.registry,
|
|
98
|
-
username: username,
|
|
99
|
-
authentication: authentication
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
return AwsCredentialsClient.getTemporaryCredentials(platform.awsAccountId, platform.awsRegion)
|
|
103
|
-
.then(credentials => {
|
|
104
|
-
const deploymentQueueClient = DeploymentQueueClient.new({ deploymentPlatform: platform, credentials })
|
|
105
|
-
return deploymentQueueClient.sendMessage(body)
|
|
106
|
-
.then(() => console.log('[ContainerManagerCommands] Deployment request has been sent'))
|
|
107
|
-
.catch(error => console.log('[ContainerManagerCommands] Could not send deployment request', error))
|
|
108
|
-
|
|
109
|
-
})
|
|
110
|
-
})
|
|
111
|
-
})
|
|
112
|
-
.catch(error => console.log(error))
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
}
|
|
@@ -1,91 +0,0 @@
|
|
|
1
|
-
import express from 'express'
|
|
2
|
-
import { CaddyClient } from './caddy-client.js'
|
|
3
|
-
import { DockerClient } from './docker-client.js'
|
|
4
|
-
import { DeploymentQueueClient } from './deployment-queue-client.js'
|
|
5
|
-
import { resolveConfiguration } from './config.js'
|
|
6
|
-
|
|
7
|
-
const config = resolveConfiguration()
|
|
8
|
-
|
|
9
|
-
export class ContainerManagerServer {
|
|
10
|
-
|
|
11
|
-
static start() {
|
|
12
|
-
return new ContainerManagerServer()
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
constructor() {
|
|
16
|
-
this.server = express()
|
|
17
|
-
this.dockerClient = DockerClient.new()
|
|
18
|
-
this.caddyClient = CaddyClient.new()
|
|
19
|
-
this.deploymentQueueClient = DeploymentQueueClient.new()
|
|
20
|
-
|
|
21
|
-
this.applyDefaultCaddyConfig()
|
|
22
|
-
this.setupServerEndpoints()
|
|
23
|
-
this.pollForDeploymentRequests()
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
setupServerEndpoints() {
|
|
27
|
-
this.server.use(express.json())
|
|
28
|
-
|
|
29
|
-
this.server.get('/', (req, res) => {
|
|
30
|
-
res.redirect('/status')
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
this.server.get('/status', (req, res) => {
|
|
34
|
-
res.json('Server is live')
|
|
35
|
-
})
|
|
36
|
-
|
|
37
|
-
this.server.listen(config.port, () => {
|
|
38
|
-
console.log(`Web API is live on port ${config.port}`)
|
|
39
|
-
})
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
applyDefaultCaddyConfig() {
|
|
43
|
-
this.caddyClient.applyDefaultServerConfig()
|
|
44
|
-
.then(() => this.caddyClient.deploy({
|
|
45
|
-
domain: `${config.ciSubDomain}.${config.environmentName}.${config.domain}`,
|
|
46
|
-
targetPort: config.port
|
|
47
|
-
}))
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
pollForDeploymentRequests() {
|
|
51
|
-
const FIVE_MINUTES = 3000
|
|
52
|
-
|
|
53
|
-
const handleIcomingMessages = () => this.deploymentQueueClient.receiveMessages()
|
|
54
|
-
.then(data => data.Messages.map(message => {
|
|
55
|
-
console.log('[ContainerManagerServer] handleIcomingMessages')
|
|
56
|
-
this.deploy(message)
|
|
57
|
-
this.deploymentQueueClient.deleteMessage(message.ReceiptHandle)
|
|
58
|
-
}))
|
|
59
|
-
.catch(error => console.log('[ContainerManagerServer] handleIcomingMessages error', error))
|
|
60
|
-
|
|
61
|
-
setInterval(handleIcomingMessages, FIVE_MINUTES)
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
deploy(deploymentRequestMessage) {
|
|
65
|
-
|
|
66
|
-
const {
|
|
67
|
-
registry,
|
|
68
|
-
username,
|
|
69
|
-
authentication,
|
|
70
|
-
containerPort,
|
|
71
|
-
hostPort,
|
|
72
|
-
image,
|
|
73
|
-
domain,
|
|
74
|
-
env
|
|
75
|
-
} = deploymentRequestMessage
|
|
76
|
-
|
|
77
|
-
this.dockerClient.deploy({
|
|
78
|
-
image,
|
|
79
|
-
registry,
|
|
80
|
-
username,
|
|
81
|
-
authentication,
|
|
82
|
-
containerPort,
|
|
83
|
-
hostPort,
|
|
84
|
-
env
|
|
85
|
-
})
|
|
86
|
-
|
|
87
|
-
domain && this.caddyClient.deploy({ domain, targetPort: hostPort })
|
|
88
|
-
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
}
|
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
SQSClient,
|
|
3
|
-
SendMessageCommand,
|
|
4
|
-
DeleteMessageCommand,
|
|
5
|
-
ReceiveMessageCommand
|
|
6
|
-
} from '@aws-sdk/client-sqs'
|
|
7
|
-
import { resolveConfiguration } from './config.js'
|
|
8
|
-
|
|
9
|
-
export class DeploymentQueueClient {
|
|
10
|
-
|
|
11
|
-
static new(config) {
|
|
12
|
-
return new DeploymentQueueClient(config)
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
constructor({ deploymentPlatform, credentials } = {}) {
|
|
16
|
-
const { region, deploymentQueueUrl: queueUrl } = resolveConfiguration(deploymentPlatform)
|
|
17
|
-
this.queueUrl = queueUrl
|
|
18
|
-
this.sqs = new SQSClient({ region, credentials })
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
sendMessage(messageBody) {
|
|
22
|
-
const command = new SendMessageCommand({
|
|
23
|
-
QueueUrl: this.queueUrl,
|
|
24
|
-
MessageBody: JSON.stringify(messageBody)
|
|
25
|
-
})
|
|
26
|
-
|
|
27
|
-
return this.sqs.send(command)
|
|
28
|
-
.then(() => console.log('Success'))
|
|
29
|
-
.catch(err => console.log(err))
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
receiveMessages() {
|
|
33
|
-
const command = new ReceiveMessageCommand({ QueueUrl: this.queueUrl })
|
|
34
|
-
|
|
35
|
-
return this.sqs.send(command)
|
|
36
|
-
.catch(error => console.log('Error', error))
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
deleteMessage(receiptHandle) {
|
|
40
|
-
const command = new DeleteMessageCommand({
|
|
41
|
-
QueueUrl: this.queueUrl,
|
|
42
|
-
ReceiptHandle: receiptHandle
|
|
43
|
-
})
|
|
44
|
-
|
|
45
|
-
return this.sqs.send(command)
|
|
46
|
-
.then(() => console.log('success'))
|
|
47
|
-
.catch(error => console.log('Error', error))
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
}
|
package/src/docker-client.js
DELETED
|
@@ -1,80 +0,0 @@
|
|
|
1
|
-
import { exec } from 'child_process'
|
|
2
|
-
import fs from 'fs'
|
|
3
|
-
import { nanoid } from 'nanoid'
|
|
4
|
-
import path from 'path'
|
|
5
|
-
import { fileURLToPath } from 'url'
|
|
6
|
-
|
|
7
|
-
const __filename = fileURLToPath(import.meta.url)
|
|
8
|
-
const __dirname = path.dirname(__filename)
|
|
9
|
-
|
|
10
|
-
export class DockerClient {
|
|
11
|
-
|
|
12
|
-
static new() {
|
|
13
|
-
return new DockerClient()
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
constructor() {
|
|
17
|
-
this.networkName = 'deployment-tools'
|
|
18
|
-
exec(`sudo docker network create ${this.networkName}`)
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
stopContainer({ image }) {
|
|
22
|
-
const name = image.replaceAll('/', '_')
|
|
23
|
-
return `sudo docker stop ${name} ||`
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
startContainer({ image, containerPort, hostPort, registry, env }) {
|
|
27
|
-
const name = image.replaceAll('/', '_')
|
|
28
|
-
const imageUrl = !!registry ? `${registry}/${image}` : image
|
|
29
|
-
const envsAsString = Object.entries(env || {}).reduce((envs, [name, value]) => `${envs} --env ${name}=${value}`, '')
|
|
30
|
-
return `sudo docker run -d -p ${hostPort}:${containerPort} --name=${name} --network=${this.networkName} --network-alias=${name} --rm ${envsAsString} ${imageUrl}`
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
resolveCredentials({ registry, username, authentication }) {
|
|
34
|
-
return (username || authentication)
|
|
35
|
-
? `sudo docker login ${registry} -u ${username} -p ${authentication}`
|
|
36
|
-
: 'echo No credentials provided, assuming image is publicly hosted'
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
deploy({
|
|
40
|
-
image,
|
|
41
|
-
registry,
|
|
42
|
-
username,
|
|
43
|
-
authentication,
|
|
44
|
-
containerPort,
|
|
45
|
-
hostPort,
|
|
46
|
-
env
|
|
47
|
-
}) {
|
|
48
|
-
return new Promise(resolve => {
|
|
49
|
-
const dockerCommandScript = `'#!/bin/bash'
|
|
50
|
-
${this.stopContainer({ image })}
|
|
51
|
-
${this.resolveCredentials({ registry, username, authentication })}
|
|
52
|
-
${this.startContainer({ image, containerPort, hostPort, registry, env })}`
|
|
53
|
-
|
|
54
|
-
const deploymentId = nanoid()
|
|
55
|
-
|
|
56
|
-
const FilePaths = {
|
|
57
|
-
DeploymentScript: `${__dirname}/${deploymentId}.sh`
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
fs.writeFileSync(FilePaths.DeploymentScript, dockerCommandScript)
|
|
61
|
-
const command = exec(`bash ${FilePaths.DeploymentScript}`)
|
|
62
|
-
|
|
63
|
-
command.stdout.on('data', (data) => {
|
|
64
|
-
console.log(`[DockerClient]: ${data}`)
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
command.stderr.on('data', (data) => {
|
|
68
|
-
console.error(`[DockerClient]: ${data}`)
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
command.on('close', (code) => {
|
|
72
|
-
console.log(`[DockerClient] command exited with code ${code}`)
|
|
73
|
-
fs.unlinkSync(FilePaths.DeploymentScript)
|
|
74
|
-
resolve()
|
|
75
|
-
})
|
|
76
|
-
})
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
}
|
package/src/index.js
DELETED
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { platform } from 'os'
|
|
3
|
-
import { exec } from 'child_process'
|
|
4
|
-
import arg from 'arg'
|
|
5
|
-
import { ContainerManagerServer } from './container-manager-server.js'
|
|
6
|
-
import { ContainerManagerClient } from './container-manager-client.js'
|
|
7
|
-
|
|
8
|
-
const [_, __, command, ...restArgs] = process.argv
|
|
9
|
-
|
|
10
|
-
const startHandler = () => {
|
|
11
|
-
console.log('Running start command')
|
|
12
|
-
|
|
13
|
-
const Platforms = {
|
|
14
|
-
windows: 'win32',
|
|
15
|
-
mac: 'darwin'
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
if ([Platforms.windows].includes(platform())) {
|
|
19
|
-
return console.error('Deployment tools do not support this os')
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
ContainerManagerServer.start()
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const stopHandler = () => {
|
|
26
|
-
console.log('Running stop command')
|
|
27
|
-
exec('systemctl stop deployment-tools.service')
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
const statusHandler = () => {
|
|
31
|
-
console.log('Running status command')
|
|
32
|
-
exec('systemctl status deployment-tools.service')
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const deployHandler = () => {
|
|
36
|
-
console.log('Running deploy command')
|
|
37
|
-
|
|
38
|
-
const args = arg({
|
|
39
|
-
'--username': String,
|
|
40
|
-
'-u': '--username',
|
|
41
|
-
|
|
42
|
-
'--authentication': String,
|
|
43
|
-
'--a': '--authentication',
|
|
44
|
-
|
|
45
|
-
'--target-env': String,
|
|
46
|
-
'-t': '--target-env',
|
|
47
|
-
|
|
48
|
-
'--ossyfile': String,
|
|
49
|
-
'-o': '--ossyfile',
|
|
50
|
-
|
|
51
|
-
'--platforms': String,
|
|
52
|
-
'-p': '--platforms'
|
|
53
|
-
}, { argv: restArgs })
|
|
54
|
-
|
|
55
|
-
console.log('args', args)
|
|
56
|
-
|
|
57
|
-
ContainerManagerClient.deploy(
|
|
58
|
-
args['--username'],
|
|
59
|
-
args['--authentication'],
|
|
60
|
-
args['--target-env'],
|
|
61
|
-
args['--platforms'],
|
|
62
|
-
args['--ossyfile']
|
|
63
|
-
)
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const Commands = {
|
|
67
|
-
start: startHandler,
|
|
68
|
-
stop: stopHandler,
|
|
69
|
-
status: statusHandler,
|
|
70
|
-
deploy: deployHandler
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
const commandHandler = Commands[command]
|
|
74
|
-
|
|
75
|
-
if (!commandHandler) {
|
|
76
|
-
console.log('Command not implemented, did you spell it correctly?')
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
commandHandler()
|