Skip to content
Open
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: 58 additions & 0 deletions .github/workflows/e2e-tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ on:
- all
- android
- ios
- web
pull_request:
types: [ready_for_review]
branches:
Expand Down Expand Up @@ -189,3 +190,60 @@ jobs:
app: ios/build/Build/Products/Debug-iphonesimulator/HarnessPlayground.app
runner: ios
projectRoot: apps/playground

e2e-web:
name: E2E Web
runs-on: macos-latest
if: ${{ (github.event_name == 'pull_request' && github.event.pull_request.base.ref == 'main') || (github.event_name == 'workflow_dispatch' && (github.event.inputs.platform == 'all' || github.event.inputs.platform == 'web')) }}

env:
HARNESS_DEBUG: true

steps:
- name: Checkout code
uses: actions/checkout@v4

- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: latest

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '24.10.0'
cache: 'pnpm'

- name: Install dependencies
run: |
pnpm install

- name: Build packages
run: |
pnpm nx run-many -t build --projects="packages/*"

- name: Run React Native Harness (Safari)
uses: ./actions/web
with:
runner: web:safari
projectRoot: apps/playground

# Test: "waitFor › should use custom interval and timeout options",
# is flacky on Chrome in GitHub CI runners
#
# - name: Run React Native Harness (Chrome)
# uses: ./actions/web
# with:
# runner: web:chrome
# projectRoot: apps/playground

# Test: "waitFor › should use custom interval and timeout options",
# fails on Firefox in GitHub CI runners
#
# - name: Run React Native Harness (Firefox)
# uses: ./actions/web
# with:
# runner: web:firefox
# projectRoot: apps/playground


183 changes: 183 additions & 0 deletions .github/workflows/release-github.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,183 @@
name: Publish packages to GitHub Release

on:
workflow_dispatch:
inputs:
tag:
description: "Release tag (e.g. v1.2.3)"
required: true
type: string
title:
description: "Release title (optional). Defaults to the tag."
required: false
type: string
prerelease:
description: "Mark as prerelease?"
required: false
default: false
type: boolean

permissions:
contents: write

jobs:
release:
runs-on: ubuntu-22.04

steps:
- name: Checkout code
uses: actions/checkout@v4
with:
fetch-depth: 0

# Optional but recommended: ensure tag doesn't already exist
- name: Check if tag already exists
id: tagcheck
run: |
set -euo pipefail
TAG="${{ inputs.tag }}"
if git rev-parse "$TAG" >/dev/null 2>&1; then
echo "Tag $TAG already exists locally."
fi
if git ls-remote --tags origin "$TAG" | grep -q "$TAG"; then
echo "Tag $TAG already exists on origin. Refusing to overwrite."
exit 1
fi

# Create and push the tag pointing to the current commit
- name: Create and push tag
run: |
set -euo pipefail
TAG="${{ inputs.tag }}"
git tag "$TAG"
git push origin "$TAG"

- name: Reclaim disk space
uses: AdityaGarg8/remove-unwanted-software@v5
with:
remove-dotnet: true
remove-haskell: true
remove-codeql: true
remove-docker-images: true

- name: Install pnpm
uses: pnpm/action-setup@v2
with:
version: latest

- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: "24.10.0"
cache: "pnpm"

- name: Install dependencies
run: pnpm install

- name: Build packages
run: pnpm nx run-many -t build --projects="packages/*"

- name: Patch package.json monorepo deps to tarball URLs
env:
OLD_VER: "workspace:*"
TAG: "1.0.0-alpha.20"
BASE_URL: "https://github.com/nachooya/react-native-harness/releases/download"
SCOPE: "@react-native-harness/"
run: |
set -euo pipefail

node <<'NODE'
const fs = require("fs");
const path = require("path");

const OLD_VER = process.env.OLD_VER;
const TAG = process.env.TAG;
const BASE_URL = process.env.BASE_URL;
const SCOPE = process.env.SCOPE;

// Recursively find all package.json files (excluding node_modules/dist)
function findPackageJson(dir, out = []) {
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
if (entry.name === "node_modules" || entry.name === "dist" || entry.name === ".git") continue;
const full = path.join(dir, entry.name);
if (entry.isDirectory()) findPackageJson(full, out);
else if (entry.isFile() && entry.name === "package.json") out.push(full);
}
return out;
}

