Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Cherry-picking monorepo CI scripts to 0.71-stable #35946

Merged
merged 8 commits into from
Jan 25, 2023
44 changes: 32 additions & 12 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,18 @@ references:
attach_workspace:
at: *hermes_workspace_root

main_only: &main_only
filters:
branches:
only: main
main_or_stable_only: &main_or_stable_only
filters:
branches:
only:
- main
- /0\.[0-9]+[\.[0-9]+]?-stable/


# -------------------------
# Dependency Anchors
# -------------------------
Expand Down Expand Up @@ -817,10 +829,7 @@ jobs:
command: |
REPO_ROOT=$(pwd)
node ./scripts/set-rn-template-version.js "file:$REPO_ROOT/build/$(cat build/react-native-package-version)"
node cli.js init $PROJECT_NAME --directory "/tmp/$PROJECT_NAME" --template $REPO_ROOT --verbose --skip-install
cd /tmp/$PROJECT_NAME
yarn

node ./scripts/template/initialize.js --reactNativeRootPath $REPO_ROOT --templateName $PROJECT_NAME --templateConfigPath $REPO_ROOT --directory "/tmp/$PROJECT_NAME"
- run:
name: Build the template application for << parameters.flavor >> with Architecture set to << parameters.architecture >>, and using the << parameters.jsengine>> JS engine.
command: |
Expand Down Expand Up @@ -923,13 +932,11 @@ jobs:
PACKAGE=$(cat build/react-native-package-version)
PATH_TO_PACKAGE="$REPO_ROOT/build/$PACKAGE"
node ./scripts/set-rn-template-version.js "file:$PATH_TO_PACKAGE"
node cli.js init $PROJECT_NAME --directory "/tmp/$PROJECT_NAME" --template $REPO_ROOT --verbose --skip-install
node ./scripts/template/initialize.js --reactNativeRootPath $REPO_ROOT --templateName $PROJECT_NAME --templateConfigPath $REPO_ROOT --directory "/tmp/$PROJECT_NAME"
- run:
name: Install iOS dependencies - Configuration << parameters.flavor >>; New Architecture << parameters.architecture >>; JS Engine << parameters.jsengine>>; Flipper << parameters.flipper >>
command: |
cd /tmp/$PROJECT_NAME
yarn install
cd ios
cd /tmp/$PROJECT_NAME/ios

bundle install

Expand Down Expand Up @@ -1570,6 +1577,17 @@ jobs:
command: |
echo "Nightly build run"

find_and_publish_bumped_packages:
executor: reactnativeandroid
steps:
- checkout
- run:
name: Set NPM auth token
command: echo "//registry.npmjs.org/:_authToken=${CIRCLE_NPM_TOKEN}" > ~/.npmrc
- run:
name: Find and publish all bumped packages
command: node ./scripts/monorepo/find-and-publish-all-bumped-packages.js


# -------------------------
# PIPELINE PARAMETERS
Expand Down Expand Up @@ -1758,11 +1776,8 @@ workflows:
unless: << pipeline.parameters.run_package_release_workflow_only >>
triggers:
- schedule:
<<: *main_only
cron: "0 20 * * *"
filters:
branches:
only:
- main
jobs:
- nightly_job

Expand All @@ -1785,3 +1800,8 @@ workflows:
- build_hermesc_linux
- build_hermes_macos
- build_hermesc_windows

