@ossy/cli 0.16.10 → 0.16.12

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/README.md CHANGED
@@ -49,18 +49,26 @@ Details:
49
49
  cd packages/my-website
50
50
  npx @ossy/cli publish \
51
51
  --username <github-username> \
52
- --authentication <token> \
52
+ --authentication <github-or-deploy-token> \
53
+ --cms-authentication <ossy-api-jwt> \
53
54
  --platforms-path ../infrastructure/platforms.json \
54
55
  --deployments-path ../infrastructure/deployments.json
55
56
  ```
56
57
 
58
+ - **`--cms-authentication`** — Ossy **API JWT** (from the app’s API tokens) for `POST /resource-templates` and site-artifacts. If omitted, **`--authentication`** is used for CMS calls too (only works when that value is already a valid Ossy JWT). In GitHub Actions, set repo secret **`OSSY_API_KEY`** and pass **`--cms-authentication`** explicitly; the deploy token is usually **not** valid for the API.
59
+
60
+ ### Unifying deploy and CMS authentication (future)
61
+
62
+ **`--authentication`** and **`--cms-authentication`** exist because **`deployment deploy`** (via **`@ossy/deployment-tools`**) and the **Ossy HTTP API** currently expect **different** credentials (e.g. GitHub / queue token vs Ossy-signed JWT). They could be merged into **one** flag (or a single env secret in CI) if either: (1) deploy is **no longer** invoked from the CLI and **`publish`** is only CMS/API work, or (2) the deploy path is changed to accept the **same** Ossy-issued token the API uses. That lines up with [**Future direction**](#future-direction-planned) (platform-driven rollouts instead of queue + dual tokens).
63
+
64
+ API and worker packages can use the same pattern: a minimal **`src/config.js`** with string-literal **`domain`** (and optional **`platform`**) matching **`deployments.json`**, then run **`publish`** from **`packages/api`** or **`packages/worker`** with **`--skip-resource-templates`** and **`--skip-site-artifacts`** (no website `workspaceId` / `build/` flow).
65
+
57
66
  - **`--domain` / `--platform`** — Optional if `src/config.js` contains string literals `domain: '…'` and `platform: '…'` (or `targetDeploymentPlatform`).
58
67
  - **`--config`** — Path to another `config.js` if not `./src/config.js`.
59
68
  - If `platform` is omitted but `domain` is set (from flags or config), it is inferred from `deployments.json` when that domain appears under exactly one `targetDeploymentPlatform`.
60
69
  - **`--all`** — Runs `deployment deploy-all` for the platform; requires `--platform` or `platform` in config.
61
70
  - **Resource templates** — After a successful deploy, the CLI reads **`workspaceId`** / **`resourceTemplates`** from `./src/config.js` (or `--config`) using the **static extraction** described above (not `import()`). If **`workspaceId`** is set and **`resourceTemplates`** is a non-empty array, they are **POST**ed to **`{api}/resource-templates`** with the **`workspaceId`** header (same as the CMS). Skipped when **`--skip-resource-templates`** is set, or when `workspaceId` / `resourceTemplates` are missing or not extractable.
62
71
  - **Site artifacts** — Uses the same **static `workspaceId` / `apiUrl` extraction** as resource templates. If **`workspaceId`** is set and **`build/`** exists next to `src/` (i.e. run **`npm run build`** first), the CLI calls **`/site-artifacts/presign-batch`**, **PUT**s each file to S3, then **`/site-artifacts/commit-batch`** so a **`@ossy/platform/site-artifact-batch`** resource is created in the CMS. Skipped with **`--skip-site-artifacts`**, when there is no **`build/`**, or when **`workspaceId`** is missing. Override the build output directory with **`--site-artifacts-build-dir`** (absolute or cwd-relative).
63
- - **`--cms-authentication`** — Optional token used only for CMS/API steps (templates + site artifacts); defaults to **`--authentication`** (deployment token).
64
72
  - **`--api-url`** — Optional API base for CMS calls (e.g. `https://api.ossy.se/api/v0`). Otherwise **`OSSY_API_URL`**, else an **absolute** `apiUrl` from config, else `https://api.ossy.se/api/v0`. Relative app `apiUrl` values (e.g. `/@ossy`) are ignored unless you pass **`--api-url`** or set **`OSSY_API_URL`**.
