Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
4d93597
fix(ci): forward-port release integration fixes from 0.83
Saadnajmi Sep 15, 2026
a976521
fix(codegen): match package-relative source paths
Saadnajmi Sep 15, 2026
87adb52
fix(types): canonicalize checkout-relative API imports
Saadnajmi Sep 15, 2026
2b97335
fix(pods): resolve dependency package fallback
Saadnajmi Sep 15, 2026
07921f8
fix(prebuild): fail invalid platform requests
Saadnajmi Sep 15, 2026
584cfd3
fix(text): preserve explicit custom font weights
Saadnajmi Sep 16, 2026
581aa73
fix(release): publish prepared Changesets versions with one tag
Saadnajmi Sep 16, 2026
ac2c766
fix(release): validate prepared version PRs against their base
Saadnajmi Sep 16, 2026
809fa80
test(release): isolate invalid publication graph fixtures
Saadnajmi Sep 16, 2026
75926e2
fix(packaging): complete fork package dependencies and notices
Saadnajmi Sep 17, 2026
ef79dc1
fix(release): preserve fork workspace version relationships
Saadnajmi Sep 17, 2026
35a72da
fix(macos): initialize and dismiss RedBox through its controller
Saadnajmi Sep 17, 2026
3c658c0
fix(pods): resolve platform-specific framework dependency headers
Saadnajmi Sep 17, 2026
b18a9d3
fix(graphics): use canonical HostPlatformColor header import
Saadnajmi Sep 17, 2026
5a7c77b
fix(macos): decouple core views from the optional text framework
Saadnajmi Sep 17, 2026
d7d9fc0
fix(pods): declare direct RCTUIKit framework dependencies
Saadnajmi Sep 17, 2026
ea7972a
fix(macos): link UniformTypeIdentifiers from Fabric
Saadnajmi Sep 17, 2026
0b1306d
fix(pods): link CoreGraphics from its graphics consumer
Saadnajmi Sep 17, 2026
a9e5b9e
fix(pods): link CoreGraphics from the sample module
Saadnajmi Sep 17, 2026
29191b6
fix(macos): size RedBox rows from native table content
Saadnajmi Sep 17, 2026
ff90a9b
fix(fabric): preserve canonical platform headers in frameworks
Saadnajmi Sep 17, 2026
52c69fc
fix(hermes): resolve default artifacts from branch metadata
Saadnajmi Sep 16, 2026
2dd03ee
test(hermes): validate each branch metadata without fixed versions
Saadnajmi Sep 17, 2026
685d05a
fix(packaging): include the Hermes V1 source tag
Saadnajmi Sep 17, 2026
f2467af
test(release): validate graph policy across private and public branches
Saadnajmi Sep 17, 2026
05d941b
fix(release): honor local Changesets bases and repository remotes
Saadnajmi Sep 17, 2026
c440e99
fix(release): generate exported package types before publication
Saadnajmi Sep 17, 2026
4157235
fix(release): preserve private upstream workspace versions
Saadnajmi Sep 17, 2026
0145de8
fix(types): preserve canonical fork event declarations
Saadnajmi Sep 17, 2026
e8a68d0
fix(hermes): preserve macOS slices and symbols during recomposition
Saadnajmi Sep 17, 2026
febd7d6
fix(flow): type Hermes framework validation metadata
Saadnajmi Sep 17, 2026
4de3e75
fix(flow): normalize Hermes metadata command output
Saadnajmi Sep 20, 2026
f741b50
fix(types): resolve private workspace dependencies during generation
Saadnajmi Sep 21, 2026
226a7ab
fix(ci): apply reviewed release validation repairs
Saadnajmi Sep 22, 2026
9b59f96
fix(ci): align lock metadata with the public registry
Saadnajmi Sep 22, 2026
3b5551f
chore(macos): normalize shared fork diff tags
Saadnajmi Sep 25, 2026
b0b3e40
test(ci): validate release policy and Changesets config
Saadnajmi Sep 25, 2026
2f231ad
chore(ci): restore Meta Actions ownership
Saadnajmi Sep 25, 2026
c4cbc4d
ci: adopt 0.83-stable Changesets v3 publish workflow
Saadnajmi Oct 14, 2026
a3fc706
build: upgrade to Yarn 4.18.1 and Changesets v3
Saadnajmi Oct 14, 2026
996f147
chore(macos): add missing diff tags from 0.83-stable
Saadnajmi Oct 14, 2026
9016c85
build: migrate docsite lock to Yarn 4.18.1
Saadnajmi Oct 14, 2026
09c0eb1
fix(macos): restore release workflow fixes
Saadnajmi Oct 14, 2026
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
2 changes: 2 additions & 0 deletions .ado/jobs/npm-publish.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
jobs:
- job: NPMPublish
displayName: NPM Publish
# Also disable direct template consumers until ADO publication is re-enabled.
condition: false
pool:
name: cxeiss-ubuntu-20-04-large
image: cxe-ubuntu-20-04-1es-pt
Expand Down
3 changes: 3 additions & 0 deletions .ado/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,5 +51,8 @@ extends:
stages:
- stage: NPM
dependsOn: []
# GitHub Trusted Publishing owns releases. Retain the ADO publication
# path for explicit re-enablement after its contract/auth are reviewed.
condition: false
jobs:
- template: /.ado/jobs/npm-publish.yml@self
4 changes: 4 additions & 0 deletions .changeset/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,9 @@
"changelog": "@changesets/cli/changelog",
"commit": false,
"ignore": [],
"privatePackages": {
"version": true,
"tag": false
},
"linked": []
}
2 changes: 1 addition & 1 deletion .github/actions/microsoft-setup-toolchain/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ runs:
distribution: temurin
java-version: ${{ inputs.java-version }}
- name: Set up Node.js
uses: actions/setup-node@v4.4.0
uses: actions/setup-node@v7
with:
node-version: ${{ inputs.node-version }}
cache: ${{ inputs.cache-npm-dependencies }}
Expand Down
55 changes: 55 additions & 0 deletions .github/scripts/__tests__/__fixtures__/resolve-hermes.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* 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.
*/

