Skip to content

Commit bb3228a

Browse files
committed
chore(scaffolding): align socket-cli with fleet scaffolding contract
Closes the per-repo gaps surfaced by sync-scaffolding: - Wire `check:paths` script. The runner `scripts/check-paths.mts` already existed; just missing from package.json. - Rename `scripts/test-monorepo.mts` → `scripts/test.mts`. The fleet expects a canonical `scripts/test.mts` location (per the manifest comment: "the *file* exists so package.json:scripts.test can delegate; the body's content varies"). Two refs in package.json updated; no other call sites. - Add `scripts/xport` + `xport:emit-schema` wiring (script files already existed) and an empty `xport.json` lock-step manifest — socket-cli is a CLI distribution with no submodule upstreams or sibling language ports, so empty rows is the expected state. - Copy `.github/dependabot.yml` from the template. - Copy consumer-kind release-checksum scaffolding from the template (`packages/build-infra/lib/release-checksums/{core,consumer}.mts` + `packages/build-infra/release-assets.schema.json`). socket-cli's `.socket-repo-template.json` declares `kind: "consumer"`; the kind expects this scaffolding present.
1 parent 113967f commit bb3228a

7 files changed

Lines changed: 486 additions & 2 deletions

File tree

.github/dependabot.yml

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Dependabot disabled - we manage dependencies manually
2+
# Using open-pull-requests-limit: 0 to disable version updates
3+
# See: https://docs.github.com/en/code-security/supply-chain-security/keeping-your-dependencies-updated-automatically/configuration-options-for-dependency-updates
4+
version: 2
5+
updates:
6+
- package-ecosystem: npm
7+
directory: /
8+
schedule:
9+
interval: yearly
10+
open-pull-requests-limit: 0
11+
cooldown:
12+
default-days: 7

