UNPKG

@acalcutt/node-pre-gyp-github

Version:

A node-pre-gyp module which provides the ability to publish to GitHub releases.

150 lines (121 loc) 5.31 kB
import path from 'path'; import fs from 'fs'; import { Octokit } from '@octokit/rest'; const cwd = process.cwd(); let verbose; function consoleLog(x) { return (verbose) ? console.log(x) : false; } export default class NodePreGypGithub { constructor() { this.stage_dir = path.join(cwd, 'build', 'stage'); } init() { const token = process.env.NODE_PRE_GYP_GITHUB_TOKEN; if (!token) { throw new Error('NODE_PRE_GYP_GITHUB_TOKEN environment variable not found'); } this.package_json = JSON.parse(fs.readFileSync(path.join(cwd, 'package.json'), 'utf-8')); if (!this.package_json.repository || !this.package_json.repository.url) { throw new Error('Missing repository.url in package.json'); } else { const ownerRepo = this.package_json.repository.url.match(/https?:\/\/([^\/]+)\/(.*)(?=\.git)/i); if (!ownerRepo) { throw new Error('A correctly formatted GitHub repository.url was not found within package.json'); } this.host = 'api.' + ownerRepo[1]; const [owner, repo] = ownerRepo[2].split('/'); this.owner = owner; this.repo = repo; } const hostPrefix = 'https://' + this.host + '/' + this.owner + '/' + this.repo + '/releases/download/'; if (!this.package_json.binary || typeof this.package_json.binary !== 'object' || typeof this.package_json.binary.host !== 'string') { throw new Error('Missing binary.host in package.json'); } else if (this.package_json.binary.host.replace('https://', 'https://api.').substring(0, hostPrefix.length) !== hostPrefix) { throw new Error('binary.host in package.json should begin with: "' + hostPrefix + '"'); } this.octokit = this.createOctokitInstance(token); } createOctokitInstance(token) { return new Octokit({ baseUrl: "https://" + this.host, auth: token, userAgent: this.package_json.name ? this.package_json.name : "node-pre-gyp-github", }); } async createRelease(args) { const prerelease = this.package_json.version.includes('pre'); const options = { host: this.host, owner: this.owner, repo: this.repo, tag_name: this.package_json.version, target_commitish: 'main', name: 'node-v' + this.package_json.version, body: this.package_json.name + ' ' + this.package_json.version, draft: true, prerelease: prerelease }; Object.keys(args).forEach(function (key) { if (args.hasOwnProperty(key) && options.hasOwnProperty(key)) { options[key] = args[key]; } }); const release = await this.octokit.rest.repos.createRelease(options); return release; } async uploadAssets() { consoleLog("Stage directory path: " + path.join(this.stage_dir)); const files = fs.readdirSync(path.join(this.stage_dir)); if (!files.length) throw new Error('No files found within the stage directory: ' + this.stage_dir); for (const file of files) { if (this.release && this.release.assets) { const asset = this.release.assets.filter(element => element.name === file); if (asset.length) { throw new Error("Staged file " + file + " found but it already exists in release " + this.release.tag_name + ". If you would like to replace it, you must first manually delete it within GitHub."); } } consoleLog(`Staged file ${file} found - proceeding to upload`); const filePath = path.join(this.stage_dir, file); const fileContents = fs.readFileSync(filePath); await this.octokit.rest.repos.uploadReleaseAsset({ owner: this.owner, repo: this.repo, release_id: this.release.id, name: file, data: fileContents }); consoleLog('Staged file ' + file + ' saved to ' + this.owner + '/' + this.repo + ' release ' + this.release.tag_name + ' successfully.'); } } async publish(options = {}) { verbose = (typeof options.verbose === 'undefined' || options.verbose) ? true : false; await this.init(); const { data } = await this.octokit.rest.repos.listReleases({ owner: this.owner, repo: this.repo }); // when remote_path is set expect files to be in stage_dir / remote_path after substitution if (this.package_json.binary.remote_path) { options.tag_name = this.package_json.binary.remote_path.replace(/\{version\}/g, this.package_json.version); this.stage_dir = path.join(this.stage_dir, options.tag_name); } else { // This is here for backwards compatibility for before binary.remote_path support was added in version 1.2.0. options.tag_name = this.package_json.version; } const release = data.filter(element => element.tag_name === options.tag_name); if (release.length === 0) { const release = await this.createRelease(options); this.release = release.data; if (this.release.draft) { consoleLog(`Release ${this.release.tag_name} not found, so a draft release was created. YOU MUST MANUALLY PUBLISH THIS DRAFT WITHIN GITHUB FOR IT TO BE ACCESSIBLE.`); } else { consoleLog(`Release ${this.release.tag_name} not found, so a new release was created and published.`); } } else { this.release = release[0]; } await this.uploadAssets(); } }