'use strict';

// Preload in the CLI subprocess. Exercise the real ESM entry point and URL
// helper without network access or changes to checked-in metadata.
const fs = require('node:fs');
const path = require('node:path');

const propertiesPath = path.resolve(
__dirname,
'../../../../packages/react-native/sdks/hermes-engine/version.properties',
);
const readFileSync = fs.readFileSync;
fs.readFileSync = function (file, ...args) {
if (file === propertiesPath && process.env.HERMES_TEST_PROPERTIES != null) {
if (process.env.HERMES_TEST_PROPERTIES === 'MISSING') {
throw Object.assign(new Error(`ENOENT: ${propertiesPath}`), {
code: 'ENOENT',
});
}
if (process.env.HERMES_TEST_PROPERTIES === 'UNREADABLE') {
throw Object.assign(new Error(`EACCES: ${propertiesPath}`), {
code: 'EACCES',
});
}
return process.env.HERMES_TEST_PROPERTIES;
}
return readFileSync.call(this, file, ...args);
};

const urls = [];
global.fetch = async url => {
urls.push(url);
const mode = process.env.HERMES_TEST_DOWNLOAD;
if (url.endsWith('/maven-metadata.xml')) {
return {
ok: mode === 'snapshot',
text: async () =>
'<metadata><snapshot><timestamp>20260101.010203</timestamp><buildNumber>4</buildNumber></snapshot></metadata>',
};
}
return {
ok: mode === 'release' || (mode === 'snapshot' && url.includes('SNAPSHOT')),
status: 404,
statusText: 'Not Found',
arrayBuffer: async () => Buffer.from('mock Hermes archive'),
};
};
process.on('exit', () => console.log(`HERMES_TEST_URLS=${JSON.stringify(urls)}`));
185 changes: 185 additions & 0 deletions .github/scripts/__tests__/resolve-hermes-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
/**
* 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.
*
* @noflow
* @format
*/

'use strict';

const {spawnSync} = require('child_process');
const fs = require('fs');
const ini = require('ini');
const os = require('os');
const path = require('path');

const root = path.resolve(__dirname, '../../..');
const metadata = ini.parse(
fs.readFileSync(
path.join(
root,
'packages/react-native/sdks/hermes-engine/version.properties',
),
'utf8',
),
);
const script = path.join(root, '.github/scripts/resolve-hermes.mts');
const preload = path.join(__dirname, '__fixtures__/resolve-hermes.cjs');
let tmp;

beforeEach(() => {
tmp = fs.mkdtempSync(path.join(os.tmpdir(), 'resolve-hermes-test-'));
});

afterEach(() => {
fs.rmSync(tmp, {recursive: true, force: true});
});

