Skip to content

Commit

Permalink
Allow upgrading to RCs with fern upgrade (#991)
Browse files Browse the repository at this point in the history
  • Loading branch information
zachkirsch authored Dec 13, 2022
1 parent 0608840 commit 5658f0b
Show file tree
Hide file tree
Showing 18 changed files with 157 additions and 41 deletions.
11 changes: 9 additions & 2 deletions packages/cli/cli/src/cli-context/CliContext.ts
Original file line number Diff line number Diff line change
Expand Up @@ -218,11 +218,18 @@ export class CliContext {
}

private _isUpgradeAvailable: FernCliUpgradeInfo | undefined;
public async isUpgradeAvailable(): Promise<FernCliUpgradeInfo> {
public async isUpgradeAvailable({
includePreReleases = false,
}: {
includePreReleases?: boolean;
} = {}): Promise<FernCliUpgradeInfo> {
if (this._isUpgradeAvailable == null) {
this.logger.debug(`Checking if ${this.environment.packageName} upgrade is available...`);

const latestPackageVersion = await getLatestVersionOfCli(this.environment);
const latestPackageVersion = await getLatestVersionOfCli({
cliEnvironment: this.environment,
includePreReleases,
});
const isUpgradeAvailable = isVersionAhead(latestPackageVersion, this.environment.packageVersion);

this.logger.debug(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,19 @@
import latestVersion from "latest-version";
import { CliEnvironment } from "../CliEnvironment";

export async function getLatestVersionOfCli(cliEnvironment: CliEnvironment): Promise<string> {
export async function getLatestVersionOfCli({
cliEnvironment,
includePreReleases = false,
}: {
cliEnvironment: CliEnvironment;
includePreReleases?: boolean;
}): Promise<string> {
// when running a non-published version of the CLI (e.g. in ETE tests),
// don't try to upgrade
if (cliEnvironment.packageVersion === "0.0.0") {
return cliEnvironment.packageVersion;
}
return latestVersion(cliEnvironment.packageName, {
version: "latest",
version: includePreReleases ? "prerelease" : "latest",
});
}
15 changes: 10 additions & 5 deletions packages/cli/cli/src/cli.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
import { noop } from "@fern-api/core-utils";
import { cwd, resolve } from "@fern-api/fs-utils";
import { initialize } from "@fern-api/init";
import { Language } from "@fern-api/ir-generator";
Expand Down Expand Up @@ -145,7 +144,7 @@ async function getIntendedVersionOfCli(cliContext: CliContext): Promise<string>
);
return projectConfig.version;
}
return getLatestVersionOfCli(cliContext.environment);
return getLatestVersionOfCli({ cliEnvironment: cliContext.environment });
}

function addInitCommand(cli: Argv<GlobalCliOptions>, cliContext: CliContext) {
Expand All @@ -162,7 +161,7 @@ function addInitCommand(cli: Argv<GlobalCliOptions>, cliContext: CliContext) {
await cliContext.runTask(async (context) =>
initialize({
organization: argv.organization,
versionOfCli: await getLatestVersionOfCli(cliContext.environment),
versionOfCli: await getLatestVersionOfCli({ cliEnvironment: cliContext.environment }),
context,
})
);
Expand Down Expand Up @@ -302,10 +301,16 @@ function addUpgradeCommand({
cli.command(
"upgrade",
`Upgrades versions in ${GENERATORS_CONFIGURATION_FILENAME} and ${PROJECT_CONFIG_FILENAME}`,
noop,
async () => {
(yargs) =>
yargs.option("rc", {
boolean: true,
hidden: true,
default: false,
}),
async (argv) => {
await upgrade({
cliContext,
includePreReleases: argv.rc,
});
onRun();
}
Expand Down
12 changes: 10 additions & 2 deletions packages/cli/cli/src/commands/upgrade/upgrade.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,16 @@ const PREVIOUS_VERSION_ENV_VAR = "FERN_PRE_UPGRADE_VERSION";
* - run any migrations between PREVIOUS_VERSION_ENV_VAR and the latest version of the CLI.
* - change the generator versions in generators.yml to the latest stable versions
*/
export async function upgrade({ cliContext }: { cliContext: CliContext }): Promise<void> {
const fernCliUpgradeInfo = await cliContext.isUpgradeAvailable();
export async function upgrade({
cliContext,
includePreReleases,
}: {
cliContext: CliContext;
includePreReleases: boolean;
}): Promise<void> {
const fernCliUpgradeInfo = await cliContext.isUpgradeAvailable({
includePreReleases,
});
if (!fernCliUpgradeInfo.isUpgradeAvailable) {
const previousVersion = process.env[PREVIOUS_VERSION_ENV_VAR];
if (previousVersion == null) {
Expand Down
20 changes: 14 additions & 6 deletions packages/cli/semver-utils/src/__test__/isVersionAhead.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,20 @@ import { isVersionAhead } from "../isVersionAhead";

describe("isVersionAhead", () => {
it.each`
a | b | expected
${"0.0.191"} | ${"0.0.190"} | ${true}
${"0.0.191"} | ${"0.0.191"} | ${false}
${"0.0.191"} | ${"0.0.192"} | ${false}
${"0.0.191"} | ${"0.0.191-4-abc"} | ${false}
${"0.0.191-4-abc"} | ${"0.0.191"} | ${true}
a | b | expected
${"0.0.191"} | ${"0.0.190"} | ${true}
${"0.0.191"} | ${"0.0.191"} | ${false}
${"0.0.191"} | ${"0.0.192"} | ${false}
${"0.0.191"} | ${"0.0.191-4-abc"} | ${false}
${"0.0.191-4-abc"} | ${"0.0.191"} | ${true}
${"0.0.191-2-abc"} | ${"0.0.191-1-abc"} | ${true}
${"0.0.191-11-abc"} | ${"0.0.191-2-abc"} | ${true}
${"0.0.191-rc0"} | ${"0.0.190"} | ${true}
${"0.0.191-rc0"} | ${"0.0.191"} | ${false}
${"0.0.191-rc0"} | ${"0.0.192"} | ${false}
${"0.0.191-rc0"} | ${"0.0.191-4-abc"} | ${false}
${"0.0.191-rc2"} | ${"0.0.191-rc1"} | ${true}
${"0.0.191-rc2"} | ${"0.0.191-rc11"} | ${false}
`("$a vs. $b", ({ a, b, expected }) => {
expect(isVersionAhead(a, b)).toBe(expected);
});
Expand Down
122 changes: 102 additions & 20 deletions packages/cli/semver-utils/src/isVersionAhead.ts
Original file line number Diff line number Diff line change
@@ -1,32 +1,114 @@
import semverDiff from "semver-diff";

const COMMIT_VERSION_REGEX = /([0-9]+)\.([0-9]+)\.([0-9]+)-([0-9]+)-.+/;
const RC_VERSION_REGEX = /([0-9]+)\.([0-9]+)\.([0-9]+)-rc([0-9]+)/;

type Version = ReleaseCandidate | Release | PostReleaseCommit;

interface ReleaseCandidate {
type: "rc";
forVersion: string;
releaseCandidateIndex: number;
}

interface Release {
type: "release";
version: string;
}

interface PostReleaseCommit {
type: "commit";
releasedVersion: string;
commitIndex: number;
}

/**
* returns whether version a came after version b
*/
export function isVersionAhead(a: string, b: string): boolean {
if (a === b) {
return false;
const aVersion = parseVersion(a);
const bVersion = parseVersion(b);

// if versions are different, then default to semverDiff
const aVersionString = getVersionString(aVersion);
const bVersionString = getVersionString(bVersion);
if (aVersionString !== bVersionString) {
return semverDiff(aVersionString, bVersionString) == null;
}

if (aVersion.type === "commit") {
return bVersion.type !== "commit" || aVersion.commitIndex > bVersion.commitIndex;
}

const diff = semverDiff(a, b);

// generally, diff == null implies that a >= b.
if (diff == null) {
// however, fern versions use git describe, which is not exactly
// semver-compatible.
// e.g. in git describe, 0.0.191-2-abc is ahead of 0.0.191.
// in semver, 0.0.191-2-abc is a prerelease of (i.e. precedes) 0.0.191.
// so if semverDiff thinks that b is a prerelease of a, then we know that b
// actually came after a.
if (semverDiff(b, a) === "prerelease") {
return false;
} else {
return true;
if (aVersion.type === "release") {
switch (bVersion.type) {
case "commit":
case "release":
return false;
case "rc":
return true;
}
}

// same case here. generally, if diff is defined, then a < b.
// but if semverDiff thinks that a is a prerelease of b, then we know that a
// actually came after b.
return diff === "prerelease";
return bVersion.type === "rc" && aVersion.releaseCandidateIndex > bVersion.releaseCandidateIndex;
}

function parseVersion(versionString: string): Version {
const commitMatch = versionString.match(COMMIT_VERSION_REGEX);
if (commitMatch != null) {
const [_, major, minor, patch, commitIndex] = commitMatch;
const parsedCommitIndex = Number(commitIndex);
if (
major == null ||
minor == null ||
patch == null ||
commitIndex == null ||
commitIndex.length === 0 ||
isNaN(parsedCommitIndex)
) {
throw new Error("Cannot parse commit version: " + versionString);
}
return {
type: "commit",
releasedVersion: `${major}.${minor}.${patch}`,
commitIndex: parsedCommitIndex,
};
}

const rcMatch = versionString.match(RC_VERSION_REGEX);
if (rcMatch != null) {
const [_, major, minor, patch, rcIndex] = rcMatch;
const parsedRcIndex = Number(rcIndex);
if (
major == null ||
minor == null ||
patch == null ||
rcIndex == null ||
rcIndex.length === 0 ||
isNaN(parsedRcIndex)
) {
throw new Error("Cannot parse RC version: " + versionString);
}
return {
type: "rc",
forVersion: `${major}.${minor}.${patch}`,
releaseCandidateIndex: parsedRcIndex,
};
}

return {
type: "release",
version: versionString,
};
}

function getVersionString(version: Version): string {
switch (version.type) {
case "commit":
return version.releasedVersion;
case "rc":
return version.forVersion;
case "release":
return version.version;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { VersionMigrations } from "../../types/VersionMigrations";
import AddInlineRequestsMigration from "./add-inline-requests";

const versionMigrations: VersionMigrations = {
version: "0.1.2",
version: "0.1.3-rc3",
migrations: [AddInlineRequestsMigration],
};

Expand Down
4 changes: 2 additions & 2 deletions packages/cli/yaml/yaml-migrations/src/migrations/all.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import migrations_0_0_220 from "./0.0.220";
import migrations_0_0_221 from "./0.0.221";
import migrations_0_0_241 from "./0.0.241";
import migrations_0_0_248 from "./0.0.248";
import migrations_0_1_2 from "./0.1.2";
import migrations_0_1_3_rc3 from "./0.1.3-rc3";

export const ALL_MIGRATIONS: VersionMigrations[] = [
migrations_0_0_188,
Expand All @@ -22,5 +22,5 @@ export const ALL_MIGRATIONS: VersionMigrations[] = [
migrations_0_0_221,
migrations_0_0_241,
migrations_0_0_248,
migrations_0_1_2,
migrations_0_1_3_rc3,
];
2 changes: 1 addition & 1 deletion scripts/is-release-candidate.sh
Original file line number Diff line number Diff line change
Expand Up @@ -8,4 +8,4 @@ if [[ -z "$version" ]]; then
exit 1
fi

[[ "$version" =~ ^^([0-9]+)\.([0-9]+)\.([0-9]+)-rc([0-9]+)$ ]]
[[ "$version" =~ ^([0-9]+)\.([0-9]+)\.([0-9]+)-rc([0-9]+)$ ]]

0 comments on commit 5658f0b

Please sign in to comment.