Skip to content
Draft
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
246 changes: 222 additions & 24 deletions .ado/azure-pipelines.publish.yml
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
# Build pipeline for publishing
# Build and publish pipeline
# This pipeline runs on every commit to main and intelligently detects
# when packages need to be published to NPM

trigger:
batch: true
branches:
include:
- main
- releases/*

pr: none

Expand All @@ -14,10 +15,6 @@ parameters:
displayName: Skip Npm Publish
type: boolean
default: false
- name: skipGitPush
displayName: Skip Git Push
type: boolean
default: false
- name: skipNugetPublish
displayName: Skip Nuget Publish
type: boolean
Expand All @@ -28,6 +25,8 @@ variables:
- group: InfoSec-SecurityResults
- name: tags
value: production,externalfacing
- name: BRANCH_NAME
value: 'beachball/version-bump/main'
- template: variables/vars.yml

resources:
Expand All @@ -54,7 +53,205 @@ extends:
environmentsEs6: true
environmentsNode: true
stages:
# Stage 1: Create version bump PR if change files exist
- stage: VersionBump
displayName: 'Create Version Bump PR'
jobs:
- job: CreatePR
displayName: 'Create or Update Version Bump PR'
pool:
name: Azure-Pipelines-1ESPT-ExDShared
image: ubuntu-latest
os: linux
steps:
- checkout: self
fetchDepth: 0
persistCredentials: true

- template: .ado/templates/setup-repo.yml@self

- script: |
set -eox pipefail
yarn install --immutable
displayName: 'Install dependencies'

# Check if there are change files
- script: |
set -eox pipefail
if npx beachball check --verbose; then
echo "##vso[task.setvariable variable=HasChangeFiles;isOutput=true]yes"
echo "✅ Change files detected"
else
echo "##vso[task.setvariable variable=HasChangeFiles;isOutput=true]no"
echo "ℹ️ No change files found"
fi
name: CheckChanges
displayName: 'Check for change files'

# Configure Git
- script: |
set -eox pipefail
git config user.name "React Native Bot"
git config user.email "53619745+rnbot@users.noreply.github.com"
displayName: 'Configure Git'
condition: eq(variables['CheckChanges.HasChangeFiles'], 'yes')

# Check for existing PR
- task: AzureCLI@2
displayName: 'Check for existing PR'
condition: eq(variables['CheckChanges.HasChangeFiles'], 'yes')
inputs:
azureSubscription: 'FluentUI React Native'
scriptType: 'bash'
scriptLocation: 'inlineScript'
inlineScript: |
set -eox pipefail
# Get GitHub token from Key Vault
GITHUB_TOKEN=$(az keyvault secret show --vault-name fluentui-rn-keyvault --name github-token --query value -o tsv)
export GH_TOKEN=$GITHUB_TOKEN

# Check if PR already exists
EXISTING_PR=$(gh pr list --head "$(BRANCH_NAME)" --state open --json number --jq '.[0].number' || echo "")

if [ -n "$EXISTING_PR" ]; then
echo "##vso[task.setvariable variable=ExistingPR;isOutput=true]$EXISTING_PR"
echo "📝 Found existing PR #$EXISTING_PR, will update it"
else
echo "##vso[task.setvariable variable=ExistingPR;isOutput=true]"
echo "📝 No existing PR found, will create new one"
fi
name: CheckPR

# Create or update branch
- script: |
set -eox pipefail
# Check if branch exists remotely
if git ls-remote --heads origin "$(BRANCH_NAME)" | grep -q "$(BRANCH_NAME)"; then
echo "🔄 Branch exists, updating it"
git fetch origin "$(BRANCH_NAME)"
git switch "$(BRANCH_NAME)"
git reset --hard origin/main
else
echo "📝 Creating new branch: $(BRANCH_NAME)"
git switch -c "$(BRANCH_NAME)"
fi
displayName: 'Create or update version bump branch'
condition: eq(variables['CheckChanges.HasChangeFiles'], 'yes')

# Run beachball bump
- script: |
set -eox pipefail
echo "🔄 Running beachball bump..."
npx beachball bump --verbose
displayName: 'Run beachball bump'
condition: eq(variables['CheckChanges.HasChangeFiles'], 'yes')

# Commit changes
- script: |
set -eox pipefail
git add .

# Check if there are any changes to commit
if git diff --staged --quiet; then
echo "⚠️ No changes after beachball bump, skipping commit"
echo "##vso[task.setvariable variable=HasCommit;isOutput=true]no"
exit 0
fi

git commit -m "chore(release): bump package versions [skip ci]"
echo "##vso[task.setvariable variable=HasCommit;isOutput=true]yes"
echo "✅ Committed version changes"
name: CommitChanges
displayName: 'Commit version bumps'
condition: eq(variables['CheckChanges.HasChangeFiles'], 'yes')

# Push branch
- task: AzureCLI@2
displayName: 'Push version bump branch'
condition: and(succeeded(), eq(variables['CheckChanges.HasChangeFiles'], 'yes'), eq(variables['CommitChanges.HasCommit'], 'yes'))
inputs:
azureSubscription: 'FluentUI React Native'
scriptType: 'bash'
scriptLocation: 'inlineScript'
inlineScript: |
set -eox pipefail
# Get GitHub token from Key Vault
GITHUB_TOKEN=$(az keyvault secret show --vault-name fluentui-rn-keyvault --name github-token --query value -o tsv)

# Configure git to use token for authentication
git remote set-url origin "https://x-access-token:${GITHUB_TOKEN}@github.com/microsoft/fluentui-react-native.git"

# Force push since we might be updating an existing branch
git push --force-with-lease origin "$(BRANCH_NAME)"
echo "✅ Pushed branch to origin"

# Create or update PR
- task: AzureCLI@2
displayName: 'Create or Update Pull Request'
condition: and(succeeded(), eq(variables['CheckChanges.HasChangeFiles'], 'yes'), eq(variables['CommitChanges.HasCommit'], 'yes'))
inputs:
azureSubscription: 'FluentUI React Native'
scriptType: 'bash'
scriptLocation: 'inlineScript'
inlineScript: |
set -eox pipefail
# Get GitHub token from Key Vault
GITHUB_TOKEN=$(az keyvault secret show --vault-name fluentui-rn-keyvault --name github-token --query value -o tsv)
export GH_TOKEN=$GITHUB_TOKEN

# Create PR body
PR_BODY=$(cat <<'EOFBODY'
## Version Bump

This PR was automatically generated by Azure Pipelines.

### What to do next:
1. Review the version changes and CHANGELOG.md files
2. Merge this PR when ready
3. Azure Pipelines will automatically publish to NPM

**Note:** Once merged, Azure Pipelines will detect the merge and publish to NPM.
EOFBODY
)

EXISTING_PR="$(CheckPR.ExistingPR)"

if [ -n "$EXISTING_PR" ]; then
# Update existing PR
echo "$PR_BODY" | gh pr edit "$EXISTING_PR" \
--title "chore(release): bump package versions" \
--body-file -

PR_URL=$(gh pr view "$EXISTING_PR" --json url --jq '.url')

echo "##vso[task.setvariable variable=PRNumber;isOutput=true]$EXISTING_PR"
echo "##vso[task.setvariable variable=PRUrl;isOutput=true]$PR_URL"

echo "✅ Updated PR #$EXISTING_PR: $PR_URL"
else
# Create new PR
PR_URL=$(echo "$PR_BODY" | gh pr create \
--title "chore(release): bump package versions" \
--body-file - \
--base main \
--head "$(BRANCH_NAME)" \
--label "automated" \
--label "version-bump")

PR_NUMBER=$(echo "$PR_URL" | grep -oE '[0-9]+$')

echo "##vso[task.setvariable variable=PRNumber;isOutput=true]$PR_NUMBER"
echo "##vso[task.setvariable variable=PRUrl;isOutput=true]$PR_URL"

echo "✅ Created PR #$PR_NUMBER: $PR_URL"
fi
name: CreatePRStep

# Stage 2: Build and publish packages
- stage: main
displayName: 'Build and Publish'
dependsOn: VersionBump
condition: always()
jobs:
- job: NPMPublish
displayName: NPM Publish
Expand All @@ -71,38 +268,39 @@ extends:
- template: .ado/templates/setup-repo.yml@self

- script: |
git config user.name "UI-Fabric-RN-Bot"
git config user.email "uifrnbot@microsoft.com"
git remote set-url origin https://$(githubUser):$(githubPAT)@github.com/microsoft/fluentui-react-native.git
displayName: Git Authentication

- script: |
yarn
set -eox pipefail
yarn install --immutable
displayName: 'yarn install'

- script: |
set -eox pipefail
yarn buildci
displayName: 'yarn buildci [test]'

- script: |
set -eox pipefail
echo ##vso[task.setvariable variable=SkipNpmPublishArgs]--no-publish
displayName: Enable No-Publish (npm)
condition: ${{ parameters.skipNpmPublish }}

- script: |
echo ##vso[task.setvariable variable=SkipGitPushPublishArgs]--no-push
displayName: Enable No-Publish (git)
condition: ${{ parameters.skipGitPush }}

- script: |
yarn publish:beachball $(SkipNpmPublishArgs) $(SkipGitPushPublishArgs) --access public --token $(npmAuth) -b origin/main -y
displayName: 'Publish NPM Packages (for main branch)'
condition: and(succeeded(), eq(variables['Build.SourceBranch'], 'refs/heads/main'))
set -eox pipefail
if node scripts/check-packages-need-publishing.ts; then
echo "##vso[task.setvariable variable=PackagesNeedPublishing]true"
echo "✅ Packages need publishing"
else
echo "##vso[task.setvariable variable=PackagesNeedPublishing]false"
echo "ℹ️ No packages need publishing (all versions already exist on NPM)"
fi
displayName: 'Check if packages need publishing'
condition: and(succeeded(), ne('${{ parameters.skipNpmPublish }}', true))

- script: |
yarn publish:beachball $(SkipNpmPublishArgs) $(SkipGitPushPublishArgs) --access public --token $(npmAuth) -y -t v${{ replace(variables['Build.SourceBranch'],'refs/heads/releases/','') }} -b origin/${{ replace(variables['Build.SourceBranch'],'refs/heads/','') }} --prerelease-prefix ${{ replace(variables['Build.SourceBranch'],'refs/heads/releases/','') }}
displayName: 'Publish NPM Packages (for other release branches)'
condition: and(succeeded(), ne(variables['Build.SourceBranch'], 'refs/heads/main'))
set -eox pipefail
# Use --no-bump and --no-push because versions have already been committed via version bump PR
npx beachball publish --no-bump --no-push $(SkipNpmPublishArgs) --access public --token $(npmAuth) -y --verbose
displayName: 'Publish NPM Packages'
condition: and(succeeded(), eq(variables['PackagesNeedPublishing'], 'true'))

- template: .ado/templates/win32-nuget-publish.yml@self
parameters:
Expand Down
52 changes: 52 additions & 0 deletions .ado/azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,58 @@ jobs:
yarn check-for-changed-files
displayName: 'verify API and Ensure Changed Files'

# Dedicated job to preview version bumps and package publishing
- job: NPMPublishDryRun
displayName: NPM Publish Dry Run
pool:
vmImage: 'ubuntu-latest'
timeoutInMinutes: 30
cancelTimeoutInMinutes: 5

steps:
- checkout: self
persistCredentials: true

- template: templates/setup-repo.yml

- script: |
set -eox pipefail
echo "=========================================="
echo "Running beachball bump (dry-run)..."
echo "=========================================="
npx beachball bump --verbose

echo ""
echo "=========================================="
echo "Packages that would be bumped:"
echo "=========================================="

# Show which package.json files were modified
git diff --name-only | grep "package.json" | grep -v "^package.json$" | while read pkg; do
if [ -f "$pkg" ]; then
NAME=$(grep '"name"' "$pkg" | head -1 | sed 's/.*"name": "\(.*\)".*/\1/')
VERSION=$(grep '"version"' "$pkg" | head -1 | sed 's/.*"version": "\(.*\)".*/\1/')
PRIVATE=$(grep '"private"' "$pkg" | head -1 || echo "")