function run(command, overrides = {}) {
const env = {...process.env};
for (const key of [
'RCT_HERMES_V1_ENABLED',
'HERMES_VERSION',
'HERMES_ENGINE_TARBALL_PATH',
'HERMES_TEST_PROPERTIES',
'HERMES_TEST_DOWNLOAD',
]) {
delete env[key];
}
const outputPath = path.join(tmp, 'output');
const result = spawnSync(
process.execPath,
['--experimental-strip-types', '--require', preload, script, ...command],
{
cwd: tmp,
env: {...env, TMPDIR: tmp, GITHUB_OUTPUT: outputPath, ...overrides},
encoding: 'utf8',
timeout: 10000,
},
);
if (result.error) {
throw result.error;
}
const output = fs.existsSync(outputPath)
? fs.readFileSync(outputPath, 'utf8')
: '';
const urls = JSON.parse(result.stdout.match(/HERMES_TEST_URLS=(.*)/)[1]);
return {...result, output, urls};
}

test.each([
['0', metadata.HERMES_VERSION_NAME, 'HERMES_VERSION_NAME', 'Debug'],
['1', metadata.HERMES_V1_VERSION_NAME, 'HERMES_V1_VERSION_NAME', 'Release'],
])(
'CI downloads flag %s with the selected key and version',
(flag, version, key, flavor) => {
const result = run(['download-hermes', flavor], {
RCT_HERMES_V1_ENABLED: flag,
HERMES_TEST_DOWNLOAD: 'release',
});
expect(result.status).toBe(0);
expect(result.stdout).toContain(`Using ${key}=${version}`);
expect(result.output).toContain(`version=${version}\n`);
expect(result.urls).toEqual([
`https://central.sonatype.com/repository/maven-snapshots/com/facebook/hermes/hermes-ios/${version}-SNAPSHOT/maven-metadata.xml`,
`https://repo1.maven.org/maven2/com/facebook/hermes/hermes-ios/${version}/hermes-ios-${version}-hermes-ios-${flavor.toLowerCase()}.tar.gz`,
]);
const tarball = result.output.match(/^tarball=(.+)$/m)[1];
expect(fs.readFileSync(tarball, 'utf8')).toBe('mock Hermes archive');
},
);

test('CI snapshot fallback preserves the four-argument URL helper contract', () => {
const version = '123.4.56';
const result = run(['download-hermes'], {
HERMES_TEST_DOWNLOAD: 'snapshot',
HERMES_TEST_PROPERTIES: `HERMES_VERSION_NAME=${version}`,
});
expect(result.status).toBe(0);
expect(result.output).toContain(`version=${version}\n`);
expect(result.urls).toEqual([
`https://central.sonatype.com/repository/maven-snapshots/com/facebook/hermes/hermes-ios/${version}-SNAPSHOT/maven-metadata.xml`,
`https://repo1.maven.org/maven2/com/facebook/hermes/hermes-ios/${version}/hermes-ios-${version}-hermes-ios-debug.tar.gz`,
`https://central.sonatype.com/repository/maven-snapshots/com/facebook/hermes/hermes-ios/${version}-SNAPSHOT/hermes-ios-${version}-20260101.010203-4-hermes-ios-debug.tar.gz`,
]);
});

test('CI can still select source when valid pinned artifacts are unavailable', () => {
const result = run(['download-hermes']);
expect(result.status).toBe(0);
expect(result.output).toBe('');
expect(result.stdout).toContain('will build from source');
expect(result.urls).toHaveLength(2);
});

test.each([
['', 'Expected one exact HERMES_VERSION_NAME'],
['UNREADABLE', 'EACCES'],
['HERMES_VERSION_NAME=^1.2.3', 'Expected one exact HERMES_VERSION_NAME'],
[
'HERMES_VERSION_NAME=1.2.3\nHERMES_VERSION_NAME=1.2.3',
'Expected one exact HERMES_VERSION_NAME',
],
])('CI fails invalid metadata before any download: %s', (properties, error) => {
const result = run(['download-hermes'], {HERMES_TEST_PROPERTIES: properties});
expect(result.status).toBe(1);
expect(result.stderr).toContain(error);
expect(result.output).toBe('');
expect(result.urls).toEqual([]);
expect(result.stdout).not.toContain('will build from source');
});

test('CI selects source without a download when version.properties is missing', () => {
const result = run(['download-hermes'], {HERMES_TEST_PROPERTIES: 'MISSING'});
expect(result.status).toBe(0);
expect(result.output).toBe('');
expect(result.stdout).toContain('will build from source');
expect(result.urls).toEqual([]);
});

test.each([
[undefined, '.hermesversion'],
['0', '.hermesversion'],
['1', '.hermesv1version'],
])('CI resolve-commit uses the tag file for flag %s', (flag, tagFile) => {
const result = run(
['resolve-commit'],
flag == null ? {} : {RCT_HERMES_V1_ENABLED: flag},
);
const tag = fs
.readFileSync(
path.join(root, 'packages/react-native/sdks', tagFile),
'utf8',
)
.trim();
expect(result.status).toBe(0);
expect(result.output).toBe(`hermes-commit=${tag}\n`);
expect(result.urls).toEqual([]);
});