function tarballUrl(pkgName) {
// pkgName is like "@react-native-harness/runtime"
const monorepoPackage = pkgName.startsWith(SCOPE)
? pkgName.slice(SCOPE.length)
: pkgName;

return `${BASE_URL}/${TAG}/react-native-harness-${monorepoPackage}-${TAG}.tgz`;
}

const files = findPackageJson(process.cwd());

let totalChanges = 0;

for (const file of files) {
const raw = fs.readFileSync(file, "utf8");
const pkg = JSON.parse(raw);

let changed = false;

// fields to patch
const depFields = [
"dependencies",
"devDependencies",
"peerDependencies",
"optionalDependencies",
];

for (const field of depFields) {
if (!pkg[field]) continue;

for (const [name, ver] of Object.entries(pkg[field])) {
if (ver === OLD_VER && name.startsWith(SCOPE)) {
pkg[field][name] = tarballUrl(name);
changed = true;
}
}
}

if (changed) {
fs.writeFileSync(file, JSON.stringify(pkg, null, 2) + "\n");
console.log("Patched:", file);
totalChanges++;
}
}

console.log(`✅ Patched ${totalChanges} package.json files`);
NODE

- name: Create package tarballs
run: |
set -euo pipefail
mkdir -p dist
for pkg in ./packages/*; do
if [ -f "$pkg/package.json" ]; then
echo "Packing $pkg"
(cd "$pkg" && pnpm pack --pack-destination ../../dist)
fi
done

# Optional: generate checksums
- name: Generate checksums
run: |
set -euo pipefail
sha256sum dist/*.tgz > dist/checksums.txt

- name: Create GitHub Release and upload assets
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ inputs.tag }}
name: ${{ inputs.title || inputs.tag }}
prerelease: ${{ inputs.prerelease }}
generate_release_notes: true
files: |
dist/*.tgz
dist/checksums.txt
33 changes: 17 additions & 16 deletions actions/shared/index.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
mod
));

// ../../node_modules/.pnpm/[email protected]/node_modules/picocolors/picocolors.js
// ../../node_modules/picocolors/picocolors.js
var require_picocolors = __commonJS({
"../../node_modules/.pnpm/[email protected]/node_modules/picocolors/picocolors.js"(exports2, module2) {
"../../node_modules/picocolors/picocolors.js"(exports2, module2) {
"use strict";
var p = process || {};
var argv = p.argv || [];
Expand Down Expand Up @@ -102,9 +102,9 @@ var require_picocolors = __commonJS({
}
});

// ../../node_modules/.pnpm/[email protected]/node_modules/sisteransi/src/index.js
// ../../node_modules/sisteransi/src/index.js
var require_src = __commonJS({
"../../node_modules/.pnpm/[email protected]/node_modules/sisteransi/src/index.js"(exports2, module2) {
"../../node_modules/sisteransi/src/index.js"(exports2, module2) {
"use strict";
var ESC = "\x1B";
var CSI = `${ESC}[`;
Expand Down Expand Up @@ -158,9 +158,9 @@ var require_src = __commonJS({
}
});

// ../../node_modules/.pnpm/[email protected]/node_modules/is-unicode-supported/index.js
// ../../node_modules/is-unicode-supported/index.js
var require_is_unicode_supported = __commonJS({
"../../node_modules/.pnpm/[email protected]/node_modules/is-unicode-supported/index.js"(exports2, module2) {
"../../node_modules/is-unicode-supported/index.js"(exports2, module2) {
"use strict";
module2.exports = () => {
if (process.platform !== "win32") {
Expand All @@ -172,7 +172,7 @@ var require_is_unicode_supported = __commonJS({
}
});

// ../../node_modules/.pnpm/[email protected]/node_modules/zod/dist/esm/v3/external.js
// ../../node_modules/zod/dist/esm/v3/external.js
var external_exports = {};
__export(external_exports, {
BRAND: () => BRAND,
Expand Down Expand Up @@ -284,7 +284,7 @@ __export(external_exports, {
void: () => voidType
});

// ../../node_modules/.pnpm/[email protected]/node_modules/zod/dist/esm/v3/helpers/util.js
// ../../node_modules/zod/dist/esm/v3/helpers/util.js
var util;
(function(util3) {
util3.assertEqual = (_) => {
Expand Down Expand Up @@ -418,7 +418,7 @@ var getParsedType = (data) => {
}
};

// ../../node_modules/.pnpm/[email protected]/node_modules/zod/dist/esm/v3/ZodError.js
// ../../node_modules/zod/dist/esm/v3/ZodError.js
var ZodIssueCode = util.arrayToEnum([
"invalid_type",
"invalid_literal",
Expand Down Expand Up @@ -535,7 +535,7 @@ ZodError.create = (issues) => {
return error;
};

// ../../node_modules/.pnpm/[email protected]/node_modules/zod/dist/esm/v3/locales/en.js
// ../../node_modules/zod/dist/esm/v3/locales/en.js
var errorMap = (issue, _ctx) => {
let message;
switch (issue.code) {
Expand Down Expand Up @@ -636,7 +636,7 @@ var errorMap = (issue, _ctx) => {
};
var en_default = errorMap;

// ../../node_modules/.pnpm/[email protected]/node_modules/zod/dist/esm/v3/errors.js
// ../../node_modules/zod/dist/esm/v3/errors.js
var overrideErrorMap = en_default;
function setErrorMap(map) {
overrideErrorMap = map;
Expand All @@ -645,7 +645,7 @@ function getErrorMap() {
return overrideErrorMap;
}

// ../../node_modules/.pnpm/[email protected]/node_modules/zod/dist/esm/v3/helpers/parseUtil.js
// ../../node_modules/zod/dist/esm/v3/helpers/parseUtil.js
var makeIssue = (params) => {
const { data, path: path4, errorMaps, issueData } = params;
const fullPath = [...path4, ...issueData.path || []];
Expand Down Expand Up @@ -755,14 +755,14 @@ var isDirty = (x) => x.status === "dirty";
var isValid = (x) => x.status === "valid";
var isAsync = (x) => typeof Promise !== "undefined" && x instanceof Promise;

// ../../node_modules/.pnpm/[email protected]/node_modules/zod/dist/esm/v3/helpers/errorUtil.js
// ../../node_modules/zod/dist/esm/v3/helpers/errorUtil.js
var errorUtil;
(function(errorUtil2) {
errorUtil2.errToObj = (message) => typeof message === "string" ? { message } : message || {};
errorUtil2.toString = (message) => typeof message === "string" ? message : message?.message;
})(errorUtil || (errorUtil = {}));

// ../../node_modules/.pnpm/[email protected]/node_modules/zod/dist/esm/v3/types.js
// ../../node_modules/zod/dist/esm/v3/types.js
var ParseInputLazyPath = class {
constructor(parent, value, path4, key) {
this._cachedPath = [];
Expand Down Expand Up @@ -4214,6 +4214,7 @@ var ConfigSchema = external_exports.object({
appRegistryComponentName: external_exports.string().min(1, "App registry component name is required"),
runners: external_exports.array(external_exports.any()).min(1, "At least one runner is required"),
defaultRunner: external_exports.string().optional(),
webSocketPort: external_exports.number().optional().default(3001),
bridgeTimeout: external_exports.number().min(1e3, "Bridge timeout must be at least 1 second").default(6e4),
resetEnvironmentBetweenTestFiles: external_exports.boolean().optional().default(true),
unstable__skipAlreadyIncludedModules: external_exports.boolean().optional().default(false),
Expand All @@ -4233,7 +4234,7 @@ var ConfigSchema = external_exports.object({
// ../tools/dist/logger.js
var import_node_util2 = __toESM(require("util"), 1);

// ../../node_modules/.pnpm/@[email protected]/node_modules/@clack/core/dist/index.mjs
// ../../node_modules/@clack/core/dist/index.mjs
var import_node_process = require("process");
var V = __toESM(require("readline"), 1);
var import_node_readline = __toESM(require("readline"), 1);
Expand All @@ -4251,7 +4252,7 @@ var C = { actions: new Set(gt), aliases: /* @__PURE__ */ new Map([["k", "up"], [
var At = globalThis.process.platform.startsWith("win");
var G = Symbol("clack:cancel");

// ../../node_modules/.pnpm/@[email protected]/node_modules/@clack/prompts/dist/index.mjs
// ../../node_modules/@clack/prompts/dist/index.mjs
var import_picocolors = __toESM(require_picocolors(), 1);
var import_node_process2 = __toESM(require("process"), 1);
var import_node_fs = require("fs");
Expand Down
Loading