65
73
 
66
74
  Requires network access so `npx` can run `@ossy/deployment-tools`.
@@ -102,7 +110,7 @@ jobs:
102
110
  - name: Upload
103
111
  run: |
104
112
  npx --yes @ossy/cli cms upload \
105
- --authentication ${{ secrets.CMS_API_TOKEN }} \
113
+ --authentication ${{ secrets.OSSY_API_KEY }} \
106
114
  --config src/config.js
107
115
  ```
108
116
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ossy/cli",
3
- "version": "0.16.10",
3
+ "version": "0.16.12",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "git+https://github.com/ossy-se/packages.git"
@@ -18,7 +18,7 @@
18
18
  "bin": "./src/index.js",
19
19
  "dependencies": {
20
20
  "@babel/parser": "^7.28.6",
21
- "@ossy/app": "^0.15.10",
21
+ "@ossy/app": "^0.15.12",
22
22
  "arg": "^5.0.2",
23
23
  "glob": "^10.3.10"
24
24
  },
@@ -30,5 +30,5 @@
30
30
  "/src",
31
31
  "README.md"
32
32
  ],
33
- "gitHead": "cc54553550674b98f8a53bb3249307e06c4daa82"
33
+ "gitHead": "1da35d94f324f917d5ea790f838ce8e50214730e"
34
34
  }
@@ -1,3 +1,31 @@
1
+ /**
2
+ * Strips optional `Bearer ` prefix. The Ossy API expects the raw JWT in `Authorization`.
3
+ * @param {string | undefined} token
4
+ * @returns {string}
5
+ */
6
+ export function normalizeAuthorizationToken (token) {
7
+ const t = String(token ?? '').trim()
8
+ if (!t) return ''
9
+ return t.replace(/^Bearer\s+/i, '').trim()
10
+ }
11
+
12
+ /**
13
+ * @param {string | undefined} token
14
+ * @param {string} label e.g. "Resource template upload"
15
+ * @returns {string} normalized JWT
16
+ */
17
+ export function requireCmsAuthentication (token, label) {
18
+ const normalized = normalizeAuthorizationToken(token)
19
+ if (!normalized) {
20
+ throw new Error(
21
+ `[@ossy/cli] publish: ${label} needs a non-empty Ossy API JWT. ` +
22
+ 'Pass --cms-authentication with a token from the Ossy app (API tokens), or set the OSSY_API_KEY secret in CI. ' +
23
+ 'The GitHub token used for --authentication (container deploy) is not accepted by the API.'
24
+ )
25
+ }
26
+ return normalized
27
+ }
28
+
1
29
  /**
2
30
  * POST workspace-imported resource templates to the Ossy API.
3
31
  * @param {{ apiBaseUrl: string, token: string, workspaceId: string, resourceTemplates: unknown[] }} opts
@@ -11,10 +39,11 @@ export function postResourceTemplates ({
11
39
  }) {
12
40
  const base = apiBaseUrl.replace(/\/$/, '')
13
41
  const url = `${base}/resource-templates`
42
+ const auth = normalizeAuthorizationToken(token)
14
43
  return fetch(url, {
15
44
  method: 'POST',
16
45
  headers: {
17
- Authorization: token,
46
+ Authorization: auth,
18
47
  'Content-Type': 'application/json',
19
48
  workspaceId,
20
49
  },
@@ -2,6 +2,7 @@ import { logInfo } from '../log.js'
2
2
  import { readPublishFieldsFromWebsiteConfig } from './load-website-config.js'
3
3
  import {
4
4
  postResourceTemplates,
5
+ requireCmsAuthentication,
5
6
  resolveApiBaseUrlForUpload,
6
7
  } from '../cms/upload-resource-templates.js'
7
8
 
@@ -37,20 +38,35 @@ export async function maybeUploadResourceTemplatesAfterPublish ({
37
38
  envVar: process.env.OSSY_API_URL,
38
39
  configApiUrl: config?.apiUrl,
39
40
  })
41
+ const uploadUrl = `${apiBaseUrl.replace(/\/$/, '')}/resource-templates`
42
+ const authToken = requireCmsAuthentication(
43
+ cmsToken,
44
+ 'Resource template upload'
45
+ )
40
46
 
41
- logInfo({ message: '[@ossy/cli] publish: uploading resource templates…' })
47
+ logInfo({
48
+ message: `[@ossy/cli] publish: uploading resource templates → POST ${uploadUrl}`,
49
+ })
42
50
 
43
51
  const response = await postResourceTemplates({
44
52
  apiBaseUrl,
45
- token: cmsToken,
53
+ token: authToken,
46
54
  workspaceId,
47
55
  resourceTemplates,
48
56
  })
49
57
 
50
58
  if (!response.ok) {
51
59
  const text = await response.text().catch(() => '')
60
+ const hint404 =
61
+ response.status === 404
62
+ ? ` Wrong host or path: requested ${uploadUrl}. Confirm OSSY_API_URL / --api-url points at the Ossy API base including /api/v0.`
63
+ : ''
64
+ const hint401 =
65
+ response.status === 401
66
+ ? ' Use an Ossy API JWT with --cms-authentication (repo secret OSSY_API_KEY in CI), not the GitHub deploy token.'
67
+ : ''
52
68
  throw new Error(
53
- `Resource template upload failed: HTTP ${response.status}${
69
+ `Resource template upload failed: HTTP ${response.status}${hint404}${hint401}${
54
70
  text ? ` — ${text.slice(0, 200)}` : ''
55
71
  }`
56
72
  )
