@ossy/cli 1.16.11 → 1.17.3
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 +97 -53
- package/package.json +4 -4
- package/src/{cms → app}/cli.js +10 -4
- package/src/{publish/load-website-config.js → app/load-config.js} +6 -20
- package/src/app/publish.js +206 -0
- package/src/{cms → app}/upload-resource-templates.js +1 -1
- package/src/call/cli.js +2 -18
- package/src/file.js +26 -0
- package/src/index.js +35 -56
- package/src/registry/ecr-push-credentials.js +2 -2
- package/src/upload-dir/cli.js +270 -0
- package/src/publish/cli.js +0 -181
- package/src/publish/resolve-config.js +0 -44
- package/src/publish/resource-templates-after-publish.js +0 -75
- package/src/publish/site-artifacts-after-publish.js +0 -183
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import { logInfo } from '../log.js'
|
|
2
|
-
import { readPublishFieldsFromWebsiteConfig } from './load-website-config.js'
|
|
3
|
-
import {
|
|
4
|
-
postResourceTemplates,
|
|
5
|
-
requireCmsAuthentication,
|
|
6
|
-
resolveApiBaseUrlForUpload,
|
|
7
|
-
} from '../cms/upload-resource-templates.js'
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* After a successful deployment, sync `resourceTemplates` from app config to the workspace API.
|
|
11
|
-
*/
|
|
12
|
-
export async function maybeUploadResourceTemplatesAfterPublish ({
|
|
13
|
-
configPath,
|
|
14
|
-
cmsToken,
|
|
15
|
-
apiUrlFlag,
|
|
16
|
-
}) {
|
|
17
|
-
const config = readPublishFieldsFromWebsiteConfig(configPath)
|
|
18
|
-
const workspaceId = config.workspaceId
|
|
19
|
-
const resourceTemplates = config.resourceTemplates
|
|
20
|
-
|
|
21
|
-
if (!workspaceId) {
|
|
22
|
-
logInfo({
|
|
23
|
-
message:
|
|
24
|
-
'[@ossy/cli] publish: skipping resource template upload (no workspaceId in config)',
|
|
25
|
-
})
|
|
26
|
-
return
|
|
27
|
-
}
|
|
28
|
-
if (!Array.isArray(resourceTemplates) || resourceTemplates.length === 0) {
|
|
29
|
-
logInfo({
|
|
30
|
-
message:
|
|
31
|
-
'[@ossy/cli] publish: skipping resource template upload (resourceTemplates missing or empty)',
|
|
32
|
-
})
|
|
33
|
-
return
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const apiBaseUrl = resolveApiBaseUrlForUpload({
|
|
37
|
-
flag: apiUrlFlag,
|
|
38
|
-
envVar: process.env.OSSY_API_URL,
|
|
39
|
-
configApiUrl: config?.apiUrl,
|
|
40
|
-
})
|
|
41
|
-
const uploadUrl = `${apiBaseUrl.replace(/\/$/, '')}/resource-templates`
|
|
42
|
-
const authToken = requireCmsAuthentication(
|
|
43
|
-
cmsToken,
|
|
44
|
-
'Resource template upload'
|
|
45
|
-
)
|
|
46
|
-
|
|
47
|
-
logInfo({
|
|
48
|
-
message: `[@ossy/cli] publish: uploading resource templates → POST ${uploadUrl}`,
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
const response = await postResourceTemplates({
|
|
52
|
-
apiBaseUrl,
|
|
53
|
-
token: authToken,
|
|
54
|
-
workspaceId,
|
|
55
|
-
resourceTemplates,
|
|
56
|
-
})
|
|
57
|
-
|
|
58
|
-
if (!response.ok) {
|
|
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 --authentication (or OSSY_API_KEY in CI), not a non-API deploy token.'
|
|
67
|
-
: ''
|
|
68
|
-
throw new Error(
|
|
69
|
-
`Resource template upload failed: HTTP ${response.status}${hint404}${hint401}${
|
|
70
|
-
text ? ` — ${text.slice(0, 200)}` : ''
|
|
71
|
-
}`
|
|
72
|
-
)
|
|
73
|
-
}
|
|
74
|
-
logInfo({ message: '[@ossy/cli] publish: resource templates uploaded' })
|
|
75
|
-
}
|
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
import { readdir, stat, readFile } from 'fs/promises'
|
|
2
|
-
import { existsSync } from 'fs'
|
|
3
|
-
import path from 'path'
|
|
4
|
-
import { logInfo } from '../log.js'
|
|
5
|
-
import { readPublishFieldsFromWebsiteConfig } from './load-website-config.js'
|
|
6
|
-
import {
|
|
7
|
-
normalizeAuthorizationToken,
|
|
8
|
-
requireCmsAuthentication,
|
|
9
|
-
resolveApiBaseUrlForUpload,
|
|
10
|
-
} from '../cms/upload-resource-templates.js'
|
|
11
|
-
import { guessContentType } from '../file.js'
|
|
12
|
-
|
|
13
|
-
const MAX_FILES = 200
|
|
14
|
-
|
|
15
|
-
export { guessContentType }
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* @param {string} buildDir absolute path to `build/` output
|
|
19
|
-
* @returns {Promise<{ absPath: string, relativePath: string, size: number }[]>}
|
|
20
|
-
*/
|
|
21
|
-
export async function collectBuildFiles (buildDir) {
|
|
22
|
-
const out = []
|
|
23
|
-
|
|
24
|
-
async function walk (dir, rel = '') {
|
|
25
|
-
const entries = await readdir(dir, { withFileTypes: true })
|
|
26
|
-
for (const e of entries) {
|
|
27
|
-
const abs = path.join(dir, e.name)
|
|
28
|
-
const relPath = rel ? `${rel}/${e.name}` : e.name
|
|
29
|
-
if (e.isDirectory()) {
|
|
30
|
-
await walk(abs, relPath)
|
|
31
|
-
} else {
|
|
32
|
-
const st = await stat(abs)
|
|
33
|
-
out.push({
|
|
34
|
-
absPath: abs,
|
|
35
|
-
relativePath: relPath.split(path.sep).join('/'),
|
|
36
|
-
size: st.size,
|
|
37
|
-
})
|
|
38
|
-
}
|
|
39
|
-
}
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
await walk(buildDir)
|
|
43
|
-
return out
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function workspaceHeaders (token, workspaceId) {
|
|
47
|
-
return {
|
|
48
|
-
Authorization: normalizeAuthorizationToken(token),
|
|
49
|
-
'Content-Type': 'application/json',
|
|
50
|
-
workspaceId,
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/**
|
|
55
|
-
* After deploy: upload `build/` to S3 via presigned URLs and commit a CMS resource (`@ossy/platform/site-artifact-batch`).
|
|
56
|
-
*/
|
|
57
|
-
export async function maybeUploadSiteArtifactsAfterPublish ({
|
|
58
|
-
configPath,
|
|
59
|
-
cmsToken,
|
|
60
|
-
apiUrlFlag,
|
|
61
|
-
buildDir: buildDirOpt,
|
|
62
|
-
}) {
|
|
63
|
-
const config = readPublishFieldsFromWebsiteConfig(configPath)
|
|
64
|
-
const workspaceId = config.workspaceId
|
|
65
|
-
|
|
66
|
-
if (!workspaceId) {
|
|
67
|
-
logInfo({
|
|
68
|
-
message:
|
|
69
|
-
'[@ossy/cli] publish: skipping site artifacts (no workspaceId in config)',
|
|
70
|
-
})
|
|
71
|
-
return
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
const packageRoot = path.join(path.dirname(configPath), '..')
|
|
75
|
-
const buildDir = buildDirOpt
|
|
76
|
-
? path.resolve(buildDirOpt)
|
|
77
|
-
: path.join(packageRoot, 'build')
|
|
78
|
-
|
|
79
|
-
if (!existsSync(buildDir)) {
|
|
80
|
-
logInfo({
|
|
81
|
-
message: `[@ossy/cli] publish: skipping site artifacts (no build at ${buildDir}; run npm run build first)`,
|
|
82
|
-
})
|
|
83
|
-
return
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
const apiBaseUrl = resolveApiBaseUrlForUpload({
|
|
87
|
-
flag: apiUrlFlag,
|
|
88
|
-
envVar: process.env.OSSY_API_URL,
|
|
89
|
-
configApiUrl: config.apiUrl,
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
const files = await collectBuildFiles(buildDir)
|
|
93
|
-
if (files.length === 0) {
|
|
94
|
-
logInfo({ message: '[@ossy/cli] publish: skipping site artifacts (build directory is empty)' })
|
|
95
|
-
return
|
|
96
|
-
}
|
|
97
|
-
if (files.length > MAX_FILES) {
|
|
98
|
-
throw new Error(
|
|
99
|
-
`[@ossy/cli] publish: site artifact upload supports at most ${MAX_FILES} files; build has ${files.length}`
|
|
100
|
-
)
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const authToken = requireCmsAuthentication(
|
|
104
|
-
cmsToken,
|
|
105
|
-
'Site artifact upload'
|
|
106
|
-
)
|
|
107
|
-
|
|
108
|
-
logInfo({ message: `[@ossy/cli] publish: uploading ${files.length} site artifact file(s) to CMS…` })
|
|
109
|
-
|
|
110
|
-
const presignRes = await fetch(`${apiBaseUrl}/site-artifacts/presign-batch`, {
|
|
111
|
-
method: 'POST',
|
|
112
|
-
headers: workspaceHeaders(authToken, workspaceId),
|
|
113
|
-
body: JSON.stringify({
|
|
114
|
-
files: files.map((f) => ({
|
|
115
|
-
path: f.relativePath,
|
|
116
|
-
contentType: guessContentType(f.relativePath),
|
|
117
|
-
contentLength: f.size,
|
|
118
|
-
})),
|
|
119
|
-
}),
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
if (!presignRes.ok) {
|
|
123
|
-
const text = await presignRes.text().catch(() => '')
|
|
124
|
-
throw new Error(
|
|
125
|
-
`Site artifact presign failed: HTTP ${presignRes.status}${text ? ` — ${text.slice(0, 300)}` : ''}`
|
|
126
|
-
)
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/** @type {{ batchId: string, files: { path: string, key: string, uploadUrl: string, contentType: string, contentLength: number }[] }} */
|
|
130
|
-
const presignJson = await presignRes.json()
|
|
131
|
-
const { batchId, files: uploadSlots } = presignJson
|
|
132
|
-
|
|
133
|
-
if (!batchId || !Array.isArray(uploadSlots) || uploadSlots.length !== files.length) {
|
|
134
|
-
throw new Error('[@ossy/cli] publish: invalid presign-batch response')
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const byPath = new Map(uploadSlots.map((s) => [s.path, s]))
|
|
138
|
-
|
|
139
|
-
for (const local of files) {
|
|
140
|
-
const slot = byPath.get(local.relativePath)
|
|
141
|
-
if (!slot?.uploadUrl) {
|
|
142
|
-
throw new Error(`[@ossy/cli] publish: presign response missing entry for ${local.relativePath}`)
|
|
143
|
-
}
|
|
144
|
-
const body = await readFile(local.absPath)
|
|
145
|
-
if (body.length !== local.size) {
|
|
146
|
-
throw new Error(`[@ossy/cli] publish: file size mismatch for ${local.relativePath}`)
|
|
147
|
-
}
|
|
148
|
-
const putRes = await fetch(slot.uploadUrl, {
|
|
149
|
-
method: 'PUT',
|
|
150
|
-
headers: {
|
|
151
|
-
'Content-Type': slot.contentType,
|
|
152
|
-
'Content-Length': String(slot.contentLength),
|
|
153
|
-
},
|
|
154
|
-
body,
|
|
155
|
-
})
|
|
156
|
-
if (!putRes.ok) {
|
|
157
|
-
const t = await putRes.text().catch(() => '')
|
|
158
|
-
throw new Error(
|
|
159
|
-
`S3 PUT failed for ${local.relativePath}: HTTP ${putRes.status}${t ? ` — ${t.slice(0, 200)}` : ''}`
|
|
160
|
-
)
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
const commitRes = await fetch(`${apiBaseUrl}/site-artifacts/commit-batch`, {
|
|
166
|
-
method: 'POST',
|
|
167
|
-
headers: workspaceHeaders(authToken, workspaceId),
|
|
168
|
-
body: JSON.stringify({
|
|
169
|
-
batchId,
|
|
170
|
-
paths: files.map((f) => f.relativePath),
|
|
171
|
-
name: batchId,
|
|
172
|
-
}),
|
|
173
|
-
})
|
|
174
|
-
|
|
175
|
-
if (!commitRes.ok) {
|
|
176
|
-
const text = await commitRes.text().catch(() => '')
|
|
177
|
-
throw new Error(
|
|
178
|
-
`Site artifact commit failed: HTTP ${commitRes.status}${text ? ` — ${text.slice(0, 300)}` : ''}`
|
|
179
|
-
)
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
logInfo({ message: '[@ossy/cli] publish: site artifacts uploaded and CMS resource created' })
|
|
183
|
-
}
|