diff --git a/.github/workflows/release-merge.yml b/.github/workflows/release-merge.yml new file mode 100644 index 000000000..05785d093 --- /dev/null +++ b/.github/workflows/release-merge.yml @@ -0,0 +1,31 @@ +name: "Merge release" + +on: + issue_comment: + types: [created] + + workflow_dispatch: + +jobs: + merge-comment: + name: Merge release to main + runs-on: macos-14 + if: github.event_name == 'workflow_dispatch' || (github.event.issue.pull_request && github.event.issue.state == 'open' && github.event.comment.body == '/merge release') + steps: + - name: Connect iOS Bot + uses: webfactory/ssh-agent@v0.7.0 + with: + ssh-private-key: ${{ secrets.BOT_SSH_PRIVATE_KEY }} + + - uses: actions/checkout@v4.1.1 + with: + fetch-depth: 0 + + - uses: ./.github/actions/ruby-cache + + - name: Merge + run: bundle exec fastlane merge_release_to_main author:"$USER_LOGIN" --verbose + env: + GITHUB_TOKEN: ${{ secrets.ADMIN_API_TOKEN }} # A token with the "admin:org" scope to get the list of the team members on GitHub + GITHUB_PR_NUM: ${{ github.event.issue.number }} + USER_LOGIN: ${{ github.event.comment.user.login != null && github.event.comment.user.login || github.event.sender.login }} diff --git a/.github/workflows/publish-release.yml b/.github/workflows/release-publish.yml similarity index 99% rename from .github/workflows/publish-release.yml rename to .github/workflows/release-publish.yml index 93f1ad60f..80113e243 100644 --- a/.github/workflows/publish-release.yml +++ b/.github/workflows/release-publish.yml @@ -17,16 +17,20 @@ jobs: uses: webfactory/ssh-agent@v0.7.0 with: ssh-private-key: ${{ secrets.BOT_SSH_PRIVATE_KEY }} + - uses: actions/checkout@v4.1.1 with: fetch-depth: 0 + - name: Extract version from branch name (for release branches) if: startsWith(github.event.pull_request.head.ref, 'release/') run: | BRANCH_NAME="${{ github.event.pull_request.head.ref }}" VERSION=${BRANCH_NAME#release/} echo "RELEASE_VERSION=$VERSION" >> $GITHUB_ENV + - uses: ./.github/actions/ruby-cache + - name: "Fastlane - Publish Release" if: startsWith(github.event.pull_request.head.ref, 'release/') env: diff --git a/.github/workflows/start-new-release.yml b/.github/workflows/release-start.yml similarity index 99% rename from .github/workflows/start-new-release.yml rename to .github/workflows/release-start.yml index e66a1eac5..a03592c30 100644 --- a/.github/workflows/start-new-release.yml +++ b/.github/workflows/release-start.yml @@ -17,11 +17,15 @@ jobs: uses: webfactory/ssh-agent@v0.7.0 with: ssh-private-key: ${{ secrets.BOT_SSH_PRIVATE_KEY }} + - uses: actions/checkout@v4.1.1 with: fetch-depth: 0 # to fetch git tags + - uses: ./.github/actions/ruby-cache + - uses: ./.github/actions/xcode-cache + - name: Create Release PR run: bundle exec fastlane release version:"${{ github.event.inputs.version }}" --verbose env: diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 2108872a3..6175d8bbe 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -34,6 +34,7 @@ outstanding_status = '🚀' # Outstanding performance before_all do |lane| if is_ci setup_ci + sh('git config --global user.name "Stream Bot"') xcversion(version: xcode_version) unless [:publish_release, :allure_launch, :allure_upload, :pod_lint, :stop_e2e_helpers].include?(lane) elsif lane == :test_e2e stop_e2e_helpers @@ -489,37 +490,46 @@ private_lane :build_example_app do |options| ) end -lane :merge_release_to_main do +lane :merge_release_to_main do |options| ensure_git_status_clean - sh('git checkout main') - sh('git pull') - # Grep all remote release branches and ensure there's only one - release_branches = sh(command: 'git branch -a', log: false).delete(' ').split("\n").grep(%r(origin/.*release/)) - UI.user_error!("Expected 1 release branch, found #{release_branches.size}") if release_branches.size != 1 + release_branch = + if is_ci + # This API operation needs the "admin:org" scope. + ios_team = sh('gh api orgs/GetStream/teams/ios-developers/members -q ".[].login"', log: false).split + UI.user_error!("#{options[:author]} is not a member of the iOS Team") unless ios_team.include?(options[:author]) + + current_branch + else + release_branches = sh(command: 'git branch -a', log: false).delete(' ').split("\n").grep(%r(origin/.*release/)) + UI.user_error!("Expected 1 release branch, found #{release_branches.size}") if release_branches.size != 1 + + release_branches.first + end + + UI.user_error!("`#{release_branch}`` branch does not match the release branch pattern: `release/*`") unless release_branch.start_with?('release/') + + sh('git checkout origin/main') + sh('git pull origin main') # Merge release branch to main. For more info, read: https://notion.so/iOS-Branching-Strategy-37c10127dc26493e937769d44b1d6d9a - sh("git merge #{release_branches.first} --ff-only") - UI.user_error!('Not pushing changes') unless prompt(text: 'Will push changes. All looking good?', boolean: true) - sh('git push') - UI.important('Please, wait for the `Publish new release` workflow to pass on GitHub Actions: ' \ - "https://github.com/#{github_repo}/actions/workflows/publish-release.yml") + sh("git merge #{release_branch} --ff-only") + sh('git push origin main') + + comment = "[Publication of the release](https://github.com/#{github_repo}/actions/workflows/release-publish.yml) has been launched 👍" + UI.important(comment) + create_pr_comment(pr_num: ENV.fetch('GITHUB_PR_NUM'), text: comment) end lane :merge_main_to_develop do - if is_ci - sh('git reset --hard') - else - ensure_git_status_clean - end - + ensure_git_status_clean sh('git checkout main') sh('git pull origin main') - sh('git checkout develop') + sh('git checkout origin/develop') sh('git pull origin develop') sh('git log develop..main') sh('git merge main') - sh('git push') + sh('git push origin develop') end desc 'Compresses the XCFrameworks into zip files' @@ -594,7 +604,6 @@ private_lane :update_spm do |options| File.open('./Package.swift', 'w') { |file| file << file_data } # Update the repo - sh('git config --global user.name "Stream Bot"') sh('git add -A') sh("git commit -m 'Bump #{version}'") sh('git push') @@ -893,7 +902,7 @@ end private_lane :create_pr_comment do |options| if is_ci && !options[:pr_num].to_s.empty? last_comment = sh("gh pr view #{options[:pr_num]} --json comments --jq '.comments | map(select(.author.login == \"Stream-iOS-Bot\")) | last'") - edit_last_comment = last_comment.include?(options[:edit_last_comment_with_text]) ? '--edit-last' : '' + edit_last_comment = options[:edit_last_comment_with_text] && last_comment.include?(options[:edit_last_comment_with_text]) ? '--edit-last' : '' sh("gh pr comment #{options[:pr_num]} #{edit_last_comment} -b '#{options[:text]}'") end end