mirror of
https://github.com/softprops/action-gh-release.git
synced 2026-03-20 05:58:51 +08:00
fix: fetch correct asset URL after finalization; test; some refactoring (#738)
This commit is contained in:
104
src/github.ts
104
src/github.ts
@@ -65,6 +65,22 @@ export interface Releaser {
|
||||
}): Promise<{ data: Release }>;
|
||||
|
||||
allReleases(params: { owner: string; repo: string }): AsyncIterable<{ data: Release[] }>;
|
||||
|
||||
listReleaseAssets(params: {
|
||||
owner: string;
|
||||
repo: string;
|
||||
release_id: number;
|
||||
}): Promise<Array<{ id: number; name: string; [key: string]: any }>>;
|
||||
|
||||
deleteReleaseAsset(params: { owner: string; repo: string; asset_id: number }): Promise<void>;
|
||||
|
||||
uploadReleaseAsset(params: {
|
||||
url: string;
|
||||
size: number;
|
||||
mime: string;
|
||||
token: string;
|
||||
data: any;
|
||||
}): Promise<{ status: number; data: any }>;
|
||||
}
|
||||
|
||||
export class GitHubReleaser implements Releaser {
|
||||
@@ -181,6 +197,44 @@ export class GitHubReleaser implements Releaser {
|
||||
this.github.rest.repos.listReleases.endpoint.merge(updatedParams),
|
||||
);
|
||||
}
|
||||
|
||||
async listReleaseAssets(params: {
|
||||
owner: string;
|
||||
repo: string;
|
||||
release_id: number;
|
||||
}): Promise<Array<{ id: number; name: string; [key: string]: any }>> {
|
||||
return this.github.paginate(this.github.rest.repos.listReleaseAssets, {
|
||||
...params,
|
||||
per_page: 100,
|
||||
});
|
||||
}
|
||||
|
||||
async deleteReleaseAsset(params: {
|
||||
owner: string;
|
||||
repo: string;
|
||||
asset_id: number;
|
||||
}): Promise<void> {
|
||||
await this.github.rest.repos.deleteReleaseAsset(params);
|
||||
}
|
||||
|
||||
async uploadReleaseAsset(params: {
|
||||
url: string;
|
||||
size: number;
|
||||
mime: string;
|
||||
token: string;
|
||||
data: any;
|
||||
}): Promise<{ status: number; data: any }> {
|
||||
return this.github.request({
|
||||
method: 'POST',
|
||||
url: params.url,
|
||||
headers: {
|
||||
'content-length': `${params.size}`,
|
||||
'content-type': params.mime,
|
||||
authorization: `token ${params.token}`,
|
||||
},
|
||||
data: params.data,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export const asset = (path: string): ReleaseAsset => {
|
||||
@@ -197,7 +251,7 @@ export const mimeOrDefault = (path: string): string => {
|
||||
|
||||
export const upload = async (
|
||||
config: Config,
|
||||
github: GitHub,
|
||||
releaser: Releaser,
|
||||
url: string,
|
||||
path: string,
|
||||
currentAssets: Array<{ id: number; name: string }>,
|
||||
@@ -216,7 +270,7 @@ export const upload = async (
|
||||
return null;
|
||||
} else {
|
||||
console.log(`♻️ Deleting previously uploaded asset ${name}...`);
|
||||
await github.rest.repos.deleteReleaseAsset({
|
||||
await releaser.deleteReleaseAsset({
|
||||
asset_id: currentAsset.id || 1,
|
||||
owner,
|
||||
repo,
|
||||
@@ -228,14 +282,11 @@ export const upload = async (
|
||||
endpoint.searchParams.append('name', name);
|
||||
const fh = await open(path);
|
||||
try {
|
||||
const resp = await github.request({
|
||||
method: 'POST',
|
||||
const resp = await releaser.uploadReleaseAsset({
|
||||
url: endpoint.toString(),
|
||||
headers: {
|
||||
'content-length': `${size}`,
|
||||
'content-type': mime,
|
||||
authorization: `token ${config.github_token}`,
|
||||
},
|
||||
size,
|
||||
mime,
|
||||
token: config.github_token,
|
||||
data: fh.readableWebStream({ type: 'bytes' }),
|
||||
});
|
||||
const json = resp.data;
|
||||
@@ -399,6 +450,41 @@ export const finalizeRelease = async (
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Lists assets belonging to a release.
|
||||
*
|
||||
* @param config - Release configuration as specified by user
|
||||
* @param releaser - The GitHub API wrapper for release operations
|
||||
* @param release - The existing release to be checked
|
||||
* @param maxRetries - The maximum number of attempts
|
||||
*/
|
||||
export const listReleaseAssets = async (
|
||||
config: Config,
|
||||
releaser: Releaser,
|
||||
release: Release,
|
||||
maxRetries: number = 3,
|
||||
): Promise<Array<{ id: number; name: string; [key: string]: any }>> => {
|
||||
if (maxRetries <= 0) {
|
||||
console.log(`❌ Too many retries. Aborting...`);
|
||||
throw new Error('Too many retries.');
|
||||
}
|
||||
|
||||
const [owner, repo] = config.github_repository.split('/');
|
||||
try {
|
||||
const assets = await releaser.listReleaseAssets({
|
||||
owner,
|
||||
repo,
|
||||
release_id: release.id,
|
||||
});
|
||||
|
||||
return assets;
|
||||
} catch (error) {
|
||||
console.warn(`error listing assets of release: ${error}`);
|
||||
console.log(`retrying... (${maxRetries - 1} retries remaining)`);
|
||||
return listReleaseAssets(config, releaser, release, maxRetries - 1);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Finds a release by tag name from all a repository's releases.
|
||||
*
|
||||
|
||||
34
src/main.ts
34
src/main.ts
@@ -1,6 +1,6 @@
|
||||
import { setFailed, setOutput } from '@actions/core';
|
||||
import { getOctokit } from '@actions/github';
|
||||
import { GitHubReleaser, release, finalizeRelease, upload } from './github';
|
||||
import { GitHubReleaser, release, finalizeRelease, upload, listReleaseAssets } from './github';
|
||||
import { isTag, parseConfig, paths, unmatchedPatterns, uploadUrl } from './util';
|
||||
|
||||
import { env } from 'process';
|
||||
@@ -50,6 +50,7 @@ async function run() {
|
||||
//);
|
||||
const releaser = new GitHubReleaser(gh);
|
||||
let rel = await release(config, releaser);
|
||||
let uploadedAssetIds: Set<number> = new Set();
|
||||
if (config.input_files && config.input_files.length > 0) {
|
||||
const files = paths(config.input_files, config.input_working_directory);
|
||||
if (files.length == 0) {
|
||||
@@ -61,15 +62,12 @@ async function run() {
|
||||
}
|
||||
const currentAssets = rel.assets;
|
||||
|
||||
const uploadFile = async (path) => {
|
||||
const json = await upload(config, gh, uploadUrl(rel.upload_url), path, currentAssets);
|
||||
if (json) {
|
||||
delete json.uploader;
|
||||
}
|
||||
return json;
|
||||
const uploadFile = async (path: string) => {
|
||||
const json = await upload(config, releaser, uploadUrl(rel.upload_url), path, currentAssets);
|
||||
return json ? (json.id as number) : undefined;
|
||||
};
|
||||
|
||||
let results: (any | null)[];
|
||||
let results: (number | undefined)[];
|
||||
if (!config.input_preserve_order) {
|
||||
results = await Promise.all(files.map(uploadFile));
|
||||
} else {
|
||||
@@ -79,13 +77,29 @@ async function run() {
|
||||
}
|
||||
}
|
||||
|
||||
const assets = results.filter(Boolean);
|
||||
setOutput('assets', assets);
|
||||
uploadedAssetIds = new Set(results.filter((id): id is number => id !== undefined));
|
||||
}
|
||||
|
||||
console.log('Finalizing release...');
|
||||
rel = await finalizeRelease(config, releaser, rel);
|
||||
|
||||
// Draft releases use temporary "untagged-..." URLs for assets.
|
||||
// URLs will be changed to correct ones once the release is published.
|
||||
console.log('Getting assets list...');
|
||||
{
|
||||
let assets: any[] = [];
|
||||
if (uploadedAssetIds.size > 0) {
|
||||
const updatedAssets = await listReleaseAssets(config, releaser, rel);
|
||||
assets = updatedAssets
|
||||
.filter((a) => uploadedAssetIds.has(a.id))
|
||||
.map((a) => {
|
||||
const { uploader, ...rest } = a;
|
||||
return rest;
|
||||
});
|
||||
}
|
||||
setOutput('assets', assets);
|
||||
}
|
||||
|
||||
console.log(`🎉 Release ready at ${rel.html_url}`);
|
||||
setOutput('url', rel.html_url);
|
||||
setOutput('id', rel.id.toString());
|
||||
|
||||
Reference in New Issue
Block a user