import fs from 'node:fs' import path from 'node:path' /** * Print the names of packages/* whose current version is NOT yet published * to the Gitea npm registry. Makes a publish workflow idempotent. * * Reads publishConfig.registry from each package.json; auth token from env * GITEA_NPM_TOKEN. Prints one bare package name per line (suitable for the * `pnpm --filter publish` loop). Never prints the token. */ const token = process.env.GITEA_NPM_TOKEN if (!token) { console.error('GITEA_NPM_TOKEN not set') process.exit(2) } const packagesDir = path.resolve('packages') const unpublished = [] for (const entry of fs.readdirSync(packagesDir, { withFileTypes: true })) { if (!entry.isDirectory()) continue const pkgPath = path.join(packagesDir, entry.name, 'package.json') if (!fs.existsSync(pkgPath)) continue const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')) if (pkg.private) continue const registry = pkg.publishConfig?.registry if (!registry) { console.error(`skipping ${pkg.name}: no publishConfig.registry`) continue } // Gitea npm registry URL form: .../api/packages/{owner}/npm/ const esc = (pkg.name).replace('/', '%2F') const url = `${registry}${esc}` try { const res = await fetch(url, { headers: { Authorization: `token ${token}` }, }) if (res.status === 404) { unpublished.push(pkg.name) } else if (res.ok) { // registry returns 200 when the package exists console.error(`already published: ${pkg.name}`) } else { console.error(`registry check ${pkg.name}: HTTP ${res.status}`) } } catch (err) { console.error(`registry check ${pkg.name} failed: ${err.message}`) } } if (unpublished.length) { process.stdout.write(unpublished.join('\n') + '\n') }