Skip to content
Merged
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
14 changes: 6 additions & 8 deletions .github/workflows/microsoft-pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -132,13 +132,11 @@ jobs:
permissions: {}
uses: ./.github/workflows/microsoft-build-rntester.yml

# https://github.com/microsoft/react-native-macos/issues/2344
# Disable these tests because verdaccio hangs
# test-react-native-macos-init:
# name: "Test react-native-macos init"
# permissions: {}
# if: ${{ endsWith(github.base_ref, '-stable') }}
# uses: ./.github/workflows/microsoft-test-react-native-macos-init.yml
test-react-native-macos-init:
name: "Test react-native-macos init"
permissions: {}
if: ${{ endsWith(github.base_ref, '-stable') }}
uses: ./.github/workflows/microsoft-test-react-native-macos-init.yml

# https://github.com/microsoft/react-native-macos/issues/2344
# Disable these tests because verdaccio hangs
Expand All @@ -158,7 +156,7 @@ jobs:
- yarn-constraints
- javascript-tests
- build-rntester
# - test-react-native-macos-init
- test-react-native-macos-init
# - react-native-test-app-integration
steps:
- name: All required jobs passed
Expand Down
39 changes: 11 additions & 28 deletions .github/workflows/microsoft-test-react-native-macos-init.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,53 +32,36 @@ jobs:
working-directory: packages/react-native-macos-init
run: yarn build

- name: Start Verdaccio server
- name: Determine React Native version
id: rn-version
run: |
set -euo pipefail
nohup npx --yes verdaccio --config .ado/verdaccio/config.yaml >/dev/null 2>&1 &
echo $! > $RUNNER_TEMP/verdaccio.pid
- name: Wait for Verdaccio to be ready
run: node .ado/scripts/waitForVerdaccio.mjs http://localhost:4873

- name: Configure npm for Verdaccio
run: .ado/scripts/verdaccio.sh init

- name: Publish to Verdaccio
run: .ado/scripts/verdaccio.sh publish --branch origin/${{ github.base_ref }}

