From f06c87080354d742d9d45f5035935a6bcb31ec73 Mon Sep 17 00:00:00 2001 From: Andrew Lisowski Date: Fri, 15 Jan 2021 23:23:20 -0800 Subject: [PATCH 1/2] enable canary releases for upload-assets plugin --- package.json | 16 +- packages/cli/auto | 0 plugins/upload-assets/README.md | 26 +- .../__tests__/upload-assets-ci.test.ts | 70 ++++ .../__tests__/upload-assets.test.ts | 321 +++++++++++++++++- plugins/upload-assets/package.json | 1 + plugins/upload-assets/src/index.ts | 312 +++++++++++++---- 7 files changed, 663 insertions(+), 83 deletions(-) delete mode 100644 packages/cli/auto create mode 100644 plugins/upload-assets/__tests__/upload-assets-ci.test.ts diff --git a/package.json b/package.json index 9b29b1873..ab9989910 100644 --- a/package.json +++ b/package.json @@ -134,6 +134,14 @@ }, "auto": { "plugins": [ + [ + "upload-assets", + [ + "./packages/cli/binary/auto-linux.gz", + "./packages/cli/binary/auto-macos.gz", + "./packages/cli/binary/auto-win.exe.gz" + ] + ], [ "npm", { @@ -158,14 +166,6 @@ } } ], - [ - "upload-assets", - [ - "./packages/cli/binary/auto-linux.gz", - "./packages/cli/binary/auto-macos.gz", - "./packages/cli/binary/auto-win.exe.gz" - ] - ], [ "brew", { diff --git a/packages/cli/auto b/packages/cli/auto deleted file mode 100644 index e69de29bb..000000000 diff --git a/plugins/upload-assets/README.md b/plugins/upload-assets/README.md index 8f6bdea22..662dac7e1 100644 --- a/plugins/upload-assets/README.md +++ b/plugins/upload-assets/README.md @@ -1,6 +1,10 @@ # Upload Assets Plugin -Upload assets to the release. Good for executables and extra downloadable files. +Upload assets to the release. +Good for executables and extra downloadable files. +Also supports canaries! + +> NOTE: For canaries to work this plugin must be listed before any other publishing plugin. ## Installation @@ -27,3 +31,23 @@ Simply supply the paths to the assets to add to the release. ] } ``` + +## Options + +### `maxCanaryAssets` + +Max number of assets to keep in the canary release. + +```json +{ + "plugins": [ + [ + "upload-assets", + { + "assets": ["./path/to/file"], + "maxAssets": 100 + } + ] + ] +} +``` diff --git a/plugins/upload-assets/__tests__/upload-assets-ci.test.ts b/plugins/upload-assets/__tests__/upload-assets-ci.test.ts new file mode 100644 index 000000000..18dbb7d6f --- /dev/null +++ b/plugins/upload-assets/__tests__/upload-assets-ci.test.ts @@ -0,0 +1,70 @@ +import envCi from "env-ci"; + +jest.mock("env-ci"); + +const envSpy = envCi as jest.Mock; +envSpy.mockImplementation(() => ({ + isCi: true, + pr: 123, +})); + +import Auto, { SEMVER } from "@auto-it/core"; +import { makeHooks } from "@auto-it/core/dist/utils/make-hooks"; +import path from "path"; + +import { dummyLog } from "@auto-it/core/dist/utils/logger"; +import UploadAssets from "../src"; +import endent from "endent"; + +const options = { + owner: "test", + repo: "repo", +}; + +jest.spyOn(console, "log").mockImplementation(); + +describe("Upload Assets Plugin", () => { + test("should add to pr body for pull requests", async () => { + const plugin = new UploadAssets([ + path.join(__dirname, "./test-assets/macos"), + ]); + const hooks = makeHooks(); + const uploadReleaseAsset = jest + .fn() + .mockImplementation(({ name }) => + Promise.resolve({ + data: { id: 2, name, browser_download_url: `http://${name}` }, + }) + ); + const createRelease = jest.fn().mockResolvedValue({ data: { id: 1 } }); + const addToPrBody = jest.fn(); + + plugin.apply(({ + hooks, + logger: dummyLog(), + git: { + options, + addToPrBody, + github: { + repos: { uploadReleaseAsset, createRelease }, + paginate: jest.fn().mockResolvedValue([]), + }, + }, + } as unknown) as Auto); + + await hooks.canary.promise({ + canaryIdentifier: "canary.123", + bump: SEMVER.patch, + }); + + expect(addToPrBody).toHaveBeenCalledWith( + endent` + :baby_chick: Download canary assets: + + [macos-canary.123](http://macos-canary.123) + `, + 123, + "canary-assets" + ); + }); +}); diff --git a/plugins/upload-assets/__tests__/upload-assets.test.ts b/plugins/upload-assets/__tests__/upload-assets.test.ts index e7a18a54d..18f3e9624 100644 --- a/plugins/upload-assets/__tests__/upload-assets.test.ts +++ b/plugins/upload-assets/__tests__/upload-assets.test.ts @@ -1,4 +1,13 @@ -import Auto from "@auto-it/core"; +import envCi from "env-ci"; + +jest.mock("env-ci"); + +const envSpy = envCi as jest.Mock; +envSpy.mockImplementation(() => ({ + isCi: false, +})); + +import Auto, { SEMVER } from "@auto-it/core"; import { makeHooks } from "@auto-it/core/dist/utils/make-hooks"; import { RestEndpointMethodTypes } from "@octokit/rest"; import path from "path"; @@ -11,18 +20,27 @@ const options = { repo: "repo", }; +jest.spyOn(console, "log").mockImplementation(); + describe("Upload Assets Plugin", () => { test("should do nothing without a response", async () => { const plugin = new UploadAssets([ path.join(__dirname, "./test-assets/macos"), ]); const hooks = makeHooks(); - const uploadReleaseAsset = jest.fn(); + const uploadReleaseAsset = jest.fn().mockResolvedValue({}); + const createRelease = jest.fn().mockResolvedValue({ data: { id: 1 } }); plugin.apply(({ hooks, logger: dummyLog(), - git: { options, github: { repos: { uploadReleaseAsset } } }, + git: { + options, + github: { + repos: { uploadReleaseAsset, createRelease }, + paginate: jest.fn().mockResolvedValue([]), + }, + }, } as unknown) as Auto); await hooks.afterRelease.promise({ @@ -40,12 +58,19 @@ describe("Upload Assets Plugin", () => { path.join(__dirname, "./test-assets/macos"), ]); const hooks = makeHooks(); - const uploadReleaseAsset = jest.fn(); + const uploadReleaseAsset = jest.fn().mockResolvedValue({}); + const createRelease = jest.fn().mockResolvedValue({ data: { id: 1 } }); plugin.apply(({ hooks, logger: dummyLog(), - git: { options, github: { repos: { uploadReleaseAsset } } }, + git: { + options, + github: { + repos: { uploadReleaseAsset, createRelease }, + paginate: jest.fn().mockResolvedValue([]), + }, + }, } as unknown) as Auto); await hooks.afterRelease.promise({ @@ -70,19 +95,167 @@ describe("Upload Assets Plugin", () => { ); }); + test("should upload a single canary asset", async () => { + const plugin = new UploadAssets([ + path.join(__dirname, "./test-assets/macos"), + ]); + const hooks = makeHooks(); + const uploadReleaseAsset = jest + .fn() + .mockImplementation(({ name }) => + Promise.resolve({ data: { id: 2, name } }) + ); + const createRelease = jest.fn().mockResolvedValue({ data: { id: 1 } }); + + plugin.apply(({ + hooks, + logger: dummyLog(), + git: { + options, + github: { + repos: { uploadReleaseAsset, createRelease }, + paginate: jest.fn().mockResolvedValue([]), + }, + }, + } as unknown) as Auto); + + await hooks.canary.promise({ + canaryIdentifier: "canary.123", + bump: SEMVER.patch, + }); + + expect(uploadReleaseAsset).toHaveBeenCalledWith( + expect.objectContaining({ + name: "macos-canary.123", + release_id: 1, + }) + ); + }); + + test("should use canary release if exists", async () => { + const plugin = new UploadAssets([ + path.join(__dirname, "./test-assets/macos"), + ]); + const hooks = makeHooks(); + const uploadReleaseAsset = jest + .fn() + .mockImplementation(({ name }) => + Promise.resolve({ data: { id: 2, name } }) + ); + const getReleaseByTag = jest.fn().mockResolvedValue({ data: { id: 1 } }); + + plugin.apply(({ + hooks, + logger: dummyLog(), + git: { + options, + github: { + repos: { uploadReleaseAsset, getReleaseByTag }, + paginate: jest.fn().mockResolvedValue([]), + }, + }, + } as unknown) as Auto); + + await hooks.canary.promise({ + canaryIdentifier: "canary.123", + bump: SEMVER.patch, + }); + + expect(uploadReleaseAsset).toHaveBeenCalledWith( + expect.objectContaining({ + name: "macos-canary.123", + release_id: 1, + }) + ); + }); + + test("should not upload a canary asset in dryRun", async () => { + const plugin = new UploadAssets([ + path.join(__dirname, "./test-assets/macos"), + ]); + const hooks = makeHooks(); + const uploadReleaseAsset = jest + .fn() + .mockImplementation(({ name }) => + Promise.resolve({ data: { id: 2, name } }) + ); + const createRelease = jest.fn().mockResolvedValue({ data: { id: 1 } }); + + plugin.apply(({ + hooks, + logger: dummyLog(), + git: { + options, + github: { + repos: { uploadReleaseAsset, createRelease }, + paginate: jest.fn().mockResolvedValue([]), + }, + }, + } as unknown) as Auto); + + await hooks.canary.promise({ + canaryIdentifier: "canary.123", + bump: SEMVER.patch, + dryRun: true + }); + + expect(uploadReleaseAsset).not.toHaveBeenCalled(); + }); + + test("should upload a single canary asset with extension", async () => { + const plugin = new UploadAssets([ + path.join(__dirname, "./test-assets/test.txt"), + ]); + const hooks = makeHooks(); + const uploadReleaseAsset = jest + .fn() + .mockImplementation(({ name }) => + Promise.resolve({ data: { id: 2, name } }) + ); + const createRelease = jest.fn().mockResolvedValue({ data: { id: 1 } }); + + plugin.apply(({ + hooks, + logger: dummyLog(), + git: { + options, + github: { + repos: { uploadReleaseAsset, createRelease }, + paginate: jest.fn().mockResolvedValue([]), + }, + }, + } as unknown) as Auto); + + await hooks.canary.promise({ + canaryIdentifier: "canary.123", + bump: SEMVER.patch, + }); + + expect(uploadReleaseAsset).toHaveBeenCalledWith( + expect.objectContaining({ + name: "test-canary.123.txt", + release_id: 1, + }) + ); + }); + test("should upload to enterprise", async () => { const plugin = new UploadAssets([ path.join(__dirname, "./test-assets/macos"), ]); const hooks = makeHooks(); - const uploadReleaseAsset = jest.fn(); + const uploadReleaseAsset = jest.fn().mockResolvedValue({}); + const createRelease = jest.fn().mockResolvedValue({ data: { id: 1 } }); plugin.apply(({ hooks, logger: dummyLog(), git: { options: { ...options, baseUrl: "https://github.my.com/api/v3" }, - github: { repos: { uploadReleaseAsset } }, + github: { + repos: { uploadReleaseAsset, createRelease }, + paginate: jest.fn().mockResolvedValue([]), + }, }, } as unknown) as Auto); @@ -117,12 +290,19 @@ describe("Upload Assets Plugin", () => { ], }); const hooks = makeHooks(); - const uploadReleaseAsset = jest.fn(); + const uploadReleaseAsset = jest.fn().mockResolvedValue({}); + const createRelease = jest.fn().mockResolvedValue({ data: { id: 1 } }); plugin.apply(({ hooks, logger: dummyLog(), - git: { options, github: { repos: { uploadReleaseAsset } } }, + git: { + options, + github: { + repos: { uploadReleaseAsset, createRelease }, + paginate: jest.fn().mockResolvedValue([]), + }, + }, } as unknown) as Auto); await hooks.afterRelease.promise({ @@ -143,12 +323,19 @@ describe("Upload Assets Plugin", () => { assets: [path.join(__dirname, "./test-assets/test*.txt")], }); const hooks = makeHooks(); - const uploadReleaseAsset = jest.fn(); + const uploadReleaseAsset = jest.fn().mockResolvedValue({}); + const createRelease = jest.fn().mockResolvedValue({ data: { id: 1 } }); plugin.apply(({ hooks, logger: dummyLog(), - git: { options, github: { repos: { uploadReleaseAsset } } }, + git: { + options, + github: { + repos: { uploadReleaseAsset, createRelease }, + paginate: jest.fn().mockResolvedValue([]), + }, + }, } as unknown) as Auto); await hooks.afterRelease.promise({ @@ -169,4 +356,116 @@ describe("Upload Assets Plugin", () => { expect.objectContaining({ name: "test.txt" }) ); }); + + test("should not delete maxAssets", async () => { + const plugin = new UploadAssets({ + assets: [ + path.join(__dirname, "./test-assets/macos"), + path.join(__dirname, "./test-assets/test.txt"), + ], + }); + const hooks = makeHooks(); + const deleteReleaseAsset = jest.fn(); + const uploadReleaseAsset = jest.fn().mockResolvedValue({}); + const createRelease = jest.fn().mockResolvedValue({ data: { id: 1 } }); + + plugin.apply(({ + hooks, + logger: dummyLog(), + git: { + options, + github: { + repos: { uploadReleaseAsset, createRelease, deleteReleaseAsset }, + paginate: jest.fn().mockResolvedValue(new Array(300).fill({})), + }, + }, + } as unknown) as Auto); + + await hooks.afterRelease.promise({ + newVersion: "1.0.0", + lastRelease: "0.1.0", + commits: [], + releaseNotes: "", + response: { + data: { upload_url: "https://foo.com" }, + } as RestEndpointMethodTypes["repos"]["createRelease"]["response"], + }); + + expect(deleteReleaseAsset).not.toHaveBeenCalled(); + }); + + test("should delete old canary assets", async () => { + const plugin = new UploadAssets({ + assets: [ + path.join(__dirname, "./test-assets/macos"), + path.join(__dirname, "./test-assets/test.txt"), + ], + }); + const hooks = makeHooks(); + const deleteReleaseAsset = jest.fn(); + const uploadReleaseAsset = jest.fn().mockResolvedValue({}); + const createRelease = jest.fn().mockResolvedValue({ data: { id: 1 } }); + + plugin.apply(({ + hooks, + logger: dummyLog(), + git: { + options, + github: { + repos: { uploadReleaseAsset, createRelease, deleteReleaseAsset }, + paginate: jest.fn().mockResolvedValue(new Array(303).fill({})), + }, + }, + } as unknown) as Auto); + + await hooks.afterRelease.promise({ + newVersion: "1.0.0", + lastRelease: "0.1.0", + commits: [], + releaseNotes: "", + response: { + data: { upload_url: "https://foo.com" }, + } as RestEndpointMethodTypes["repos"]["createRelease"]["response"], + }); + + expect(deleteReleaseAsset).toHaveBeenCalledTimes(3); + }); + + test("should ba able to configure max assets", async () => { + const plugin = new UploadAssets({ + maxCanaryAssets: 100, + assets: [ + path.join(__dirname, "./test-assets/macos"), + path.join(__dirname, "./test-assets/test.txt"), + ], + }); + const hooks = makeHooks(); + const deleteReleaseAsset = jest.fn(); + const uploadReleaseAsset = jest.fn().mockResolvedValue({}); + const createRelease = jest.fn().mockResolvedValue({ data: { id: 1 } }); + + plugin.apply(({ + hooks, + logger: dummyLog(), + git: { + options, + github: { + repos: { uploadReleaseAsset, createRelease, deleteReleaseAsset }, + paginate: jest.fn().mockResolvedValue(new Array(300).fill({})), + }, + }, + } as unknown) as Auto); + + await hooks.afterRelease.promise({ + newVersion: "1.0.0", + lastRelease: "0.1.0", + commits: [], + releaseNotes: "", + response: { + data: { upload_url: "https://foo.com" }, + } as RestEndpointMethodTypes["repos"]["createRelease"]["response"], + }); + + expect(deleteReleaseAsset).toHaveBeenCalledTimes(200); + }); }); diff --git a/plugins/upload-assets/package.json b/plugins/upload-assets/package.json index 4756795f2..7e633ab74 100644 --- a/plugins/upload-assets/package.json +++ b/plugins/upload-assets/package.json @@ -44,6 +44,7 @@ "file-type": "^16.0.0", "fp-ts": "^2.5.3", "io-ts": "^2.1.2", + "terminal-link": "^2.1.1", "tslib": "2.0.3" }, "devDependencies": { diff --git a/plugins/upload-assets/src/index.ts b/plugins/upload-assets/src/index.ts index ea2fd11f7..0e0954aca 100644 --- a/plugins/upload-assets/src/index.ts +++ b/plugins/upload-assets/src/index.ts @@ -1,38 +1,68 @@ import { RestEndpointMethodTypes } from "@octokit/rest"; -import { Auto, IPlugin, validatePluginConfiguration } from "@auto-it/core"; +import { + Auto, + getPrNumberFromEnv, + IPlugin, + validatePluginConfiguration, +} from "@auto-it/core"; import endent from "endent"; import * as FileType from "file-type"; import fs from "fs"; import glob from "fast-glob"; import path from "path"; import { promisify } from "util"; +import link from "terminal-link"; import * as t from "io-ts"; +type AssetResponse = RestEndpointMethodTypes["repos"]["uploadReleaseAsset"]["response"]["data"]; + const stat = promisify(fs.stat); const readFile = promisify(fs.readFile); -const pluginOptions = t.interface({ +const requiredPluginOptions = t.interface({ /** Paths to assets to upload */ assets: t.array(t.string), }); +const optionalPluginOptions = t.partial({ + /** Max number of assets to keep in the canary release */ + maxCanaryAssets: t.number, +}); + +const pluginOptions = t.intersection([ + requiredPluginOptions, + optionalPluginOptions, +]); + /** Convert shorthand options to noraml shape */ const normalizeOptions = (options: IUploadAssetsPluginOptions | string[]) => Array.isArray(options) ? { assets: options } : options; export type IUploadAssetsPluginOptions = t.TypeOf; +interface Release { + /** The response data */ + data: { + /** the release id */ + id: number; + }; +} + /** Attach extra assets to a GitHub Release */ export default class UploadAssetsPlugin implements IPlugin { /** The name of the plugin */ name = "upload-assets"; /** The options of the plugin */ - readonly options: IUploadAssetsPluginOptions; + readonly options: Required; /** Initialize the plugin with it's options */ constructor(options: IUploadAssetsPluginOptions | string[]) { - this.options = normalizeOptions(options); + const normalizedOptions = normalizeOptions(options); + this.options = { + ...normalizedOptions, + maxCanaryAssets: normalizedOptions.maxCanaryAssets || 300, + }; } /** Tap into auto plugin points. */ @@ -47,66 +77,222 @@ export default class UploadAssetsPlugin implements IPlugin { } }); + auto.hooks.canary.tapPromise( + this.name, + async ({ canaryIdentifier, dryRun }) => { + const canaryRelease = await this.getCanaryGitHubRelease(auto); + + auto.logger.log.info(endent`${ + dryRun ? "Would update" : "Updating" + } Canary Release: + + ${canaryRelease.html_url} + `); + + const assets = await this.uploadAssets( + auto, + { data: canaryRelease }, + dryRun, + canaryIdentifier + ); + + if (!assets.length) { + return; + } + + auto.logger.log.success(endent` + Download canary assets: + + ${assets + .map((asset) => link(asset.name, asset.browser_download_url)) + .join(" \n")} + `); + + const prNumber = getPrNumberFromEnv(); + + if (!prNumber) { + return; + } + + const assetList = assets + .map((asset) => `[${asset.name}](${asset.browser_download_url}) `) + .join("\n"); + + await auto.git?.addToPrBody( + endent` + :baby_chick: Download canary assets: + + ${assetList} + `, + prNumber, + "canary-assets" + ); + } + ); + auto.hooks.afterRelease.tapPromise(this.name, async ({ response }) => { - const assets = await glob(this.options.assets); - - auto.logger.log.info(endent` - Uploading: - - ${assets.map((asset) => `\t- ${asset}`).join("\n")} - - `); - - await Promise.all( - assets.map(async (asset) => { - if (!auto.git || !response) { - return; - } - - const file = await readFile(asset); - const stats = await stat(asset); - const type = await FileType.fromBuffer(file); - - const DEFAULT_BASE_URL = "https://api.github.com"; - const baseUrl = auto.git.options.baseUrl || DEFAULT_BASE_URL; - const options: RestEndpointMethodTypes['repos']['uploadReleaseAsset']['parameters'] = { - // placeholder to appease typescript - release_id: -1, - data: (file as unknown) as string, - name: path.basename(asset), - owner: auto.git.options.owner, - repo: auto.git.options.repo, - headers: { - "content-length": stats.size, - "content-type": type ? type.mime : "application/octet-stream", - }, - }; - - if (baseUrl !== DEFAULT_BASE_URL) { - const { origin } = new URL(baseUrl); - options.baseUrl = `${origin}/api/uploads`; - } - - // Multiple releases were made - if (Array.isArray(response)) { - await Promise.all( - response.map((r) => - auto.git!.github.repos.uploadReleaseAsset({ - ...options, - release_id: r.data.id, - }) - ) - ); - } else { - await auto.git.github.repos.uploadReleaseAsset({ - ...options, - release_id: response.data.id, - }); - } - - auto.logger.log.success(`Uploaded asset: ${asset}`); - }) - ); + if (!response) { + return; + } + + await this.uploadAssets(auto, response); + await this.cleanupCanaryAssets(auto); }); } + + /** Upload the configured asset to a release */ + private async uploadAssets( + auto: Auto, + release: Release | Release[], + dryRun = false, + id?: string + ) { + const assets = await glob(this.options.assets); + + auto.logger.log.info(endent` + ${dryRun ? "Would upload" : "Uploading"}: + + ${assets.map((asset) => ` - ${asset}`).join("\n")} + `); + console.log(""); + + if (dryRun) { + return []; + } + + const responses = await Promise.all( + assets.map(async (asset) => { + if (!auto.git) { + return; + } + + const file = await readFile(asset); + const stats = await stat(asset); + const type = await FileType.fromBuffer(file); + + const DEFAULT_BASE_URL = "https://api.github.com"; + const baseUrl = auto.git.options.baseUrl || DEFAULT_BASE_URL; + const fileName = path.basename(asset); + const extension = path.extname(fileName); + const options: RestEndpointMethodTypes["repos"]["uploadReleaseAsset"]["parameters"] = { + // placeholder to appease typescript + release_id: -1, + data: (file as unknown) as string, + name: id + ? extension + ? fileName.replace(extension, `-${id}${extension}`) + : `${fileName}-${id}` + : fileName, + owner: auto.git.options.owner, + repo: auto.git.options.repo, + headers: { + "content-length": stats.size, + "content-type": type ? type.mime : "application/octet-stream", + }, + }; + + if (baseUrl !== DEFAULT_BASE_URL) { + const { origin } = new URL(baseUrl); + options.baseUrl = `${origin}/api/uploads`; + } + + const assetResponses: AssetResponse[] = []; + + // Multiple releases were made + if (Array.isArray(release)) { + await Promise.all( + release.map(async (r) => { + const { + data: releaseAsset, + } = await auto.git!.github.repos.uploadReleaseAsset({ + ...options, + release_id: r.data.id, + }); + + assetResponses.push(releaseAsset); + }) + ); + } else { + const { + data: releaseAsset, + } = await auto.git.github.repos.uploadReleaseAsset({ + ...options, + release_id: release.data.id, + }); + + assetResponses.push(releaseAsset); + } + + auto.logger.log.success(`Uploaded asset: ${asset}`); + return assetResponses; + }) + ); + + return responses + .filter((r): r is AssetResponse[] => Boolean(r)) + .reduce((acc, item) => [...acc, ...item], []); + } + + /** Get the release all the canaries are stored in */ + private async getCanaryGitHubRelease(auto: Auto) { + try { + const canaryRelease = await auto.git!.github.repos.getReleaseByTag({ + repo: auto.git!.options.repo, + owner: auto.git!.options.owner, + tag: "canary", + }); + + return canaryRelease.data; + } catch (error) { + const canaryRelease = await auto.git!.github.repos.createRelease({ + repo: auto.git!.options.repo, + owner: auto.git!.options.owner, + tag_name: "canary", + prerelease: true, + body: `This release contains preview assets of Pull Requests.`, + }); + + return canaryRelease.data; + } + } + + /** Delete old canary asset */ + private async cleanupCanaryAssets(auto: Auto) { + const canaryRelease = await this.getCanaryGitHubRelease(auto); + const canaryReleaseAssets = await auto.git!.github.paginate( + auto.git!.github.repos.listReleaseAssets, + { + repo: auto.git!.options.repo, + owner: auto.git!.options.owner, + release_id: canaryRelease.id, + } + ); + const assetsToDelete = canaryReleaseAssets + .sort( + (a, b) => + new Date(a.created_at).getTime() - new Date(b.created_at).getTime() + ) + .slice(0, canaryReleaseAssets.length - this.options.maxCanaryAssets); + + if (!assetsToDelete.length) { + return; + } + + auto.logger.log.info(endent` + Deleting old canary assets: + + ${assetsToDelete.map((asset) => ` - ${asset}`).join("\n")} + `); + + await Promise.all( + assetsToDelete.map(async (assetToDelete) => { + await auto.git!.github.repos.deleteReleaseAsset({ + repo: auto.git!.options.repo, + owner: auto.git!.options.owner, + release_id: canaryRelease.id, + asset_id: assetToDelete.id, + }); + }) + ); + } } From c708200b779d491deb6b2b104b8018e7a0d00870 Mon Sep 17 00:00:00 2001 From: Andrew Lisowski Date: Sat, 16 Jan 2021 11:18:51 -0800 Subject: [PATCH 2/2] add link --- plugins/git-tag/README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/plugins/git-tag/README.md b/plugins/git-tag/README.md index f2329e75c..79d11cf6e 100644 --- a/plugins/git-tag/README.md +++ b/plugins/git-tag/README.md @@ -39,3 +39,8 @@ Simply add the plugins to your auto configuration. "plugins": ["git-tag"] } ``` + +## Canary Releases + +This plugin does not support canaries. +For canary support try using [the upload-assets plugin](./upload-assets)