if [ -z "$PRIVATE" ]; then
echo " 📦 $NAME@$VERSION"
fi
fi
done

# Reset the changes so they don't affect other steps
git reset --hard HEAD
displayName: 'Preview version bumps (dry-run)'

- script: |
set -eox pipefail
echo ""
echo "=========================================="
echo "Checking which packages need publishing..."
echo "=========================================="
node scripts/check-packages-need-publishing.ts --dry-run
displayName: 'Check packages to publish (dry-run)'

- job: AndroidPR
displayName: Android PR
pool:
Expand Down
4 changes: 2 additions & 2 deletions .ado/templates/setup-repo.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ steps:

- task: UseNode@1
inputs:
version: '22.x'
displayName: 'Use Node.js 22.x'
version: '24'
displayName: 'Use Node.js 24'

- script: |
yarn
Expand Down
1 change: 0 additions & 1 deletion .node-version

This file was deleted.

16 changes: 16 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,22 @@ This repo manages semantic versioning and publishing using [Beachball](https://g
1. `yarn change` will take you through a command line wizard to generate change files
2. Make sure to push the newly generated change file

#### Publishing Workflow

The repository uses an automated publishing workflow powered by a single Azure Pipeline (`.ado/azure-pipelines.publish.yml`):

1. **Change Files**: Contributors create change files using `yarn change` in their PRs
2. **Version Bump PR**: When change files are merged to `main`, Azure Pipelines automatically creates/updates a version bump PR
- This PR is updated automatically as more changes are merged
- The PR shows all packages that will be published and their new versions
3. **Review and Merge**: Maintainers review the version bump PR and merge when ready
4. **Automatic Publishing**: After the version bump PR is merged, Azure Pipelines automatically detects the version changes and publishes packages to NPM
- The pipeline intelligently skips publishing if all package versions already exist on NPM

**Branch Support**: Only the `main` branch is configured for automatic publishing. Release branches are not supported in this workflow.

**For Maintainers**: The version bump PR is created by Azure Pipelines using a fixed branch name (`beachball/version-bump/main`). This PR will be automatically updated as new change files are merged to main. Do not manually close or recreate this PR unless necessary.

#### Testing changes

Before you create a pull request, test your changes with the FluentUI Tester on the platforms that are affected by your change. For more information on the FluentUI Tester, please follow instructions in the [FluentUI Tester readme](./apps/fluent-tester/README.md).
Expand Down
Loading
Loading