Skip to content

Commit

Permalink
chore: set up proper publishing pipeline
Browse files Browse the repository at this point in the history
  • Loading branch information
tido64 committed Nov 7, 2024
1 parent d61d747 commit e4e70f3
Show file tree
Hide file tree
Showing 10 changed files with 1,196 additions and 1,212 deletions.
4 changes: 1 addition & 3 deletions .ado/jobs/npm-publish-dry-run.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,4 @@ jobs:
submodules: recursive # set to 'true' for a single level of submodules or 'recursive' to get submodules of submodules
persistCredentials: true # set to 'true' to leave the OAuth token in the Git config after the initial fetch

- template: /.ado/templates/apple-steps-publish.yml@self
parameters:
build_type: 'dry-run'
- template: /.ado/templates/npm-publish.yml@self
155 changes: 155 additions & 0 deletions .ado/scripts/prepublish-check.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,155 @@
// @ts-check
import { spawnSync } from "node:child_process";
import * as fs from "node:fs";
import * as util from "node:util";

/**
* @typedef {typeof import("../../nx.json")} NxConfig
* @typedef {{ tag?: string }} Options
*/

/**
* Exports a variable, `publish_react_native_macos`, to signal that we want to
* enable publishing on Azure Pipelines.
*
* Note that pipelines need to read this variable separately and do the actual
* work to publish bits.
*/
function enablePublishingOnAzurePipelines() {
console.log(`##vso[task.setvariable variable=publish_react_native_macos]1`);
}

/**
* Loads Nx configuration.
* @returns {NxConfig}
*/
function loadNxConfig(configFile = "nx.json") {
const nx = fs.readFileSync(configFile, { encoding: "utf-8" });
return JSON.parse(nx);
}

/**
* Returns the currently checked out branch. Note that this function prefers
* predefined CI environment variables over local clone.
* @returns {string}
*/
function getCurrentBranch() {
// https://learn.microsoft.com/en-us/azure/devops/pipelines/build/variables?view=azure-devops&tabs=yaml#build-variables-devops-services
const adoSourceBranchName = process.env["BUILD_SOURCEBRANCHNAME"];
if (adoSourceBranchName) {
return adoSourceBranchName.replace(/^refs\/heads\//g, "");
}

// Depending on how the repo was cloned, HEAD may not exist. We only use this
// method as fallback.
const { stdout } = spawnSync("git", ["rev-parse", "--abbrev-ref", "HEAD"]);
return stdout.toString().trim();
}

/**
* @param {string} branch
* @param {Options} options
* @returns {{ npmTag: string; prerelease?: string; }}
*/
function getStableTag(branch, { tag }) {
switch (tag) {
case "latest":
return { npmTag: tag };

case "next":
return { npmTag: tag, prerelease: "rc" };

default:
return { npmTag: "v" + branch };
}
}

/**
* Returns whether the given branch is considered main branch.
* @param {string} branch
*/
function isMainBranch(branch) {
// There is currently no good way to consistently get the main branch. We
// hardcode the value for now.
return branch === "main";
}

/**
* Returns whether the given branch is considered a stable branch.
* @param {string} branch
*/
function isStableBranch(branch) {
return /^\d+\.\d+-stable$/.test(branch);
}

/**
* Verifies the configuration and enables publishing on CI.
* @param {NxConfig} config
* @param {string} currentBranch
* @param {string} tag
* @param {string} [prerelease]
* @returns {asserts config is NxConfig["release"]}
*/
function enablePublishing({ defaultBase, release: config }, currentBranch, tag, prerelease) {
/** @type {string[]} */
const errors = [];

// `defaultBase` determines what we diff against when looking for tags or
// released version and must therefore be set to either the main branch or one
// of the stable branches.
if (currentBranch !== defaultBase) {
errors.push(`'defaultBase' must be set to '${currentBranch}'`);
}

// Determines whether we need to add "nightly" or "rc" to the version string.
const { currentVersionResolverMetadata, preid } = config.version.generatorOptions;
if (preid !== prerelease) {
errors.push(`'release.version.generatorOptions.preid' must be set to '${prerelease || ""}'`);
}

// What the published version should be tagged as e.g., "latest" or "nightly".
if (currentVersionResolverMetadata.tag !== tag) {
errors.push(`'release.version.generatorOptions.currentVersionResolverMetadata.tag' must be set to '${tag}'`);
}

if (errors.length > 0) {
for (const e of errors) {
console.error("❌", e);
}
throw new Error("Nx Release is not correctly configured for the current branch");
}

enablePublishingOnAzurePipelines();
}

/**
* @param {Options} options
*/
function main(options) {
const branch = getCurrentBranch();
if (!branch) {
throw new Error("Could not get current branch");
}

const config = loadNxConfig();
if (isMainBranch(branch)) {
enablePublishing(config, branch, "nightly", "nightly");
} else if (isStableBranch(branch)) {
const { npmTag, prerelease } = getStableTag(branch, options);
enablePublishing(config, branch, npmTag, prerelease);
}
}

const { values } = util.parseArgs({
args: process.argv.slice(2),
options: {
tag: {
type: "string",
default: "next",
},
},
strict: true,
allowNegative: true,
});

main(values);
11 changes: 3 additions & 8 deletions .ado/scripts/verdaccio.sh
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,9 @@ case ${1-} in

"publish")
checkpoint=$(git rev-parse HEAD)
yarn set-version 1000.0.0-pr
git commit --all --message 'bump' --no-verify
packages=()
for json in $(yarn workspaces list --no-private --json); do
packages+=(--package $(node --print "JSON.parse('$json').name"))
done
npx beachball change --no-fetch --type patch --message 'bump for testing purposes' ${packages[@]}
npx beachball $* --no-push --registry $NPM_REGISTRY --yes --access public --no-generate-changelog
cp nx.test.json nx.json
yarn nx release version 1000.0.0
yarn nx release publish --registry $NPM_REGISTRY
git reset --hard $checkpoint
;;
esac
24 changes: 24 additions & 0 deletions .ado/templates/npm-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
parameters:
build_type: ''

