Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 6 additions & 52 deletions .ado/jobs/npm-publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,6 @@ jobs:
variables:
- name: BUILDSECMON_OPT_IN
value: true
- name: USE_YARN_FOR_PUBLISH
value: false

timeoutInMinutes: 90
cancelTimeoutInMinutes: 5
Expand All @@ -26,71 +24,27 @@ jobs:

- template: /.ado/templates/configure-git.yml@self

- script: |
PUBLISH_TAG=$(jq -r '.release.version.versionActionsOptions.currentVersionResolverMetadata.tag' nx.json)
if [ -z "$PUBLISH_TAG" ] || [ "$PUBLISH_TAG" = "null" ]; then
echo "Error: Failed to read publish tag from nx.json"
exit 1
fi
echo "##vso[task.setvariable variable=publishTag]$PUBLISH_TAG"
echo "Using publish tag from nx.json: $PUBLISH_TAG"
displayName: Read publish tag from nx.json

- script: |
yarn install
displayName: Install npm dependencies

- script: |
node .ado/scripts/prepublish-check.mjs --verbose --skip-auth --tag $(publishTag)
displayName: Verify release config

- script: |
echo Target branch: $(System.PullRequest.TargetBranch)
yarn nx release --dry-run --verbose
displayName: Version and publish packages (dry run)
node .ado/scripts/release.mjs --dry-run --verbose
displayName: Release (dry run)
condition: and(succeeded(), ne(variables['publish_react_native_macos'], '1'))

# Disable Nightly publishing on the main branch
- ${{ if endsWith(variables['Build.SourceBranchName'], '-stable') }}:
- script: |
git switch $(Build.SourceBranchName)
yarn nx release --skip-publish --verbose
RELEASE_TAG=$(tail -1 .ado/release-config.yml)
node .ado/scripts/release.mjs --verbose --tag "$RELEASE_TAG" --token "$(npmAuthToken)"
env:
GITHUB_TOKEN: $(githubAuthToken)
displayName: Version Packages and Github Release
condition: and(succeeded(), eq(variables['publish_react_native_macos'], '1'))

- script: |
set -eox pipefail
if [[ -f .rnm-publish ]]; then
# https://github.com/microsoft/react-native-macos/issues/2580
# `nx release publish` gets confused by the output of RNM's prepack script.
# Let's call publish directly instead on the packages we want to publish.
# yarn nx release publish --tag $(publishTag) --excludeTaskDependencies
if [ "$(USE_YARN_FOR_PUBLISH)" = "true" ]; then
echo "Configuring yarn for npm publishing"
yarn config set npmPublishRegistry "https://registry.npmjs.org"
yarn config set npmAuthToken $(npmAuthToken)
echo "Publishing with yarn npm publish"
yarn ./packages/virtualized-lists npm publish --tag $(publishTag)
yarn ./packages/react-native npm publish --tag $(publishTag)
else
echo "Publishing with npm publish"
npm publish ./packages/virtualized-lists --tag $(publishTag) --registry https://registry.npmjs.org/ --//registry.npmjs.org/:_authToken=$(npmAuthToken)
npm publish ./packages/react-native --tag $(publishTag) --registry https://registry.npmjs.org/ --//registry.npmjs.org/:_authToken=$(npmAuthToken)
fi
fi
displayName: Publish packages
displayName: Release
condition: and(succeeded(), eq(variables['publish_react_native_macos'], '1'))

- script: |
if [ "$(USE_YARN_FOR_PUBLISH)" = "true" ]; then
echo "Cleaning up yarn npm configuration"
yarn config unset npmAuthToken || true
yarn config unset npmPublishRegistry || true
else
echo "Cleaning up npm configuration"
rm -f ~/.npmrc
fi
rm -f ~/.npmrc
displayName: Remove NPM auth configuration
condition: always()
5 changes: 5 additions & 0 deletions .ado/release-config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Release tag for react-native-macos
# Set to "latest" to promote this branch to @latest on npm
# Set to "next" to publish release candidates
# Set to "stable" otherwise (releases continue automatically)
stable
189 changes: 189 additions & 0 deletions .ado/scripts/__tests__/release-test.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
/**
* Copyright (c) Microsoft Corporation.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* Run with: node --test .ado/scripts/__tests__/release-test.mjs
*
* @format
*/

