@ossy/deployment-tools 0.0.24 → 0.0.26

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/package.json CHANGED
@@ -1,12 +1,13 @@
1
1
  {
2
2
  "name": "@ossy/deployment-tools",
3
- "version": "0.0.24",
3
+ "version": "0.0.26",
4
4
  "description": "Collection of scripts and tools to aid deployment of containers and static files",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
7
7
  "scripts": {
8
8
  "test": "echo \"Error: no test specified\" && exit 1",
9
- "build": "npx --yes @vercel/ncc build src/index.js --out dist --license licenses.txt"
9
+ "build": "npx --yes @vercel/ncc build src/index.ts --out dist --license licenses.txt",
10
+ "build:esbuild": "npx --yes esbuild src/index.ts --platform=node --bundle --outfile=dist/index.js"
10
11
  },
11
12
  "author": "Ossy",
12
13
  "license": "ISC",
@@ -18,6 +19,10 @@
18
19
  "arg": "^5.0.2",
19
20
  "express": "^4.18.1",
20
21
  "nanoid": "^4.0.0",
21
- "node-fetch": "^3.2.6"
22
+ "node-fetch": "^3.2.6",
23
+ "typescript": "^4.8.4"
24
+ },
25
+ "devDependencies": {
26
+ "@types/node": "^18.11.0"
22
27
  }
23
28
  }
@@ -0,0 +1,32 @@
1
+ import * as core from '@actions/core'
2
+ import { STSClient, AssumeRoleWithWebIdentityCommand } from '@aws-sdk/client-sts'
3
+ import { DeploymentPlatform } from 'types'
4
+
5
+ export class AwsCredentialsClient {
6
+
7
+ static resolveAwsCredentials(deploymentPlatform: DeploymentPlatform) {
8
+ // If awsRoleToAssume is present, then we assume we run in a github workflow
9
+ // If awsRoleToAssume is not present, then we assume they are resolved localy by aws-sdk
10
+ return deploymentPlatform.awsRoleToAssume
11
+ ? AwsCredentialsClient.getTemporaryCredentials(deploymentPlatform)
12
+ : Promise.resolve(undefined)
13
+ }
14
+
15
+ static getTemporaryCredentials({ awsAccountId, awsRegion, awsRoleToAssume }: DeploymentPlatform) {
16
+ const stsClient = new STSClient({ region: awsRegion })
17
+
18
+ return core.getIDToken('sts.amazonaws.com')
19
+ .then(webIdentityToken => stsClient.send(new AssumeRoleWithWebIdentityCommand({
20
+ RoleArn: `arn:aws:iam::${awsAccountId}:role/${awsRoleToAssume}`,
21
+ RoleSessionName: 'GitHubActions',
22
+ DurationSeconds: 15 * 60,
23
+ WebIdentityToken: webIdentityToken
24
+ })))
25
+ .then(responseData => ({
26
+ accessKeyId: responseData.Credentials.AccessKeyId,
27
+ secretAccessKey: responseData.Credentials.SecretAccessKey,
28
+ sessionToken: responseData.Credentials.SessionToken
29
+ }))
30
+ }
31
+
32
+ }
@@ -1,5 +1,6 @@
1
1
  import fetch from 'node-fetch'
2
- import * as config from './config.js'
2
+ import { log } from './log'
3
+ import { DeploymentPlatform, ContainerDeploymentRequest } from 'types'
3
4
 
4
5
  export const Matchers = {
5
6
  host: host => ({ host: [host] }),
@@ -11,7 +12,7 @@ const Handlers = {
11
12
  handler: 'reverse_proxy',
12
13
  upstreams: [{ dial: `localhost:${upstreamPort}` }]
13
14
  }),
