diff --git a/README.md b/README.md index ba3e978..e7289a4 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,7 @@ jobs: | changelog-file | A glob of the changelog file | ❌ | `CHANGELOG.md` | | loaders | A list of supported mod loaders | `fabric` | `fabric`
`forge`
`rift` | | game-versions | A list of supported Minecraft versions | Will be parsed from the `version` value (e.g., `0.40.0+1.18_experimental` results in `21w37a` and `21w38a` at the moment) | `21w37a`
`1.17` | +| version-resolver | Determines the way automatic `game-versions` resolvement works | `releasesIfAny` | `exact` - exact game version *(`1.16` -> `1.16`)*

`latest` - the latest release of the given minor *(`1.16` -> `1.16.5`)*

`all` - all versions of the given minor starting with the specified build

`releases` - all releases of the given minor starting with the specified build *(`1.16.3` -> `[1.16.3, 1.16.4, 1.16.5]`)*

`releasesIfAny` - all releases of the given minor starting with the specified build, if any; otherwise, all versions | | java | A list of supported Java versions | *empty string* | `Java 8`
`Java 1.8`
`8` | ### Minimalistic Example diff --git a/action.yml b/action.yml index db0bfb1..912b4c4 100644 --- a/action.yml +++ b/action.yml @@ -59,6 +59,9 @@ inputs: game-versions: description: A list of supported Minecraft versions required: false + version-resolver: + description: Determines the way automatic game-versions resolvement works + required: false java: description: A list of supported Java versions required: false diff --git a/src/publishing/mod-publisher.ts b/src/publishing/mod-publisher.ts index 35a6a57..079615c 100644 --- a/src/publishing/mod-publisher.ts +++ b/src/publishing/mod-publisher.ts @@ -1,8 +1,9 @@ import { context } from "@actions/github"; -import { getCompatibleBuilds, parseVersionNameFromFileVersion } from "../utils/minecraft-utils"; +import { parseVersionNameFromFileVersion } from "../utils/minecraft-utils"; import { File, getFiles, parseVersionFromFilename, parseVersionTypeFromFilename } from "../utils/file-utils"; import Publisher from "./publisher"; import PublisherTarget from "./publisher-target"; +import MinecraftVersionResolver from "../utils/minecraft-version-resolver"; interface ModPublisherOptions { id: string; @@ -12,6 +13,7 @@ interface ModPublisherOptions { name?: string; version?: string; changelog?: string | { file?: string }; + versionResolver?: string; gameVersions?: string | string[]; java?: string | string[]; files?: string | { primary?: string, secondary?: string }; @@ -67,7 +69,8 @@ export default abstract class ModPublisher extends Publisher x.id)); + const resolver = options.versionResolver && MinecraftVersionResolver.byName(options.versionResolver) || MinecraftVersionResolver.releasesIfAny; + gameVersions.push(...(await resolver.resolve(minecraftVersion)).map(x => x.id)); } if (!gameVersions.length) { throw new Error("At least one game version should be specified"); diff --git a/src/utils/game-version-resolver.ts b/src/utils/game-version-resolver.ts new file mode 100644 index 0000000..8c03814 --- /dev/null +++ b/src/utils/game-version-resolver.ts @@ -0,0 +1,19 @@ +import Version from "./version"; + +export default abstract class GameVersionResolver { + private readonly _filter: (version: string | Version, versions: TGameVersion[]) => TGameVersion[]; + + protected constructor(filter?: (version: string | Version, versions: TGameVersion[]) => TGameVersion[]) { + this._filter = filter || ((_, x) => x); + } + + public async resolve(version: string | Version): Promise { + return this.filter(version, await this.getCompatibleVersions(version)); + } + + public filter(version: string | Version, versions: TGameVersion[]): TGameVersion[] { + return this._filter(version, versions); + } + + public abstract getCompatibleVersions(version: string | Version): Promise; +} diff --git a/src/utils/minecraft-version-resolver.ts b/src/utils/minecraft-version-resolver.ts new file mode 100644 index 0000000..d0d3b83 --- /dev/null +++ b/src/utils/minecraft-version-resolver.ts @@ -0,0 +1,24 @@ +import GameVersionResolver from "./game-version-resolver"; +import { getCompatibleBuilds, MinecraftVersion } from "./minecraft-utils"; +import Version from "./version"; + +export default class MinecraftVersionResolver extends GameVersionResolver { + public static readonly exact = new MinecraftVersionResolver((n, v) => [v.find(x => x.version.equals(n))].filter(x => x)); + public static readonly latest = new MinecraftVersionResolver((_, v) => v.find(x => x.isRelease) ? [v.find(x => x.isRelease)] : v.length ? [v[0]] : []); + public static readonly all = new MinecraftVersionResolver((_, v) => v); + public static readonly releases = new MinecraftVersionResolver((_, v) => v.filter(x => x.isRelease)); + public static readonly releasesIfAny = new MinecraftVersionResolver((_, v) => v.find(x => x.isRelease) ? v.filter(x => x.isRelease) : v); + + public static byName(name: string): MinecraftVersionResolver | null { + for (const [key, value] of Object.entries(MinecraftVersionResolver)) { + if (value instanceof MinecraftVersionResolver && key.localeCompare(name, undefined, { sensitivity: "accent" }) === 0) { + return value; + } + } + return null; + } + + public getCompatibleVersions(version: string | Version): Promise { + return getCompatibleBuilds(version); + } +} diff --git a/src/utils/version.ts b/src/utils/version.ts index ff118a7..d37782e 100644 --- a/src/utils/version.ts +++ b/src/utils/version.ts @@ -16,4 +16,11 @@ export default class Version { this.build = build || 0; } } + + public equals(version: unknown): boolean { + if (version instanceof Version) { + return this.major === version.major && this.minor === version.minor && this.build === version.build; + } + return typeof version === "string" && this.equals(new Version(version)); + } } diff --git a/test/minecraft-version-resolver.test.ts b/test/minecraft-version-resolver.test.ts new file mode 100644 index 0000000..234024c --- /dev/null +++ b/test/minecraft-version-resolver.test.ts @@ -0,0 +1,115 @@ +import { describe, test, expect } from "@jest/globals"; +import MinecraftVersionResolver from "../src/utils/minecraft-version-resolver"; + +describe("MinecraftVersionResolver.byName", () => { + test("every predefined MinecraftVersionResolver can be resolved", () => { + for (const [key, value] of Object.entries(MinecraftVersionResolver).filter(([_, x]) => x instanceof MinecraftVersionResolver)) { + expect(MinecraftVersionResolver.byName(key)).toStrictEqual(value); + } + }); + + test("null is returned if MinecraftVersionResolver with the given name doesn't exist", () => { + expect(MinecraftVersionResolver.byName("non-existing-resolver")).toBeNull(); + }); + + test("name of MinecraftVersionResolver is case insensitive", () => { + expect(MinecraftVersionResolver.byName("latest")).toStrictEqual(MinecraftVersionResolver.latest); + expect(MinecraftVersionResolver.byName("Latest")).toStrictEqual(MinecraftVersionResolver.latest); + expect(MinecraftVersionResolver.byName("LATEST")).toStrictEqual(MinecraftVersionResolver.latest); + expect(MinecraftVersionResolver.byName("LatesT")).toStrictEqual(MinecraftVersionResolver.latest); + }); +}); + +describe("MinecraftVersionResolver.exact", () => { + test("the exact version is returned", async () => { + const _1_17 = (await MinecraftVersionResolver.exact.resolve("1.17")).map(x => x.id); + expect(_1_17).toHaveLength(1); + expect(_1_17).toContain("1.17"); + + const _1_17_1 = (await MinecraftVersionResolver.exact.resolve("1.17.1")).map(x => x.id); + expect(_1_17_1).toHaveLength(1); + expect(_1_17_1).toContain("1.17.1"); + }); + + test("empty array is returned if no versions were found", async () => { + expect(await MinecraftVersionResolver.exact.resolve("42.0")).toHaveLength(0); + }); +}); + +describe("MinecraftVersionResolver.latest", () => { + test("the latest version of the given minor is returned", async () => { + const versions = (await MinecraftVersionResolver.latest.resolve("1.17")).map(x => x.id); + expect(versions).toHaveLength(1); + expect(versions).toContain("1.17.1"); + }); + + test("empty array is returned if no versions were found", async () => { + expect(await MinecraftVersionResolver.latest.resolve("42.0")).toHaveLength(0); + }); +}); + +describe("MinecraftVersionResolver.all", () => { + test("all versions of the given minor are returned", async () => { + const versions = (await MinecraftVersionResolver.all.resolve("1.17")).map(x => x.id); + expect(versions).toHaveLength(31); + expect(versions).toContain("1.17"); + expect(versions).toContain("1.17.1"); + expect(versions).toContain("1.17.1-rc2"); + expect(versions).toContain("1.17.1-rc1"); + expect(versions).toContain("1.17.1-pre3"); + expect(versions).toContain("1.17.1-pre2"); + expect(versions).toContain("1.17.1-pre1"); + expect(versions).toContain("21w03a"); + }); + + test("all versions of the given minor starting with the given build are returned", async () => { + const versions = (await MinecraftVersionResolver.all.resolve("1.17.1")).map(x => x.id); + expect(versions).toHaveLength(6); + expect(versions).toContain("1.17.1"); + expect(versions).toContain("1.17.1-rc2"); + expect(versions).toContain("1.17.1-rc1"); + expect(versions).toContain("1.17.1-pre3"); + expect(versions).toContain("1.17.1-pre2"); + expect(versions).toContain("1.17.1-pre1"); + }); + + test("empty array is returned if no versions were found", async () => { + expect(await MinecraftVersionResolver.all.resolve("42.0")).toHaveLength(0); + }); +}); + +describe("MinecraftVersionResolver.releases", () => { + test("all releases of the given minor are returned", async () => { + const versions = (await MinecraftVersionResolver.releases.resolve("1.17")).map(x => x.id); + expect(versions).toHaveLength(2); + expect(versions).toContain("1.17"); + expect(versions).toContain("1.17.1"); + }); + + test("all releases of the given minor starting with the given build are returned", async () => { + const versions = (await MinecraftVersionResolver.releases.resolve("1.16.1")).map(x => x.id); + expect(versions).toHaveLength(5); + expect(versions).toContain("1.16.1"); + expect(versions).toContain("1.16.2"); + expect(versions).toContain("1.16.3"); + expect(versions).toContain("1.16.4"); + expect(versions).toContain("1.16.5"); + }); + + test("an empty array is returned if no versions were found", async () => { + expect(await MinecraftVersionResolver.releases.resolve("42.0")).toHaveLength(0); + }); +}); + +describe("MinecraftVersionResolver.releasesIfAny", () => { + test("all releases of the given minor are returned", async () => { + const versions = (await MinecraftVersionResolver.releasesIfAny.resolve("1.17")).map(x => x.id); + expect(versions).toHaveLength(2); + expect(versions).toContain("1.17"); + expect(versions).toContain("1.17.1"); + }); + + test("empty array is returned if no versions were found", async () => { + expect(await MinecraftVersionResolver.releasesIfAny.resolve("42.0")).toHaveLength(0); + }); +});