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