name: _release #TODO test without these permissions: actions: read contents: write issues: write pull-requests: write concurrency: group: release cancel-in-progress: false on: workflow_call: inputs: force_release: description: "Force release even if validation fails" required: false default: false type: boolean jobs: validate-staging-build: runs-on: ubuntu-latest outputs: should_release: ${{ steps.validation.outputs.should-proceed }} steps: - name: Checkout staging uses: actions/checkout@v4 with: fetch-depth: 0 ref: staging - name: Validate staging build status id: validation uses: ./.github/actions/validate-build-status with: job-name: trigger-staging-build / build from-branch: staging to-branch: main force: ${{ inputs.force_release }} generate-release-and-push-all: needs: validate-staging-build runs-on: ubuntu-latest if: ${{ inputs.force_release == true || needs.validate-staging-build.outputs.should_release == 'true' }} outputs: skip-release: ${{steps.semantic-release.outputs.skip-release }} steps: - name: Checkout staging uses: actions/checkout@v4 with: fetch-depth: 0 ref: staging - name: Setup Git SSH for commit signing and push operations uses: ./.github/actions/setup-git-ssh with: commit-ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }} push-ssh-private-key: ${{ secrets.PUSH_SSH_PRIVATE_KEY }} actor: ${{ github.actor }} actor-id: ${{ github.actor_id }} - name: Set git origin to GIT_ORIGIN_REPO_URL run: | GIT_ORIGIN_URL="${{ secrets.GIT_ORIGIN_REPO_URL }}" if [ -n "$GIT_ORIGIN_URL" ]; then git remote remove origin if git remote add -f --tags origin "$GIT_ORIGIN_URL"; then echo "✅ Git origin configured correctly" else echo "❌ Failed to configure Git origin" echo "::error::Failed to configure Git origin" exit 1 fi else echo "❌ No Git origin URL configured - cannot proceed without origin URL" echo "::error::GIT_ORIGIN_REPO_URL secret is required but not configured" exit 1 fi - name: Merge staging commits to main run: | echo "🔄 Merging staging commits to main..." git checkout main STAGING_COMMITS=$(git log --oneline main..staging --pretty=format:"* %s") if [ -z "$STAGING_COMMITS" ]; then if [[ "${{inputs.force_release}}" == "true" ]]; then echo "âš ī¸ No new commits in staging to merge, but force release is enabled" echo "::warning::No commits to merge from staging to main" exit 0 else echo "❌ No new commits in staging to merge" echo "::error::No commits to merge from staging to main" exit 1 fi else echo "📋 Commits to be merged:" echo "$STAGING_COMMITS" if git merge staging -X theirs -m "chore: merge staging changes for next release"; then echo "✅ Staging commits merged to main successfully" else echo "âš ī¸ Merge conflicts detected - resolving with staging preference..." git checkout staging -- . if [ -f CHANGELOG.md ]; then git checkout HEAD -- CHANGELOG.md echo "📋 Kept main's CHANGELOG.md (managed by semantic-release)" fi git add -A git commit -m "chore: merge staging changes for next release" echo "✅ Conflicts resolved - staging files preferred, main's CHANGELOG.md kept" fi fi - name: Setup Node.js uses: actions/setup-node@v4 with: node-version-file: ".nvmrc" - name: Install semantic-release run: npm install semantic-release - name: Run semantic-release on main, generate release, and push id: semantic-release env: GITHUB_TOKEN: "${{ github.token }}" # used for release generation GIT_AUTHOR_NAME: "${{ github.actor }}" GIT_AUTHOR_EMAIL: "${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com" GIT_COMMITTER_NAME: "${{ github.actor }}" GIT_COMMITTER_EMAIL: "${{ github.actor_id }}+${{ github.actor }}@users.noreply.github.com" run: | echo "🚀 Running semantic-release on main branch..." npx semantic-release if git log main -1 --format="%s" | grep -q "^chore: Release"; then echo "✅ Semantic-release completed and pushed to origin/main" echo "skip-release=false" >> $GITHUB_OUTPUT else echo "❌ Semantic-release did not generate a release, exiting." echo "skip-release=true" >> $GITHUB_OUTPUT [[ "${{ inputs.force_release }}" == "true" ]] && exit 1 fi - name: Notify successful release if: ${{ steps.semantic-release.outputs.skip-release != 'true' }} run: | echo "🎉 Production release completed successfully!" echo "📋 Summary:" echo " - Source: staging branch commits (regular merged)" echo " - Target: main branch (semantic-release)" echo " - Semantic-release: ✅ Complete on main" echo " - staging branch: â„šī¸ Maintains independent history" echo " - dev branch: â„šī¸ Maintains independent history" echo " - Tags: ✅ Created and pushed" echo " - GitHub & Git Origin: ✅ Main branch synchronized" echo " - Status: 🚀 Ready to build and deploy!"