import { describe, it } from "node:test";
import assert from "node:assert";
import {
isMainBranch,
isStableBranch,
versionToNumber,
getReleaseState,
getNxConfig,
getPublishTags,
} from "../release.mjs";

describe("release.mjs", () => {
describe("isMainBranch", () => {
it('returns true for "main"', () => {
assert.strictEqual(isMainBranch("main"), true);
});

it('returns false for "master"', () => {
assert.strictEqual(isMainBranch("master"), false);
});

it("returns false for stable branches", () => {
assert.strictEqual(isMainBranch("0.77-stable"), false);
});

it("returns false for feature branches", () => {
assert.strictEqual(isMainBranch("feature/my-feature"), false);
});
});

describe("isStableBranch", () => {
it("returns true for valid stable branches", () => {
assert.strictEqual(isStableBranch("0.77-stable"), true);
assert.strictEqual(isStableBranch("0.78-stable"), true);
assert.strictEqual(isStableBranch("1.0-stable"), true);
assert.strictEqual(isStableBranch("12.34-stable"), true);
});

it("returns false for main branch", () => {
assert.strictEqual(isStableBranch("main"), false);
});

it("returns false for invalid stable branch formats", () => {
assert.strictEqual(isStableBranch("0.77-stable-hotfix"), false);
assert.strictEqual(isStableBranch("stable"), false);
assert.strictEqual(isStableBranch("v0.77-stable"), false);
assert.strictEqual(isStableBranch("0.77.1-stable"), false);
assert.strictEqual(isStableBranch("77-stable"), false);
});

it("returns false for feature branches", () => {
assert.strictEqual(isStableBranch("feature/0.77-stable"), false);
});
});

describe("versionToNumber", () => {
it("compares major.minor versions correctly", () => {
assert.ok(versionToNumber("0.78") > versionToNumber("0.77"));
assert.ok(versionToNumber("1.0") > versionToNumber("0.99"));
assert.ok(versionToNumber("1.5") > versionToNumber("1.0"));
assert.ok(versionToNumber("12.34") > versionToNumber("12.33"));
});

it("treats -stable suffix as equivalent", () => {
assert.strictEqual(versionToNumber("0.77-stable"), versionToNumber("0.77"));
assert.strictEqual(versionToNumber("1.0-stable"), versionToNumber("1.0"));
});

it("ignores patch version for comparison", () => {
assert.strictEqual(versionToNumber("0.77.0"), versionToNumber("0.77.5"));
assert.strictEqual(versionToNumber("0.77.0"), versionToNumber("0.77.99"));
});

it("ignores prerelease suffix for comparison", () => {
assert.strictEqual(versionToNumber("0.77.0-rc.1"), versionToNumber("0.77.0"));
assert.strictEqual(versionToNumber("0.77.0-nightly"), versionToNumber("0.77"));
});
});

describe("getReleaseState", () => {
/** @param {{latest: number, next: number}} versions */
const mockVersions = (versions) => (tag) => versions[tag] ?? 0;

it("returns NIGHTLY for main branch", () => {
const result = getReleaseState("main", mockVersions({ latest: 77, next: 78 }));
assert.strictEqual(result.state, "NIGHTLY");
});

it("returns NOT_RELEASE_BRANCH for feature branches", () => {
const result = getReleaseState("feature/foo", mockVersions({ latest: 77, next: 78 }));
assert.strictEqual(result.state, "NOT_RELEASE_BRANCH");
});

it("returns STABLE_LATEST when current equals latest", () => {
const result = getReleaseState("0.77-stable", mockVersions({ latest: 77, next: 78 }));
assert.strictEqual(result.state, "STABLE_LATEST");
assert.strictEqual(result.currentVersion, 77);
assert.strictEqual(result.latestVersion, 77);
});

it("returns STABLE_OLD when current is less than latest", () => {
const result = getReleaseState("0.77-stable", mockVersions({ latest: 78, next: 79 }));
assert.strictEqual(result.state, "STABLE_OLD");
});

it("returns STABLE_NEW when current is greater than latest", () => {
const result = getReleaseState("0.78-stable", mockVersions({ latest: 77, next: 77 }));
assert.strictEqual(result.state, "STABLE_NEW");
});
});

describe("getNxConfig", () => {
it("returns nightly config for NIGHTLY state", () => {
const stateInfo = { state: "NIGHTLY", currentVersion: 0, latestVersion: 0, nextVersion: 0 };
const result = getNxConfig(stateInfo, "main");
assert.deepStrictEqual(result, { resolveFromTag: "nightly", preid: "nightly" });
});

it("returns @next config with rc preid for STABLE_NEW state", () => {
const stateInfo = { state: "STABLE_NEW", currentVersion: 78, latestVersion: 77, nextVersion: 78 };
const result = getNxConfig(stateInfo, "0.78-stable");
assert.deepStrictEqual(result, { resolveFromTag: "next", preid: "rc" });
});

it("returns @latest config for STABLE_LATEST state", () => {
const stateInfo = { state: "STABLE_LATEST", currentVersion: 77, latestVersion: 77, nextVersion: 78 };
const result = getNxConfig(stateInfo, "0.77-stable");
assert.deepStrictEqual(result, { resolveFromTag: "latest" });
});

it("returns version tag config for STABLE_OLD state", () => {
const stateInfo = { state: "STABLE_OLD", currentVersion: 76, latestVersion: 77, nextVersion: 78 };
const result = getNxConfig(stateInfo, "0.76-stable");
assert.deepStrictEqual(result, { resolveFromTag: "v0.76-stable" });
});

it("returns null for NOT_RELEASE_BRANCH state", () => {
const stateInfo = { state: "NOT_RELEASE_BRANCH", currentVersion: 0, latestVersion: 0, nextVersion: 0 };
const result = getNxConfig(stateInfo, "feature/foo");
assert.strictEqual(result, null);
});
});

describe("getPublishTags", () => {
/** @param {{latest: number, next: number}} versions */
const mockVersions = (versions) => (tag) => versions[tag] ?? 0;

it("returns nightly for nightly versions", () => {
const result = getPublishTags("0.79.0-nightly.123", "main", mockVersions({ latest: 77, next: 78 }));
assert.deepStrictEqual(result, ["nightly"]);
});

it("returns next for RC versions", () => {
const result = getPublishTags("0.78.0-rc.0", "0.78-stable", mockVersions({ latest: 77, next: 78 }));
assert.deepStrictEqual(result, ["next"]);
});

it("returns latest + version tag for stable version equal to @latest", () => {
const result = getPublishTags("0.77.5", "0.77-stable", mockVersions({ latest: 77, next: 78 }));
assert.deepStrictEqual(result, ["latest", "v0.77-stable"]);
});

it("returns only version tag for stable version older than @latest", () => {
const result = getPublishTags("0.76.3", "0.76-stable", mockVersions({ latest: 77, next: 78 }));
assert.deepStrictEqual(result, ["v0.76-stable"]);
});

it("returns latest + version + next for new stable version newer than both", () => {
const result = getPublishTags("0.78.0", "0.78-stable", mockVersions({ latest: 77, next: 77 }));
assert.deepStrictEqual(result, ["latest", "v0.78-stable", "next"]);
});

it("returns latest + version for new stable version not newer than @next", () => {
const result = getPublishTags("0.78.0", "0.78-stable", mockVersions({ latest: 77, next: 78 }));
assert.deepStrictEqual(result, ["latest", "v0.78-stable"]);
});
});
});
Loading
Loading