feat(website): improve editor playground guidance #344
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: CI | |
| on: | |
| pull_request: | |
| types: [opened, synchronize, reopened] | |
| workflow_dispatch: | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.head_ref }} | |
| cancel-in-progress: true | |
| jobs: | |
| changes: | |
| runs-on: ubuntu-latest | |
| name: Detect changes | |
| outputs: | |
| android: ${{ steps.filter.outputs.android }} | |
| ios: ${{ steps.filter.outputs.ios }} | |
| js: ${{ steps.filter.outputs.js }} | |
| web: ${{ steps.filter.outputs.web }} | |
| docs: ${{ steps.filter.outputs.docs }} | |
| c2pa: ${{ steps.filter.outputs.c2pa }} | |
| trace: ${{ steps.filter.outputs.trace }} | |
| native: ${{ steps.filter.outputs.native }} | |
| security: ${{ steps.filter.outputs.security }} | |
| editor: ${{ steps.filter.outputs.editor }} | |
| steps: | |
| - name: Checkout the code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| with: | |
| fetch-depth: 0 | |
| - name: Verify immutable GitHub Actions references | |
| run: | | |
| python3 <<'PY' | |
| import pathlib | |
| import re | |
| action_ref = re.compile(r"^\s*uses:\s*([^\s#]+)", re.MULTILINE) | |
| immutable_ref = re.compile(r"^[^@]+@[0-9a-f]{40}$") | |
| failures = [] | |
| for path in pathlib.Path(".github").rglob("*"): | |
| if path.suffix not in {".yml", ".yaml"}: | |
| continue | |
| content = path.read_text(encoding="utf-8") | |
| for match in action_ref.finditer(content): | |
| value = match.group(1) | |
| if value.startswith("./") or value.startswith("docker://"): | |
| continue | |
| if not immutable_ref.fullmatch(value): | |
| line = content.count("\n", 0, match.start()) + 1 | |
| failures.append(f"{path}:{line}: {value}") | |
| if failures: | |
| raise SystemExit( | |
| "Remote GitHub Actions must use a full commit SHA:\n" | |
| + "\n".join(failures) | |
| ) | |
| print("Verified immutable GitHub Actions references.") | |
| PY | |
| - name: Classify changed files | |
| id: filter | |
| env: | |
| EVENT_NAME: ${{ github.event_name }} | |
| BASE_SHA: ${{ github.event.pull_request.base.sha }} | |
| HEAD_SHA: ${{ github.event.pull_request.head.sha }} | |
| run: | | |
| python3 <<'PY' | |
| import fnmatch | |
| import os | |
| import subprocess | |
| def changed_files(): | |
| if os.environ["EVENT_NAME"] == "workflow_dispatch": | |
| return None | |
| base = os.environ.get("BASE_SHA") | |
| head = os.environ.get("HEAD_SHA") or "HEAD" | |
| if not base: | |
| return None | |
| try: | |
| output = subprocess.check_output( | |
| ["git", "diff", "--name-only", base, head], | |
| text=True, | |
| ) | |
| except subprocess.CalledProcessError: | |
| output = subprocess.check_output( | |
| ["git", "diff", "--name-only", base, "HEAD"], | |
| text=True, | |
| ) | |
| return [line for line in output.splitlines() if line] | |
| files = changed_files() | |
| all_jobs = files is None | |
| def matches(path, patterns): | |
| return any(fnmatch.fnmatch(path, pattern) for pattern in patterns) | |
| def any_changed(include, exclude=()): | |
| if all_jobs: | |
| return True | |
| return any( | |
| matches(path, include) and not matches(path, exclude) | |
| for path in files | |
| ) | |
| android = any_changed( | |
| [ | |
| "android/**", | |
| "cpp/**", | |
| "src/**", | |
| "packages/editor/**", | |
| "package.json", | |
| "package-lock.json", | |
| "app.plugin.js", | |
| ".github/workflows/ci.yml", | |
| "example/android/**", | |
| "example/app.json", | |
| "example/babel.config.js", | |
| "example/e2e/**", | |
| "example/index.js", | |
| "example/metro.config.js", | |
| "example/package.json", | |
| "example/package-lock.json", | |
| "example/tsconfig.json", | |
| "example/src/**", | |
| "scripts/run-android-instrumentation.sh", | |
| ], | |
| ["example/ios/**"], | |
| ) | |
| ios = any_changed( | |
| [ | |
| "ios/**", | |
| "cpp/**", | |
| "src/**", | |
| "packages/editor/**", | |
| "package.json", | |
| "package-lock.json", | |
| "app.plugin.js", | |
| "react-native-image-marker.podspec", | |
| ".github/workflows/ci.yml", | |
| "example/app.json", | |
| "example/babel.config.js", | |
| "example/e2e/**", | |
| "example/index.js", | |
| "example/ios/**", | |
| "example/metro.config.js", | |
| "example/package.json", | |
| "example/package-lock.json", | |
| "example/tsconfig.json", | |
| "example/src/**", | |
| ], | |
| ["example/android/**"], | |
| ) | |
| js = any_changed( | |
| [ | |
| "src/**", | |
| "cpp/**", | |
| "packages/editor/**", | |
| "package.json", | |
| "package-lock.json", | |
| "app.plugin.js", | |
| "tsconfig*.json", | |
| "lefthook.yml", | |
| ".eslintignore", | |
| ".eslintrc.js", | |
| ".github/workflows/ci.yml", | |
| "example/package.json", | |
| "example/package-lock.json", | |
| "example/tsconfig.json", | |
| "example/src/**", | |
| "expo-example/**", | |
| ] | |
| ) | |
| web = any_changed( | |
| [ | |
| "src/**", | |
| "package.json", | |
| "package-lock.json", | |
| "expo-example/**", | |
| ".github/workflows/ci.yml", | |
| ] | |
| ) | |
| docs = any_changed( | |
| [ | |
| "src/**", | |
| "website/**", | |
| "README.MD", | |
| "package.json", | |
| ".github/workflows/ci.yml", | |
| ".github/workflows/docs-site.yml", | |
| ] | |
| ) | |
| c2pa = any_changed( | |
| [ | |
| "examples/c2pa-service/**", | |
| ".github/workflows/ci.yml", | |
| ] | |
| ) | |
| trace = any_changed( | |
| [ | |
| "examples/trace-service/**", | |
| "src/batch.ts", | |
| "src/index.ts", | |
| "src/invisible-watermark.ts", | |
| "src/trace-runtime.ts", | |
| "trace-runtime.js", | |
| "trace-runtime.d.ts", | |
| "package.json", | |
| "package-lock.json", | |
| ".github/workflows/ci.yml", | |
| ] | |
| ) | |
| security = any_changed( | |
| [ | |
| "package.json", | |
| "package-lock.json", | |
| "website/package.json", | |
| "website/package-lock.json", | |
| "example/package.json", | |
| "example/package-lock.json", | |
| "expo-example/package.json", | |
| "expo-example/package-lock.json", | |
| "examples/c2pa-service/package.json", | |
| "examples/c2pa-service/package-lock.json", | |
| "examples/trace-service/package.json", | |
| "examples/trace-service/package-lock.json", | |
| "react-native-image-marker.podspec", | |
| "android/build.gradle", | |
| "android/gradle.properties", | |
| "android/gradle/wrapper/**", | |
| "example/android/build.gradle", | |
| "example/android/app/build.gradle", | |
| "example/android/gradle.properties", | |
| "example/android/gradle/wrapper/**", | |
| "ios/Podfile", | |
| "example/ios/Podfile", | |
| ] | |
| ) | |
| editor = any_changed( | |
| [ | |
| "packages/editor/**", | |
| "src/**", | |
| "scripts/fix-esm-imports.mjs", | |
| "scripts/verify-package-consumer.mjs", | |
| "package.json", | |
| "package-lock.json", | |
| "tsconfig*.json", | |
| ".github/workflows/ci.yml", | |
| ] | |
| ) | |
| results = { | |
| "android": android, | |
| "ios": ios, | |
| "js": js, | |
| "web": web, | |
| "docs": docs, | |
| "c2pa": c2pa, | |
| "trace": trace, | |
| "native": android or ios, | |
| "security": security, | |
| "editor": editor, | |
| } | |
| with open(os.environ["GITHUB_OUTPUT"], "a", encoding="utf-8") as output: | |
| for key, value in results.items(): | |
| print(f"{key}={'true' if value else 'false'}", file=output) | |
| PY | |
| security: | |
| runs-on: ubuntu-latest | |
| name: Security & Dependency Review | |
| needs: changes | |
| if: needs.changes.outputs.security == 'true' | |
| steps: | |
| - name: Checkout the code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - name: Dependency Review | |
| uses: actions/dependency-review-action@a1d282b36b6f3519aa1f3fc636f609c47dddb294 # v5 | |
| if: github.event_name == 'pull_request' | |
| with: | |
| fail-on-severity: moderate | |
| # ESLint 8 still requires minimatch 3's CommonJS brace-expansion API, | |
| # and the RN 0.73 CLI still pulls ip and fast-xml-parser without | |
| # compatible patched releases. These advisories are accepted only | |
| # for the dev graph; the packed Core and Editor production graphs | |
| # are audited below. | |
| allow-ghsas: GHSA-mh99-v99m-4gvg,GHSA-2p57-rm9w-gvfp,GHSA-gh4j-gqv2-49f6 | |
| - name: Setup node | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 | |
| with: | |
| node-version: '22.22.0' | |
| - name: Install dependencies | |
| run: | | |
| npm ci | |
| npm ci --prefix website | |
| - name: Enforce production dependency audit | |
| run: | | |
| npm run verify:consumer | |
| npm audit --prefix website --omit=dev --audit-level=moderate | |
| - name: Report development-tooling audit | |
| run: | | |
| npm audit --audit-level=moderate | |
| npm audit --prefix website --audit-level=moderate | |
| continue-on-error: true | |
| js-check: | |
| runs-on: ubuntu-latest | |
| name: JS Typecheck and Test | |
| needs: changes | |
| if: needs.changes.outputs.js == 'true' | |
| steps: | |
| - name: Checkout the code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - name: Setup node | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 | |
| with: | |
| node-version: '22.22.0' | |
| - name: Install root and Expo example dependencies | |
| run: | | |
| npm ci | |
| npm --prefix expo-example install --ignore-scripts --no-audit --no-fund | |
| - name: Verify Expo clean-install bundle | |
| run: | | |
| test ! -d example/node_modules | |
| npm --prefix expo-example run bundle:smoke | |
| - name: Install React Native example dependencies | |
| run: npm --prefix example install | |
| - name: Typecheck | |
| run: | | |
| npm run typecheck | |
| npm run typecheck:example | |
| npm run typecheck:expo-example | |
| - name: Lint | |
| run: npm run lint | |
| - name: Verify release notes generation | |
| run: npm run check:release-notes | |
| - name: Test | |
| run: | | |
| npm test -- --coverage --runInBand | |
| npm run test:cpp | |
| npm run test:release-routing | |
| - name: Build package | |
| run: npm run prepack | |
| editor-check: | |
| runs-on: ubuntu-latest | |
| name: Editor Package and Consumer | |
| needs: changes | |
| if: needs.changes.outputs.editor == 'true' | |
| steps: | |
| - name: Checkout the code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - name: Setup node | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 | |
| with: | |
| node-version: '22.22.0' | |
| cache: npm | |
| cache-dependency-path: package-lock.json | |
| - name: Install dependencies | |
| run: npm ci | |
| - name: Test and build Editor independently | |
| run: | | |
| npm run prepack | |
| npm run test:editor | |
| npm run build:editor | |
| npm run check:editor-api | |
| - name: Verify packed Core and Editor consumers | |
| run: node scripts/verify-package-consumer.mjs | |
| c2pa-service: | |
| runs-on: ubuntu-latest | |
| name: C2PA Service Example | |
| needs: changes | |
| if: needs.changes.outputs.c2pa == 'true' | |
| steps: | |
| - name: Checkout the code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - name: Setup node | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 | |
| with: | |
| node-version: '22.22.0' | |
| cache: npm | |
| cache-dependency-path: examples/c2pa-service/package-lock.json | |
| - name: Install dependencies | |
| run: npm ci --prefix examples/c2pa-service | |
| - name: Typecheck and test | |
| run: | | |
| npm --prefix examples/c2pa-service run typecheck | |
| npm --prefix examples/c2pa-service test | |
| - name: Audit production dependencies | |
| run: npm audit --prefix examples/c2pa-service --omit=dev --audit-level=moderate | |
| - name: Load official C2PA Node module | |
| working-directory: examples/c2pa-service | |
| run: | | |
| node -e "import('@contentauth/c2pa-node').then(({ Builder, Reader, LocalSigner }) => { if (!Builder || !Reader || !LocalSigner) process.exit(1) })" | |
| trace-service: | |
| runs-on: ubuntu-latest | |
| name: Trace Service (Node ${{ matrix.node }}) | |
| needs: changes | |
| if: needs.changes.outputs.trace == 'true' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| node: ['22.22.0', '24'] | |
| steps: | |
| - name: Checkout the code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - name: Setup node | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 | |
| with: | |
| node-version: ${{ matrix.node }} | |
| cache: npm | |
| cache-dependency-path: | | |
| package-lock.json | |
| examples/trace-service/package-lock.json | |
| - name: Install and build the core package | |
| run: | | |
| npm ci | |
| npm run prepack | |
| - name: Install Trace Service dependencies | |
| run: npm ci --prefix examples/trace-service | |
| - name: Typecheck and test | |
| run: | | |
| npm --prefix examples/trace-service run typecheck | |
| npm --prefix examples/trace-service test | |
| - name: Load sharp and audit production dependencies | |
| working-directory: examples/trace-service | |
| run: | | |
| node -e "import('sharp').then(({ default: sharp }) => { if (!sharp.versions.sharp) process.exit(1) })" | |
| npm audit --omit=dev --audit-level=moderate | |
| - name: Build Trace Service container | |
| if: matrix.node == '22.22.0' | |
| run: docker build -f examples/trace-service/Dockerfile -t image-marker-trace:test . | |
| - name: Verify on-demand package boundary | |
| if: matrix.node == '22.22.0' | |
| run: | | |
| node - <<'NODE' | |
| const Module = require('module'); | |
| const loaded = []; | |
| const original = Module._load; | |
| Module._load = function (request, parent, isMain) { | |
| loaded.push(request); | |
| return original.apply(this, arguments); | |
| }; | |
| require('./lib/commonjs/index'); | |
| if (loaded.some((request) => request.includes('trace-runtime'))) { | |
| throw new Error('The root entry loaded trace-runtime eagerly.'); | |
| } | |
| const runtime = require('./trace-runtime.js'); | |
| if (typeof runtime.createInvisibleWatermarkRuntime !== 'function') { | |
| throw new Error('The trace-runtime subpath is missing.'); | |
| } | |
| NODE | |
| npm pack --ignore-scripts --dry-run --json > package-files.json | |
| node - <<'NODE' | |
| const names = require('./package-files.json')[0].files.map((file) => file.path); | |
| for (const required of [ | |
| 'trace-runtime.js', | |
| 'trace-runtime.d.ts', | |
| 'lib/commonjs/trace-runtime.js', | |
| 'lib/typescript/module/trace-runtime.d.ts', | |
| 'lib/worker/invisible-watermark.js', | |
| ]) { | |
| if (!names.includes(required)) { | |
| throw new Error(`${required} is missing from the package.`); | |
| } | |
| } | |
| if (names.some((name) => name.includes('examples/trace-service') || name.toLowerCase().includes('sharp'))) { | |
| throw new Error('Trace Service dependencies leaked into the package.'); | |
| } | |
| NODE | |
| docs-site: | |
| runs-on: ubuntu-latest | |
| name: Documentation Site | |
| needs: changes | |
| if: needs.changes.outputs.docs == 'true' | |
| steps: | |
| - name: Checkout the code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - name: Setup node | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 | |
| with: | |
| node-version: '22.22.0' | |
| cache: npm | |
| cache-dependency-path: | | |
| package-lock.json | |
| website/package-lock.json | |
| - name: Install dependencies | |
| run: | | |
| npm ci | |
| npm ci --prefix website | |
| - name: Build and verify documentation | |
| run: npm run docs | |
| - name: Install Chromium | |
| run: npm exec --prefix website -- playwright install --with-deps chromium firefox webkit | |
| - name: Smoke test playground and online tools | |
| run: npm --prefix website run smoke | |
| web-example: | |
| runs-on: ubuntu-latest | |
| name: React Native Web Example | |
| needs: changes | |
| if: needs.changes.outputs.web == 'true' | |
| steps: | |
| - name: Checkout the code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - name: Setup node | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 | |
| with: | |
| node-version: '22.22.0' | |
| cache: npm | |
| cache-dependency-path: | | |
| package-lock.json | |
| expo-example/package-lock.json | |
| - name: Install dependencies | |
| run: | | |
| npm ci | |
| npm --prefix example install --ignore-scripts --no-audit --no-fund | |
| npm ci --prefix expo-example --ignore-scripts --no-audit --no-fund | |
| - name: Test Web SDK | |
| run: npm run test:web -- --coverage | |
| - name: Upload Web coverage report | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: web-coverage-${{ github.sha }} | |
| path: coverage | |
| if-no-files-found: error | |
| retention-days: 14 | |
| - name: Typecheck web example | |
| run: npm --prefix expo-example run typecheck | |
| - name: Export web example | |
| run: npm --prefix expo-example run bundle:web | |
| - name: Verify web platform resolution | |
| run: npm --prefix expo-example run verify:web-bundle | |
| - name: Install Playwright browsers | |
| run: npm exec --prefix expo-example -- playwright install --with-deps chromium firefox webkit | |
| - name: Smoke test web example | |
| run: npm --prefix expo-example run smoke:web | |
| expo-current: | |
| runs-on: ubuntu-latest | |
| name: Expo 57 / RN 0.86 (New Architecture) | |
| needs: changes | |
| if: needs.changes.outputs.js == 'true' | |
| timeout-minutes: 45 | |
| steps: | |
| - name: Checkout the code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - name: Setup node | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 | |
| with: | |
| node-version: '22.22.0' | |
| cache: npm | |
| cache-dependency-path: expo-example/package-lock.json | |
| - name: Set up JDK | |
| uses: actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95 # v5 | |
| with: | |
| distribution: temurin | |
| java-version: '17' | |
| cache: gradle | |
| - name: Setup Android SDK | |
| uses: android-actions/setup-android@40fd30fb8d7440372e1316f5d1809ec01dcd3699 # v4 | |
| - name: Install dependencies | |
| run: npm ci --prefix expo-example --ignore-scripts --no-audit --no-fund | |
| - name: Verify Expo versions and bundle | |
| run: | | |
| (cd expo-example && npx --yes expo-doctor@1.20.1) | |
| npm --prefix expo-example run typecheck | |
| npm --prefix expo-example run bundle:smoke | |
| - name: Generate Expo development-build project | |
| working-directory: expo-example | |
| run: npx expo prebuild --platform android --clean --no-install | |
| - name: Compile New Architecture development build | |
| env: | |
| NODE_ENV: production | |
| run: | | |
| cd expo-example/android | |
| ./gradlew :app:assembleDebug -PreactNativeArchitectures=arm64-v8a --stacktrace --no-daemon | |
| react-native-current: | |
| runs-on: ubuntu-latest | |
| name: RN 0.86 (New Architecture) | |
| needs: changes | |
| if: needs.changes.outputs.native == 'true' | |
| timeout-minutes: 45 | |
| steps: | |
| - name: Checkout the code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - name: Setup node | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 | |
| with: | |
| node-version: '22.22.0' | |
| cache: npm | |
| cache-dependency-path: package-lock.json | |
| - name: Set up JDK | |
| uses: actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95 # v5 | |
| with: | |
| distribution: temurin | |
| java-version: '17' | |
| cache: gradle | |
| - name: Setup Android SDK | |
| uses: android-actions/setup-android@40fd30fb8d7440372e1316f5d1809ec01dcd3699 # v4 | |
| - name: Install and build current React Native fixture | |
| run: | | |
| npm ci | |
| bash scripts/test-react-native-current.sh | |
| install-dep: | |
| runs-on: macos-latest | |
| env: | |
| NO_FLIPPER: '1' | |
| name: Install dependencies | |
| needs: changes | |
| if: needs.changes.outputs.native == 'true' | |
| steps: | |
| - name: Checkout the code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - name: Verify Dev Changed files | |
| uses: tj-actions/changed-files@24d32ffd492484c1d75e0c0b894501ddb9d30d62 # v47 | |
| id: verify-dev-changed-files | |
| with: | |
| files: | | |
| !*.md | |
| !*.MD | |
| !*.yml | |
| - uses: actions/cache@v6 | |
| name: Cache node_modules | |
| id: cache-node-modules | |
| if: steps.verify-dev-changed-files.outputs.any_changed == 'true' | |
| with: | |
| path: | | |
| node_modules | |
| example/node_modules | |
| key: ${{ runner.os }}-nodeModules-${{ hashFiles('package.json') }}-${{ hashFiles('example/package.json') }} | |
| restore-keys: | | |
| ${{ runner.os }}-nodeModules- | |
| - name: Set up Ruby | |
| if: steps.verify-dev-changed-files.outputs.any_changed == 'true' | |
| uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1 | |
| with: | |
| ruby-version: '3.2' | |
| bundler-cache: true | |
| - name: Setup node | |
| if: steps.verify-dev-changed-files.outputs.any_changed == 'true' | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 | |
| with: | |
| node-version: '22.22.0' | |
| - name: Install npm dependencies | |
| if: steps.cache-node-modules.outputs.cache-hit != 'true' && steps.verify-dev-changed-files.outputs.any_changed == 'true' | |
| run: | | |
| ls | |
| pwd | |
| npm install | |
| cd example | |
| ls | |
| pwd | |
| npm install | |
| - name: Typecheck example | |
| if: steps.verify-dev-changed-files.outputs.any_changed == 'true' | |
| run: npm run typecheck:example | |
| android-build: | |
| runs-on: macos-latest | |
| name: RN 0.73 Android (Legacy + New Architecture) | |
| needs: [changes, install-dep] | |
| if: needs.changes.outputs.android == 'true' | |
| steps: | |
| - name: Checkout the code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - name: Verify Android Changed files | |
| uses: tj-actions/changed-files@24d32ffd492484c1d75e0c0b894501ddb9d30d62 # v47 | |
| id: verify-android-changed-files | |
| with: | |
| files: | | |
| android/** | |
| src/** | |
| package.json | |
| package-lock.json | |
| app.plugin.js | |
| .github/workflows/ci.yml | |
| example/android/** | |
| example/app.json | |
| example/babel.config.js | |
| !example/ios/** | |
| example/e2e/** | |
| example/index.js | |
| example/metro.config.js | |
| example/package.json | |
| example/package-lock.json | |
| example/tsconfig.json | |
| example/src/** | |
| scripts/run-android-instrumentation.sh | |
| - uses: actions/cache@v6 | |
| name: Cache node_modules | |
| if: steps.verify-android-changed-files.outputs.any_changed == 'true' | |
| id: cache-node-modules | |
| with: | |
| path: | | |
| node_modules | |
| example/node_modules | |
| key: ${{ runner.os }}-nodeModules-${{ hashFiles('package.json') }}-${{ hashFiles('example/package.json') }} | |
| restore-keys: | | |
| ${{ runner.os }}-nodeModules- | |
| - uses: actions/cache@v6 | |
| id: cache-gradle | |
| name: Cache Gradle dependencies | |
| if: steps.verify-android-changed-files.outputs.any_changed == 'true' | |
| with: | |
| path: | | |
| ~/.gradle/caches | |
| ~/.gradle/wrapper | |
| key: ${{ runner.os }}-gradle-${{ hashFiles('example/android/gradle/wrapper/gradle-wrapper.properties') }}-${{ hashFiles('android/src/**/*.kt') }} | |
| restore-keys: | | |
| ${{ runner.os }}-gradle- | |
| - name: Setup node | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 | |
| if: steps.verify-android-changed-files.outputs.any_changed == 'true' | |
| with: | |
| node-version: '22.22.0' | |
| - name: Set up JDK | |
| uses: actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95 # v5 | |
| if: steps.verify-android-changed-files.outputs.any_changed == 'true' | |
| with: | |
| distribution: 'zulu' | |
| java-version: 17 | |
| - name: Install npm dependencies | |
| if: steps.cache-node-modules.outputs.cache-hit != 'true' && steps.verify-android-changed-files.outputs.any_changed == 'true' | |
| run: | | |
| npm install | |
| cd example | |
| npm install | |
| - name: Install Gradle dependencies | |
| if: steps.cache-gradle.outputs.cache-hit != 'true' && steps.verify-android-changed-files.outputs.any_changed == 'true' | |
| run: | | |
| cd example/android | |
| ./gradlew build --stacktrace | |
| - name: Run unit tests | |
| if: steps.verify-android-changed-files.outputs.any_changed == 'true' | |
| run: | | |
| cd example/android | |
| ./gradlew test --stacktrace | |
| - name: Build APK | |
| if: steps.verify-android-changed-files.outputs.any_changed == 'true' | |
| run: | | |
| npm run prepack | |
| cd example/android | |
| ./gradlew assembleRelease | |
| mv app/build/outputs/apk/release/app-release.apk app-release-${{ github.sha }}.apk | |
| - name: Upload APK | |
| if: steps.verify-android-changed-files.outputs.any_changed == 'true' | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| with: | |
| name: app-release-${{ github.sha }}.apk | |
| path: ${{ github.workspace }}/example/android/app-release-${{ github.sha }}.apk | |
| - name: Build APK New Architecture | |
| if: steps.verify-android-changed-files.outputs.any_changed == 'true' | |
| run: | | |
| cd example/android | |
| ./gradlew clean | |
| ./gradlew -PnewArchEnabled=true assembleDebug --stacktrace | |
| - name: Compile Android tests New Architecture | |
| if: steps.verify-android-changed-files.outputs.any_changed == 'true' | |
| run: | | |
| cd example/android | |
| ./gradlew -PnewArchEnabled=true :app:compileDebugAndroidTestKotlin --stacktrace | |
| android-api-level-test: | |
| runs-on: ubuntu-latest | |
| needs: [changes, android-build] | |
| if: needs.changes.outputs.android == 'true' | |
| name: Android Test | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| api-level: [24, 31] | |
| target: [default] | |
| steps: | |
| - name: Checkout the code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - name: Verify Android Changed files | |
| uses: tj-actions/changed-files@24d32ffd492484c1d75e0c0b894501ddb9d30d62 # v47 | |
| id: verify-android-changed-files | |
| with: | |
| files: | | |
| android/** | |
| src/** | |
| package.json | |
| package-lock.json | |
| app.plugin.js | |
| .github/workflows/ci.yml | |
| example/android/** | |
| example/app.json | |
| example/babel.config.js | |
| !example/ios/** | |
| example/e2e/** | |
| example/index.js | |
| example/metro.config.js | |
| example/package.json | |
| example/package-lock.json | |
| example/tsconfig.json | |
| example/src/** | |
| scripts/run-android-instrumentation.sh | |
| - uses: actions/cache@v6 | |
| name: Cache node_modules | |
| if: steps.verify-android-changed-files.outputs.any_changed == 'true' | |
| id: cache-node-modules | |
| with: | |
| path: | | |
| node_modules | |
| example/node_modules | |
| key: ${{ runner.os }}-nodeModules-${{ hashFiles('package.json') }}-${{ hashFiles('example/package.json') }} | |
| restore-keys: | | |
| ${{ runner.os }}-nodeModules- | |
| - uses: actions/cache@v6 | |
| name: Cache Gradle dependencies | |
| if: steps.verify-android-changed-files.outputs.any_changed == 'true' | |
| id: cache-gradle | |
| with: | |
| path: | | |
| ~/.gradle/caches | |
| ~/.gradle/wrapper | |
| key: ${{ runner.os }}-gradle-${{ hashFiles('example/android/gradle/wrapper/gradle-wrapper.properties') }}-${{ hashFiles('android/src/**/*.kt') }} | |
| restore-keys: | | |
| ${{ runner.os }}-gradle- | |
| - name: Setup node | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 | |
| if: steps.verify-android-changed-files.outputs.any_changed == 'true' | |
| with: | |
| node-version: '22.22.0' | |
| - name: Set up JDK | |
| uses: actions/setup-java@03ad4de0992f5dab5e18fcb136590ce7c4a0ac95 # v5 | |
| if: steps.verify-android-changed-files.outputs.any_changed == 'true' | |
| with: | |
| distribution: 'zulu' | |
| java-version: 17 | |
| - name: Install npm dependencies | |
| if: steps.cache-node-modules.outputs.cache-hit != 'true' && steps.verify-android-changed-files.outputs.any_changed == 'true' | |
| run: | | |
| npm install | |
| cd example | |
| npm install | |
| - name: Enable KVM group perms | |
| if: steps.verify-android-changed-files.outputs.any_changed == 'true' | |
| run: | | |
| echo 'KERNEL=="kvm", GROUP="kvm", MODE="0666", OPTIONS+="static_node=kvm"' | sudo tee /etc/udev/rules.d/99-kvm4all.rules | |
| sudo udevadm control --reload-rules | |
| sudo udevadm trigger --name-match=kvm | |
| - name: Instrumentation Tests | |
| uses: reactivecircus/android-emulator-runner@a421e43855164a8197daf9d8d40fe71c6996bb0d # v2 | |
| if: steps.verify-android-changed-files.outputs.any_changed == 'true' | |
| with: | |
| api-level: ${{ matrix.api-level }} | |
| target: ${{ matrix.target }} | |
| arch: x86_64 | |
| profile: pixel_5 | |
| script: bash scripts/run-android-instrumentation.sh | |
| - name: Upload Reports | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7 | |
| if: steps.verify-android-changed-files.outputs.any_changed == 'true' | |
| with: | |
| name: Test-Reports-api-${{ matrix.api-level }} | |
| path: ${{ github.workspace }}/example/android/app/build/reports | |
| ios-build-test: | |
| runs-on: macos-15 | |
| env: | |
| NO_FLIPPER: '1' | |
| needs: [changes, install-dep] | |
| if: needs.changes.outputs.ios == 'true' | |
| name: RN 0.73 iOS (Legacy Architecture) | |
| strategy: | |
| matrix: | |
| cocoapods: ['1.15.2'] | |
| steps: | |
| - name: Checkout the code | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7 | |
| - name: Verify iOS Changed files | |
| uses: tj-actions/changed-files@24d32ffd492484c1d75e0c0b894501ddb9d30d62 # v47 | |
| id: verify-iOS-changed-files | |
| with: | |
| files: | | |
| ios/** | |
| src/** | |
| package.json | |
| package-lock.json | |
| app.plugin.js | |
| react-native-image-marker.podspec | |
| .github/workflows/ci.yml | |
| example/app.json | |
| example/babel.config.js | |
| !example/android/** | |
| example/e2e/** | |
| example/index.js | |
| example/ios/** | |
| example/metro.config.js | |
| example/package.json | |
| example/package-lock.json | |
| example/tsconfig.json | |
| example/src/** | |
| - uses: actions/cache@v6 | |
| name: Cache node_modules | |
| id: cache-node-modules | |
| if: steps.verify-iOS-changed-files.outputs.any_changed == 'true' | |
| with: | |
| path: | | |
| node_modules | |
| example/node_modules | |
| key: ${{ runner.os }}-nodeModules-${{ hashFiles('package.json') }}-${{ hashFiles('example/package.json') }} | |
| restore-keys: | | |
| ${{ runner.os }}-nodeModules- | |
| - name: Cache Pods | |
| id: cache-pods | |
| uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6 | |
| if: steps.verify-iOS-changed-files.outputs.any_changed == 'true' | |
| with: | |
| path: example/ios/Pods | |
| key: ${{ runner.os }}-pods-${{ matrix.cocoapods }}-${{ hashFiles('example/ios/Podfile', 'react-native-image-marker.podspec', 'package.json', 'ios/**/*', 'src/NativeImageMarker.ts') }} | |
| restore-keys: | | |
| ${{ runner.os }}-pods-${{ matrix.cocoapods }}- | |
| ${{ runner.os }}-pods- | |
| - name: Set up Ruby | |
| uses: ruby/setup-ruby@95ef2b042f9d7a56d8268cba8559e2842e2ad01b # v1 | |
| if: steps.verify-iOS-changed-files.outputs.any_changed == 'true' | |
| with: | |
| ruby-version: '3.2' | |
| bundler-cache: true | |
| - name: Install Cocoapods | |
| if: steps.verify-iOS-changed-files.outputs.any_changed == 'true' | |
| run: gem install cocoapods -v ${{ matrix.cocoapods }} | |
| - name: Setup node | |
| if: steps.verify-iOS-changed-files.outputs.any_changed == 'true' | |
| uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7 | |
| with: | |
| node-version: '22.22.0' | |
| - name: Install npm dependencies | |
| if: steps.cache-node-modules.outputs.cache-hit != 'true' && steps.verify-iOS-changed-files.outputs.any_changed == 'true' | |
| run: | | |
| npm install | |
| cd example | |
| npm install | |
| - name: Compile shared C++ Swift bridge | |
| if: steps.verify-iOS-changed-files.outputs.any_changed == 'true' | |
| run: npm run test:cpp:ios-bridge | |
| - name: Install Pods | |
| if: steps.verify-iOS-changed-files.outputs.any_changed == 'true' | |
| run: | | |
| cd example/ios | |
| if [ "${{ steps.cache-pods.outputs.cache-hit }}" != "true" ]; then | |
| pod cache clean --all | |
| fi | |
| pod install | |
| - name: Install xcpretty | |
| if: steps.verify-iOS-changed-files.outputs.any_changed == 'true' | |
| run: gem install xcpretty | |
| - name: Select iOS simulator | |
| if: steps.verify-iOS-changed-files.outputs.any_changed == 'true' | |
| id: ios-simulator | |
| run: | | |
| simulator_id="$( | |
| xcrun simctl list devices available --json | ruby -rjson -e ' | |
| devices = JSON.parse(STDIN.read).fetch("devices").values.flatten | |
| simulator = devices.find { |device| device["name"] == "iPhone 16" } || | |
| devices.find { |device| device["name"].include?("iPhone") } | |
| abort("No available iPhone simulator found") unless simulator | |
| puts simulator.fetch("udid") | |
| ' | |
| )" | |
| echo "id=$simulator_id" >> "$GITHUB_OUTPUT" | |
| - name: Build | |
| if: steps.verify-iOS-changed-files.outputs.any_changed == 'true' | |
| run: | | |
| set -o pipefail | |
| cd example/ios | |
| xcodebuild -workspace ImageMarkerExample.xcworkspace -scheme ImageMarkerExample -configuration Release -sdk iphonesimulator -destination 'id=${{ steps.ios-simulator.outputs.id }}' 2>&1 | tee xcodebuild-build.log | xcpretty || { | |
| status=$? | |
| tail -n 200 xcodebuild-build.log | |
| exit $status | |
| } | |
| - name: Test | |
| if: steps.verify-iOS-changed-files.outputs.any_changed == 'true' | |
| run: | | |
| set -o pipefail | |
| cd example/ios | |
| FORCE_BUNDLING=1 xcodebuild \ | |
| -workspace ImageMarkerExample.xcworkspace \ | |
| -scheme ImageMarkerExample \ | |
| -sdk iphonesimulator \ | |
| -destination 'id=${{ steps.ios-simulator.outputs.id }}' \ | |
| -parallel-testing-enabled NO \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testApp \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testEditorUndoPreviewAndOriginalExportRunThroughCore \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testTextAnchorOffsetFeatureProducesPreview \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testMixedWatermarkFeatureProducesPreview \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testTiledTextFeatureProducesPreview \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testInvisibleWatermarkFeatureEmbedsAndDetects \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testInvisibleWatermarkMatchesCrossPlatformVectors \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testInvisibleWatermarkEmbedsAndAuthenticatesPixels \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testInvisibleWatermarkRecoversLightImageResizing \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testSharpScaledWatermarkFeatureProducesPreview \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testRotationOutputPolicyFeatureProducesPreview \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testWatermarkOrientationMatchesUprightPixelReference \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testNormalizesUIImageOrientationBeforeCGImageRendering \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testKeepsAlreadyUprightUIImageInstance \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testTileLayoutResolvesOffsetsStaggerAndCopyLimit \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testTiledImageRepeatsAcrossCanvas \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testPositionDefaultsAndEdgeInsetOverrides \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testRotatesFortyFiveDegreesWithExpandedTransparentCanvas \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testRotatesFortyFiveDegreesWithCroppedOriginalCanvas \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testJPEGUsesExplicitMatteWhilePNGPreservesTransparency \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testTrimsTransparentWatermarkPaddingBeforeAnchoredPositioning \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testOptionsPreferFilenameAndProvideRenderingDefaults \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testMaxSizeDefaultsValidatesAndBuildsBoundedLoadRequests \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testSharedCppCoreFitsPixelBounds \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testLargeImageDownsamplePerformanceAndMemory \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testImageIODownsamplesDataAndBase64BeforeRendering \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testRejectsInvalidQualityAlphaAndUnsafeFilenames \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testParsesShadowHexColorAndRejectsInvalidValues \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testBackgroundScaleChangesCanvasWithoutImplicitlyScalingLayers \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testBackgroundScaleDoesNotImplicitlyScaleText \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testSequentialAsyncMapBoundsConcurrencyAndPreservesOrder \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testAsyncLimiterBoundsConcurrencyAndSkipsCancelledWaiter \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testCancellableContinuationHandlesCancellationCompletionRace \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testRenderAndReleaseSourcesDropsInputsBeforeEncodingPhase \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testAnchorsRotatedWatermarkByVisibleBounds \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testRendersImageWatermarkUsingAnchoredOffsets \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testRotatesRenderedBackgroundWhenRequested \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testRendersScaledImageWatermarkWithoutInterpolatedEdges \ | |
| -only-testing:ImageMarkerExampleUITests/ImageMarkerExampleUITests/testPreservesAsymmetricWatermarkOrientationAcrossScaleAndRotation \ | |
| test 2>&1 | tee xcodebuild-test.log | xcpretty || { | |
| status=$? | |
| tail -n 200 xcodebuild-test.log | |
| exit $status | |
| } | |
| - name: Install Pods New Architecture | |
| if: steps.verify-iOS-changed-files.outputs.any_changed == 'true' | |
| run: | | |
| cd example/ios | |
| RCT_NEW_ARCH_ENABLED=1 NO_FLIPPER=1 pod install | |
| - name: Build New Architecture | |
| if: steps.verify-iOS-changed-files.outputs.any_changed == 'true' | |
| run: | | |
| set -o pipefail | |
| cd example/ios | |
| RCT_NEW_ARCH_ENABLED=1 xcodebuild -workspace ImageMarkerExample.xcworkspace -scheme ImageMarkerExample -configuration Debug -sdk iphonesimulator -destination 'id=${{ steps.ios-simulator.outputs.id }}' 2>&1 | tee xcodebuild-new-arch-build.log | xcpretty || { | |
| status=$? | |
| tail -n 200 xcodebuild-new-arch-build.log | |
| exit $status | |
| } | |
| ci-complete: | |
| name: Complete CI | |
| needs: | |
| [ | |
| changes, | |
| security, | |
| js-check, | |
| editor-check, | |
| c2pa-service, | |
| trace-service, | |
| docs-site, | |
| web-example, | |
| expo-current, | |
| react-native-current, | |
| install-dep, | |
| android-build, | |
| android-api-level-test, | |
| ios-build-test, | |
| ] | |
| if: ${{ always() }} | |
| runs-on: ubuntu-latest | |
| steps: | |
| - name: Check all job status | |
| if: >- | |
| ${{ contains(needs.*.result, 'failure') || contains(needs.*.result, 'cancelled') }} | |
| run: exit 1 |