@@ -3,7 +3,11 @@ import { existsSync } from 'fs'
3
3
  import path from 'path'
4
4
  import { logInfo } from '../log.js'
5
5
  import { readPublishFieldsFromWebsiteConfig } from './load-website-config.js'
6
- import { resolveApiBaseUrlForUpload } from '../cms/upload-resource-templates.js'
6
+ import {
7
+ normalizeAuthorizationToken,
8
+ requireCmsAuthentication,
9
+ resolveApiBaseUrlForUpload,
10
+ } from '../cms/upload-resource-templates.js'
7
11
 
8
12
  const MAX_FILES = 200
9
13
 
@@ -68,7 +72,7 @@ export async function collectBuildFiles (buildDir) {
68
72
 
69
73
  function workspaceHeaders (token, workspaceId) {
70
74
  return {
71
- Authorization: token,
75
+ Authorization: normalizeAuthorizationToken(token),
72
76
  'Content-Type': 'application/json',
73
77
  workspaceId,
74
78
  }
@@ -123,11 +127,16 @@ export async function maybeUploadSiteArtifactsAfterPublish ({
123
127
  )
124
128
  }
125
129
 
130
+ const authToken = requireCmsAuthentication(
131
+ cmsToken,
132
+ 'Site artifact upload'
133
+ )
134
+
126
135
  logInfo({ message: `[@ossy/cli] publish: uploading ${files.length} site artifact file(s) to CMS…` })
127
136
 
128
137
  const presignRes = await fetch(`${apiBaseUrl}/site-artifacts/presign-batch`, {
129
138
  method: 'POST',
130
- headers: workspaceHeaders(cmsToken, workspaceId),
139
+ headers: workspaceHeaders(authToken, workspaceId),
131
140
  body: JSON.stringify({
132
141
  files: files.map((f) => ({
133
142
  path: f.relativePath,
@@ -182,7 +191,7 @@ export async function maybeUploadSiteArtifactsAfterPublish ({
182
191
 
183
192
  const commitRes = await fetch(`${apiBaseUrl}/site-artifacts/commit-batch`, {
184
193
  method: 'POST',
185
- headers: workspaceHeaders(cmsToken, workspaceId),
194
+ headers: workspaceHeaders(authToken, workspaceId),
186
195
  body: JSON.stringify({
187
196
  batchId,
188
197
  paths: files.map((f) => f.relativePath),