mirror of
https://github.com/Kir-Antipov/mc-publish.git
synced 2024-11-21 16:00:59 -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