steps:
- script: |
yarn install
displayName: Install npm dependencies
- script: |
# If this is a new stable branch, change to `--tag latest` when going stable
# If this is the previous stable branch, remove `--tag` before the new stable is live
node .ado/scripts/prepublish-check.mjs --tag next
displayName: Verify release config
- script: |
yarn nx release --dry-run
displayName: Version and publish packages (dry run)
condition: ne(variables['publish_react_native_macos'], '1')
- script: |
#yarn nx release --yes
yarn nx release --dry-run
displayName: Version and publish packages
condition: eq(variables['publish_react_native_macos'], '1')
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -174,4 +174,8 @@ vendor/
.ado/Brewfile.lock.json
.ado/verdaccio/htpasswd
.ado/verdaccio/storage/.verdaccio-db.json

# Nx
.nx/cache
.nx/workspace-data
# macOS]
29 changes: 29 additions & 0 deletions nx.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
{
"$schema": "./node_modules/nx/schemas/nx-schema.json",
"defaultBase": "main",
"targetDefaults": {
"build": {
"dependsOn": ["^build"]
}
},
"release": {
"changelog": {
"projectChangelogs": false,
"workspaceChangelog": false
},
"projects": ["packages/react-native", "packages/virtualized-lists"],
"projectsRelationship": "independent",
"versionPlans": true,
"version": {
"generatorOptions": {
"currentVersionResolver": "registry",
"currentVersionResolverMetadata": {
"tag": "nightly"
},
"fallbackCurrentVersionResolver": "disk",
"preid": "nightly",
"skipLockFileUpdate": true
}
}
}
}
27 changes: 27 additions & 0 deletions nx.test.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"$schema": "./node_modules/nx/schemas/nx-schema.json",
"defaultBase": "main",
"targetDefaults": {
"build": {
"dependsOn": ["^build"]
}
},
"release": {
"changelog": {
"projectChangelogs": false,
"workspaceChangelog": false
},
"projects": ["packages/*"],
"projectsRelationship": "independent",
"versionPlans": true,
"version": {
"generatorOptions": {
"currentVersionResolver": "disk",
"currentVersionResolverMetadata": {
"tag": "latest"
},
"skipLockFileUpdate": true
}
}
}
}
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@
"@babel/preset-flow": "^7.24.7",
"@definitelytyped/dtslint": "^0.0.127",
"@jest/create-cache-key-function": "^29.6.3",
"@nx/js": "~20.0.0",
"@pkgjs/parseargs": "^0.11.0",
"@react-native/metro-babel-transformer": "0.76.0-main",
"@react-native/metro-config": "0.76.0-main",
Expand Down Expand Up @@ -96,6 +97,7 @@
"mkdirp": "^0.5.1",
"node-fetch": "^2.2.0",
"nullthrows": "^1.1.1",
"nx": "~20.0.0",
"prettier": "2.8.8",
"prettier-plugin-hermes-parser": "0.23.1",
"react": "19.0.0-rc-fb9a90fa48-20240614",
Expand Down
2 changes: 1 addition & 1 deletion packages/react-native/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@
},
"dependencies": {
"@jest/create-cache-key-function": "^29.6.3",
"@react-native-mac/virtualized-lists": "0.76.0-main",
"@react-native-mac/virtualized-lists": "workspace:*",
"@react-native/assets-registry": "0.76.0-main",
"@react-native/codegen": "0.76.0-main",
"@react-native/community-cli-plugin": "0.76.0-main",
Expand Down
Loading

0 comments on commit e4e70f3

Please sign in to comment.