maxmilton/action-create-release

View on GitHub
src/action.ts

Summary

Maintainability
A
2 hrs
Test Coverage
F
0%
// https://github.com/actions/create-release
// https://github.com/actions/toolkit/tree/master/packages/core
// https://github.com/actions/toolkit/tree/master/packages/github

/// <reference types="node" />
/* eslint @typescript-eslint/no-non-null-assertion: "warn" */

import {
  getBooleanInput,
  getInput,
  getMultilineInput,
  info,
  setFailed,
} from '@actions/core';
import { context, getOctokit } from '@actions/github';
import { readFile } from 'node:fs/promises';
import { basename, resolve } from 'node:path';

process.on('uncaughtExceptionMonitor', setFailed);
process.on('unhandledRejection', setFailed);

type Await<T> = T extends Promise<infer U> ? U : T;
type OctoKit = ReturnType<typeof getOctokit>;
type Release = Await<ReturnType<OctoKit['rest']['repos']['createRelease']>>;

export async function run(): Promise<void> {
  let octokit: OctoKit | undefined;
  let release: Release | undefined;

  try {
    const token = getInput('github-token', { required: true });
    const gitTag = getInput('git-tag', { required: true }).replace(
      'refs/tags/',
      '',
    );
    const draft = getBooleanInput('draft');
    const files = getMultilineInput('files');
    const uploads = [];

    octokit = getOctokit(token);

    // https://octokit.github.io/rest.js/v18/#repos-create-release
    release = await octokit.rest.repos.createRelease({
      owner: context.repo.owner,
      repo: context.repo.repo,
      tag_name: gitTag,
      name: gitTag,
      // body: '',
      // create as draft so a human still needs to review and publish
      draft,
      // assume prerelease tags look like vx.x.x-beta.1
      prerelease: gitTag.includes('-'),
      target_commitish: context.sha,
    });

    for (const file of files) {
      // https://octokit.github.io/rest.js/v18/#repos-upload-release-asset
      uploads.push(
        readFile(resolve(file)).then((data) =>
          octokit!.rest.repos.uploadReleaseAsset({
            owner: context.repo.owner,
            repo: context.repo.repo,
            release_id: release!.data.id,
            // TODO: Handle files types other than .zip
            headers: { 'content-type': 'application/zip' },
            name: basename(file),
            // @ts-expect-error - Buffer is actually correct, otherwise file is broken
            data,
          }),
        ),
      );
    }

    await Promise.all(uploads);

    info(`Git tag: ${gitTag}`);
    info(`Release URL: ${release.data.html_url}`);
  } catch (error) {
    if (octokit && release) {
      try {
        await octokit.rest.repos.deleteRelease({
          owner: context.repo.owner,
          repo: context.repo.repo,
          release_id: release.data.id,
        });
      } catch {
        /* noop */
      }
    }

    setFailed(error as Error);
  }
}