publish_bumped_packages:
jobs:
- find_and_publish_bumped_packages:
<<: *main_or_stable_only
4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,9 @@
"test-e2e-local-clean": "node ./scripts/test-e2e-local-clean.js",
"test-ios": "./scripts/objc-test.sh test",
"test-typescript": "dtslint types",
"test-typescript-offline": "dtslint --localTs node_modules/typescript/lib types"
"test-typescript-offline": "dtslint --localTs node_modules/typescript/lib types",
"bump-all-updated-packages": "node ./scripts/monorepo/bump-all-updated-packages",
"align-package-versions": "node ./scripts/monorepo/align-package-versions.js"
},
"peerDependencies": {
"react": "18.2.0"
Expand Down
39 changes: 39 additions & 0 deletions scripts/monorepo/__tests__/bump-package-version-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/

const path = require('path');
const {writeFileSync} = require('fs');

const bumpPackageVersion = require('../bump-all-updated-packages/bump-package-version');

jest.mock('fs', () => ({
writeFileSync: jest.fn(),
readFileSync: jest.fn(() => '{}'),
}));

jest.mock('../for-each-package', () => callback => {});

describe('bumpPackageVersionTest', () => {
it('updates patch version of the package', () => {
const mockedPackageLocation = '~/packages/assets';
const mockedPackageManifest = {
name: '@react-native/test',
version: '1.2.3',
};

bumpPackageVersion(mockedPackageLocation, mockedPackageManifest);

expect(writeFileSync).toHaveBeenCalledWith(
path.join(mockedPackageLocation, 'package.json'),
JSON.stringify({...mockedPackageManifest, version: '1.2.4'}, null, 2) +
'\n',
'utf-8',
);
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/

const {spawnSync} = require('child_process');

const {BUMP_COMMIT_MESSAGE} = require('../constants');
const forEachPackage = require('../for-each-package');
const findAndPublishAllBumpedPackages = require('../find-and-publish-all-bumped-packages');

jest.mock('child_process', () => ({spawnSync: jest.fn()}));
jest.mock('../for-each-package', () => jest.fn());

describe('findAndPublishAllBumpedPackages', () => {
it('throws an error if updated version is not 0.x.y', () => {
const mockedPackageNewVersion = '1.0.0';

forEachPackage.mockImplementationOnce(callback => {
callback('absolute/path/to/package', 'to/package', {
version: mockedPackageNewVersion,
});
});

spawnSync.mockImplementationOnce(() => ({
stdout: `- "version": "0.72.0"\n+ "version": "${mockedPackageNewVersion}"\n`,
}));

spawnSync.mockImplementationOnce(() => ({
stdout: BUMP_COMMIT_MESSAGE,
}));

expect(() => findAndPublishAllBumpedPackages()).toThrow(
`Package version expected to be 0.x.y, but received ${mockedPackageNewVersion}`,
);
});
});
51 changes: 51 additions & 0 deletions scripts/monorepo/__tests__/for-each-package-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/

const path = require('path');
const {readdirSync, readFileSync} = require('fs');

const forEachPackage = require('../for-each-package');

jest.mock('fs', () => ({
readdirSync: jest.fn(),
readFileSync: jest.fn(),
}));

describe('forEachPackage', () => {
it('executes callback call with parameters', () => {
const callback = jest.fn();
const mockedPackageManifest = '{"name": "my-new-package"}';
const mockedParsedPackageManifest = JSON.parse(mockedPackageManifest);
const mockedPackageName = 'my-new-package';

readdirSync.mockImplementationOnce(() => [
{name: mockedPackageName, isDirectory: () => true},
]);
readFileSync.mockImplementationOnce(() => mockedPackageManifest);

forEachPackage(callback);

expect(callback).toHaveBeenCalledWith(
path.join(__dirname, '..', '..', '..', 'packages', mockedPackageName),
path.join('packages', mockedPackageName),
mockedParsedPackageManifest,
);
});

it('filters react-native folder', () => {
const callback = jest.fn();
readdirSync.mockImplementationOnce(() => [
{name: 'react-native', isDirectory: () => true},
]);

forEachPackage(callback);

expect(callback).not.toHaveBeenCalled();
});
});
146 changes: 146 additions & 0 deletions scripts/monorepo/align-package-versions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @format
*/

const {spawnSync} = require('child_process');
const {writeFileSync, readFileSync} = require('fs');
const path = require('path');

const checkForGitChanges = require('./check-for-git-changes');
const forEachPackage = require('./for-each-package');

const ROOT_LOCATION = path.join(__dirname, '..', '..');
const TEMPLATE_LOCATION = path.join(ROOT_LOCATION, 'template');
const REPO_CONFIG_LOCATION = path.join(ROOT_LOCATION, 'repo-config');

const readJSONFile = pathToFile => JSON.parse(readFileSync(pathToFile));

const checkIfShouldUpdateDependencyPackageVersion = (
consumerPackageAbsolutePath,
updatedPackageName,
updatedPackageVersion,
) => {
const consumerPackageManifestPath = path.join(
consumerPackageAbsolutePath,
'package.json',
);
const consumerPackageManifest = readJSONFile(consumerPackageManifestPath);

const dependencyVersion =
consumerPackageManifest.dependencies?.[updatedPackageName];

if (dependencyVersion && dependencyVersion !== '*') {
const updatedDependencyVersion = dependencyVersion.startsWith('^')
? `^${updatedPackageVersion}`
: updatedPackageVersion;

if (updatedDependencyVersion !== dependencyVersion) {
console.log(
`\uD83D\uDCA1 ${consumerPackageManifest.name} was updated: now using version ${updatedPackageVersion} of ${updatedPackageName}`,
);

const updatedPackageManifest = {
...consumerPackageManifest,
dependencies: {
...consumerPackageManifest.dependencies,
[updatedPackageName]: updatedDependencyVersion,
},
};

writeFileSync(
consumerPackageManifestPath,
JSON.stringify(updatedPackageManifest, null, 2) + '\n',
'utf-8',
);
}
}

const devDependencyVersion =
consumerPackageManifest.devDependencies?.[updatedPackageName];

if (devDependencyVersion && devDependencyVersion !== '*') {
const updatedDependencyVersion = devDependencyVersion.startsWith('^')
? `^${updatedPackageVersion}`
: updatedPackageVersion;

if (updatedDependencyVersion !== devDependencyVersion) {
console.log(
`\uD83D\uDCA1 ${consumerPackageManifest.name} was updated: now using version ${updatedPackageVersion} of ${updatedPackageName}`,
);

const updatedPackageManifest = {
...consumerPackageManifest,
devDependencies: {
...consumerPackageManifest.devDependencies,
[updatedPackageName]: updatedDependencyVersion,
},
};

writeFileSync(
consumerPackageManifestPath,
JSON.stringify(updatedPackageManifest, null, 2) + '\n',
'utf-8',
);
}
}
};

const alignPackageVersions = () => {
if (checkForGitChanges()) {
console.log(
'\u274c Found uncommitted changes. Please commit or stash them before running this script',
);

process.exit(1);
}

forEachPackage((packageAbsolutePath, _, packageManifest) => {
checkIfShouldUpdateDependencyPackageVersion(
ROOT_LOCATION,
packageManifest.name,
packageManifest.version,
);

checkIfShouldUpdateDependencyPackageVersion(
TEMPLATE_LOCATION,
packageManifest.name,
packageManifest.version,
);

checkIfShouldUpdateDependencyPackageVersion(
REPO_CONFIG_LOCATION,
packageManifest.name,
packageManifest.version,
);

forEachPackage(pathToPackage =>
checkIfShouldUpdateDependencyPackageVersion(
pathToPackage,
packageManifest.name,
packageManifest.version,
),
);
});

if (!checkForGitChanges()) {
console.log(
'\u2705 There were no changes. Every consumer package uses the actual version of dependency package.',
);
return;
}

console.log('Running yarn to update lock file...');
spawnSync('yarn', ['install'], {
cwd: ROOT_LOCATION,
shell: true,
stdio: 'inherit',
encoding: 'utf-8',
});
};

alignPackageVersions();
Loading