From f3b3c1640fe55b7c2fa2f7a0f93ad1d0abb89d54 Mon Sep 17 00:00:00 2001 From: Saad Najmi Date: Thu, 5 Mar 2026 11:54:40 -0800 Subject: [PATCH 1/6] ci(publish): only pack tarballs that need to be published --- scripts/src/worker/pack.mts | 7 +++++++ scripts/src/worker/publish.mts | 18 ++++++++---------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/scripts/src/worker/pack.mts b/scripts/src/worker/pack.mts index 00e9890e3c..5cdf016526 100644 --- a/scripts/src/worker/pack.mts +++ b/scripts/src/worker/pack.mts @@ -23,6 +23,13 @@ export const run: WorkerRunnerFunction = async ({ target }) => { throw new Error('pack worker requires options.outputDir to be set in lage.config.js'); } + // Skip if this version is already published + const result = await $`npm view ${pkg.name}@${pkg.version} version`.nothrow().quiet(); + if (result.exitCode === 0) { + console.log(`Skipping ${pkg.name}@${pkg.version} — already published`); + return; + } + // Resolve relative to cwd (lage runs from repo root, so this resolves correctly) const stagingDir = resolve(outputDir); await fs.mkdirp(stagingDir); diff --git a/scripts/src/worker/publish.mts b/scripts/src/worker/publish.mts index 2ffa5ff92a..260e10dd10 100644 --- a/scripts/src/worker/publish.mts +++ b/scripts/src/worker/publish.mts @@ -38,17 +38,15 @@ export const run: WorkerRunnerFunction = async ({ target }) => { const dryRun = target.options?.dryRun ?? false; const outputDir = target.options?.outputDir as string | undefined; - - // If an outputDir is configured, look for a pre-packed tarball const tarball = outputDir ? await findTarball(pkg, outputDir) : undefined; - if (tarball) { - // yarn npm publish doesn't support tarballs, so use npm directly - const args = ['publish', tarball, '--access', 'public', ...(dryRun ? ['--dry-run'] : [])]; - await $({ cwd: target.cwd, verbose: true })`npm ${args}`; - } else { - // No tarball found — publish from source (local dev / dry-run) - const args = ['--tolerate-republish', ...(dryRun ? ['--dry-run'] : [])]; - await $({ cwd: target.cwd, verbose: true })`yarn npm publish ${args}`; + if (!tarball) { + // No tarball means pack skipped this package (already published) — nothing to do + console.log(`Skipping ${pkg.name}@${pkg.version} — no tarball found`); + return; } + + // yarn npm publish doesn't support tarballs, so use npm directly + const args = ['publish', tarball, '--access', 'public', ...(dryRun ? ['--dry-run'] : [])]; + await $({ cwd: target.cwd, verbose: true })`npm ${args}`; }; From 91fa232dcda1212e1b2dc647ccd64302a7e0936c Mon Sep 17 00:00:00 2001 From: Saad Najmi Date: Thu, 5 Mar 2026 12:13:04 -0800 Subject: [PATCH 2/6] update publish dry run --- .github/workflows/pr.yml | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 20b1655c9b..a191457536 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -301,8 +301,8 @@ jobs: - name: Validate changesets run: yarn change:check - publish-dry-run: - name: NPM Publish Dry Run + publish-dry-run-pack: + name: NPM Publish Dry Run — Pack runs-on: ubuntu-latest timeout-minutes: 60 steps: @@ -323,6 +323,40 @@ jobs: - name: Pack packages run: yarn lage pack --verbose --grouped + - name: List packed tarballs + run: | + echo "Packed tarballs:" + ls -la $(System.DefaultWorkingDirectory)/_packed/ + + - name: Upload packed tarballs + uses: actions/upload-artifact@v4 + with: + name: packed-tarballs-dry-run + path: _packed/ + + publish-dry-run: + name: NPM Publish Dry Run — Publish + runs-on: ubuntu-latest + timeout-minutes: 60 + needs: publish-dry-run-pack + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up toolchain + uses: microsoft/react-native-test-app/.github/actions/setup-toolchain@5.0.14 + with: + node-version: 22 + + - name: Install dependencies + run: yarn + + - name: Download packed tarballs + uses: actions/download-artifact@v4 + with: + name: packed-tarballs-dry-run + path: _packed/ + - name: Simulate publish run: yarn lage publish-dry-run --verbose --grouped @@ -357,6 +391,7 @@ jobs: - windows - win32 - check-changesets + - publish-dry-run-pack - publish-dry-run - test-links steps: From 4b4df1586ab5bca99248d27aa7633a7902c6c033 Mon Sep 17 00:00:00 2001 From: Saad Najmi Date: Thu, 5 Mar 2026 12:50:44 -0800 Subject: [PATCH 3/6] fix env var --- .github/workflows/pr.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index a191457536..47a3e58a33 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -326,7 +326,7 @@ jobs: - name: List packed tarballs run: | echo "Packed tarballs:" - ls -la $(System.DefaultWorkingDirectory)/_packed/ + ls -la ${{ github.workspace }}/_packed/ - name: Upload packed tarballs uses: actions/upload-artifact@v4 From 945a6ebc3c86533b7a3f064c0adcb9313cdb719c Mon Sep 17 00:00:00 2001 From: Saad Najmi Date: Thu, 5 Mar 2026 12:59:59 -0800 Subject: [PATCH 4/6] dont fail if no _packed --- .github/workflows/pr.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 47a3e58a33..ec2e0d1806 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -324,9 +324,7 @@ jobs: run: yarn lage pack --verbose --grouped - name: List packed tarballs - run: | - echo "Packed tarballs:" - ls -la ${{ github.workspace }}/_packed/ + run: ls -la _packed/ || true - name: Upload packed tarballs uses: actions/upload-artifact@v4 From 5f51e389de12735e3ee88179dec58dad341f8938 Mon Sep 17 00:00:00 2001 From: Saad Najmi Date: Thu, 5 Mar 2026 13:02:09 -0800 Subject: [PATCH 5/6] always make dir --- .github/workflows/pr.yml | 2 +- scripts/src/worker/pack.mts | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index ec2e0d1806..95f2479be4 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -324,7 +324,7 @@ jobs: run: yarn lage pack --verbose --grouped - name: List packed tarballs - run: ls -la _packed/ || true + run: ls -la _packed/ - name: Upload packed tarballs uses: actions/upload-artifact@v4 diff --git a/scripts/src/worker/pack.mts b/scripts/src/worker/pack.mts index 5cdf016526..c3dd9c0352 100644 --- a/scripts/src/worker/pack.mts +++ b/scripts/src/worker/pack.mts @@ -23,6 +23,10 @@ export const run: WorkerRunnerFunction = async ({ target }) => { throw new Error('pack worker requires options.outputDir to be set in lage.config.js'); } + // Resolve relative to cwd (lage runs from repo root, so this resolves correctly) + const stagingDir = resolve(outputDir); + await fs.mkdirp(stagingDir); + // Skip if this version is already published const result = await $`npm view ${pkg.name}@${pkg.version} version`.nothrow().quiet(); if (result.exitCode === 0) { @@ -30,10 +34,6 @@ export const run: WorkerRunnerFunction = async ({ target }) => { return; } - // Resolve relative to cwd (lage runs from repo root, so this resolves correctly) - const stagingDir = resolve(outputDir); - await fs.mkdirp(stagingDir); - // Build a safe filename: @fluentui-react-native/button@1.0.0 -> fluentui-react-native-button-1.0.0.tgz const safeName = (pkg.name as string).replace(/@/g, '').replace(/\//g, '-'); const tgzFilename = `${safeName}-${pkg.version}.tgz`; From d758863c4db959190c3415f732b35821b5b51b0f Mon Sep 17 00:00:00 2001 From: Saad Najmi Date: Thu, 5 Mar 2026 13:15:46 -0800 Subject: [PATCH 6/6] Skip publish stage if nothing to publish --- .ado/azure-pipelines.publish.yml | 9 +++++++-- .github/workflows/pr.yml | 13 ++++++++++++- 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/.ado/azure-pipelines.publish.yml b/.ado/azure-pipelines.publish.yml index 62541cb73d..7f0bd283da 100644 --- a/.ado/azure-pipelines.publish.yml +++ b/.ado/azure-pipelines.publish.yml @@ -78,14 +78,19 @@ extends: displayName: 'Pack all public packages' - script: | - echo "Packed tarballs:" ls -la $(System.DefaultWorkingDirectory)/_packed/ + if ls $(System.DefaultWorkingDirectory)/_packed/*.tgz > /dev/null 2>&1; then + echo "##vso[task.setvariable variable=hasTarballs;isOutput=true]true" + else + echo "##vso[task.setvariable variable=hasTarballs;isOutput=true]false" + fi + name: check displayName: 'List packed tarballs' - stage: Publish displayName: Publish to NPM dependsOn: Build - condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'), not(${{ parameters.skipNpmPublish }})) + condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'), not(${{ parameters.skipNpmPublish }}), eq(stageDependencies.Build.BuildAndPack.outputs['check.hasTarballs'], 'true')) jobs: - job: PublishPackages displayName: Publish NPM Packages diff --git a/.github/workflows/pr.yml b/.github/workflows/pr.yml index 95f2479be4..6cc7ec073c 100644 --- a/.github/workflows/pr.yml +++ b/.github/workflows/pr.yml @@ -305,6 +305,8 @@ jobs: name: NPM Publish Dry Run — Pack runs-on: ubuntu-latest timeout-minutes: 60 + outputs: + has-tarballs: ${{ steps.check.outputs.has-tarballs }} steps: - name: Checkout uses: actions/checkout@v4 @@ -324,9 +326,17 @@ jobs: run: yarn lage pack --verbose --grouped - name: List packed tarballs - run: ls -la _packed/ + id: check + run: | + ls -la _packed/ + if ls _packed/*.tgz > /dev/null 2>&1; then + echo "has-tarballs=true" >> $GITHUB_OUTPUT + else + echo "has-tarballs=false" >> $GITHUB_OUTPUT + fi - name: Upload packed tarballs + if: steps.check.outputs.has-tarballs == 'true' uses: actions/upload-artifact@v4 with: name: packed-tarballs-dry-run @@ -337,6 +347,7 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 60 needs: publish-dry-run-pack + if: needs.publish-dry-run-pack.outputs.has-tarballs == 'true' steps: - name: Checkout uses: actions/checkout@v4