@ossy/deployment-tools 1.17.6 → 1.17.8

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/CHANGELOG.md CHANGED
@@ -3,6 +3,22 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ ## 1.17.8 (2026-05-25)
7
+
8
+ **Note:** Version bump only for package @ossy/deployment-tools
9
+
10
+
11
+
12
+
13
+
14
+ ## 1.17.7 (2026-05-25)
15
+
16
+ **Note:** Version bump only for package @ossy/deployment-tools
17
+
18
+
19
+
20
+
21
+
6
22
  ## 1.17.6 (2026-05-25)
7
23
 
8
24
  **Note:** Version bump only for package @ossy/deployment-tools
package/README.md CHANGED
@@ -22,49 +22,49 @@ All services run as Docker containers managed by systemd. Service files are writ
22
22
  | Service | Unit file | Port | Auto-start |
23
23
  |---|---|---|---|
24
24
  | Caddy | `caddy-route53.service` | 80, 443 | Yes |
25
- | Ossy API | `ossy-api.service` | 3001 | Manual |
26
- | Ossy Runtime | `ossy-runtime.service` | 3000 | Manual |
25
+ | Ossy API | `ossy-api.service` | 3001 | Yes |
26
+ | Ossy Runtime | `ossy-runtime.service` | 3000 | Yes |
27
27
 
28
- The API and runtime are **not** auto-started because their Docker images must be built on the host first. After SSHing in and building the images, enable them once:
28
+ All three services start automatically on first boot:
29
29
 
30
- ```bash
31
- sudo systemctl enable --now ossy-api ossy-runtime
32
- ```
30
+ - **Runtime** — image built from the published `@ossy/platform` npm package (no repo needed)
31
+ - **API** image pulled from `ghcr.io/ossy-se/api:latest` (pushed by CI on every merge to main). The `ExecStartPre` in the service unit pulls the latest image before each start, so redeploying is just `systemctl restart ossy-api`
32
+
33
+ > **TODO:** Pin `@ossy/platform` to a specific version at CDK deploy time (thread through `PlatformConfig`) rather than always pulling `@latest`.
33
34
 
34
35
  ## Bootstrapping a new host
35
36
 
36
- After CDK provisions the EC2 instance (Caddy starts automatically), SSH in and:
37
+ CDK provisions the instance and starts all three services automatically. The only manual step is setting required environment variables on the host before the first start:
37
38
 
38
39
  ```bash
39
- # Clone the repo
40
- git clone <repo-url> ~/ossy
41
- cd ~/ossy
42
-
43
- # Build the API image
44
- docker build -t ossy-api:latest -f api/Dockerfile api
45
-
46
- # Build the runtime image
47
- docker build -t ossy-runtime:latest \
48
- -f packages/runtime/Dockerfile packages/runtime
49
-
50
- # Start both services
51
- sudo systemctl enable --now ossy-api ossy-runtime
40
+ # SSH in and edit /etc/environment:
41
+ sudo tee -a /etc/environment << 'EOF'
42
+ DB_URL=mongodb+srv://...
43
+ DB_NAME=ossy
44
+ TOKEN_SECRET=...
45
+ MEDIA_REPOSITORY=...
46
+ MEDIA_CDN_DOMAIN_NAME=...
47
+ AWS_ACCESS_KEY_ID=...
48
+ AWS_SECRET_ACCESS_KEY=...
49
+ OSSY_API_KEY=...
50
+ EOF
51
+
52
+ # Restart services to pick up the new env vars
53
+ sudo systemctl restart ossy-api ossy-runtime
52
54
  ```
53
55
 
54
- ## Redeploying after a code change
56
+ > Note: `OSSY_API_KEY` is used by the runtime to authenticate CMS reads. It should be a long-lived API token issued by the API itself.
55
57
 
56
- ```bash
57
- cd ~/ossy && git pull
58
+ ## Redeploying after a code change
58
59
 
59
- # Rebuild whichever image changed
60
- docker build -t ossy-api:latest -f api/Dockerfile api
61
- # or
62
- docker build -t ossy-runtime:latest \
63
- -f packages/runtime/Dockerfile packages/runtime
60
+ Both services pull the latest image from GHCR on every restart — no manual build step needed.
64
61
 
65
- # Restart the service
62
+ ```bash
63
+ # API — push to main triggers CI, which builds and pushes ghcr.io/ossy-se/api:latest
66
64
  sudo systemctl restart ossy-api
