napi-rs/cli/src/pre-publish.ts

200 lines
5.5 KiB
TypeScript
Raw Normal View History

2021-11-21 22:39:36 +09:00
import { createReadStream, existsSync, statSync } from 'fs'
2020-09-04 17:22:15 +09:00
import { join } from 'path'
import { Octokit } from '@octokit/rest'
import chalk from 'chalk'
import { Command, Option } from 'clipanion'
2020-09-04 17:22:15 +09:00
import { getNapiConfig } from './consts'
import { debugFactory } from './debug'
import { spawn } from './spawn'
import { updatePackageJson } from './update-package'
import { VersionCommand } from './version'
2020-09-04 17:22:15 +09:00
const debug = debugFactory('prepublish')
interface PackageInfo {
name: string
version: string
tag: string
}
export class PrePublishCommand extends Command {
static usage = Command.Usage({
description:
'Update package.json and copy addons into per platform packages',
})
static paths = [['prepublish']]
2020-09-04 17:22:15 +09:00
prefix = Option.String(`-p,--prefix`, 'npm')
2020-09-04 17:22:15 +09:00
tagStyle: 'npm' | 'lerna' = Option.String('--tagstyle,-t', 'lerna')
2020-09-04 17:22:15 +09:00
configFileName?: string = Option.String('-c,--config')
2020-09-04 17:22:15 +09:00
isDryRun = Option.Boolean('--dry-run', false)
skipGHRelease = Option.Boolean('--skip-gh-release', false)
2020-09-04 17:22:15 +09:00
async execute() {
const { packageJsonPath, platforms, version, packageName, binaryName } =
getNapiConfig(this.configFileName)
2020-09-04 17:22:15 +09:00
debug(`Update optionalDependencies in [${packageJsonPath}]`)
if (!this.isDryRun) {
await VersionCommand.updatePackageJson(this.prefix, this.configFileName)
2020-09-04 17:22:15 +09:00
await updatePackageJson(packageJsonPath, {
optionalDependencies: platforms.reduce(
(acc: Record<string, string>, cur) => {
acc[`${packageName}-${cur.platformArchABI}`] = `${version}`
2020-09-04 17:22:15 +09:00
return acc
},
{},
),
})
}
2021-11-19 15:58:21 +09:00
const { owner, repo, pkgInfo, octokit } = await this.createGhRelease(
2020-09-04 17:22:15 +09:00
packageName,
version,
)
for (const platformDetail of platforms) {
const pkgDir = join(
process.cwd(),
this.prefix,
`${platformDetail.platformArchABI}`,
)
const filename = `${binaryName}.${platformDetail.platformArchABI}.node`
2020-09-04 17:22:15 +09:00
const dstPath = join(pkgDir, filename)
2020-09-04 17:22:15 +09:00
if (!this.isDryRun) {
2021-11-21 22:39:36 +09:00
if (!existsSync(dstPath)) {
console.warn(`[${chalk.yellowBright(dstPath)}] is not existed`)
continue
}
await spawn('npm publish', {
cwd: pkgDir,
env: process.env,
})
if (!this.skipGHRelease) {
debug(
`Start upload [${chalk.greenBright(
dstPath,
)}] to Github release, [${chalk.greenBright(pkgInfo.tag)}]`,
)
try {
2021-11-19 15:58:21 +09:00
const releaseInfo = await octokit!.repos.getReleaseByTag({
repo: repo!,
owner: owner!,
tag: pkgInfo.tag!,
})
2021-11-21 22:39:36 +09:00
const dstFileStats = statSync(dstPath)
2021-11-19 15:58:21 +09:00
const assetInfo = await octokit!.repos.uploadReleaseAsset({
owner: owner!,
repo: repo!,
name: filename,
release_id: releaseInfo.data.id,
2021-11-21 22:39:36 +09:00
mediaType: { format: 'raw' },
headers: {
'content-length': dstFileStats.size,
'content-type': 'application/octet-stream',
},
// @ts-expect-error
data: createReadStream(dstPath),
})
console.info(`${chalk.green(dstPath)} upload success`)
2021-11-19 15:58:21 +09:00
console.info(
`Download url: ${chalk.blueBright(
assetInfo.data.browser_download_url,
)}`,
)
} catch (e) {
debug(
`Param: ${JSON.stringify(
{ owner, repo, tag: pkgInfo.tag, filename: dstPath },
null,
2,
)}`,
)
console.error(e)
}
}
2020-09-04 17:22:15 +09:00
}
}
}
private async createGhRelease(packageName: string, version: string) {
if (this.skipGHRelease) {
return {
owner: null,
repo: null,
pkgInfo: { name: null, version: null, tag: null },
}
}
2020-09-04 17:22:15 +09:00
const headCommit = (await spawn('git log -1 --pretty=%B'))
.toString('utf8')
.trim()
debug(`Github repository: ${process.env.GITHUB_REPOSITORY}`)
2020-09-04 17:22:15 +09:00
const [owner, repo] = process.env.GITHUB_REPOSITORY!.split('/')
const octokit = new Octokit({
auth: process.env.GITHUB_TOKEN,
})
let pkgInfo: PackageInfo | undefined
if (this.tagStyle === 'lerna') {
const packagesToPublish = headCommit
.split('\n')
.map((line) => line.trim())
.filter((line, index) => line.length && index)
.map((line) => line.substr(2))
.map(this.parseTag)
pkgInfo = packagesToPublish.find(
(pkgInfo) => pkgInfo.name === packageName,
)
if (!pkgInfo) {
throw new TypeError(
`No release commit found with ${packageName}, original commit info: ${headCommit}`,
)
}
} else {
pkgInfo = {
tag: `v${version}`,
2020-09-04 17:22:15 +09:00
version,
name: packageName,
}
}
if (!this.isDryRun) {
try {
await octokit.repos.createRelease({
owner,
repo,
tag_name: pkgInfo.tag,
})
} catch (e) {
debug(
`Params: ${JSON.stringify(
{ owner, repo, tag_name: pkgInfo.tag },
null,
2,
)}`,
)
console.error(e)
}
2020-09-04 17:22:15 +09:00
}
2021-11-19 15:58:21 +09:00
return { owner, repo, pkgInfo, octokit }
2020-09-04 17:22:15 +09:00
}
private parseTag(tag: string) {
const segments = tag.split('@')
const version = segments.pop()!
const name = segments.join('@')
return {
name,
version,
tag,
}
}
}