@crossdelta/platform-sdk 0.13.0 → 0.13.2
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 +8 -6
- package/package.json +1 -1
- package/bin/cli.js +0 -312
- package/bin/docs/generators/README.md +0 -56
- package/bin/docs/generators/code-style.md +0 -96
- package/bin/docs/generators/hono-bun.md +0 -181
- package/bin/docs/generators/hono-node.md +0 -194
- package/bin/docs/generators/nest.md +0 -358
- package/bin/docs/generators/service.md +0 -564
- package/bin/docs/generators/testing.md +0 -97
- package/bin/integration.collection.json +0 -18
- package/bin/templates/hono-microservice/Dockerfile.hbs +0 -16
- package/bin/templates/hono-microservice/biome.json.hbs +0 -3
- package/bin/templates/hono-microservice/src/index.ts.hbs +0 -18
- package/bin/templates/hono-microservice/tsconfig.json.hbs +0 -14
- package/bin/templates/nest-microservice/Dockerfile.hbs +0 -37
- package/bin/templates/nest-microservice/biome.json.hbs +0 -3
- package/bin/templates/nest-microservice/src/app.context.ts.hbs +0 -17
- package/bin/templates/nest-microservice/src/events/events.module.ts.hbs +0 -8
- package/bin/templates/nest-microservice/src/events/events.service.ts.hbs +0 -22
- package/bin/templates/nest-microservice/src/main.ts.hbs +0 -34
- package/bin/templates/workspace/.github/README.md +0 -70
- package/bin/templates/workspace/.github/actions/check-image-tag-exists/action.yml +0 -27
- package/bin/templates/workspace/.github/actions/check-image-tag-exists/index.js +0 -179
- package/bin/templates/workspace/.github/actions/check-path-changes/action.yml +0 -21
- package/bin/templates/workspace/.github/actions/check-path-changes/index.js +0 -192
- package/bin/templates/workspace/.github/actions/detect-skipped-services/action.yml +0 -38
- package/bin/templates/workspace/.github/actions/generate-scope-matrix/action.yml +0 -17
- package/bin/templates/workspace/.github/actions/generate-scope-matrix/index.js +0 -355
- package/bin/templates/workspace/.github/actions/prepare-build-context/action.yml +0 -49
- package/bin/templates/workspace/.github/actions/resolve-scope-tags/action.yml +0 -31
- package/bin/templates/workspace/.github/actions/resolve-scope-tags/index.js +0 -398
- package/bin/templates/workspace/.github/actions/setup-bun-install/action.yml.hbs +0 -57
- package/bin/templates/workspace/.github/copilot-chat-configuration.json +0 -49
- package/bin/templates/workspace/.github/copilot-instructions.md.hbs +0 -72
- package/bin/templates/workspace/.github/dependabot.yml +0 -18
- package/bin/templates/workspace/.github/workflows/build-and-deploy.yml.hbs +0 -231
- package/bin/templates/workspace/.github/workflows/lint-and-tests.yml.hbs +0 -32
- package/bin/templates/workspace/.github/workflows/publish-packages.yml +0 -155
- package/bin/templates/workspace/apps/.gitkeep +0 -0
- package/bin/templates/workspace/biome.json.hbs +0 -62
- package/bin/templates/workspace/bunfig.toml.hbs +0 -5
- package/bin/templates/workspace/docs/.gitkeep +0 -0
- package/bin/templates/workspace/editorconfig.hbs +0 -9
- package/bin/templates/workspace/gitignore.hbs +0 -15
- package/bin/templates/workspace/infra/Pulumi.dev.yaml.hbs +0 -5
- package/bin/templates/workspace/infra/Pulumi.yaml.hbs +0 -6
- package/bin/templates/workspace/infra/index.ts.hbs +0 -56
- package/bin/templates/workspace/infra/package.json.hbs +0 -21
- package/bin/templates/workspace/infra/services/.gitkeep +0 -0
- package/bin/templates/workspace/infra/tsconfig.json.hbs +0 -15
- package/bin/templates/workspace/npmrc.hbs +0 -2
- package/bin/templates/workspace/package.json.hbs +0 -51
- package/bin/templates/workspace/packages/.gitkeep +0 -0
- package/bin/templates/workspace/packages/contracts/README.md.hbs +0 -166
- package/bin/templates/workspace/packages/contracts/package.json.hbs +0 -22
- package/bin/templates/workspace/packages/contracts/src/events/index.ts +0 -16
- package/bin/templates/workspace/packages/contracts/src/index.ts +0 -10
- package/bin/templates/workspace/packages/contracts/src/stream-policies.ts.hbs +0 -40
- package/bin/templates/workspace/packages/contracts/tsconfig.json.hbs +0 -7
- package/bin/templates/workspace/pnpm-workspace.yaml.hbs +0 -5
- package/bin/templates/workspace/turbo.json +0 -37
- package/bin/templates/workspace/turbo.json.hbs +0 -29
|
@@ -1,398 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Resolve Scope Tags Action
|
|
3
|
-
*
|
|
4
|
-
* Determines the correct Docker image tags for all deployable scopes.
|
|
5
|
-
* Uses change markers from the build job when available, falls back to
|
|
6
|
-
* querying GHCR for the latest tag if a scope wasn't rebuilt.
|
|
7
|
-
*
|
|
8
|
-
* This action bridges the build and deploy jobs by mapping scope short names
|
|
9
|
-
* to their checksum-based image tags.
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* - uses: ./.github/actions/resolve-scope-tags
|
|
13
|
-
* with:
|
|
14
|
-
* markers-dir: .artifacts
|
|
15
|
-
* scope-roots: apps,services
|
|
16
|
-
* github-token: ${{ secrets.GITHUB_TOKEN }}
|
|
17
|
-
*
|
|
18
|
-
* @outputs scope_image_tags - JSON map of shortName -> checksum tag
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
|
-
const { appendFileSync } = require('node:fs')
|
|
22
|
-
const { existsSync, lstatSync, readdirSync, readFileSync, statSync } = require('node:fs')
|
|
23
|
-
const { basename, join, relative } = require('node:path')
|
|
24
|
-
|
|
25
|
-
// Default directories to scan for scopes (Turborepo convention)
|
|
26
|
-
const defaultRoots = ['apps', 'services']
|
|
27
|
-
|
|
28
|
-
// Prefix for change marker artifact directories
|
|
29
|
-
const changedMarkerPrefix = 'changed-'
|
|
30
|
-
|
|
31
|
-
// Maximum pages to check when querying GHCR for existing tags
|
|
32
|
-
const maxPages = Number(process.env.TAG_CHECK_MAX_PAGES ?? '10')
|
|
33
|
-
|
|
34
|
-
// ============================================================================
|
|
35
|
-
// Input/Output Helpers
|
|
36
|
-
// ============================================================================
|
|
37
|
-
|
|
38
|
-
/**
|
|
39
|
-
* Builds environment variable keys for GitHub Actions inputs.
|
|
40
|
-
* @param {string} name - Input name
|
|
41
|
-
* @returns {string[]} Possible environment variable keys
|
|
42
|
-
*/
|
|
43
|
-
const buildInputKeys = (name) => {
|
|
44
|
-
const trimmed = name.trim()
|
|
45
|
-
const upper = trimmed.toUpperCase()
|
|
46
|
-
const normalized = upper.replace(/[^A-Z0-9]+/g, '_')
|
|
47
|
-
return Array.from(new Set([`INPUT_${upper}`, `INPUT_${normalized}`]))
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Retrieves a GitHub Actions input value from environment variables.
|
|
52
|
-
* @param {string} name - Input name as defined in action.yml
|
|
53
|
-
* @param {Object} options - Options
|
|
54
|
-
* @param {boolean} [options.required=false] - Whether the input is required
|
|
55
|
-
* @param {string} [options.defaultValue=''] - Default value if input is not set
|
|
56
|
-
* @returns {string} The input value
|
|
57
|
-
*/
|
|
58
|
-
const getInput = (name, { required = false, defaultValue = '' } = {}) => {
|
|
59
|
-
const keys = buildInputKeys(name)
|
|
60
|
-
const raw = keys.map((key) => process.env[key]).find((value) => typeof value === 'string')
|
|
61
|
-
const value = typeof raw === 'string' ? raw.trim() : defaultValue
|
|
62
|
-
|
|
63
|
-
if (required && !value) {
|
|
64
|
-
console.error(`Input "${name}" is required`)
|
|
65
|
-
process.exit(1)
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return value
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
/**
|
|
72
|
-
* Sets a GitHub Actions output value.
|
|
73
|
-
* @param {string} name - Output name
|
|
74
|
-
* @param {string} value - Output value
|
|
75
|
-
*/
|
|
76
|
-
const setOutput = (name, value) => {
|
|
77
|
-
const outputFile = process.env.GITHUB_OUTPUT
|
|
78
|
-
if (outputFile) {
|
|
79
|
-
appendFileSync(outputFile, `${name}=${value}\n`)
|
|
80
|
-
} else {
|
|
81
|
-
console.log(`${name}=${value}`)
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
/**
|
|
86
|
-
* Parses a comma/space separated list into an array.
|
|
87
|
-
* @param {string} value - Input string
|
|
88
|
-
* @returns {string[]} Array of parsed items
|
|
89
|
-
*/
|
|
90
|
-
const parseList = (value) => {
|
|
91
|
-
if (!value) return []
|
|
92
|
-
|
|
93
|
-
return value
|
|
94
|
-
.split(/[^a-zA-Z0-9._-]+/)
|
|
95
|
-
.map((entry) => entry.trim())
|
|
96
|
-
.filter(Boolean)
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
// ============================================================================
|
|
100
|
-
// Configuration
|
|
101
|
-
// ============================================================================
|
|
102
|
-
|
|
103
|
-
const markersDir = getInput('markers-dir', { defaultValue: '.artifacts' })
|
|
104
|
-
const scopeRoots = parseList(getInput('scope-roots'))
|
|
105
|
-
const deployableScopes = parseList(getInput('deployable-scopes'))
|
|
106
|
-
const allowMissingScopes = new Set(parseList(getInput('allow-missing-scopes')))
|
|
107
|
-
const githubToken = getInput('github-token', { required: true })
|
|
108
|
-
|
|
109
|
-
// Optional inputs - defaults from environment for portability
|
|
110
|
-
const repositoryOwner = getInput('repository-owner', { defaultValue: process.env.GITHUB_REPOSITORY_OWNER })
|
|
111
|
-
const repositoryPrefixInput = getInput('repository-prefix')
|
|
112
|
-
const repositoryPrefix =
|
|
113
|
-
repositoryPrefixInput || process.env.GHCR_REPOSITORY_PREFIX || process.env.GITHUB_REPOSITORY?.split('/')[1] || 'platform'
|
|
114
|
-
|
|
115
|
-
// ============================================================================
|
|
116
|
-
// Scope Discovery
|
|
117
|
-
// ============================================================================
|
|
118
|
-
|
|
119
|
-
const loadRoots = () => (scopeRoots.length > 0 ? scopeRoots : defaultRoots)
|
|
120
|
-
|
|
121
|
-
/**
|
|
122
|
-
* Resolves the scope name from package.json or falls back to directory name.
|
|
123
|
-
* @param {string} entryPath - Path to the scope directory
|
|
124
|
-
* @param {string} fallback - Fallback name (usually directory basename)
|
|
125
|
-
* @returns {string} The resolved scope name
|
|
126
|
-
*/
|
|
127
|
-
const resolveScopeName = (entryPath, fallback) => {
|
|
128
|
-
const pkgPath = join(entryPath, 'package.json')
|
|
129
|
-
|
|
130
|
-
if (!existsSync(pkgPath)) return fallback
|
|
131
|
-
|
|
132
|
-
try {
|
|
133
|
-
const raw = readFileSync(pkgPath, 'utf8')
|
|
134
|
-
const pkg = JSON.parse(raw)
|
|
135
|
-
if (typeof pkg.name === 'string' && pkg.name.trim().length > 0) {
|
|
136
|
-
return pkg.name
|
|
137
|
-
}
|
|
138
|
-
} catch (error) {
|
|
139
|
-
console.error(`Failed to read package.json for ${entryPath}:`, error)
|
|
140
|
-
process.exit(1)
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
return fallback
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
const processScopeEntry = (root, entry, scopes) => {
|
|
147
|
-
const entryPath = join(root, entry)
|
|
148
|
-
|
|
149
|
-
const isDirectory = statSync(entryPath).isDirectory()
|
|
150
|
-
if (!isDirectory) return
|
|
151
|
-
|
|
152
|
-
const dockerfilePath = join(entryPath, 'Dockerfile')
|
|
153
|
-
const hasDockerfile = existsSync(dockerfilePath)
|
|
154
|
-
if (!hasDockerfile) return
|
|
155
|
-
|
|
156
|
-
const scopeName = resolveScopeName(entryPath, entry)
|
|
157
|
-
if (scopes.has(scopeName)) return
|
|
158
|
-
|
|
159
|
-
const relativeDir = relative(process.cwd(), entryPath).replace(/\\/g, '/')
|
|
160
|
-
scopes.set(scopeName, {
|
|
161
|
-
name: scopeName,
|
|
162
|
-
dir: relativeDir,
|
|
163
|
-
shortName: basename(relativeDir),
|
|
164
|
-
})
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
const discoverScopes = (roots) => {
|
|
168
|
-
const scopes = new Map()
|
|
169
|
-
|
|
170
|
-
for (const root of roots) {
|
|
171
|
-
let entries = []
|
|
172
|
-
try {
|
|
173
|
-
entries = readdirSync(root)
|
|
174
|
-
} catch (error) {
|
|
175
|
-
if (error.code === 'ENOENT') {
|
|
176
|
-
continue
|
|
177
|
-
}
|
|
178
|
-
throw error
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
for (const entry of entries) {
|
|
182
|
-
processScopeEntry(root, entry, scopes)
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
return scopes
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
const loadMetadata = () => {
|
|
190
|
-
const metadataByScope = new Map()
|
|
191
|
-
if (!existsSync(markersDir)) {
|
|
192
|
-
return metadataByScope
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
const markers = readdirSync(markersDir).filter((name) => name.startsWith(changedMarkerPrefix))
|
|
196
|
-
|
|
197
|
-
for (const marker of markers) {
|
|
198
|
-
const markerPath = join(markersDir, marker)
|
|
199
|
-
if (!existsSync(markerPath)) continue
|
|
200
|
-
|
|
201
|
-
if (!lstatSync(markerPath).isDirectory()) continue
|
|
202
|
-
const metadataPath = join(markerPath, 'metadata.json')
|
|
203
|
-
if (!existsSync(metadataPath) || !lstatSync(metadataPath).isFile()) continue
|
|
204
|
-
|
|
205
|
-
try {
|
|
206
|
-
const raw = readFileSync(metadataPath, 'utf8')
|
|
207
|
-
const parsed = JSON.parse(raw)
|
|
208
|
-
if (
|
|
209
|
-
parsed &&
|
|
210
|
-
typeof parsed.shortName === 'string' &&
|
|
211
|
-
parsed.shortName.trim() &&
|
|
212
|
-
typeof parsed.imageTag === 'string' &&
|
|
213
|
-
parsed.imageTag.trim()
|
|
214
|
-
) {
|
|
215
|
-
metadataByScope.set(parsed.shortName.trim(), parsed.imageTag.trim())
|
|
216
|
-
}
|
|
217
|
-
} catch (error) {
|
|
218
|
-
console.warn(`Unable to parse metadata at ${metadataPath}:`, error)
|
|
219
|
-
}
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
return metadataByScope
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
const headers = {
|
|
226
|
-
Authorization: `Bearer ${githubToken}`,
|
|
227
|
-
Accept: 'application/vnd.github+json',
|
|
228
|
-
'X-GitHub-Api-Version': '2022-11-28',
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
const repositoryOwnerTypeInput = getInput('repository-owner-type').toLowerCase()
|
|
232
|
-
const ownerTypeCandidates = (() => {
|
|
233
|
-
if (repositoryOwnerTypeInput === 'user' || repositoryOwnerTypeInput === 'users') {
|
|
234
|
-
return ['users']
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
if (
|
|
238
|
-
repositoryOwnerTypeInput === 'org' ||
|
|
239
|
-
repositoryOwnerTypeInput === 'orgs' ||
|
|
240
|
-
repositoryOwnerTypeInput === 'organization'
|
|
241
|
-
) {
|
|
242
|
-
return ['orgs']
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
return ['orgs', 'users']
|
|
246
|
-
})()
|
|
247
|
-
|
|
248
|
-
const requestPackageVersions = async (encodedPackage, page, ownerType) => {
|
|
249
|
-
const ownerSegment = ownerType === 'users' ? 'users' : 'orgs'
|
|
250
|
-
const url = `https://api.github.com/${ownerSegment}/${repositoryOwner}/packages/container/${encodedPackage}/versions?per_page=100&page=${page}`
|
|
251
|
-
const response = await fetch(url, { headers })
|
|
252
|
-
|
|
253
|
-
if (response.status === 404) {
|
|
254
|
-
return { notFound: true, versions: [], hasMore: false }
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
if (!response.ok) {
|
|
258
|
-
const text = await response.text()
|
|
259
|
-
throw new Error(`GitHub API error (${response.status}): ${text}`)
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
const data = await response.json()
|
|
263
|
-
const versions = Array.isArray(data) ? data : []
|
|
264
|
-
|
|
265
|
-
return {
|
|
266
|
-
versions,
|
|
267
|
-
hasMore: versions.length === 100,
|
|
268
|
-
notFound: false,
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
const pickChecksumTag = (versions) => {
|
|
273
|
-
for (const version of versions) {
|
|
274
|
-
const tags = version?.metadata?.container?.tags
|
|
275
|
-
if (!Array.isArray(tags)) continue
|
|
276
|
-
|
|
277
|
-
// Prefer checksum tag (64 hex chars), fallback to 'latest'
|
|
278
|
-
const checksumTag = tags.find((tag) => typeof tag === 'string' && /^[0-9a-f]{64}$/.test(tag))
|
|
279
|
-
if (checksumTag) {
|
|
280
|
-
return checksumTag
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
// If no checksum tag found, look for 'latest' tag
|
|
285
|
-
for (const version of versions) {
|
|
286
|
-
const tags = version?.metadata?.container?.tags
|
|
287
|
-
if (!Array.isArray(tags)) continue
|
|
288
|
-
|
|
289
|
-
if (tags.includes('latest')) {
|
|
290
|
-
return 'latest'
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
return null
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
const fetchLatestChecksumTag = async (packageName) => {
|
|
298
|
-
const encodedPackage = encodeURIComponent(packageName)
|
|
299
|
-
|
|
300
|
-
for (const ownerType of ownerTypeCandidates) {
|
|
301
|
-
let ownerMissing = false
|
|
302
|
-
|
|
303
|
-
for (let page = 1; page <= maxPages; page += 1) {
|
|
304
|
-
const { versions, hasMore, notFound } = await requestPackageVersions(encodedPackage, page, ownerType)
|
|
305
|
-
if (notFound) {
|
|
306
|
-
ownerMissing = true
|
|
307
|
-
break
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
const checksumTag = pickChecksumTag(versions)
|
|
311
|
-
if (checksumTag) {
|
|
312
|
-
return checksumTag
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
if (!hasMore) {
|
|
316
|
-
break
|
|
317
|
-
}
|
|
318
|
-
}
|
|
319
|
-
|
|
320
|
-
if (ownerMissing) {
|
|
321
|
-
console.warn(
|
|
322
|
-
`Container package ${packageName} not found under ${ownerType === 'users' ? 'user' : 'organization'} ${repositoryOwner}. Trying alternate scope.`,
|
|
323
|
-
)
|
|
324
|
-
continue
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
break
|
|
328
|
-
}
|
|
329
|
-
|
|
330
|
-
return null
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
const main = async () => {
|
|
334
|
-
const roots = loadRoots()
|
|
335
|
-
const discoveredScopes = Array.from(discoverScopes(roots).values())
|
|
336
|
-
const scopes = (() => {
|
|
337
|
-
if (deployableScopes.length === 0) {
|
|
338
|
-
return discoveredScopes
|
|
339
|
-
}
|
|
340
|
-
|
|
341
|
-
const scopeMap = new Map(discoveredScopes.map((scope) => [scope.shortName, scope]))
|
|
342
|
-
const selected = []
|
|
343
|
-
|
|
344
|
-
for (const name of deployableScopes) {
|
|
345
|
-
const match = scopeMap.get(name)
|
|
346
|
-
if (match) {
|
|
347
|
-
selected.push(match)
|
|
348
|
-
} else {
|
|
349
|
-
console.warn(`DEPLOYABLE_SCOPES entry "${name}" was not found among discovered scopes`)
|
|
350
|
-
}
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
return selected
|
|
354
|
-
})()
|
|
355
|
-
|
|
356
|
-
const metadataByScope = loadMetadata()
|
|
357
|
-
|
|
358
|
-
if (scopes.length === 0) {
|
|
359
|
-
console.warn('No scopes discovered; emitting empty scope_image_tags output')
|
|
360
|
-
setOutput('scope_image_tags', '{}')
|
|
361
|
-
return
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
const result = {}
|
|
365
|
-
const missing = []
|
|
366
|
-
|
|
367
|
-
// Get registry URL from environment (e.g., "ghcr.io/orderboss/platform")
|
|
368
|
-
const registry = process.env.REGISTRY || `ghcr.io/${repositoryOwner}/${repositoryPrefix}`
|
|
369
|
-
|
|
370
|
-
for (const scope of scopes) {
|
|
371
|
-
const metadataTag = metadataByScope.get(scope.shortName)
|
|
372
|
-
const imageTag = metadataTag || (await fetchLatestChecksumTag(`${repositoryPrefix}/${scope.shortName}`))
|
|
373
|
-
|
|
374
|
-
if (!imageTag) {
|
|
375
|
-
if (allowMissingScopes.has(scope.shortName)) {
|
|
376
|
-
console.warn(`Skipping unresolved scope ${scope.shortName} (allowed missing)`)
|
|
377
|
-
continue
|
|
378
|
-
}
|
|
379
|
-
missing.push(scope.shortName)
|
|
380
|
-
continue
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
// Build full image URL: registry/service:tag
|
|
384
|
-
result[scope.shortName] = `${registry}/${scope.shortName}:${imageTag}`
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
if (missing.length > 0) {
|
|
388
|
-
console.error('Unable to resolve image tags for scopes:', missing.join(', '))
|
|
389
|
-
process.exit(1)
|
|
390
|
-
}
|
|
391
|
-
|
|
392
|
-
setOutput('scope_image_tags', JSON.stringify(result))
|
|
393
|
-
}
|
|
394
|
-
|
|
395
|
-
main().catch((error) => {
|
|
396
|
-
console.error('resolve-scope-tags action failed:', error)
|
|
397
|
-
process.exit(1)
|
|
398
|
-
})
|
|
@@ -1,57 +0,0 @@
|
|
|
1
|
-
name: Setup Bun and install dependencies
|
|
2
|
-
description: Installs Bun, optionally restores cache, and runs Bun install commands.
|
|
3
|
-
author: {{githubOwner}}
|
|
4
|
-
|
|
5
|
-
inputs:
|
|
6
|
-
working-directory:
|
|
7
|
-
description: Directory to run the install command in.
|
|
8
|
-
default: .
|
|
9
|
-
install-command:
|
|
10
|
-
description: Command to install dependencies.
|
|
11
|
-
default: bun install --frozen-lockfile
|
|
12
|
-
enable-cache:
|
|
13
|
-
description: Whether to run the cache step.
|
|
14
|
-
default: 'false'
|
|
15
|
-
cache-key:
|
|
16
|
-
description: Cache key to use when caching is enabled.
|
|
17
|
-
default: ''
|
|
18
|
-
cache-restore-keys:
|
|
19
|
-
description: Restore keys for the cache step.
|
|
20
|
-
default: ''
|
|
21
|
-
cache-paths:
|
|
22
|
-
description: Newline-delimited paths to include in the cache.
|
|
23
|
-
default: |
|
|
24
|
-
~/.bun
|
|
25
|
-
node_modules
|
|
26
|
-
bun-version-file:
|
|
27
|
-
description: Path to a file containing the Bun version (passed to setup-bun).
|
|
28
|
-
default: ''
|
|
29
|
-
npm-token:
|
|
30
|
-
description: NPM token for private registry access.
|
|
31
|
-
required: false
|
|
32
|
-
default: ''
|
|
33
|
-
|
|
34
|
-
runs:
|
|
35
|
-
using: composite
|
|
36
|
-
steps:
|
|
37
|
-
- name: Install Bun
|
|
38
|
-
uses: oven-sh/setup-bun@v2
|
|
39
|
-
with:
|
|
40
|
-
bun-version-file: $\{{ inputs.bun-version-file }}
|
|
41
|
-
|
|
42
|
-
- name: Cache dependencies
|
|
43
|
-
if: $\{{ inputs.enable-cache == 'true' && inputs.cache-key != '' }}
|
|
44
|
-
uses: actions/cache@v4
|
|
45
|
-
with:
|
|
46
|
-
path: $\{{ inputs.cache-paths }}
|
|
47
|
-
key: $\{{ inputs.cache-key }}
|
|
48
|
-
restore-keys: $\{{ inputs.cache-restore-keys }}
|
|
49
|
-
|
|
50
|
-
- name: Install dependencies
|
|
51
|
-
shell: bash
|
|
52
|
-
env:
|
|
53
|
-
NPM_TOKEN: $\{{ inputs.npm-token }}
|
|
54
|
-
run: |
|
|
55
|
-
set -euo pipefail
|
|
56
|
-
cd "$\{{ inputs.working-directory }}"
|
|
57
|
-
$\{{ inputs.install-command }}
|
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 1,
|
|
3
|
-
"scope": {
|
|
4
|
-
"include": [
|
|
5
|
-
"apps",
|
|
6
|
-
"services",
|
|
7
|
-
"packages",
|
|
8
|
-
"infra"
|
|
9
|
-
],
|
|
10
|
-
"exclude": [
|
|
11
|
-
"node_modules",
|
|
12
|
-
"dist",
|
|
13
|
-
"build",
|
|
14
|
-
"coverage",
|
|
15
|
-
".turbo",
|
|
16
|
-
".github"
|
|
17
|
-
]
|
|
18
|
-
},
|
|
19
|
-
"guidelines": [
|
|
20
|
-
"This repository is a Bun-based monorepo orchestrated with Turborepo. Prefer using existing scripts from package.json instead of introducing custom build steps.",
|
|
21
|
-
"For backend services, use Hono or NestJS depending on the service. Do not introduce new server frameworks such as Express or Fastify.",
|
|
22
|
-
"Payload validation must be implemented using Zod. Types should be inferred from Zod schemas (z.infer) rather than manually defined or using any.",
|
|
23
|
-
"Infrastructure is defined via Pulumi inside the infra/ folder. New services must follow existing ServiceConfig patterns inside infra/services/*.ts.",
|
|
24
|
-
"Code style follows the Biome configuration (biome.json): single quotes, no semicolons, 2-space indentation, and the configured max line width.",
|
|
25
|
-
"Commit messages must follow Conventional Commits (feat:, fix:, chore:, docs:) to support release automation.",
|
|
26
|
-
"Before introducing new patterns, inspect existing examples in the same folder or similar services/packages and follow them closely.",
|
|
27
|
-
"Code should be elegant, readable, and intentionally simple. Prefer clarity over clever constructs.",
|
|
28
|
-
"Comments, inline documentation, and JSDoc must be written in English.",
|
|
29
|
-
"Avoid overly complex or deeply nested functions. Keep functions small, focused, and with a single responsibility.",
|
|
30
|
-
"Avoid unnecessary higher-order abstractions or generic-heavy over-engineering. Only introduce abstractions when they simplify the implementation."
|
|
31
|
-
],
|
|
32
|
-
"quickPrompts": [
|
|
33
|
-
{
|
|
34
|
-
"name": "Scaffold New Hono Microservice",
|
|
35
|
-
"description": "Create a new Hono-based microservice using the platform-sdk.",
|
|
36
|
-
"prompt": "Use the platform-sdk to scaffold a new Hono microservice. Run `bun pf new hono-micro services/<service-name>` and ensure the generated structure follows the existing pattern: telemetry as the first import, a health endpoint, proper port resolution from environment variables, and a matching infra config under infra/services/<service-name>.ts."
|
|
37
|
-
},
|
|
38
|
-
{
|
|
39
|
-
"name": "Scaffold New NestJS Microservice",
|
|
40
|
-
"description": "Generate a NestJS microservice using platform-sdk conventions.",
|
|
41
|
-
"prompt": "Use `bun pf new nest-micro services/<service-name>` to generate a new NestJS microservice. Follow the established folder structure, ensure telemetry is loaded first, and apply the project's conventions. Rely on the generated template instead of inventing a custom layout."
|
|
42
|
-
},
|
|
43
|
-
{
|
|
44
|
-
"name": "Create Service Infra Config",
|
|
45
|
-
"description": "Generate a Pulumi ServiceConfig using existing infra patterns.",
|
|
46
|
-
"prompt": "Create a new ServiceConfig file inside infra/services based on existing configurations. Include name, containerPort, healthCheck, env variables, and required secrets. Keep the configuration clean and document special aspects in English."
|
|
47
|
-
}
|
|
48
|
-
]
|
|
49
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
# Copilot Instructions (Project-Agnostic, TypeScript-Focused)
|
|
2
|
-
|
|
3
|
-
You are assisting in TypeScript-first monorepos using modern TypeScript tooling such as Bun or Node, Turborepo, Hono, NestJS, Zod, event-driven patterns, and Pulumi/Kubernetes infrastructure.
|
|
4
|
-
|
|
5
|
-
Always generate **minimal diffs**, never full rewrites.
|
|
6
|
-
|
|
7
|
-
## General Behavior
|
|
8
|
-
- Produce short, focused, code-first answers.
|
|
9
|
-
- Modify only the necessary parts.
|
|
10
|
-
- Analyze only the opened file unless explicitly asked.
|
|
11
|
-
- Follow existing architecture and naming conventions.
|
|
12
|
-
- Prefer strict typing; avoid `any`.
|
|
13
|
-
- **Reuse existing code**: Before implementing new functionality, search the codebase for existing functions, utilities, or services that can be reused. Avoid duplicating logic.
|
|
14
|
-
|
|
15
|
-
## Code Style
|
|
16
|
-
- Single quotes, no semicolons, 2-space indent, trailing commas.
|
|
17
|
-
- Alphabetically sorted imports; no unused imports.
|
|
18
|
-
- Arrow functions over `function` declarations.
|
|
19
|
-
- Template literals over string concatenation.
|
|
20
|
-
- Prefer pure and functional programming patterns (map/filter/reduce).
|
|
21
|
-
- Avoid mutable state and imperative loops.
|
|
22
|
-
- No decorative section header blocks (no ASCII art separators like `// ─────────────`).
|
|
23
|
-
- Use blank lines to separate logical sections naturally.
|
|
24
|
-
- Organize code: types → helpers → higher-order functions.
|
|
25
|
-
- Use JSDoc for exported functions and complex logic; keep inline comments minimal.
|
|
26
|
-
- Self-documenting code over comments: clear naming, pure functions, obvious control flow.
|
|
27
|
-
|
|
28
|
-
## Validation & Types
|
|
29
|
-
- Use Zod for schemas.
|
|
30
|
-
- Export inferred types using `z.infer`.
|
|
31
|
-
- Do NOT include literal event types inside schemas.
|
|
32
|
-
- Do not duplicate validation logic across layers.
|
|
33
|
-
|
|
34
|
-
## Service & Module Structure
|
|
35
|
-
- Entry points handle wiring (server start, telemetry, routing, consumers).
|
|
36
|
-
- Business logic lives in use-case modules.
|
|
37
|
-
- Event handlers must be thin wrappers around use-cases.
|
|
38
|
-
|
|
39
|
-
## Event Handling
|
|
40
|
-
- Use shared CloudEvents/messaging libraries, not raw clients.
|
|
41
|
-
- Handlers: Schema → inferred type → thin handler → use-case delegation.
|
|
42
|
-
- Handlers named `*.event.ts`.
|
|
43
|
-
|
|
44
|
-
## Infrastructure
|
|
45
|
-
- Use fluent port builders when available.
|
|
46
|
-
- Expose a `/health` endpoint.
|
|
47
|
-
- Avoid legacy containerPort usage.
|
|
48
|
-
|
|
49
|
-
## Testing
|
|
50
|
-
- Test use-cases, not handlers or frameworks.
|
|
51
|
-
- Prefer simple direct tests, no mocks.
|
|
52
|
-
- Validate both error and success paths.
|
|
53
|
-
|
|
54
|
-
## AI Output Format
|
|
55
|
-
- Provide only necessary files.
|
|
56
|
-
- Use relative paths.
|
|
57
|
-
- Keep comments minimal.
|
|
58
|
-
- Do not generate abstractions not already present.
|
|
59
|
-
|
|
60
|
-
## Service-Specific Guidelines
|
|
61
|
-
|
|
62
|
-
When working with specific service types or packages, refer to these detailed guidelines:
|
|
63
|
-
|
|
64
|
-
### Service Generation & Architecture
|
|
65
|
-
- [CLI Service Generator](../packages/platform-sdk/docs/generators/service.md) - AI code generation rules
|
|
66
|
-
|
|
67
|
-
### Key Packages
|
|
68
|
-
- [@crossdelta/cloudevents](../packages/cloudevents/README.md) - Event handling with NATS and CloudEvents
|
|
69
|
-
- [@crossdelta/telemetry](../packages/telemetry/README.md) - OpenTelemetry instrumentation
|
|
70
|
-
- [@crossdelta/infrastructure](../packages/infrastructure/README.md) - Pulumi/K8s configuration
|
|
71
|
-
|
|
72
|
-
**When generating services**: Always check the appropriate guidelines above to ensure correct patterns, especially for event-driven architectures.
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
version: 2
|
|
2
|
-
registries:
|
|
3
|
-
npm-github:
|
|
4
|
-
type: npm-registry
|
|
5
|
-
url: https://npm.pkg.github.com
|
|
6
|
-
token: ${{secrets.NPM_TOKEN}}
|
|
7
|
-
replaces-base: true
|
|
8
|
-
|
|
9
|
-
updates:
|
|
10
|
-
- package-ecosystem: "bun"
|
|
11
|
-
directory: "/"
|
|
12
|
-
schedule:
|
|
13
|
-
interval: "daily"
|
|
14
|
-
open-pull-requests-limit: 10
|
|
15
|
-
labels:
|
|
16
|
-
- "dependencies"
|
|
17
|
-
registries:
|
|
18
|
-
- npm-github
|