package.json

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
"// Quality Checks": "",
2121
"check": "node scripts/check.mts",
2222
"check:all": "node scripts/check.mts --all",
23+
"check:paths": "node scripts/check-paths.mts",
2324
"fix": "node scripts/fix.mts",
2425
"fix:all": "node scripts/fix.mts --all",
2526
"lint": "node scripts/lint.mts",
@@ -32,8 +33,8 @@
3233
"type": "node scripts/type.mts",
3334
"type:all": "node scripts/type.mts",
3435
"// Testing": "",
35-
"test": "node scripts/test-monorepo.mts",
36-
"test:all": "node scripts/test-monorepo.mts --all",
36+
"test": "node scripts/test.mts",
37+
"test:all": "node scripts/test.mts --all",
3738
"test:unit": "pnpm --filter @socketsecurity/cli run test:unit",
3839
"pretest:all": "pnpm run build",
3940
"testu": "pnpm --filter @socketsecurity/cli run test:unit:update",
@@ -45,6 +46,9 @@
4546
"clean:cache": "node scripts/clean-cache.mts",
4647
"clean:cache:all": "node scripts/clean-cache.mts --all",
4748
"update": "node scripts/update.mts",
49+
"// xport": "",
50+
"xport": "node scripts/xport.mts",
51+
"xport:emit-schema": "node scripts/xport-emit-schema.mts",
4852
"// Setup": "",
4953
"setup": "node scripts/setup.mts",
5054
"postinstall": "node scripts/setup.mts --install --quiet",
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
/**
2+
* Release-checksum consumer: network fetch + cache for sibling-repo releases.
3+
*
4+
* Use this when your repo *consumes* releases produced by another repo
5+
* (e.g. socket-addon republishes binaries from socket-btm). Composes the
6+
* embedded-checksum loader from `core.mts` with a `checksums.txt` fetch
7+
* that falls back to the network when the embedded manifest is missing
8+
* a tag.
9+
*
10+
* Repos that *produce* releases don't need this file — see `producer.mts`.
11+
*
12+
* Fleet-canonical: byte-identical across every repo that ships
13+
* `packages/build-infra/lib/release-checksums/`.
14+
*/
15+
16+
import { existsSync, promises as fs } from 'node:fs'
17+
import path from 'node:path'
18+
import process from 'node:process'
19+
20+
import { errorMessage } from '@socketsecurity/lib/errors'
21+
import { safeMkdir } from '@socketsecurity/lib/fs'
22+
import { getDefaultLogger } from '@socketsecurity/lib/logger'
23+
import { getLatestRelease } from '@socketsecurity/lib/releases/github-api'
24+
import { downloadReleaseAsset } from '@socketsecurity/lib/releases/github-downloads'
25+
import type { RepoConfig } from '@socketsecurity/lib/releases/github-types'
26+
27+
import { getEmbeddedChecksums, parseChecksums } from './core.mts'
28+
29+
const logger = getDefaultLogger()
30+
31+
export interface ChecksumsResult {
32+
checksums: Record<string, string>
33+
source: 'cache' | 'embedded' | 'network'
34+
tag: string
35+
}
36+
37+
const checksumCache = new Map<string, ChecksumsResult>()
38+
39+
interface GetChecksumsOptions {
40+
/** The producing repo whose releases we're verifying against. */
41+
repoConfig: RepoConfig
42+
/** Tool name prefix used in the producing repo's release tag (e.g. `iocraft`). */
43+
tool: string
44+
/** Specific tag to fetch. If omitted, uses the embedded tag, then `latest`. */
45+
releaseTag?: string
46+
/** Where to cache the downloaded `checksums.txt`. Defaults to `<cwd>/build/temp`. */
47+
tempDir?: string
48+
/** Suppress info/warn logging (errors still log). */
49+
quiet?: boolean
50+
/**
51+
* If true (default), use embedded checksums when available even if a
52+
* network fetch could find newer ones. Set false to force a network
53+
* fetch — useful when bumping checksums.
54+
*/
55+
preferEmbedded?: boolean
56+
}
57+
58+
/**
59+
* Get checksums for a producing repo's release.
60+
*
61+
* Lookup priority:
62+
* 1. In-memory cache (per-process)
63+
* 2. Embedded checksums from `release-assets.json` (works offline)
64+
* 3. Download `checksums.txt` from the producing repo's GitHub release
65+
*
66+
* Network failures fall back to embedded checksums when available.
67+
*/
68+
export async function getReleaseChecksums(
69+
options: GetChecksumsOptions,
70+
): Promise<ChecksumsResult> {
71+
const {
72+
preferEmbedded = true,
73+
quiet = false,
74+
releaseTag,
75+
repoConfig,
76+
tempDir,
77+
tool,
78+
} = options
79+
const toolPrefix = `${tool}-`
80+
81+
const cacheKey = `${tool}:${releaseTag ?? 'latest'}`
82+
const cached = checksumCache.get(cacheKey)
83+
if (cached) {
84+
return cached
85+
}
86+
87+
const embedded = getEmbeddedChecksums()
88+
const toolEmbedded = embedded?.[tool]
89+
if (
90+
toolEmbedded?.checksums &&
91+
Object.keys(toolEmbedded.checksums).length > 0
92+
) {
93+
if (preferEmbedded && (!releaseTag || releaseTag === toolEmbedded.tag)) {
94+
const result: ChecksumsResult = {
95+
checksums: toolEmbedded.checksums,
96+
source: 'embedded',
97+
tag: toolEmbedded.tag,
98+
}
99+
checksumCache.set(cacheKey, result)
100+
checksumCache.set(`${tool}:${toolEmbedded.tag}`, result)
101+
if (!quiet) {
102+
logger.info(
103+
`Using embedded checksums for ${tool} (${toolEmbedded.tag})`,
104+
)
105+
}
106+
return result
107+
}
108+
}
109+
110+
const tag =
111+
releaseTag ??
112+
toolEmbedded?.tag ??
113+
(await getLatestRelease(toolPrefix, repoConfig))
114+
if (!tag) {
115+
if (!quiet) {
116+
logger.warn(`No ${tool} release found, cannot fetch checksums`)
117+
}
118+
return { checksums: {}, source: 'network', tag: '' }
119+
}
120+
121+
const tagCacheKey = `${tool}:${tag}`
122+
const tagCached = checksumCache.get(tagCacheKey)
123+
if (tagCached) {
124+
if (!releaseTag) {
125+
checksumCache.set(cacheKey, tagCached)
126+
}
127+
return tagCached
128+
}
129+
130+
const resolvedTempDir = tempDir ?? path.join(process.cwd(), 'build', 'temp')
131+
await safeMkdir(resolvedTempDir)
132+
const checksumPath = path.join(
133+
resolvedTempDir,
134+
`${tool}-checksums-${tag}.txt`,
135+
)
136+
137+
if (existsSync(checksumPath)) {
138+
try {
139+
const content = await fs.readFile(checksumPath, 'utf8')
140+
const checksums = parseChecksums(content)
141+
const result: ChecksumsResult = { checksums, source: 'network', tag }
142+
checksumCache.set(tagCacheKey, result)
143+
if (!releaseTag) {
144+
checksumCache.set(cacheKey, result)
145+
}
146+
return result
147+
} catch {
148+
// Fall through to download.
149+
}
150+
}
151+
152+
try {
153+
if (!quiet) {
154+
logger.info(`Downloading checksums for ${tool} release ${tag}...`)
155+
}
156+
await downloadReleaseAsset(tag, 'checksums.txt', checksumPath, repoConfig, {
157+
quiet: true,
158+
})
159+
160+
const content = await fs.readFile(checksumPath, 'utf8')
161+
const checksums = parseChecksums(content)
162+
163+
const result: ChecksumsResult = { checksums, source: 'network', tag }
164+
checksumCache.set(tagCacheKey, result)
165+
if (!releaseTag) {
166+
checksumCache.set(cacheKey, result)
167+
}
168+
169+
if (!quiet) {
170+
logger.info(
171+
`Loaded ${Object.keys(checksums).length} checksums for ${tool}`,
172+
)
173+
}
174+
return result
175+
} catch (e) {
176+
if (
177+
toolEmbedded?.checksums &&
178+
Object.keys(toolEmbedded.checksums).length > 0
179+
) {
180+
if (!quiet) {
181+
logger.warn(
182+
`Network fetch failed, using embedded checksums for ${tool}: ${errorMessage(e)}`,
183+
)
184+
}
185+
const result: ChecksumsResult = {
186+
checksums: toolEmbedded.checksums,
187+
source: 'embedded',
188+
tag: toolEmbedded.tag,
189+
}
190+
checksumCache.set(cacheKey, result)
191+
return result
192+
}
193+
194+
if (!quiet) {
195+
logger.warn(
196+
`Failed to download checksums.txt for ${tool}: ${errorMessage(e)}`,
197+
)
198+
}
199+
return { checksums: {}, source: 'network', tag }
200+
}
201+
}
202+
203+
/**
204+
* Clear the checksum cache. Useful for testing or forcing re-download.
205+
*/
206+
export function clearChecksumCache(): void {
207+
checksumCache.clear()
208+
}

0 commit comments

Comments
 (0)