67
- # or
65
+
66
+ # Runtime — merging to main in the ossy monorepo publishes npm packages and then
67
+ # builds and pushes ghcr.io/ossy-se/platform:latest
68
68
  sudo systemctl restart ossy-runtime
69
69
  ```
70
70
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ossy/deployment-tools",
3
- "version": "1.17.6",
3
+ "version": "1.17.8",
4
4
  "description": "Collection of scripts and tools to aid deployment of containers and static files to Amazon Web Services through GitHub Actions",
5
5
  "source": "./src/index.js",
6
6
  "main": "./src/index.js",
@@ -21,11 +21,10 @@
21
21
  "aws-cdk-lib": "^2.73.0",
22
22
  "constructs": "^10.1.304",
23
23
  "glob": "^9.3.2",
24
- "nanoid": "^3.3.4",
25
- "node-fetch": "^2.6.7"
24
+ "nanoid": "^3.3.4"
26
25
  },
27
26
  "devDependencies": {
28
27
  "jest": "^27.5.1"
29
28
  },
30
- "gitHead": "236f9e8bddf2ae3ca992aea39e7d1a70c9377ea6"
29
+ "gitHead": "067a561ef254d7c0f51b7517a6b995ed53bd416e"
31
30
  }
@@ -6,12 +6,22 @@ const caddyfile = `
6
6
  }
7
7
 
8
8
  api.ossy.se {
9
+ tls {
10
+ dns route53 {
11
+ max_retries 10
12
+ profile ci-client
13
+ }
14
+ }
9
15
  reverse_proxy localhost:3001
10
16
  }
11
17
 
12
18
  :443 {
13
19
  tls {
14
20
  on_demand
21
+ dns route53 {
22
+ max_retries 10
23
+ profile ci-client
24
+ }
15
25
  }
16
26
  reverse_proxy localhost:3000
17
27
  }