test.each([
['0', '.hermesversion', 'MISSING'],
['1', '.hermesv1version', 'MISSING'],
['1', '.hermesv1version', 'HERMES_VERSION_NAME=123.4.56'],
['0', '.hermesversion', 'HERMES_VERSION_NAME=invalid'],
])(
'CI resolve-commit reads flag %s tag %s independently of metadata %s',
(flag, tagFile, properties) => {
const result = run(['resolve-commit'], {
RCT_HERMES_V1_ENABLED: flag,
HERMES_TEST_PROPERTIES: properties,
});
const tag = fs
.readFileSync(
path.join(root, 'packages/react-native/sdks', tagFile),
'utf8',
)
.trim();
expect(result.status).toBe(0);
expect(result.output).toBe(`hermes-commit=${tag}\n`);
expect(result.urls).toEqual([]);
},
);
42 changes: 17 additions & 25 deletions .github/scripts/resolve-hermes.mts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ import { $, echo, fs, path } from 'zx';

// Use createRequire to import CommonJS modules from ESM context
const require = createRequire(import.meta.url);
const {
readHermesMetadata,
selectHermesMetadata,
} = require('../../packages/react-native/scripts/ios-prebuild/hermes-version.js');
const {
computeNightlyTarballURL,
} = require('../../packages/react-native/scripts/ios-prebuild/utils.js');
Expand All @@ -31,30 +35,19 @@ function setActionOutput(key: string, value: string) {
* Reads the Hermes artifact version from
* packages/react-native/sdks/hermes-engine/version.properties.
*
* Returns HERMES_V1_VERSION_NAME when RCT_HERMES_V1_ENABLED=1, otherwise
* HERMES_VERSION_NAME. Returns null if the file or the key is missing.
* Uses the same version key and validation as the local prebuild script.
* A missing file permits a source build; malformed metadata must fail CI.
*/
function resolveHermesArtifactVersion(): string | null {
const propsPath = path.resolve(
import.meta.dirname!, '..', '..',
'packages', 'react-native', 'sdks', 'hermes-engine', 'version.properties',
);
try {
const props: Record<string, string> = {};
for (const line of fs.readFileSync(propsPath, 'utf8').split('\n')) {
const eq = line.indexOf('=');
if (eq > 0) {
props[line.slice(0, eq).trim()] = line.slice(eq + 1).trim();
}
const {version, versionKey} = readHermesMetadata();
echo(`Using ${versionKey}=${version}`);
return version;
} catch (error: any) {
if (error.code === 'ENOENT') {
return null;
}
const key =
process.env.RCT_HERMES_V1_ENABLED === '1'
? 'HERMES_V1_VERSION_NAME'
: 'HERMES_VERSION_NAME';
const version = props[key];
return version != null && version.length > 0 ? version : null;
} catch {
return null;
throw error;
}
}

Expand All @@ -64,10 +57,9 @@ function resolveHermesArtifactVersion(): string | null {
* facebook/hermes. Returns null if the file is missing or empty.
*/
function resolveHermesTag(): string | null {
const tagFile =
process.env.RCT_HERMES_V1_ENABLED === '1'
? '.hermesv1version'
: '.hermesversion';
const {tagFile} = selectHermesMetadata(
'legacy-default', process.env.RCT_HERMES_V1_ENABLED,
);
const tagPath = path.resolve(
import.meta.dirname!, '..', '..',
'packages', 'react-native', 'sdks', tagFile,
Expand All @@ -92,7 +84,7 @@ async function downloadUpstreamHermesTarball(
): Promise<{ tarballPath: string; version: string } | null> {
const version = resolveHermesArtifactVersion();
if (version == null) {
echo('Could not read Hermes version from sdks/hermes-engine/version.properties');
echo('Hermes version.properties is missing — will build from source.');
return null;
}

Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/microsoft-build-rntester.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ jobs:
continue-on-error: ${{ matrix.linkage == 'dynamic' }}
env:
RCT_NEW_ARCH_ENABLED: '1'
# Maven's upstream binaries only contain iOS slices. Build this fork's
# core and dependencies for each destination in the RNTester matrix.
RCT_USE_PREBUILT_RNCORE: '0'
RCT_USE_RN_DEP: '0'
USE_FRAMEWORKS: ${{ matrix.use_frameworks }}
run: |
set -eox pipefail
Expand Down
Loading
Loading