name: Sync Upstream # Syncs with new upstream firebase-tools releases and publishes to npm. # # Runs daily, or can be triggered manually with an optional target version # and isolate-package version override. The already-synced check exits # immediately when there is no new release, so daily polling is cheap. # When a new version is found, it merges upstream, pushes to main, and # triggers the publish workflow. on: schedule: # Every day at 09:00 UTC - cron: "0 9 * * *" workflow_dispatch: inputs: target_version: description: "Upstream version to sync to (e.g. v15.13.0). Leave empty for latest." required: false type: string isolate_package_version: description: "isolate-package semver range (e.g. ^1.27.0-4). Leave empty for default." required: false type: string permissions: contents: write actions: write concurrency: group: sync-upstream cancel-in-progress: false jobs: sync: if: github.ref == 'refs/heads/main' runs-on: ubuntu-latest steps: - name: Checkout fork uses: actions/checkout@v4 with: fetch-depth: 0 # Full history needed for merge token: ${{ secrets.GITHUB_TOKEN }} - name: Setup Node.js uses: actions/setup-node@v4 with: node-version: "24" - name: Configure git run: | git config user.name "github-actions[bot]" git config user.email "github-actions[bot]@users.noreply.github.com" - name: Add upstream remote run: | git remote add upstream https://github.com/firebase/firebase-tools.git || true git fetch upstream --tags --force - name: Determine target version id: version env: INPUT_TARGET: ${{ inputs.target_version }} run: | if [[ -n "$INPUT_TARGET" && "$INPUT_TARGET" != "latest" ]]; then echo "tag=$INPUT_TARGET" >> "$GITHUB_OUTPUT" else TAG=$(git tag --sort=-version:refname | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' | head -1) echo "tag=$TAG" >> "$GITHUB_OUTPUT" fi - name: Check if already synced id: check env: TARGET_TAG: ${{ steps.version.outputs.tag }} run: | VERSION_NUMBER="${TARGET_TAG#v}" CURRENT=$(node -e "process.stdout.write(require('./package.json').version)") CURRENT_BASE="${CURRENT%%-*}" if [[ "$CURRENT_BASE" == "$VERSION_NUMBER" ]]; then echo "skip=true" >> "$GITHUB_OUTPUT" echo "Already synced to $TARGET_TAG" else echo "skip=false" >> "$GITHUB_OUTPUT" echo "Will sync from $CURRENT to ${VERSION_NUMBER}-0" fi echo "version_number=$VERSION_NUMBER" >> "$GITHUB_OUTPUT" - name: Run sync if: steps.check.outputs.skip != 'true' env: TARGET_TAG: ${{ steps.version.outputs.tag }} INPUT_ISOLATE: ${{ inputs.isolate_package_version }} run: | ARGS=(--no-push) [[ -n "$TARGET_TAG" ]] && ARGS+=(--target "$TARGET_TAG") [[ -n "$INPUT_ISOLATE" ]] && ARGS+=(--isolate-version "$INPUT_ISOLATE") bash scripts/sync/sync-upstream.sh "${ARGS[@]}" - name: Merge to main if: steps.check.outputs.skip != 'true' run: | BRANCH=$(git branch --show-current) git checkout main git merge "$BRANCH" --ff-only - name: Push to main if: steps.check.outputs.skip != 'true' run: git push origin main # Dispatched as a separate workflow_dispatch (not workflow_call) so the # OIDC token claims match the npm trusted publisher entry for publish.yml. # Reusable workflow invocations produce a different workflow_ref claim # and are rejected by npm trusted publishing. --repo pins the dispatch to # the fork; without it gh resolves to the upstream remote and 404s. - name: Trigger publish workflow if: steps.check.outputs.skip != 'true' env: GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: gh workflow run publish.yml --repo "$GITHUB_REPOSITORY" --ref main -f dist_tag=latest