@toa.io/operations 0.8.0-dev.0 → 0.8.0-dev.4

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,6 +1,6 @@
1
1
  {
2
2
  "name": "@toa.io/operations",
3
- "version": "0.8.0-dev.0",
3
+ "version": "0.8.0-dev.4",
4
4
  "description": "Toa Deployment",
5
5
  "homepage": "https://toa.io",
6
6
  "author": {
@@ -26,10 +26,10 @@
26
26
  "test": "echo \"Error: run tests from root\" && exit 1"
27
27
  },
28
28
  "dependencies": {
29
- "@toa.io/filesystem": "1.1.0-dev.2",
30
- "@toa.io/generic": "0.11.0-dev.16",
31
- "@toa.io/yaml": "0.7.6-dev.16",
29
+ "@toa.io/filesystem": "1.1.0-dev.4",
30
+ "@toa.io/generic": "0.11.0-dev.3",
31
+ "@toa.io/yaml": "0.8.0-dev.4",
32
32
  "execa": "5.1.1"
33
33
  },
34
- "gitHead": "669be360acebac4dbe49735ec506127d9d0a05e3"
34
+ "gitHead": "24d68d70a56717f2f4441cc9884a60f9fee0863e"
35
35
  }
package/readme.md ADDED
@@ -0,0 +1,30 @@
1
+ # Toa Operations
2
+
3
+ ## Context
4
+
5
+ ### Container Registry
6
+
7
+ #### Build Options
8
+
9
+ ```yaml
10
+ # context.toa.yaml
11
+
12
+ registry:
13
+ build:
14
+ arguments: [NPM_TOKEN]
15
+ run: echo //npm.pkg.github.com/:_authToken=${NPM_TOKEN} > .npmrc
16
+ ```
17
+
18
+ `arguments` is a list of environemt varialbes to be passed to `docker build`.
19
+
20
+ `run` is a command(s) to be executed during build. Multiline is supported.
21
+
22
+ ```yaml
23
+ # context.toa.yaml
24
+
25
+ registry:
26
+ build:
27
+ run: |
28
+ echo test > .test
29
+ rm .test
30
+ ```
@@ -34,7 +34,7 @@ class Factory {
34
34
  this.#context = context
35
35
  this.#process = new Process()
36
36
 
37
- const imagesFactory = new ImagesFactory(context.name, context.runtime)
37
+ const imagesFactory = new ImagesFactory(context.name, context.runtime, context.registry)
38
38
 
39
39
  this.#registry = new Registry(context.registry, imagesFactory, this.#process)
40
40
  this.#compositions = context.compositions.map((composition) => this.#composition(composition))
@@ -1,9 +1,11 @@
1
1
  FROM node:18.16.0-alpine3.17
2
2
 
3
+ {{build.arguments}}
4
+
3
5
  ENV NODE_ENV=production
4
- RUN if [ {{registry}} != undefined ]; then npm set registry {{registry}}; fi
5
- RUN if [ {{proxy}} != undefined ]; then npm set proxy {{proxy}}; fi
6
- RUN npm i -g @toa.io/runtime@{{version}}
6
+ RUN if [ {{runtime.registry}} != undefined ]; then npm set registry {{runtime.registry}}; fi
7
+ RUN if [ {{runtime.proxy}} != undefined ]; then npm set proxy {{runtime.proxy}}; fi
8
+ RUN npm i -g @toa.io/runtime@{{runtime.version}}
7
9
 
8
10
  WORKDIR /composition
9
11
  ADD . .
@@ -11,4 +13,6 @@ ADD . .
11
13
  # run 'npm i' in each component
12
14
  RUN find . -maxdepth 1 -type d \( ! -name . \) -exec /bin/sh -c "cd '{}' && if [ -f package.json ]; then npm i; fi" \;
13
15
 
16
+ {{build.run}}
17
+
14
18
  CMD toa compose *
@@ -11,16 +11,18 @@ class Composition extends Image {
11
11
 
12
12
  /** @type {string} */
13
13
  #name
14
+
14
15
  /** @type {toa.norm.Component[]} */
15
16
  #components
16
17
 
17
18
  /**
18
- * @param scope {string}
19
- * @param runtime {toa.norm.context.Runtime}
20
- * @param composition {toa.norm.context.Composition}
19
+ * @param {string} scope
20
+ * @param {toa.norm.context.Runtime} runtime
21
+ * @param {toa.norm.context.Registry} registry
22
+ * @param {toa.norm.context.Composition} composition
21
23
  */
22
- constructor (scope, runtime, composition) {
23
- super(scope, runtime)
24
+ constructor (scope, runtime, registry, composition) {
25
+ super(scope, runtime, registry)
24
26
 
25
27
  this.#name = composition.name
26
28
  this.#components = composition.components
@@ -9,30 +9,44 @@ const { Service } = require('./service')
9
9
  class Factory {
10
10
  /** @type {string} */
11
11
  #scope
12
+
12
13
  /** @type {toa.norm.context.Runtime} */
13
14
  #runtime
14
15
 
16
+ /** @type {toa.norm.context.Registry} */
17
+ #registry
18
+
15
19
  /**
16
- * @param scope {string}
17
- * @param runtime {toa.norm.context.Runtime}
20
+ * @param {string} scope
21
+ * @param {toa.norm.context.Runtime} runtime
22
+ * @param {toa.norm.context.Registry} registry
18
23
  */
19
- constructor (scope, runtime) {
24
+ constructor (scope, runtime, registry) {
20
25
  this.#scope = scope
21
26
  this.#runtime = runtime
27
+ this.#registry = registry
22
28
  }
23
29
 
24
30
  /**
25
31
  * @returns {Composition}
26
32
  */
27
33
  composition (composition) {
28
- return new Composition(this.#scope, this.#runtime, composition)
34
+ const instance = new Composition(this.#scope, this.#runtime, this.#registry, composition)
35
+
36
+ instance.tag()
37
+
38
+ return instance
29
39
  }
30
40
 
31
41
  /**
32
42
  * @returns {Service}
33
43
  */
34
44
  service (path, service) {
35
- return new Service(this.#scope, this.#runtime, path, service)
45
+ const instance = new Service(this.#scope, this.#runtime, this.#registry, path, service)
46
+
47
+ instance.tag()
48
+
49
+ return instance
36
50
  }
37
51
  }
38
52
 
@@ -25,9 +25,15 @@ const runtime = {
25
25
  version: generate()
26
26
  }
27
27
 
28
+ /** @type {toa.norm.context.Registry} */
29
+ const registry = {
30
+ base: generate()
31
+ }
32
+
28
33
  exports.scope = generate()
29
34
  exports.name = name
30
35
  exports.version = hash(runtime.version + ';' + version)
31
36
  exports.Class = Class
32
37
  exports.runtime = runtime
38
+ exports.registry = registry
33
39
  exports.process = process
@@ -3,7 +3,7 @@
3
3
  const { join, posix } = require('node:path')
4
4
  const { readFile: read, writeFile: write } = require('node:fs/promises')
5
5
 
6
- const { hash } = require('@toa.io/generic')
6
+ const { hash, overwrite } = require('@toa.io/generic')
7
7
  const { directory } = require('@toa.io/filesystem')
8
8
 
9
9
  /**
@@ -22,19 +22,37 @@ class Image {
22
22
 
23
23
  /** @type {string} */
24
24
  #scope
25
+
26
+ /** @type {toa.norm.context.Registry} */
27
+ #registry
28
+
25
29
  /** @type {toa.norm.context.Runtime} */
26
30
  #runtime
31
+
27
32
  /** @type {string} */
28
33
  #type
29
34
 
35
+ /** @type {{ runtime?: Partial<toa.norm.context.Runtime>, build: object }} */
36
+ #values = { build: { command: 'echo hello' } }
37
+
30
38
  /**
31
- * @param scope {string}
32
- * @param runtime {toa.norm.context.Runtime}
39
+ * @param {string} scope
40
+ * @param {toa.norm.context.Runtime} runtime
41
+ * @param {toa.norm.context.Registry} registry
33
42
  */
34
- constructor (scope, runtime) {
43
+ constructor (scope, runtime, registry) {
35
44
  this.#scope = scope
45
+ this.#registry = registry
36
46
  this.#runtime = runtime
37
47
  this.#type = this.constructor.name.toLowerCase()
48
+
49
+ this.#setValues()
50
+ }
51
+
52
+ tag () {
53
+ const tag = hash(this.#runtime?.version + ';' + this.version)
54
+
55
+ this.reference = posix.join(this.#registry.base ?? '', this.#scope, `${this.#type}-${this.name}:${tag}`)
38
56
  }
39
57
 
40
58
  /**
@@ -51,12 +69,6 @@ class Image {
51
69
  */
52
70
  get version () {}
53
71
 
54
- tag (base) {
55
- const tag = hash(this.#runtime?.version + ';' + this.version)
56
-
57
- this.reference = posix.join(base ?? '', this.#scope, `${this.#type}-${this.name}:${tag}`)
58
- }
59
-
60
72
  async prepare (root) {
61
73
  if (this.dockerfile === undefined) throw new Error('Dockerfile isn\'t specified')
62
74
 
@@ -65,7 +77,7 @@ class Image {
65
77
  await directory.ensure(path)
66
78
 
67
79
  const template = await read(this.dockerfile, 'utf-8')
68
- const contents = template.replace(/{{(\.?\w+)}}/g, (_, key) => this.#value(key))
80
+ const contents = template.replace(/{{(\S{1,32})}}/g, (_, key) => this.#value(key))
69
81
  const ignore = 'Dockerfile'
70
82
 
71
83
  await write(join(path, 'Dockerfile'), contents)
@@ -76,16 +88,44 @@ class Image {
76
88
  return path
77
89
  }
78
90
 
91
+ #setValues () {
92
+ this.#values.runtime = this.#runtime
93
+ this.#values.build = overwrite(this.#values.build, this.#registry.build)
94
+
95
+ if (this.#values.build.arguments !== undefined) this.#values.build.arguments = createArguments(this.#values.build.arguments)
96
+ if (this.#values.build.run !== undefined) this.#values.build.run = createRunCommands(this.#values.build.run)
97
+ }
98
+
79
99
  /**
80
100
  * @param key {string}
81
101
  * @returns {string}
82
102
  */
83
103
  #value (key) {
84
- const [, property] = key.split('.')
104
+ const [source, property] = key.split('.')
105
+
106
+ return this.#values[source]?.[property] ?? ''
107
+ }
108
+ }
85
109
 
86
- if (property !== undefined) return this[property]
87
- else return this.#runtime[key]
110
+ function createRunCommands (input) {
111
+ const lines = input.split('\n')
112
+
113
+ return lines.reduce((commands, command) => {
114
+ commands += '\nRUN ' + command
115
+
116
+ return commands
117
+ }, '')
118
+ }
119
+
120
+ function createArguments (variables) {
121
+ const args = []
122
+
123
+ for (const variable of variables) {
124
+ args.push('ARG ' + variable)
125
+ args.push(`ENV ${variable}=$${variable}`)
88
126
  }
127
+
128
+ return args.join('\n')
89
129
  }
90
130
 
91
131
  exports.Image = Image
@@ -7,15 +7,13 @@ const { generate } = require('randomstring')
7
7
  let instance
8
8
 
9
9
  beforeEach(() => {
10
- instance = new fixtures.Class(fixtures.scope, fixtures.runtime)
10
+ instance = new fixtures.Class(fixtures.scope, fixtures.runtime, fixtures.registry)
11
11
  })
12
12
 
13
13
  it('should assign url', () => {
14
- const registry = generate()
14
+ instance.tag()
15
15
 
16
- instance.tag(registry)
17
-
18
- expect(instance.reference).toEqual(`${registry}/${fixtures.scope}/class-${fixtures.name}:${fixtures.version}`)
16
+ expect(instance.reference).toEqual(`${fixtures.registry.base}/${fixtures.scope}/class-${fixtures.name}:${fixtures.version}`)
19
17
  })
20
18
 
21
19
  describe('prepare', () => {
@@ -1,9 +1,9 @@
1
1
  FROM node:18.16.0-alpine3.17
2
2
 
3
3
  ENV NODE_ENV=production
4
- RUN if [ {{registry}} != undefined ]; then npm set registry {{registry}}; fi
5
- RUN if [ {{proxy}} != undefined ]; then npm set proxy {{proxy}}; fi
6
- RUN npm i -g @toa.io/runtime@{{version}}
4
+ RUN if [ {{runtime.registry}} != undefined ]; then npm set registry {{runtime.registry}}; fi
5
+ RUN if [ {{runtime.proxy}} != undefined ]; then npm set proxy {{runtime.proxy}}; fi
6
+ RUN npm i -g @toa.io/runtime@{{runtime.version}}
7
7
 
8
8
  WORKDIR /service
9
9
  ADD . .
@@ -28,11 +28,12 @@ class Service extends Image {
28
28
  /**
29
29
  * @param {string} scope
30
30
  * @param {toa.norm.context.Runtime} runtime
31
+ * @param {toa.norm.context.Registry} registry
31
32
  * @param {string} reference
32
33
  * @param {toa.deployment.dependency.Service} service
33
34
  */
34
- constructor (scope, runtime, reference, service) {
35
- super(scope, runtime)
35
+ constructor (scope, runtime, registry, reference, service) {
36
+ super(scope, runtime, registry)
36
37
 
37
38
  this.service = service.name
38
39
 
@@ -65,7 +65,6 @@ class Registry {
65
65
  #create (type, ...args) {
66
66
  const image = this.#factory[type](...args)
67
67
 
68
- image.tag(this.#registry?.base)
69
68
  this.#images.push(image)
70
69
 
71
70
  return image
@@ -85,6 +84,10 @@ class Registry {
85
84
 
86
85
  const multiarch = this.#registry.platforms !== null
87
86
 
87
+ if (this.#registry.build?.arguments !== undefined) {
88
+ for (const arg of this.#registry.build.arguments) args.push('--build-arg', `${arg}=${process.env[arg]}`)
89
+ }
90
+
88
91
  if (multiarch) {
89
92
  const platform = this.#registry.platforms.join(',')
90
93
 
@@ -1,13 +1,13 @@
1
1
  declare namespace toa.deployment.images {
2
2
 
3
- export interface Image {
4
- readonly reference: string
5
- readonly context: string
3
+ export interface Image {
4
+ readonly reference: string
5
+ readonly context: string
6
6
 
7
- tag(base: string): void
7
+ tag(): void
8
8
 
9
- prepare(root: string): Promise<string>
10
- }
9
+ prepare(root: string): Promise<string>
10
+ }
11
11
 
12
12
  }
13
13
 
@@ -3,7 +3,7 @@ import type * as _dependency from './dependency'
3
3
  import type * as _image from "./images/image"
4
4
 
5
5
  declare namespace toa.deployment {
6
-
6
+
7
7
  interface Registry {
8
8
  composition(composition: _norm.Composition): _image.Image
9
9