@@ -132,9 +132,11 @@ class ContainerDeploymentTarget extends Construct {
132
132
  userData.addCommands(
133
133
  'sudo systemctl daemon-reload',
134
134
  ...CaddyService.enable(),
135
- ...CaddyService.start()
136
- // ossy-runtime and ossy-api are enabled manually after building images:
137
- // sudo systemctl enable --now ossy-runtime ossy-api
135
+ ...CaddyService.start(),
136
+ ...OssyRuntimeService.enable(),
137
+ ...OssyRuntimeService.start(),
138
+ ...OssyApiService.enable(),
139
+ ...OssyApiService.start()
138
140
  )
139
141
 
140
142
  const ec2Instance = new Instance(this, 'Ec2Instance', {
@@ -7,6 +7,7 @@ Requires=docker.service
7
7
  [Service]
8
8
  EnvironmentFile=/etc/environment
9
9
  ExecStartPre=-/usr/bin/docker rm -f ossy-api
10
+ ExecStartPre=/usr/bin/docker pull ghcr.io/ossy-se/api:latest
10
11
  ExecStart=/usr/bin/docker run --name ossy-api \\
11
12
  --network ossy-network \\
12
13
  -p 3001:3001 \\
@@ -17,15 +18,17 @@ ExecStart=/usr/bin/docker run --name ossy-api \\
17
18
  -e MEDIA_CDN_DOMAIN_NAME=\${MEDIA_CDN_DOMAIN_NAME} \\
18
19
  -e AWS_ACCESS_KEY_ID=\${AWS_ACCESS_KEY_ID} \\
19
20
  -e AWS_SECRET_ACCESS_KEY=\${AWS_SECRET_ACCESS_KEY} \\
20
- ossy-api:latest
21
+ ghcr.io/ossy-se/api:latest
21
22
  ExecStop=/usr/bin/docker stop ossy-api
22
23
  Restart=on-failure
23
24
  RestartSec=5
25
+
26
+ [Install]
27
+ WantedBy=multi-user.target
24
28
  `
25
29
 
26
- // No [Install] section service is enabled manually after the image is built.
27
- // SSH into the host, build the image, then:
28
- // sudo systemctl enable --now ossy-api
30
+ // The image is pulled automatically on every start via ExecStartPre, so the
31
+ // service always runs the latest published version. To redeploy: systemctl restart ossy-api
29
32
 
30
33
  /**
31
34
  * @class
@@ -36,6 +39,14 @@ class OssyApiService {
36
39
  return [`sudo tee /etc/systemd/system/ossy-api.service > /dev/null << 'EOF'\n${systemdServiceFile}\nEOF`]
37
40
  }
38
41
 
42
+ static enable() {
43
+ return ['sudo systemctl enable ossy-api']
44
+ }
45
+
46
+ static start() {
47
+ return ['sudo systemctl start ossy-api']
48
+ }
49
+
39
50
  }
40
51
 
41
52
  module.exports = {
@@ -7,19 +7,22 @@ Requires=docker.service
7
7
  [Service]
8
8
  EnvironmentFile=/etc/environment
9
9
  ExecStartPre=-/usr/bin/docker rm -f ossy-runtime
10
+ ExecStartPre=/usr/bin/docker pull ghcr.io/ossy-se/platform:latest
10
11
  ExecStart=/usr/bin/docker run --name ossy-runtime \\
11
12
  --network ossy-network \\
12
13
  -p 3000:3000 \\
13
14
  -e OSSY_API_KEY=\${OSSY_API_KEY} \\
14
- ossy-runtime:latest
15
+ ghcr.io/ossy-se/platform:latest
15
16
  ExecStop=/usr/bin/docker stop ossy-runtime
16
17
  Restart=on-failure
17
18
  RestartSec=5
19
+
20
+ [Install]
21
+ WantedBy=multi-user.target
18
22
  `
19
23
 
20
- // No [Install] section service is enabled manually after the image is built.
21
- // SSH into the host, build the image, then:
22
- // sudo systemctl enable --now ossy-runtime
24
+ // The image is pulled automatically on every start via ExecStartPre, so the
25
+ // service always runs the latest published version. To redeploy: systemctl restart ossy-runtime
23
26
 
24
27
  /**
25
28
  * @class
@@ -30,6 +33,14 @@ class OssyRuntimeService {
30
33
  return [`sudo tee /etc/systemd/system/ossy-runtime.service > /dev/null << 'EOF'\n${systemdServiceFile}\nEOF`]
31
34
  }
32
35
 
36
+ static enable() {
37
+ return ['sudo systemctl enable ossy-runtime']
38
+ }
39
+
40
+ static start() {
41
+ return ['sudo systemctl start ossy-runtime']
42
+ }
43
+
33
44
  }
34
45
 
35
46
  module.exports = {
@@ -1,88 +0,0 @@
1
- const Matchers = {
2
- host: host => ({ host: [host] }),
3
- path: path => ({ path: [path] })
4
- }
5
-
6
- const Handlers = {
7
- reverseProxy: upstreamPort => ({
8
- handler: 'reverse_proxy',
9
- upstreams: [{ dial: `localhost:${upstreamPort}` }]
10
- }),
11
- subroute: routes => ({
12
- handler: 'subroute',
13
- routes
14
- })
15
- }
16
-
17
- /**
18
- * @class
19
- */
20
- class CaddyConfigService {
21
-
22
- static createServer() {
23
- return {
24
- apps: {
25
- tls: CaddyConfigService.tlsIssuers(),
26
- http: {
27
- servers: {
28
- 'deployment-tools': {
29
- listen: [':443'],
30
- routes: []
31
- }
32
- }
33
- },
34
- }
35
- }
36
- }
37
-
38
- static createProxyConfig(containerDeploymentTemplate) {
39
- return {
40
- '@id': containerDeploymentTemplate.domain,
41
- match: [Matchers.host(containerDeploymentTemplate.domain)],
42
- handle: [Handlers.subroute([{ handle: [Handlers.reverseProxy(containerDeploymentTemplate.hostPort)]}])]
43
- }
44
- }
45
-
46
- static tlsIssuers() {
47
- // add tls automation to the server config under apps.tls property
48
- return {
49
- automation: {
50
- policies: [
51
- {
52
- subjects: [],
53
- issuers:[
54
- {
55
- challenges: {
56
- dns:{
57
- provider: {
58
- 'max_retries': 10,
59
- name: 'route53',
60
- 'aws_profile': 'ci-client'
61
- }
62
- }
63
- },
64
- module: 'acme'
65
- },
66
- {
67
- challenges:{
68
- dns:{
69
- provider:{
70
- 'max_retries': 10,
71
- name: 'route53'
72
- }
73
- }
74
- },
75
- module: 'zerossl'
76
- }
77
- ]
78
- }
79
- ]
80
- }
81
- }
82
- }
83
-
84
- }
85
-
86
- module.exports = {
87
- CaddyConfigService
88
- }
@@ -1,113 +0,0 @@
1
- const fetch = require('node-fetch')
2
- const { logError, logInfo } = require('../log')
3
- const { CaddyConfigService } = require('./caddy-config')
4
-
5
- /**
6
- * @class
7
- */
8
- class CaddyService {
9
-
10
- static loadServerConfig() {
11
- const config = CaddyConfigService.createServer()
12
- return fetch('http://localhost:2019/load', {
13
- method: 'POST',
14
- headers: { 'Content-Type': 'application/json' },
15
- body: JSON.stringify(config)
16
- })
17
- .then(response => response?.error && logError({ message: '[CaddyService] Could not apply default caddy config', error }))
18
- .catch(error => logError({ message: '[CaddyService] Could not apply default caddy config', error }))
19
- }
20
-
21
- static apply(containerDeploymentTemplate) {
22
- return CaddyService.applyProxyConfig(containerDeploymentTemplate)
23
- .then(() => CaddyService.applyTlsSubject(containerDeploymentTemplate))
24
- }
25
-
26
- static getConfig() {
27
- return fetch('http://localhost:2019/config/', {
28
- method: 'GET',
29
- headers: { 'Content-Type': 'application/json' }
30
- }).then(response => response.json())
31
- }
32
-
33
- static getTlsSubjects() {
34
- return fetch('http://localhost:2019/config/apps/tls/automation/policies/0/subjects', {
35
- method: 'GET',
36
- headers: { 'Content-Type': 'application/json' }
37
- })
38
- .then(response => response.json())
39
- .catch(error => logError({ message: '[CaddyService] Could not get tls subjects', error }))
40
- }
41
-
42
- static applyTlsSubject(containerDeploymentTemplate) {
43
- return CaddyService.getTlsSubjects()
44
- .then(tlsSubjects => {
45
- const subjectExists = tlsSubjects.includes(containerDeploymentTemplate.domain)
46
- return subjectExists
47
- ? Promise.resolve()
48
- : CaddyService.addTlsSubject(containerDeploymentTemplate)
49
- })
50
- }
51
-
52
- static addTlsSubject(containerDeploymentTemplate) {
53
- return fetch('http://localhost:2019/config/apps/tls/automation/policies/0/subjects', {
54
- method: 'POST',
55
- headers: { 'Content-Type': 'application/json' },
56
- body: JSON.stringify(containerDeploymentTemplate.domain)
57
- })
58
- .then(response => response?.error && logError({ message: '[CaddyService] Could not add tls subject', error }))
59
- .catch(error => logError({ message: '[CaddyService] Could not add tls subject', error }))
60
- }
61
-
62
- static getProxyConfig(containerDeploymentTemplate) {
63
- logInfo({ message: `[CaddyService] Checking if a proxy for ${containerDeploymentTemplate.domain} exists` })
64
- return fetch(`http://localhost:2019/id/${containerDeploymentTemplate.domain}`, {
65
- method: 'GET',
66
- headers: { 'Content-Type': 'application/json' }
67
- })
68
- .then(response => response.json())
69
- .then(response => (!response || response?.error) ? Promise.reject() : response)
70
- .catch(error => {
71
- logInfo({ message: `[CaddyService] Could not find a proxy for ${containerDeploymentTemplate.domain}`, error })
72
- return undefined
73
- })
74
- }
75
-
76
- static applyProxyConfig(containerDeploymentTemplate) {
77
- return CaddyService.getProxyConfig(containerDeploymentTemplate)
78
- .then(proxyConfig => {
79
- return proxyConfig
80
- ? CaddyService.replaceProxyConfig(containerDeploymentTemplate)
81
- : CaddyService.addProxyConfig(containerDeploymentTemplate)
82
- })
83
- }
84
-
85
- static addProxyConfig(containerDeploymentTemplate) {
86
- logInfo({ message: `[CaddyService] Adding proxy for ${containerDeploymentTemplate.domain}` })
87
- const proxyConfig = CaddyConfigService.createProxyConfig(containerDeploymentTemplate)
88
- return fetch('http://localhost:2019/config/apps/http/servers/deployment-tools/routes', {
89
- method: 'POST',
90
- headers: { 'Content-Type': 'application/json' },
91
- body: JSON.stringify(proxyConfig)
92
- })
93
- .then(response => response?.error && logError({ message: '[CaddyService] Could not add proxy config', error }))
94
- .catch(error => logError({ message: '[CaddyService] Could not add proxy config', error }))
95
- }
96
-
97
- static replaceProxyConfig(containerDeploymentTemplate) {
98
- logInfo({ message: `[CaddyService] Replacing proxy for ${containerDeploymentTemplate.domain}` })
99
- const proxyConfig = CaddyConfigService.createProxyConfig(containerDeploymentTemplate)
100
- return fetch(`http://localhost:2019/id/${containerDeploymentTemplate.domain}`, {
101
- method: 'PUT',
102
- headers: { 'Content-Type': 'application/json' },
103
- body: JSON.stringify(proxyConfig)
104
- })
105
- .then(response => response?.error && logError({ message: '[CaddyService] Could not add proxy config', error }))
106
- .catch(error => logError({ message: '[CaddyService] Could not add proxy config', error }))
107
- }
108
-
109
- }
110
-
111
- module.exports = {
112
- CaddyService
113
- }
@@ -1,31 +0,0 @@
1
- const { CaddyService } = require('./caddy')
2
-
3
- const containerDeploymentTemplate = {
4
- "domain": "peter.ossy.se",
5
- "image": "ossy-se/website-alex",
6
- "targetDeploymentPlatform": "ossybot",
7
- "type": "CONTAINER",
8
- "hostPort": "3016"
9
- }
10
-
11
-
12
- // CaddyService.getProxyForDomain('alex.ossy.se')
13
- // .then(x => console.log(JSON.stringify(x, null, 2)))
14
-
15
- // CaddyService.getProxyForDomain('ossy.se')
16
- // .then(x => console.log(JSON.stringify(x, null, 2)))
17
-
18
- // OnStartup
19
- // CaddyService.loadServerConfig()
20
-
21
- // Get config
22
- CaddyService.getConfig()
23
- .then(x => console.log(JSON.stringify(x, null, 2)))
24
- // .then(() => CaddyService.applyProxyConfig(containerDeploymentTemplate))
25
- // .then(() => CaddyService.getConfig())
26
- // .then(x => console.log(JSON.stringify(x, null, 2)))
27
-
28
- // CaddyService.getTlsSubjects()
29
- // .then(x => console.log(JSON.stringify(x, null, 2)))
30
-
31
- // CaddyService.apply(containerDeploymentTemplate)
@@ -1,4 +0,0 @@
1
- module.exports = {
2
- ...require('./caddy.js'),
3
- ...require('./caddy-config.js')
4
- }
@@ -1,40 +0,0 @@
1
- const systemdServiceFile = `
2
- [Unit]
3
- Description=D
4
- After=network.target caddy-route53.service
5
-
6
- [Service]
7
- EnvironmentFile=/etc/environment
8
- User=ubuntu
9
- Group=docker
10
- AmbientCapabilities=CAP_NET_BIND_SERVICE
11
- CapabilityBoundingSet=CAP_NET_BIND_SERVICE
12
- ExecStart=/usr/bin/npx --yes @ossy/deployment-tools server start --platforms /home/ubuntu/platform-config.json
13
- Restart=on-failure
14
-
15
- [Install]
16
- WantedBy=multi-user.target cloud-init.target
17
- `
18
-
19
- /**
20
- * @class
21
- */
22
- class DeploymentToolsService {
23
-
24
- static install() {
25
- return [`sudo echo "${systemdServiceFile}" >> /etc/systemd/system/deployment-tools.service`]
26
- }
27
-
28
- static enable() {
29
- return ['sudo systemctl enable deployment-tools.service']
30
- }
31
-
32
- static start() {
33
- return ['sudo systemctl start deployment-tools.service']
34
- }
35
-
36
- }
37
-
38
- module.exports = {
39
- DeploymentToolsService
40
- }