| /*! | |
| * Licensed to the Apache Software Foundation (ASF) under one | |
| * or more contributor license agreements. See the NOTICE file | |
| * distributed with this work for additional information | |
| * regarding copyright ownership. The ASF licenses this file | |
| * to you under the Apache License, Version 2.0 (the | |
| * "License"); you may not use this file except in compliance | |
| * with the License. You may obtain a copy of the License at | |
| * | |
| * http://www.apache.org/licenses/LICENSE-2.0 | |
| * | |
| * Unless required by applicable law or agreed to in writing, | |
| * software distributed under the License is distributed on an | |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY | |
| * KIND, either express or implied. See the License for the | |
| * specific language governing permissions and limitations | |
| * under the License. | |
| */ | |
| import { spawnSync } from "node:child_process"; | |
| import { mkdtempSync, readFileSync, rmSync } from "node:fs"; | |
| import { tmpdir } from "node:os"; | |
| import { join } from "node:path"; | |
| import process from "node:process"; | |
| import { fileURLToPath } from "node:url"; | |
| import { describe, expect, it } from "vitest"; | |
| import { validateReleaseInputs } from "./validate-release-inputs.mjs"; | |
| describe("validateReleaseInputs", () => { | |
| it("accepts stable and prerelease channels", () => { | |
| expect(validateReleaseInputs({ releaseTag: "ts-sdk/1.0.0", npmTag: "latest" })).toEqual({ | |
| version: "1.0.0", | |
| packageName: "apache-airflow-ts-sdk", | |
| packageFile: "apache-airflow-ts-sdk-1.0.0.tgz", | |
| }); | |
| expect( | |
| validateReleaseInputs({ releaseTag: "ts-sdk/1.0.0-beta1", npmTag: "beta" }).version, | |
| ).toBe("1.0.0-beta1"); | |
| expect( | |
| validateReleaseInputs({ releaseTag: "ts-sdk/1.0.0-beta.2", npmTag: "next" }).version, | |
| ).toBe("1.0.0-beta.2"); | |
| expect(validateReleaseInputs({ releaseTag: "ts-sdk/1.0.0-rc-1", npmTag: "rc" }).version).toBe( | |
| "1.0.0-rc-1", | |
| ); | |
| }); | |
| it("rejects invalid versions", () => { | |
| for (const releaseTag of ["1.0.0", "ts-sdk/01.0.0", "ts-sdk/1.0.0-01", "ts-sdk/1.0"]) { | |
| expect(() => validateReleaseInputs({ releaseTag, npmTag: "latest" })).toThrow( | |
| /Release tag|not valid SemVer/, | |
| ); | |
| } | |
| }); | |
| it("rejects mismatched npm tags", () => { | |
| expect(() => validateReleaseInputs({ releaseTag: "ts-sdk/1.0.0", npmTag: "beta" })).toThrow( | |
| /Stable releases must use the latest/, | |
| ); | |
| expect(() => | |
| validateReleaseInputs({ releaseTag: "ts-sdk/1.0.0-beta1", npmTag: "latest" }), | |
| ).toThrow(/must use the beta or next/); | |
| expect(() => | |
| validateReleaseInputs({ releaseTag: "ts-sdk/1.0.0-beta1", npmTag: "alpha" }), | |
| ).toThrow(/must use the beta or next/); | |
| }); | |
| it("rejects a numeric-only prerelease identifier without duplicating the next dist-tag", () => { | |
| expect(() => validateReleaseInputs({ releaseTag: "ts-sdk/1.0.0-0", npmTag: "beta" })).toThrow( | |
| "Prerelease 1.0.0-0 must use the next npm dist-tag", | |
| ); | |
| }); | |
| it("rejects a next-channel prerelease without duplicating the next dist-tag", () => { | |
| expect(() => | |
| validateReleaseInputs({ releaseTag: "ts-sdk/1.0.0-next1", npmTag: "beta" }), | |
| ).toThrow("Prerelease 1.0.0-next1 must use the next npm dist-tag"); | |
| }); | |
| it("rejects malformed npm tags", () => { | |
| for (const npmTag of ["", "1.0.0", "Beta", "bad tag", "-beta"]) { | |
| expect(() => validateReleaseInputs({ releaseTag: "ts-sdk/1.0.0-beta1", npmTag })).toThrow( | |
| /npm tag|must use/, | |
| ); | |
| } | |
| }); | |
| it("requires a release to advance the selected dist-tag", () => { | |
| expect( | |
| validateReleaseInputs({ | |
| releaseTag: "ts-sdk/1.0.0-beta.2", | |
| npmTag: "beta", | |
| currentDistTagVersion: "1.0.0-beta.1", | |
| }).version, | |
| ).toBe("1.0.0-beta.2"); | |
| for (const releaseTag of ["ts-sdk/1.0.0-beta.1", "ts-sdk/1.0.0-beta.0"]) { | |
| expect(() => | |
| validateReleaseInputs({ | |
| releaseTag, | |
| npmTag: "beta", | |
| currentDistTagVersion: "1.0.0-beta.1", | |
| }), | |
| ).toThrow(/must be newer/); | |
| } | |
| }); | |
| it("writes GitHub Actions outputs when run as a command", () => { | |
| const tempDirectory = mkdtempSync(join(tmpdir(), "ts-sdk-release-inputs-")); | |
| const outputFile = join(tempDirectory, "github-output"); | |
| try { | |
| const result = spawnSync( | |
| process.execPath, | |
| [fileURLToPath(import.meta.resolve("./validate-release-inputs.mjs"))], | |
| { | |
| encoding: "utf8", | |
| env: { | |
| ...process.env, | |
| GITHUB_OUTPUT: outputFile, | |
| NPM_TAG: "beta", | |
| RELEASE_TAG: "ts-sdk/1.0.0-beta1", | |
| }, | |
| }, | |
| ); | |
| expect(result.status, result.stderr).toBe(0); | |
| expect(readFileSync(outputFile, "utf8")).toBe( | |
| "version=1.0.0-beta1\npackage_name=apache-airflow-ts-sdk\n" + | |
| "package_file=apache-airflow-ts-sdk-1.0.0-beta1.tgz\n", | |
| ); | |
| } finally { | |
| rmSync(tempDirectory, { force: true, recursive: true }); | |
| } | |
| }); | |
| }); |