diff --git a/.github/.release-please-manifest.json b/.github/.release-please-manifest.json new file mode 100644 index 00000000000..8c7ff6c7518 --- /dev/null +++ b/.github/.release-please-manifest.json @@ -0,0 +1,8 @@ +{ + "packages/logger": "4.0.2", + "packages/fs": "4.0.2", + "packages/builder": "4.0.11", + "packages/server": "4.0.7", + "packages/project": "4.0.6", + "packages/cli": "4.0.26" +} diff --git a/.github/actions/merge-release-pr/action.yml b/.github/actions/merge-release-pr/action.yml new file mode 100644 index 00000000000..cabb94998b6 --- /dev/null +++ b/.github/actions/merge-release-pr/action.yml @@ -0,0 +1,49 @@ +name: 'Merge Release PR' +description: 'Merges a release PR for a specific package' + +inputs: + package: + description: 'Package name to merge release PR for' + required: true + github-token: + description: 'GitHub token for authentication' + required: true + +outputs: + pr_merged: + description: 'Whether the PR was successfully merged' + value: ${{ steps.merge.outputs.pr_merged }} + pr_number: + description: 'The PR number that was merged' + value: ${{ steps.merge.outputs.pr_number }} + +runs: + using: 'composite' + steps: + - name: Merge release PR + id: merge + shell: bash + env: + GH_TOKEN: ${{ inputs.github-token }} + run: | + PACKAGE="${{ inputs.package }}" + echo "🔍 Looking for release PR for: $PACKAGE" + + # Find the release PR for this package + PR_NUMBER=$(gh pr list --state open --json number,title | jq -r ".[] | select(.title | contains(\"release: @ui5/ $PACKAGE\")) | .number") + + if [[ -n "$PR_NUMBER" ]]; then + echo "✅ Found PR #$PR_NUMBER for $PACKAGE" + echo "🔄 Merging..." + + # Merge the PR + gh pr merge $PR_NUMBER --squash + + echo "✅ Merged PR #$PR_NUMBER for $PACKAGE" + echo "pr_merged=true" >> $GITHUB_OUTPUT + echo "pr_number=$PR_NUMBER" >> $GITHUB_OUTPUT + else + echo "❌ No open release PR found for $PACKAGE" + echo "pr_merged=false" >> $GITHUB_OUTPUT + echo "pr_number=" >> $GITHUB_OUTPUT + fi \ No newline at end of file diff --git a/.github/workflows/auto-release-chain.yml b/.github/workflows/auto-release-chain.yml new file mode 100644 index 00000000000..b430d4b6275 --- /dev/null +++ b/.github/workflows/auto-release-chain.yml @@ -0,0 +1,184 @@ +name: auto-release-chain + +on: + workflow_run: + workflows: ["release-please"] + types: [completed] + branches: [main] + +permissions: + contents: write + pull-requests: write + +jobs: + continue-chain: + runs-on: ubuntu-24.04 + if: github.event.workflow_run.conclusion == 'success' || github.event_name == 'workflow_dispatch' + + # Job-level outputs for reuse across steps + outputs: + all_packages: ${{ steps.setup.outputs.all_packages }} + released_package: ${{ steps.detect-release.outputs.released_package }} + has_release: ${{ steps.detect-release.outputs.has_release }} + next_package: ${{ steps.find-next.outputs.next_package }} + has_next: ${{ steps.find-next.outputs.has_next }} + has_changes: ${{ steps.update-deps.outputs.has_changes }} + is_chain_mode: ${{ steps.detect-release.outputs.is_chain_mode }} + + # Environment variables for reuse + env: + GH_TOKEN: ${{ secrets.RELEASE_PLEASE_TOKEN }} + NODE_VERSION: 24.x + REGISTRY_URL: https://registry.npmjs.org/ + GIT_USER_NAME: "github-actions[bot]" + GIT_USER_EMAIL: "github-actions[bot]@users.noreply.github.com" + + steps: + - uses: actions/checkout@v5 + with: + token: ${{ secrets.RELEASE_PLEASE_TOKEN }} + fetch-depth: 0 + + - name: Detect released package and mode + id: detect-release + run: | + # Detect released package from commit + if git log -1 --pretty=format:"%s" | grep -q "^release: @ui5/"; then + RELEASED_PKG=$(git log -1 --pretty=format:"%s" | sed -n 's/^release: @ui5\/ \([a-z-]*\) v.*/\1/p') + echo "đŸ“Ļ Detected release: $RELEASED_PKG" + echo "released_package=$RELEASED_PKG" >> $GITHUB_OUTPUT + echo "has_release=true" >> $GITHUB_OUTPUT + else + echo "â„šī¸ No release detected" + echo "has_release=false" >> $GITHUB_OUTPUT + exit 0 + fi + + # Check if chain mode is active + CHAIN_MODE=$(gh variable get CHAIN_MODE 2>/dev/null || echo "false") + echo "is_chain_mode=$CHAIN_MODE" >> $GITHUB_OUTPUT + echo "🔗 Chain mode: $CHAIN_MODE" + env: + GH_TOKEN: ${{ secrets.RELEASE_PLEASE_TOKEN }} + + - name: Setup Node.js + if: steps.detect-release.outputs.has_release == 'true' + uses: actions/setup-node@v5 + with: + node-version: ${{ env.NODE_VERSION }} + registry-url: ${{ env.REGISTRY_URL }} + + - name: Setup git and packages array + if: steps.detect-release.outputs.has_release == 'true' + id: setup + run: | + git config user.name "${{ env.GIT_USER_NAME }}" + git config user.email "${{ env.GIT_USER_EMAIL }}" + + # Define packages array for reuse + ALL_PACKAGES="logger fs builder server project cli" + echo "all_packages=$ALL_PACKAGES" >> $GITHUB_OUTPUT + echo "đŸ“Ļ Package chain: $ALL_PACKAGES" + + - name: Find next package in chain + id: find-next + if: steps.detect-release.outputs.has_release == 'true' + run: | + ALL_PACKAGES_ARRAY=(${{ steps.setup.outputs.all_packages }}) + RELEASED_PKG="${{ steps.detect-release.outputs.released_package }}" + IS_CHAIN_MODE="${{ steps.detect-release.outputs.is_chain_mode }}" + + echo "🔍 Looking for next package after: $RELEASED_PKG (Chain mode: $IS_CHAIN_MODE)" + + if [[ "$IS_CHAIN_MODE" == "true" ]]; then + # CHAIN MODE: Find next package in sequence + NEXT_PKG="" + for i in "${!ALL_PACKAGES_ARRAY[@]}"; do + if [[ "${ALL_PACKAGES_ARRAY[$i]}" == "$RELEASED_PKG" ]] && [[ $((i+1)) -lt ${#ALL_PACKAGES_ARRAY[@]} ]]; then + NEXT_PKG="${ALL_PACKAGES_ARRAY[$((i+1))]}" + break + fi + done + + # If no next package and not CLI, fallback to CLI + if [[ -z "$NEXT_PKG" && "$RELEASED_PKG" != "cli" ]]; then + NEXT_PKG="cli" + fi + + # If we just processed CLI, clear the chain mode + if [[ "$RELEASED_PKG" == "cli" ]]; then + echo "🎉 Chain complete - clearing chain mode" + gh variable delete CHAIN_MODE 2>/dev/null || true + gh variable delete CHAIN_START_PACKAGE 2>/dev/null || true + fi + else + # MANUAL MODE: Only release CLI if not CLI itself + if [[ "$RELEASED_PKG" != "cli" ]]; then + NEXT_PKG="cli" + fi + fi + + # Set outputs + if [[ -n "$NEXT_PKG" ]]; then + echo "đŸŽ¯ Found next package: $NEXT_PKG" + echo "next_package=$NEXT_PKG" >> $GITHUB_OUTPUT + echo "has_next=true" >> $GITHUB_OUTPUT + else + echo "🏁 No next package found" + echo "has_next=false" >> $GITHUB_OUTPUT + fi + env: + GH_TOKEN: ${{ secrets.RELEASE_PLEASE_TOKEN }} + + - name: Update dependencies in next package + id: update-deps + if: steps.find-next.outputs.has_next == 'true' + run: | + NEXT_PKG="${{ steps.find-next.outputs.next_package }}" + RELEASED_PKG="${{ steps.detect-release.outputs.released_package }}" + + # Install npm-check-updates + npm install -g npm-check-updates + + echo "🔄 Processing package: $NEXT_PKG" + cd "packages/$NEXT_PKG" + + echo "đŸ“Ļ Updating @ui5/* dependencies to latest minor/patch versions (respecting major)" + + # Update @ui5/* packages respecting major version boundaries + ncu -u --filter "@ui5/*" --target minor --dep dev,prod,peer,optional + + # Check if there were actual changes + if git diff --quiet package.json; then + echo "â„šī¸ No dependency updates needed for $NEXT_PKG" + echo "has_changes=false" >> $GITHUB_OUTPUT + else + echo "✅ Dependencies updated for $NEXT_PKG" + echo "has_changes=true" >> $GITHUB_OUTPUT + fi + + - name: Commit dependency updates + if: steps.update-deps.outputs.has_changes == 'true' + run: | + NEXT_PKG="${{ steps.find-next.outputs.next_package }}" + + git add "packages/$NEXT_PKG/" + git commit -m "deps($NEXT_PKG): Update @ui5 dependencies to latest versions" + git push origin main + + echo "✅ Committed dependency updates for $NEXT_PKG" + + - name: Trigger release-please for updated package + if: steps.update-deps.outputs.has_changes == 'true' + uses: googleapis/release-please-action@v4 + id: trigger-release + with: + token: ${{ secrets.RELEASE_PLEASE_TOKEN }} + + - name: Merge release PR + if: steps.update-deps.outputs.has_changes == 'true' + id: merge-pr + uses: ./.github/actions/merge-release-pr + with: + package: ${{ steps.find-next.outputs.next_package }} + github-token: ${{ secrets.RELEASE_PLEASE_TOKEN }} diff --git a/.github/workflows/relase-packages.yml b/.github/workflows/relase-packages.yml new file mode 100644 index 00000000000..d87a3fbe87a --- /dev/null +++ b/.github/workflows/relase-packages.yml @@ -0,0 +1,69 @@ +name: release-packages + +on: + # Manually trigger the release chain starting from a specific package + workflow_dispatch: + inputs: + package: + description: "🚀 Start AUTOMATIC release chain from this package" + required: true + type: choice + options: + - logger + - fs + - builder + - server + - project + - cli + default: logger + +permissions: + contents: write + pull-requests: write + actions: write + +jobs: + start-release-chain: + runs-on: ubuntu-24.04 + steps: + - uses: actions/checkout@v5 + with: + token: ${{ secrets.RELEASE_PLEASE_TOKEN }} + + - name: Setup git + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + + - name: Merge first PR to start chain + id: merge-pr + uses: ./.github/actions/merge-release-pr + with: + package: ${{ github.event.inputs.package }} + github-token: ${{ secrets.RELEASE_PLEASE_TOKEN }} + + - name: Trigger auto-release-chain with chain mode + if: steps.merge-pr.outputs.pr_merged == 'true' + run: | + SELECTED_PKG="${{ github.event.inputs.package }}" + echo "🚀 Setting chain mode flag for release chain starting from: $SELECTED_PKG" + + # Set repository variable to indicate chain mode is active + gh variable set CHAIN_MODE --body "true" + gh variable set CHAIN_START_PACKAGE --body "$SELECTED_PKG" + + echo "✅ Chain mode activated" + env: + GH_TOKEN: ${{ secrets.RELEASE_PLEASE_TOKEN }} + + - name: Check result + run: | + if [[ "${{ steps.merge-pr.outputs.pr_merged }}" == "true" ]]; then + echo "🎉 Release chain started successfully!" + echo "📋 Started from package: ${{ github.event.inputs.package }}" + echo "🔗 Chain mode activated" + else + echo "❌ Failed to start release chain" + echo "📋 No PR found for package: ${{ github.event.inputs.package }}" + exit 1 + fi diff --git a/.github/workflows/release-please.yml b/.github/workflows/release-please.yml new file mode 100644 index 00000000000..7b18939f38a --- /dev/null +++ b/.github/workflows/release-please.yml @@ -0,0 +1,66 @@ +name: release-please + +on: + push: + branches: + - main + +permissions: + contents: write + pull-requests: write + +jobs: + release-please: + runs-on: ubuntu-24.04 + outputs: + package_released: ${{ steps.detect_package.outputs.is_release }} + package_name: ${{ steps.detect_package.outputs.package_name }} + package_version: ${{ steps.detect_package.outputs.package_version }} + package_path: ${{ steps.detect_package.outputs.package_path }} + steps: + - uses: actions/checkout@v5 + + - name: Node.js LTS + uses: actions/setup-node@v5 + with: + node-version: 24.x + + - name: Run Release Please to update PRs and create releases + uses: googleapis/release-please-action@v4 + id: release + with: + token: ${{ secrets.RELEASE_PLEASE_TOKEN }} + + - name: Detect which package was released + id: detect_package + run: | + COMMIT_MSG=$(git log -1 --pretty=format:"%s") + if [[ "$COMMIT_MSG" =~ ^release:\ @ui5/\ ([a-z-]+)\ v([0-9]+\.[0-9]+\.[0-9]+) ]]; then + echo "package_name=${BASH_REMATCH[1]}" >> $GITHUB_OUTPUT + echo "package_version=${BASH_REMATCH[2]}" >> $GITHUB_OUTPUT + echo "package_path=packages/${BASH_REMATCH[1]}" >> $GITHUB_OUTPUT + echo "is_release=true" >> $GITHUB_OUTPUT + else + echo "is_release=false" >> $GITHUB_OUTPUT + fi + + publish-package: + runs-on: ubuntu-24.04 + needs: release-please + if: needs.release-please.outputs.package_released == 'true' + steps: + - uses: actions/checkout@v5 + + - name: Node.js LTS + uses: actions/setup-node@v5 + with: + node-version: 24.x + + - name: Install and publish package + run: | + cd "${{ needs.release-please.outputs.package_path }}" + echo "🚀 Publishing ${{ needs.release-please.outputs.package_name }}@${{ needs.release-please.outputs.package_version }}" + + # TODO: Uncomment when ready to publish + # npm ci + # npm publish --access public