mirror of
https://github.com/Kir-Antipov/mc-publish.git
synced 2024-11-25 01:41:05 -05:00
Refactored GitHubPublisher
(-> GitHubUploader
)
This commit is contained in:
parent
806f2f658c
commit
49cd72c033
2 changed files with 136 additions and 115 deletions
136
src/platforms/github/github-uploader.ts
Normal file
136
src/platforms/github/github-uploader.ts
Normal file
|
@ -0,0 +1,136 @@
|
||||||
|
import { GitHubUploadRequest as UploadRequest, GitHubUploadReport as UploadReport } from "@/action";
|
||||||
|
import { GenericPlatformUploader, GenericPlatformUploaderOptions } from "@/platforms/generic-platform-uploader";
|
||||||
|
import { PlatformType } from "@/platforms/platform-type";
|
||||||
|
import { GitHubContext } from "./github-context";
|
||||||
|
import { ArgumentNullError } from "@/utils/errors";
|
||||||
|
import { GitHubApiClient } from "./github-api-client";
|
||||||
|
import { GitHubRelease } from "./github-release";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Configuration options for the uploader, tailored for use with GitHub.
|
||||||
|
*/
|
||||||
|
export interface GitHubUploaderOptions extends GenericPlatformUploaderOptions {
|
||||||
|
/**
|
||||||
|
* Provides the context of the current GitHub Actions workflow run.
|
||||||
|
*/
|
||||||
|
githubContext: GitHubContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the structure for an upload request, adapted for use with GitHub.
|
||||||
|
*/
|
||||||
|
export type GitHubUploadRequest = UploadRequest;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Specifies the structure of the report generated after a successful upload to GitHub.
|
||||||
|
*/
|
||||||
|
export type GitHubUploadReport = UploadReport;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Implements the uploader for GitHub.
|
||||||
|
*/
|
||||||
|
export class GitHubUploader extends GenericPlatformUploader<GitHubUploaderOptions, GitHubUploadRequest, GitHubUploadReport> {
|
||||||
|
/**
|
||||||
|
* Provides the context of the current GitHub Actions workflow run.
|
||||||
|
*/
|
||||||
|
private readonly _context: GitHubContext;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructs a new {@link GitHubUploader} instance.
|
||||||
|
*
|
||||||
|
* @param options - The options to use for the uploader.
|
||||||
|
*/
|
||||||
|
constructor(options: GitHubUploaderOptions) {
|
||||||
|
ArgumentNullError.throwIfNull(options, "options");
|
||||||
|
ArgumentNullError.throwIfNull(options.githubContext, "options.githubContext");
|
||||||
|
ArgumentNullError.throwIfNull(options.githubContext.repo, "options.githubContext.repo");
|
||||||
|
|
||||||
|
super(options);
|
||||||
|
this._context = options.githubContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
get platform(): PlatformType {
|
||||||
|
return PlatformType.GITHUB;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @inheritdoc
|
||||||
|
*/
|
||||||
|
protected async uploadCore(request: GitHubUploadRequest): Promise<GitHubUploadReport> {
|
||||||
|
const api = new GitHubApiClient({ token: request.token.unwrap(), baseUrl: this._context.apiUrl });
|
||||||
|
const repo = this._context.repo;
|
||||||
|
|
||||||
|
const releaseId = await this.getOrCreateReleaseId(request, api);
|
||||||
|
const release = await this.updateRelease(request, releaseId, api);
|
||||||
|
|
||||||
|
return {
|
||||||
|
repo: `${repo.owner}/${repo.repo}`,
|
||||||
|
tag: release.tag_name,
|
||||||
|
url: release.html_url,
|
||||||
|
files: release.assets.map(x => ({ id: x.id, name: x.name, url: x.url })),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieves the ID of an existing release that matches the request parameters.
|
||||||
|
* If no such release exists, it creates a new release and returns its ID.
|
||||||
|
*
|
||||||
|
* @param request - Contains parameters that define the desired release.
|
||||||
|
* @param api - An instance of the GitHub API client for interacting with GitHub services.
|
||||||
|
*
|
||||||
|
* @returns The ID of the release corresponding to the request parameters.
|
||||||
|
*/
|
||||||
|
private async getOrCreateReleaseId(request: GitHubUploadRequest, api: GitHubApiClient): Promise<number> {
|
||||||
|
const repo = this._context.repo;
|
||||||
|
const tag = request.tag || this._context.tag || request.version;
|
||||||
|
|
||||||
|
let releaseId = undefined as number;
|
||||||
|
if (request.tag) {
|
||||||
|
releaseId = await api.getRelease({ ...repo, tag_name: request.tag }).then(x => x?.id);
|
||||||
|
} else if (this._context.payload.release?.id) {
|
||||||
|
releaseId = this._context.payload.release.id;
|
||||||
|
} else if (tag) {
|
||||||
|
releaseId = await api.getRelease({ ...repo, tag_name: tag }).then(x => x?.id);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!releaseId && tag) {
|
||||||
|
releaseId = (await api.createRelease({
|
||||||
|
...repo,
|
||||||
|
tag_name: tag,
|
||||||
|
target_commitish: request.commitish,
|
||||||
|
name: request.name,
|
||||||
|
body: request.changelog,
|
||||||
|
draft: request.draft,
|
||||||
|
prerelease: request.prerelease,
|
||||||
|
discussion_category_name: request.discussion,
|
||||||
|
generate_release_notes: request.generateChangelog,
|
||||||
|
}))?.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!releaseId) {
|
||||||
|
throw new Error(`Cannot find or create GitHub Release${tag ? ` (${tag})` : ""}.`);
|
||||||
|
}
|
||||||
|
return releaseId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the content of an existing GitHub release based on the provided request.
|
||||||
|
*
|
||||||
|
* @param request - Contains parameters that define the changes to apply to the release.
|
||||||
|
* @param releaseId - The ID of the release to be updated.
|
||||||
|
* @param api - An instance of the GitHub API client for interacting with GitHub services.
|
||||||
|
*
|
||||||
|
* @returns The updated release data from GitHub.
|
||||||
|
*/
|
||||||
|
private async updateRelease(request: GitHubUploadRequest, releaseId: number, api: GitHubApiClient): Promise<GitHubRelease> {
|
||||||
|
return await api.updateRelease({
|
||||||
|
...this._context.repo,
|
||||||
|
id: releaseId,
|
||||||
|
body: request.changelog,
|
||||||
|
assets: request.files,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,115 +0,0 @@
|
||||||
import PublisherTarget from "../publisher-target";
|
|
||||||
import * as github from "@actions/github";
|
|
||||||
import File from "../../utils/io/file";
|
|
||||||
import ModPublisher from "../../publishing/mod-publisher";
|
|
||||||
import Dependency from "../../metadata/dependency";
|
|
||||||
import { mapStringInput, mapBooleanInput } from "../../utils/actions/input";
|
|
||||||
import VersionType from "../../utils/versioning/version-type";
|
|
||||||
import { env } from "process";
|
|
||||||
|
|
||||||
function getEnvironmentTag(): string | undefined {
|
|
||||||
if (env.GITHUB_REF?.startsWith("refs/tags/")) {
|
|
||||||
return env.GITHUB_REF.substring(10);
|
|
||||||
}
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
|
|
||||||
export default class GitHubPublisher extends ModPublisher {
|
|
||||||
public get target(): PublisherTarget {
|
|
||||||
return PublisherTarget.GitHub;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected get requiresId(): boolean {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected get requiresGameVersions(): boolean {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected get requiresModLoaders(): boolean {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected async publishMod(_id: string, token: string, name: string, version: string, channel: string, _loaders: string[], _gameVersions: string[], _java: string[], changelog: string, files: File[], _dependencies: Dependency[], options: Record<string, unknown>): Promise<void> {
|
|
||||||
const repo = github.context.repo;
|
|
||||||
const octokit = github.getOctokit(token);
|
|
||||||
const environmentTag = getEnvironmentTag();
|
|
||||||
|
|
||||||
let tag = mapStringInput(options.tag, null);
|
|
||||||
let releaseId = 0;
|
|
||||||
if (tag) {
|
|
||||||
releaseId = await this.getReleaseIdByTag(tag, token);
|
|
||||||
} else if (github.context.payload.release?.id) {
|
|
||||||
releaseId = github.context.payload.release?.id;
|
|
||||||
} else if (environmentTag) {
|
|
||||||
releaseId = await this.getReleaseIdByTag(environmentTag, token);
|
|
||||||
} else if (version) {
|
|
||||||
releaseId = await this.getReleaseIdByTag(version, token);
|
|
||||||
}
|
|
||||||
|
|
||||||
const generated = !releaseId;
|
|
||||||
if (!releaseId && (tag ??= environmentTag ?? version)) {
|
|
||||||
const generateChangelog = mapBooleanInput(options.generateChangelog, !changelog);
|
|
||||||
const draft = mapBooleanInput(options.draft, false);
|
|
||||||
const prerelease = mapBooleanInput(options.prerelease, channel !== VersionType.Release);
|
|
||||||
const commitish = mapStringInput(options.commitish, null);
|
|
||||||
const discussion = mapStringInput(options.discussion, null);
|
|
||||||
releaseId = await this.createRelease(tag, name, changelog, generateChangelog, draft, prerelease, commitish, discussion, token);
|
|
||||||
}
|
|
||||||
if (!releaseId) {
|
|
||||||
throw new Error(`Cannot find or create release ${tag}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
const existingAssets = generated ? [] : (await octokit.rest.repos.listReleaseAssets({ ...repo, release_id: releaseId })).data;
|
|
||||||
for (const file of files) {
|
|
||||||
const existingAsset = existingAssets.find(x => x.name === file.name || x.name === file.path);
|
|
||||||
if (existingAsset) {
|
|
||||||
await octokit.rest.repos.deleteReleaseAsset({ ...repo, asset_id: existingAsset.id })
|
|
||||||
}
|
|
||||||
|
|
||||||
await octokit.rest.repos.uploadReleaseAsset({
|
|
||||||
owner: repo.owner,
|
|
||||||
repo: repo.repo,
|
|
||||||
release_id: releaseId,
|
|
||||||
name: file.name,
|
|
||||||
data: <any>await file.getBuffer()
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async getReleaseIdByTag(tag: string, token: string): Promise<number | undefined> {
|
|
||||||
const octokit = github.getOctokit(token);
|
|
||||||
try {
|
|
||||||
const response = await octokit.rest.repos.getReleaseByTag({
|
|
||||||
owner: github.context.repo.owner,
|
|
||||||
repo: github.context.repo.repo,
|
|
||||||
tag
|
|
||||||
});
|
|
||||||
return response.status >= 200 && response.status < 300 ? response.data.id : undefined;
|
|
||||||
} catch {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private async createRelease(tag: string, name: string, body: string, generateReleaseNotes: boolean, draft: boolean, prerelease: boolean, targetCommitish: string, discussionCategoryName: string, token: string): Promise<number | undefined> {
|
|
||||||
const octokit = github.getOctokit(token);
|
|
||||||
try {
|
|
||||||
const response = await octokit.rest.repos.createRelease({
|
|
||||||
tag_name: tag,
|
|
||||||
owner: github.context.repo.owner,
|
|
||||||
repo: github.context.repo.repo,
|
|
||||||
target_commitish: targetCommitish || undefined,
|
|
||||||
name: name || undefined,
|
|
||||||
body: body || undefined,
|
|
||||||
draft,
|
|
||||||
prerelease,
|
|
||||||
discussion_category_name: discussionCategoryName || undefined,
|
|
||||||
generate_release_notes: generateReleaseNotes,
|
|
||||||
});
|
|
||||||
return response.status >= 200 && response.status < 300 ? response.data.id : undefined;
|
|
||||||
} catch {
|
|
||||||
return undefined;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in a new issue