14
- subroute: routes => ( {
15
+ subroute: routes => ({
15
16
  handler: 'subroute',
16
17
  routes
17
18
  })
@@ -19,28 +20,30 @@ const Handlers = {
19
20
 
20
21
  export class CaddyClient {
21
22
 
22
- static new(serverName) {
23
- return new CaddyClient(serverName)
24
- }
23
+ static deploy(deploymentPlatform: DeploymentPlatform, deploymentRequest: ContainerDeploymentRequest) {
25
24
 
26
- constructor(serverName = 'ci-client') {
27
- this.serverName = serverName
28
- }
25
+ const url = `${deploymentRequest.subdomain}.${deploymentPlatform.activeEnvironment}.${deploymentPlatform.domain}`
26
+
27
+ log({
28
+ type: 'info',
29
+ message: `[CaddyClient] Updating caddy config to route ${url} to localhost:${deploymentRequest.hostPort}`
30
+ })
29
31
 
30
- deploy({ domain, targetPort }) {
31
- return fetch(`http://localhost:2019/config/apps/http/servers/${this.serverName}/routes/0/handle`, {
32
+ return fetch(`http://localhost:2019/config/apps/http/servers/${deploymentPlatform.ciServerName}/routes/0/handle`, {
32
33
  method: 'POST',
33
34
  headers: { 'Content-Type': 'application/json' },
34
- body: JSON.stringify( Handlers.subroute([
35
+ body: JSON.stringify(Handlers.subroute([
35
36
  {
36
- match: [Matchers.host(domain)],
37
- handle: [Handlers.subroute([{ handle: [Handlers.reverseProxy(targetPort)]}])]
37
+ match: [Matchers.host(url)],
38
+ handle: [Handlers.subroute([{ handle: [Handlers.reverseProxy(deploymentRequest.hostPort)]}])]
38
39
  }
39
40
  ]))
40
41
  })
42
+ .catch(() => log({ type: 'error', message: `[CaddyClient] Could not update caddy config to include ${url}` }))
41
43
  }
42
44
 
43
- applyDefaultServerConfig() {
45
+ static applyDefaultServerConfig(deploymentPlatform: DeploymentPlatform) {
46
+ log({ type: 'info', message: '[CaddyClient] Applying default caddy config' })
44
47
  return fetch('http://localhost:2019/load', {
45
48
  method: 'POST',
46
49
  headers: { 'Content-Type': 'application/json' },
@@ -48,12 +51,17 @@ export class CaddyClient {
48
51
  apps: {
49
52
  http: {
50
53
  servers: {
51
- [this.serverName]: {
54
+ [deploymentPlatform.ciServerName]: {
52
55
  listen: [':80', ':443'],
53
56
  routes: [
54
57
  {
55
- match: [Matchers.host(`*.${config.environmentName}.${config.domain}`)],
56
- handle: [],
58
+ match: [Matchers.host(`*.${deploymentPlatform.activeEnvironment}.${deploymentPlatform.domain}`)],
59
+ handle: [Handlers.subroute([
60
+ {
61
+ match: [Matchers.host(`${deploymentPlatform.ciSubDomain}.${deploymentPlatform.activeEnvironment}.${deploymentPlatform.domain}`)],
62
+ handle: [Handlers.subroute([{ handle: [Handlers.reverseProxy(deploymentPlatform.ciInternalServerPort)]}])]
63
+ }
64
+ ])],
57
65
  terminal: true
58
66
  }
59
67
  ]
@@ -97,6 +105,7 @@ export class CaddyClient {
97
105
  }
98
106
  })
99
107
  })
108
+ .catch(() => log({ type: 'error', message: '[CaddyClient] Could not apply default caddy config' }))
100
109
  }
101
110
 
102
111
  }
@@ -0,0 +1,25 @@
1
+ import express from 'express'
2
+ import { DeploymentPlatform } from 'types'
3
+ import { log } from './log'
4
+
5
+ export class CiRestApi {
6
+
7
+ static start(deploymentPlatform: DeploymentPlatform) {
8
+ const server = express()
9
+
10
+ server.use(express.json())
11
+
12
+ server.get('/', (req, res) => {
13
+ res.redirect('/status')
14
+ })
15
+
16
+ server.get('/status', (req, res) => {
17
+ res.send('Server is live')
18
+ })
19
+
20
+ server.listen(deploymentPlatform.ciInternalServerPort, () => {
21
+ log({ type: 'info', message: `[ContainerManagerServer] API is live on port ${deploymentPlatform.ciInternalServerPort}`})
22
+ })
23
+ }
24
+
25
+ }
@@ -0,0 +1,32 @@
1
+ import arg from 'arg'
2
+ import { log } from '../log'
3
+ import { DeploymentPlatformClient } from 'deployment-platform-client'
4
+
5
+ export const deployHandler = cliArgs => {
6
+ log({ type: 'info', message: 'Running deploy command' })
7
+
8
+ const parsedArgs = arg({
9
+ '--username': String,
10
+ '-u': '--username',
11
+
12
+ '--authentication': String,
13
+ '--a': '--authentication',
14
+
15
+ '--target-env': String,
16
+ '-t': '--target-env',
17
+
18
+ '--ossyfile': String,
19
+ '-o': '--ossyfile',
20
+
21
+ '--platforms': String,
22
+ '-p': '--platforms'
23
+ }, { argv: cliArgs })
24
+
25
+ DeploymentPlatformClient.deploy(
26
+ parsedArgs['--username'],
27
+ parsedArgs['--authentication'],
28
+ parsedArgs['--target-env'],
29
+ parsedArgs['--platforms'],
30
+ parsedArgs['--ossyfile']
31
+ )
32
+ }
@@ -0,0 +1,23 @@
1
+ import { log } from '../log'
2
+ import { startHandler } from './start-handler'
3
+ import { stopHandler } from './stop-handler'
4
+ import { statusHandler } from './status-handler'
5
+ import { deployHandler } from './deploy-handler'
6
+
7
+ export const runCliCommand = ({ name, args }) => {
8
+
9
+ if (!name) return log({ type: 'error', message: 'No command provided' })
10
+
11
+ const commandHandler = {
12
+ start: startHandler,
13
+ stop: stopHandler,
14
+ status: statusHandler,
15
+ deploy: deployHandler
16
+ }[name]
17
+
18
+ if (!commandHandler) {
19
+ return log({ type: 'error', message: 'Command not implemented, did you spell it correctly?' })
20
+ }
21
+
22
+ commandHandler(args)
23
+ }
@@ -0,0 +1,24 @@
1
+ import arg from 'arg'
2
+ import { platform } from 'os'
3
+ import { log } from '../log'
4
+ import { DeploymentPlatformClient } from 'deployment-platform-client'
5
+
6
+ export const startHandler = cliArgs => {
7
+ log({ type: 'info', message: 'Running start command' })
8
+
9
+ const Platforms = {
10
+ windows: 'win32',
11
+ mac: 'darwin'
12
+ }
13
+
14
+ if ([Platforms.windows].includes(platform())) {
15
+ return log({ type: 'error', message: 'Deployment tools do not support this os' })
16
+ }
17
+
18
+ const parsedArgs = arg({
19
+ '--platforms': String,
20
+ '-p': '--platforms'
21
+ }, { argv: cliArgs })
22
+
23
+ DeploymentPlatformClient.start(parsedArgs['--platforms'])
24
+ }
@@ -0,0 +1,7 @@
1
+ import { exec } from 'child_process'
2
+ import { log } from '../log'
3
+
4
+ export const statusHandler = () => {
5
+ log({ type: 'info', message: 'Running status command' })
6
+ exec('systemctl status deployment-tools.service')
7
+ }
@@ -0,0 +1,7 @@
1
+ import { exec } from 'child_process'
2
+ import { log } from '../log'
3
+
4
+ export const stopHandler = () => {
5
+ log({ type: 'info', message: 'Running stop command' })
6
+ exec('systemctl stop deployment-tools.service')
7
+ }
@@ -0,0 +1,134 @@
1
+ import { resolve } from 'path'
2
+ import { readFileSync } from 'fs'
3
+ import {
4
+ DeploymentPlatform,
5
+ SupportedEnvironments,
6
+ ContainerDeploymentRequest,
7
+ DeploymentTemplate,
8
+ ContainerDeploymentTemplate,
9
+ SupportedDeploymentTypes
10
+ } from 'types'
11
+ import { CaddyClient } from './caddy-client'
12
+ import { DockerClient } from './docker-client'
13
+ import { DeploymentQueueClient } from './deployment-queue-client'
14
+ import { CiRestApi } from './ci-rest-api'
15
+ import { log } from 'log'
16
+
17
+ export class DeploymentPlatformClient {
18
+
19
+ static start(pathToDeploymentPlatforms: string) {
20
+ DeploymentPlatformClient.getDeploymentPlatforms(pathToDeploymentPlatforms).then(([firstPlatformFound]) => {
21
+
22
+ if (!firstPlatformFound) {
23
+ log({ type: 'error', message: '[DeploymentPlatformClient] Could not find a deployment platform' })
24
+ return Promise.reject()
25
+ }
26
+
27
+ CiRestApi.start(firstPlatformFound)
28
+ CaddyClient.applyDefaultServerConfig(firstPlatformFound)
29
+
30
+ DeploymentQueueClient.pollForDeploymentRequests(
31
+ firstPlatformFound,
32
+ (deploymentRequest: ContainerDeploymentRequest) => {
33
+ DockerClient.deploy(firstPlatformFound, deploymentRequest)
34
+ CaddyClient.deploy(firstPlatformFound, deploymentRequest)
35
+ return Promise.resolve()
36
+ }
37
+ )
38
+
39
+ })
40
+ .catch(() => log({ type: 'error', message: '[DeploymentPlatformClient] Could not start the deployment platform' }))
41
+ }
42
+
43
+ //eslint-disable-next-line max-params
44
+ static deploy(
45
+ username,
46
+ authentication,
47
+ targetEnvironment,
48
+ pathToDeploymentPlatforms,
49
+ pathToOssyFile
50
+ ) {
51
+ return Promise.all([
52
+ DeploymentPlatformClient.getDeploymentPlatforms(pathToDeploymentPlatforms),
53
+ DeploymentPlatformClient.getDeploymentTemplates(pathToOssyFile)
54
+ ])
55
+ .then(([platforms, deploymentTemplates]) => {
56
+ deploymentTemplates.map(deploymentTemplate => {
57
+ const deploymentPlatform = platforms.find(platform => platform.name === deploymentTemplate.targetDeploymentPlatform)
58
+
59
+ if (!deploymentPlatform) {
60
+ log({ type: 'error', message: `[DeploymentPlatformClient] Could not find a deployment platform with the name ${deploymentTemplate.targetDeploymentPlatform}` })
61
+ return Promise.reject()
62
+ }
63
+
64
+ process.env.NODE_TLS_REJECT_UNAUTHORIZED = '0'
65
+
66
+ if (deploymentTemplate.type !== SupportedDeploymentTypes.Container) {
67
+ log({ type: 'error', message: `[DeploymentPlatformClient] Unsupported deployment type of ${deploymentTemplate.type}` })
68
+ return Promise.reject()
69
+ }
70
+
71
+ const deploymentRequest: ContainerDeploymentRequest = {
72
+ ...deploymentTemplate as ContainerDeploymentTemplate,
73
+ env: DeploymentPlatformClient.getEnvironmentVariables(targetEnvironment, deploymentTemplate),
74
+ username: username,
75
+ authentication: authentication
76
+ }
77
+
78
+ return DeploymentQueueClient.sendDeploymentRequest(deploymentPlatform, deploymentRequest)
79
+
80
+ })
81
+ })
82
+ .catch(error => console.log(error))
83
+ }
84
+
85
+ static getDeploymentTemplates(pathToOssyFile: string): Promise<DeploymentTemplate[]> {
86
+ const ossyfile = JSON.parse(readFileSync(resolve(pathToOssyFile), 'utf8'))
87
+ return Promise.resolve(ossyfile.deployments || [])
88
+ }
89
+
90
+ static getDeploymentPlatforms(pathToDeploymentPlatforms: string) {
91
+ let deploymentPlatforms = JSON.parse(readFileSync(resolve(pathToDeploymentPlatforms), 'utf8'))
92
+
93
+ if (!Array.isArray(deploymentPlatforms)) {
94
+ deploymentPlatforms = [deploymentPlatforms]
95
+ }
96
+
97
+ return Promise.resolve((deploymentPlatforms || []).map(DeploymentPlatformClient.resolveDeploymentPlatformValues))
98
+ }
99
+
100
+ static getEnvironmentVariables(targetEnvironment: SupportedEnvironments, deploymentRequest: DeploymentTemplate) {
101
+ const envs = deploymentRequest.env || {}
102
+ return {
103
+ ...(envs.shared || {}),
104
+ ...(envs[targetEnvironment] || {})
105
+ }
106
+ }
107
+
108
+ static resolveDeploymentPlatformValues(deploymentPlatform: DeploymentPlatform): DeploymentPlatform {
109
+ return DeploymentPlatformClient.setCalculatedDeploymentPlatformValues(DeploymentPlatformClient.setDefaultDeploymentPlatformValues(deploymentPlatform))
110
+ }
111
+
112
+ static setDefaultDeploymentPlatformValues(deploymentPlatform: DeploymentPlatform): DeploymentPlatform {
113
+ return {
114
+ platformName: SupportedEnvironments.LOCAL,
115
+ domain: 'localhost',
116
+ activeEnvironment: SupportedEnvironments.LOCAL,
117
+ supportedDeploymentTypes: ['CONTAINER'],
118
+ ciSubDomain: 'ci',
119
+ ciInternalServerPort: 3000,
120
+ ciServerName: 'ci-client',
121
+ ciDockerNetworkName: 'deployment-tools',
122
+ // awsRoleToAssume: 'github-ci-role',
123
+ ...deploymentPlatform
124
+ }
125
+ }
126
+
127
+ static setCalculatedDeploymentPlatformValues(deploymentPlatform: DeploymentPlatform): DeploymentPlatform {
128
+ return {
129
+ awsDeploymentSqsArn: `https://sqs.${deploymentPlatform.awsRegion}.amazonaws.com/${deploymentPlatform.awsAccountId}/${deploymentPlatform.platformName}-${deploymentPlatform.activeEnvironment}`,
130
+ ...deploymentPlatform
131
+ }
132
+ }
133
+
134
+ }
@@ -0,0 +1,73 @@
1
+ import {
2
+ SQSClient,
3
+ SendMessageCommand,
4
+ DeleteMessageCommand,
5
+ ReceiveMessageCommand
6
+ } from '@aws-sdk/client-sqs'
7
+ import { DeploymentPlatform, ContainerDeploymentRequest } from 'types'
8
+ import { AwsCredentialsClient } from 'aws-credentials-client'
9
+ import { log } from 'log'
10
+
11
+ export class DeploymentQueueClient {
12
+
13
+ static sendDeploymentRequest(deploymentPlatform: DeploymentPlatform, deploymentRequest: ContainerDeploymentRequest) {
14
+ log({ type: 'info', message: '[DeploymentQueueClient] Sending deployment request...' })
15
+ return DeploymentQueueClient.createAwsSqsClient(deploymentPlatform)
16
+ .then(sqsClient => {
17
+
18
+ const command = new SendMessageCommand({
19
+ QueueUrl: deploymentPlatform.awsDeploymentSqsArn,
20
+ MessageBody: JSON.stringify(deploymentRequest)
21
+ })
22
+
23
+ return sqsClient.send(command)
24
+ .then(() => log({ type: 'info', message: '[DeploymentQueueClient] Deployment request sent' }))
25
+ .catch(() => log({ type: 'error', message: '[DeploymentQueueClient] Could not send deployment request' }))
26
+
27
+ })
28
+
29
+ }
30
+
31
+ static pollForDeploymentRequests(deploymentPlatform: DeploymentPlatform, handleDeploymentRequest: (deploymentRequest: ContainerDeploymentRequest) => Promise<void>) {
32
+ log({ type: 'info', message: '[DeploymentQueueClient] Starting polling for deployment requests' })
33
+ DeploymentQueueClient.createAwsSqsClient(deploymentPlatform).then(sqsClient => {
34
+ const FIVE_MINUTES = 3000
35
+
36
+ setInterval(() => {
37
+
38
+ const receiveMessageCommand = new ReceiveMessageCommand({ QueueUrl: deploymentPlatform.awsDeploymentSqsArn })
39
+
40
+ sqsClient.send(receiveMessageCommand)
41
+ .then(data => data.Messages.map(message => {
42
+
43
+ log({ type: 'info', message: '[DeploymentQueueClient] Received deployment request' })
44
+
45
+ handleDeploymentRequest(JSON.parse(message.Body))
46
+ .then(() => {
47
+
48
+ const deleteMessageCommand = new DeleteMessageCommand({
49
+ QueueUrl: deploymentPlatform.awsDeploymentSqsArn,
50
+ ReceiptHandle: message.ReceiptHandle
51
+ })
52
+
53
+ sqsClient.send(deleteMessageCommand)
54
+ .then(() => log({ type: 'info', message: '[DeploymentQueueClient] Removing deployment request from queue' }))
55
+ .catch(() => log({ type: 'error', message: '[DeploymentQueueClient] Removing deployment request from queue' }))
56
+ })
57
+
58
+ }))
59
+ .catch(() => log({ type: 'error', message: '[ContainerManagerServer] Could not handle incoming deployment request' }))
60
+ }, FIVE_MINUTES)
61
+
62
+ })
63
+ }
64
+
65
+ static createAwsSqsClient(deploymentPlatform: DeploymentPlatform) {
66
+ return AwsCredentialsClient.resolveAwsCredentials(deploymentPlatform)
67
+ .then(awsCredentials => new SQSClient({
68
+ region: deploymentPlatform.awsRegion,
69
+ credentials: awsCredentials
70
+ }))
71
+ }
72
+
73
+ }
@@ -0,0 +1,80 @@
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
+ import { log } from './log'
7
+ import { DeploymentPlatform, ContainerDeploymentRequest } from 'types'
8
+
9
+ const __filename = fileURLToPath(import.meta.url)
10
+ const __dirname = path.dirname(__filename)
11
+
12
+ export class DockerClient {
13
+
14
+ static createDockerNetworkForContainerManagerServer(deploymentPlatform: DeploymentPlatform) {
15
+ log({ type: 'info', message: '[DockerClient] Creating docker network for comunication between containers' })
16
+ exec(`sudo docker network create ${deploymentPlatform.ciDockerNetworkName}`)
17
+ }
18
+
19
+ static stopContainer(deploymentRequest: ContainerDeploymentRequest) {
20
+ const name = deploymentRequest.image.replaceAll('/', '_')
21
+ log({ type: 'info', message: `Running docker stop for image with the name of ${name}` })
22
+ return `sudo docker stop ${name} ||`
23
+ }
24
+
25
+ static startContainer(deploymentPlatform, { image, containerPort, hostPort, registry, env }: ContainerDeploymentRequest) {
26
+ const name = image.replaceAll('/', '_')
27
+ const imageUrl = !!registry ? `${registry}/${image}` : image
28
+ const envsAsString = Object.entries(env || {}).reduce((envs, [name, value]) => `${envs} --env ${name}=${value}`, '')
29
+ log({ type: 'info', message: `Running docker start for image with the name of ${name} with port mapping ${hostPort}:${containerPort} and source ${imageUrl}` })
30
+ return `sudo docker run -d -p ${hostPort}:${containerPort} --name=${name} --network=${deploymentPlatform.ciDockerNetworkName} --network-alias=${name} --rm ${envsAsString} ${imageUrl}`
31
+ }
32
+
33
+ static resolveCredentials({ registry, username, authentication }: ContainerDeploymentRequest) {
34
+ const shouldAuthenticate = username || authentication
35
+
36
+ if (!shouldAuthenticate) {
37
+ log({ type: 'info', message: 'No docker credentials provided, assuming image is publicly hosted' })
38
+ return ''
39
+ }
40
+
41
+ log({ type: 'info', message: `Resolving docker credentials for ${registry}` })
42
+ return `sudo docker login ${registry} -u ${username} -p ${authentication}`
43
+ }
44
+
45
+ static deploy(deploymentPlatform: DeploymentPlatform, deploymentRequest: ContainerDeploymentRequest): Promise<void> {
46
+ return new Promise(resolve => {
47
+ log({ type: 'info', message: 'Starting docker deployment sequence' })
48
+
49
+ const dockerCommandScript = `'#!/bin/bash'
50
+ ${DockerClient.stopContainer(deploymentRequest)}
51
+ ${DockerClient.resolveCredentials(deploymentRequest)}
52
+ ${DockerClient.startContainer(deploymentPlatform, deploymentRequest)}`
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
+ log({ type: 'info', message: `[DockerClient]: ${data}` })
65
+ })
66
+
67
+ command.stderr.on('data', (data) => {
68
+ log({ type: 'error', message: `[DockerClient]: ${data}` })
69
+ })
70
+
71
+ command.on('close', code => {
72
+ log({ type: 'info', message: `[DockerClient] command exited with code ${code}` })
73
+ fs.unlinkSync(FilePaths.DeploymentScript)
74
+ resolve()
75
+ })
76
+ })
77
+
78
+ }
79
+
80
+ }
package/src/index.ts ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ import { runCliCommand } from './cli-commands/index'
3
+
4
+ //eslint-disable-next-line no-unused-vars
5
+ const [_, __, command, ...restArgs] = process.argv
6
+
7
+ runCliCommand({ name: command, args: restArgs })
@@ -0,0 +1,4 @@
1
+ export const log = ({ type, message } = { type: 'info', message: '' }) => {
2
+ const messagePrefix = `[${type.toUpperCase()}]${message.startsWith('[') ? '' : ': '}`
3
+ console.log(`${messagePrefix}${message}`)
4
+ }
@@ -0,0 +1,65 @@
1
+ export interface DeploymentPlatformTemplate {
2
+ platformName: string;
3
+ domain: string;
4
+ supportedDeploymentTypes: SupportedDeploymentTypes[];
5
+ supportedEnvironments: SupportedEnvironments[];
6
+ awsRegion: string;
7
+ awsAccountId: string;
8
+ awsKeyPairName?: string;
9
+ awsRoleToAssume?: string;
10
+ awsDeploymentSqsArn?: string;
11
+ ciSubDomain?: string;
12
+ ciInternalServerPort?: string | number;
13
+ ciServerName?: string;
14
+ ciDockerNetworkName?: string;
15
+ }
16
+
17
+ export interface DeploymentPlatform extends Required<Omit<DeploymentPlatformTemplate, 'awsRoleToAssume' | 'awsKeyPairName'>> {
18
+ activeEnvironment: SupportedEnvironments;
19
+ awsRoleToAssume?: string;
20
+ awsKeyPairName?: string;
21
+ }
22
+
23
+ export enum SupportedRegions {
24
+ North = 'eu-north-1'
25
+ }
26
+
27
+ export enum SupportedEnvironments {
28
+ LOCAL = 'local',
29
+ QA = 'qa',
30
+ TEST = 'test',
31
+ DEMO = 'demo',
32
+ PROD = 'prod'
33
+ }
34
+
35
+ export enum SupportedDeploymentTypes {
36
+ Container = 'CONTAINER',
37
+ // Static = 'STATIC'
38
+ }
39
+
40
+ export interface DeploymentTemplate {
41
+ type: SupportedDeploymentTypes;
42
+ targetDeploymentPlatform: string;
43
+ subdomain?: string;
44
+ env?: {
45
+ shared?: { [name: string]: string | number };
46
+ prod?: { [name: string]: string | number };
47
+ test?: { [name: string]: string | number };
48
+ qa?: { [name: string]: string | number };
49
+ }
50
+ }
51
+
52
+ export interface ContainerDeploymentTemplate extends DeploymentTemplate {
53
+ type: SupportedDeploymentTypes.Container;
54
+ dockerFile: string;
55
+ dockerContext: string;
56
+ image: string;
57
+ hostPort: number;
58
+ containerPort: number;
59
+ registry: string;
60
+ }
61
+
62
+ export interface ContainerDeploymentRequest extends ContainerDeploymentTemplate {
63
+ authentication?: string;
64
+ username?: string;
65
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,9 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "es2015",
4
+ "module": "ESNext",
5
+ "moduleResolution": "nodenext",
6
+ "baseUrl": "src",
7
+ "lib": ["es2021"]
8
+ }
9
+ }