- name: Export versions
run: node .ado/scripts/export-versions.mjs
RN_VERSION=$(node -e "
const pkg = require('./packages/react-native/package.json');
console.log(pkg.peerDependencies['react-native']);
")
echo "version=$RN_VERSION" >> "$GITHUB_OUTPUT"

- name: Initialize new project
run: |
set -eox pipefail
npx --yes @react-native-community/cli init testcli --version $(cat .react_native_version) --skip-install
npx --yes @react-native-community/cli init testcli --version ${{ steps.rn-version.outputs.version }}
working-directory: ${{ runner.temp }}

- name: Install dependencies in new project
- name: Install local react-native-macos
working-directory: ${{ runner.temp }}/testcli
run: |
set -eox pipefail
yarn install --mode=update-lockfile
yarn install
npm install ${{ github.workspace }}/packages/react-native

- name: Apply macOS template
working-directory: ${{ runner.temp }}/testcli
run: |
set -eox pipefail
${{ github.workspace }}/.ado/scripts/verdaccio.sh configure
node ${{ github.workspace }}/packages/react-native-macos-init/bin.js --verbose --version latest --overwrite --prerelease
node ${{ github.workspace }}/packages/react-native-macos-init/bin.js --verbose --overwrite --prerelease
pod install --project-directory=macos

- name: Build macOS app
working-directory: ${{ runner.temp }}/testcli
run: |
set -eox pipefail
npx react-native build-macos

- name: Stop Verdaccio
if: always()
run: |
if [ -f "$RUNNER_TEMP/verdaccio.pid" ]; then
kill "$(cat $RUNNER_TEMP/verdaccio.pid)" || true
fi
6 changes: 4 additions & 2 deletions packages/react-native-macos-init/package.json
Comment thread
Saadnajmi marked this conversation as resolved.
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@
"prepublishOnly": "npm run build"
},
"bin": "./bin.js",
"engines": {
"node": ">=20.12.0"
},
"dependencies": {
"chalk": "^3",
"find-up": "^4.1.0",
"npm-registry-fetch": "^14.0.0",
"prompts": "^2.3.0",
Expand All @@ -27,7 +29,7 @@
},
"devDependencies": {
"@rnx-kit/tsconfig": "^2.0.0",
"@types/chalk": "^2.2.0",
"@types/node": "^22.0.0",
"@types/npm-registry-fetch": "^8.0.0",
"@types/prompts": "^2.0.3",
"@types/semver": "^7.1.0",
Expand Down
129 changes: 107 additions & 22 deletions packages/react-native-macos-init/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@
* @format
*/

import chalk from 'chalk';
import {execSync} from 'child_process';
import * as findUp from 'find-up';
import {styleText} from 'node:util';
import * as fs from 'fs';
import * as npmFetch from 'npm-registry-fetch';
Comment thread
Saadnajmi marked this conversation as resolved.
import prompts from 'prompts';
Expand Down Expand Up @@ -60,7 +60,9 @@ function getNpmRegistryUrl(): string {
}

function getReactNativeAppName() {
console.log(`Reading ${chalk.cyan('application name')} from package.json…`);
console.log(
`Reading ${styleText('cyan', 'application name')} from package.json…`,
);
const cwd = process.cwd();
const pkgJsonPath = findUp.sync('package.json', {cwd});
if (!pkgJsonPath) {
Expand All @@ -73,7 +75,9 @@ function getReactNativeAppName() {
if (!name) {
const appJsonPath = findUp.sync('app.json', {cwd});
if (appJsonPath) {
console.log(`Reading ${chalk.cyan('application name')} from app.json…`);
console.log(
`Reading ${styleText('cyan', 'application name')} from app.json…`,
);
name = JSON.parse(fs.readFileSync(appJsonPath, 'utf8')).name;
}
}
Expand All @@ -84,7 +88,9 @@ function getReactNativeAppName() {
}

function getPackageVersion(packageName: string, exitOnError: boolean = true) {
console.log(`Reading ${chalk.cyan(packageName)} version from node_modules…`);
console.log(
`Reading ${styleText('cyan', packageName)} version from node_modules…`,
);

try {
const pkgJsonPath = require.resolve(`${packageName}/package.json`, {
Expand Down Expand Up @@ -112,8 +118,8 @@ function getReactNativeMacOSVersion() {
}

function errorOutOnUnsupportedVersionOfReactNative(rnVersion: string): never {
const version = chalk.cyan(rnVersion);
const supportedVersions = chalk.cyan('>=0.60');
const version = styleText('cyan', rnVersion);
const supportedVersions = styleText('cyan', '>=0.60');
printError(
`Unsupported version of ${RNPKG}: ${version}\n${MACOSPKG} supports ${RNPKG} versions ${supportedVersions}`,
);
Expand Down Expand Up @@ -208,16 +214,52 @@ function isProjectUsingYarn(cwd: string) {
* Outputs decorated version of the package for the CLI
*/
function printPkg(name: string, version?: string) {
return `${chalk.yellow(name)}${
version ? `${chalk.grey('@')}${chalk.cyan(version)}` : ''
return `${styleText('yellow', name)}${
version ? `${styleText('gray', '@')}${styleText('cyan', version)}` : ''
}`;
}

/**
* Prints decorated version of console.error to the CLI
*/
function printError(message: string, ...optionalParams: any[]) {
console.error(chalk.red(chalk.bold(message)), ...optionalParams);
console.error(styleText(['red', 'bold'], message), ...optionalParams);
}

/**
* Checks if the resolved react-native-macos version's peer dependency on
* react-native is compatible with the installed version. Warns if not.
*/
async function validatePeerDependencies(
macosVersion: string,
installedRNVersion: string,
): Promise<void> {
try {
const npmResponse = await npmFetch.json(`${MACOSPKG}/${macosVersion}`, {
registry: getNpmRegistryUrl(),
});
const peerDeps = (npmResponse as any).peerDependencies;
if (peerDeps && peerDeps[RNPKG]) {
const requiredRN = peerDeps[RNPKG];
if (!semver.satisfies(installedRNVersion, requiredRN)) {
console.warn(
styleText(
'yellow',
`\n${styleText('bold', 'Warning:')} ${printPkg(
MACOSPKG,
macosVersion,
)} requires ${printPkg(RNPKG, requiredRN)}, ` +
Comment thread
Saadnajmi marked this conversation as resolved.
`but you have ${printPkg(
RNPKG,
installedRNVersion,
)} installed.\n`,
),
);
}
}
} catch {
// Non-fatal — if we can't check, proceed anyway
}
}

(async () => {
Expand All @@ -243,29 +285,38 @@ function printError(message: string, ...optionalParams: any[]) {

if (!argv.version) {
console.log(
`Latest matching version of ${chalk.green(MACOSPKG)} for ${printPkg(
RNPKG,
reactNativeVersion,
)} is ${printPkg(MACOSPKG, reactNativeMacOSResolvedVersion)}.`,
`Latest matching version of ${styleText(
'green',
MACOSPKG,
)} for ${printPkg(RNPKG, reactNativeVersion)} is ${printPkg(
MACOSPKG,
reactNativeMacOSResolvedVersion,
)}.`,
);

if (semver.prerelease(reactNativeMacOSResolvedVersion)) {
console.warn(
`
${printPkg(MACOSPKG, reactNativeMacOSResolvedVersion)} is a ${chalk.bgYellow(
${printPkg(MACOSPKG, reactNativeMacOSResolvedVersion)} is a ${styleText(
'bgYellow',
'pre-release',
)} version.
The latest supported version is ${printPkg(
MACOSPKG,
reactNativeMacOSLatestVersion,
)}.
You can either downgrade your version of ${chalk.yellow(RNPKG)} to ${chalk.cyan(
You can either downgrade your version of ${styleText(
'yellow',
RNPKG,
)} to ${styleText(
'cyan',
getMatchingReactNativeSemVerForReactNativeMacOSVersion(
reactNativeMacOSLatestVersion,
),
)}, or continue with a ${chalk.bgYellow(
)}, or continue with a ${styleText(
'bgYellow',
'pre-release',
)} version of ${chalk.yellow(MACOSPKG)}.
)} version of ${styleText('yellow', MACOSPKG)}.
`,
);

Expand All @@ -288,6 +339,11 @@ You can either downgrade your version of ${chalk.yellow(RNPKG)} to ${chalk.cyan(
}
}

await validatePeerDependencies(
reactNativeMacOSResolvedVersion,
reactNativeVersion,
);

const pkgLatest = printPkg(MACOSPKG, version);

if (reactNativeMacOSResolvedVersion !== reactNativeMacOSVersion) {
Expand All @@ -298,15 +354,44 @@ You can either downgrade your version of ${chalk.yellow(RNPKG)} to ${chalk.cyan(
);

const pkgmgr = isProjectUsingYarn(process.cwd())
? `yarn add${verbose ? '' : ' --silent'}`
: `npm install --save${verbose ? '' : ' --silent'}`;
? 'yarn add'
: 'npm install --save';
const execOptions = verbose ? {stdio: 'inherit' as const} : {};
execSync(`${pkgmgr} "${MACOSPKG}@${version}"`, execOptions);

console.log(`${pkgLatest} ${chalk.green('successfully installed!')}`);
try {
execSync(`${pkgmgr} "${MACOSPKG}@${version}"`, execOptions);
} catch (e: any) {
// When not verbose, execSync captures output in the error object
if (!verbose) {
if (e.stderr) {
console.error(e.stderr.toString());
}
if (e.stdout) {
console.log(e.stdout.toString());
}
}
printError(
`Failed to install ${printPkg(MACOSPKG, version)}.\n` +
`This can happen if there is a peer dependency mismatch between ` +
`${RNPKG} and ${MACOSPKG}.\n` +
`Check that your installed version of ${styleText(
'yellow',
RNPKG,
)} is compatible ` +
`with ${printPkg(MACOSPKG, version)}.`,
);
process.exit(EXITCODE_UNKNOWN_ERROR);
}

console.log(
`${pkgLatest} ${styleText('green', 'successfully installed!')}`,
);
} else {
console.log(
`${chalk.green('Latest version')} of ${pkgLatest} already installed.`,
`${styleText(
'green',
'Latest version',
)} of ${pkgLatest} already installed.`,
);
}

Expand Down
3 changes: 0 additions & 3 deletions packages/react-native/local-cli/generate-macos.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

const {
copyProjectTemplateAndReplace,
installDependencies,
printFinishMessage,
} = require('./generator-macos');
const fs = require('fs');
Expand All @@ -21,8 +20,6 @@ function generateMacOS (projectDir, name, options) {
fs.mkdirSync(projectDir);
}

installDependencies(options);

copyProjectTemplateAndReplace(
path.join(__dirname, 'generator-macos', 'templates'),
projectDir,
Expand Down
10 changes: 5 additions & 5 deletions packages/react-native/local-cli/generator-common/index.js
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
// @ts-check

// @noflow
const chalk = require('chalk');
const fs = require('fs');
const { styleText } = require('node:util');
const path = require('path');

// Copied from `copyAndReplace` in react-native-community/cli as it's deleted in newer versions
Expand Down Expand Up @@ -330,11 +330,11 @@ function alwaysOverwriteContentChangedCallback(
contentChanged
) {
if (contentChanged === 'new') {
console.log(`${chalk.bold('new')} ${relativeDestPath}`);
console.log(`${styleText('bold','new')} ${relativeDestPath}`);
return 'overwrite';
}
if (contentChanged === 'changed') {
console.log(`${chalk.bold('changed')} ${relativeDestPath} ${chalk.yellow('[overwriting]')}`);
console.log(`${styleText('bold','changed')} ${relativeDestPath} ${styleText('yellow','[overwriting]')}`);
return 'overwrite';
}
Comment thread
Saadnajmi marked this conversation as resolved.
if (contentChanged === 'identical') {
Expand All @@ -351,12 +351,12 @@ function upgradeFileContentChangedCallback(
contentChanged
) {
if (contentChanged === 'new') {
console.log(`${chalk.bold('new')} ${relativeDestPath}`);
console.log(`${styleText('bold','new')} ${relativeDestPath}`);
return 'overwrite';
}
if (contentChanged === 'changed') {
console.log(
`${chalk.bold(relativeDestPath)} ` +
`${styleText('bold',relativeDestPath)} ` +
`has changed in the new version.\nDo you want to keep your ${relativeDestPath} or replace it with the ` +
'latest version?\nIf you ever made any changes ' +
'to this file, you\'ll probably want to keep it.\n' +
Expand Down
Loading
Loading