← Public packages
@kentcdodds/package-app-kit
Design tokens, PWA install/update, About/version, cache helpers, and optional realtime notes sync for Kody package apps.
#!/usr/bin/env node
// Pre-publish guard: run before `kody package publish` (npm run publish:kody
// does this for you). Never prints credential values, only file names.
import { execFileSync, spawnSync } from 'node:child_process'
import fs from 'node:fs'
const problems = []
const warnings = []
const sh = (cmd, args) => execFileSync(cmd, args, { encoding: 'utf8' }).trim()
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'))
if (!pkg.name || pkg.name.includes('__')) problems.push('package.json#name is not set to @owner/name.')
if (!pkg.kody?.id) problems.push('package.json#kody.id is missing.')
if (!pkg.kody?.description) warnings.push('package.json#kody.description is empty.')
for (const file of ['README.md', 'AGENTS.md']) {
if (!fs.existsSync(file)) problems.push(`${file} is missing.`)
}
// Credential-shaped strings in tracked files (values are never printed).
const credentialPatterns = [
/kody_(at|bc)_[A-Za-z0-9]{8,}/,
/gh[pousr]_[A-Za-z0-9]{20,}/,
/github_pat_[A-Za-z0-9_]{20,}/,
/sk_(live|test)_[A-Za-z0-9]{10,}/,
/xox[abp]-[A-Za-z0-9-]{10,}/,
/AKIA[0-9A-Z]{16}/,
/-----BEGIN [A-Z ]*PRIVATE KEY-----/,
]
let tracked = []
try {
tracked = sh('git', ['ls-files']).split('\n').filter(Boolean)
} catch {
problems.push('Not a git repository. Clone with `git clone https://kody.codes/@owner/name.git`.')
}
for (const file of tracked) {
if (file === 'scripts/prepublish-check.mjs') continue
let text
try {
text = fs.readFileSync(file, 'utf8')
} catch {
continue
}
if (credentialPatterns.some((re) => re.test(text))) {
problems.push(`${file} contains something that looks like a credential. Remove it and use Kody credentials instead.`)
}
}
// Publishing ships what is pushed, so the tree must be clean and pushed.
if (tracked.length) {
if (sh('git', ['status', '--porcelain'])) problems.push('Uncommitted changes. Commit them first.')
try {
sh('git', ['fetch', '--quiet'])
const ahead = sh('git', ['rev-list', '--count', '@{u}..HEAD'])
if (ahead !== '0') problems.push(`${ahead} local commit(s) not pushed. Run \`git push\` first.`)
} catch {
problems.push('Could not compare with the remote. Check `git remote -v` and run `npm run setup` (or `kody auth setup-git`).')
}
}
if (!process.argv.includes('--skip-dev-check')) {
const check = spawnSync('npx', ['--no-install', 'kody', 'dev', 'check', '--target', 'mock'], { stdio: 'inherit' })
if (check.status !== 0) problems.push('`kody dev check` failed (see output above).')
}
for (const w of warnings) console.warn(`warn: ${w}`)
if (problems.length) {
console.error('\nNot ready to publish:')
for (const p of problems) console.error(`- ${p}`)
process.exit(1)
}
console.log('\nReady to publish.')