UNPKG

@ossjs/release

Version:

Minimalistic, opinionated, and predictable release automation tool.

148 lines (123 loc) 4.04 kB
import type { BuilderCallback } from 'yargs' import { format, invariant } from 'outvariant' import { Command } from '#/src/Command.js' import { getTag, type TagPointer } from '#/src/utils/git/get-tag.js' import { getLatestRelease } from '#/src/utils/git/get-latest-release.js' import { getTags } from '#/src/utils/git/get-tags.js' import { getCommit } from '#/src/utils/git/get-commit.js' import { getInfo } from '#/src/utils/git/get-info.js' import { execAsync } from '#/src/utils/exec-async.js' import { demandGitHubToken } from '#/src/utils/env.js' interface Argv { _: [path: string, tag?: string] } export enum ReleaseStatus { /** * Release is public and available for everybody to see * on the GitHub releases page. */ Public = 'public', /** * Release is pushed to GitHub but is marked as draft. */ Draft = 'draft', /** * Release is local, not present on GitHub. */ Unpublished = 'unpublished', } export class Show extends Command<Argv> { static command = 'show' static description = 'Show release info' static builder: BuilderCallback<{}, Argv> = (yargs) => { return yargs .usage('$0 show [tag]') .example([ ['$0 show', 'Show the latest release info'], ['$0 show 1.2.3', 'Show specific release tag info'], ]) .positional('tag', { type: 'string', description: 'Release tag', demandOption: false, }) } public run = async () => { await demandGitHubToken().catch((error) => { this.log.error(error.message) process.exit(1) }) const [, tag] = this.argv._ const pointer = await this.getTagPointer(tag?.toString()) this.log.info(format('found tag "%s"!', pointer.tag)) const commit = await getCommit(pointer.hash) invariant( commit, 'Failed to retrieve release info for tag "%s": cannot find commit associated with the tag.', tag, ) // Print local Git info about the release commit. const commitOut = await execAsync(`git log -1 ${commit.commit.long}`).then( ({ stdout }) => stdout, ) this.log.info(commitOut) // Print the remote GitHub info about the release. const repo = await getInfo() const releaseResponse = await fetch( `https://api.github.com/repos/${repo.owner}/${repo.name}/releases/tags/${pointer.tag}`, { headers: { Authorization: `token ${process.env.GITHUB_TOKEN}`, }, }, ) const isPublishedRelease = releaseResponse.status === 200 const release = await releaseResponse.json() const releaseStatus: ReleaseStatus = isPublishedRelease ? release.draft ? ReleaseStatus.Draft : ReleaseStatus.Public : ReleaseStatus.Unpublished this.log.info(format('release status: %s', releaseStatus)) if ( releaseStatus === ReleaseStatus.Public || releaseStatus === ReleaseStatus.Draft ) { this.log.info(format('release url: %s', release?.html_url)) } if (!isPublishedRelease) { this.log.warn( format('release "%s" is not published to GitHub!', pointer.tag), ) } } /** * Returns tag pointer by the given tag name. * If no tag name was given, looks up the latest release tag * and returns its pointer. */ private async getTagPointer(tag?: string): Promise<TagPointer> { if (tag) { this.log.info(format('looking up explicit "%s" tag...', tag)) const pointer = await getTag(tag) invariant( pointer, 'Failed to retrieve release tag: tag "%s" does not exist.', tag, ) return pointer } this.log.info('looking up the latest release tag...') const tags = await getTags() invariant( tags.length > 0, 'Failed to retrieve release tag: repository has no releases.', ) const latestPointer = await getLatestRelease(tags) invariant( latestPointer, 'Failed to retrieve release tag: cannot retrieve releases.', ) return latestPointer } }