diff --git a/.github/workflows/auto_branching.yml b/.github/workflows/auto_branching.yml new file mode 100644 index 00000000000..5c173549e38 --- /dev/null +++ b/.github/workflows/auto_branching.yml @@ -0,0 +1,322 @@ +### The auto-branching workflow triggered through a dispatch request from the CI +name: auto-branching + +# Run on workflow dispatch from CI +on: + workflow_dispatch: + inputs: + target_branch: + type: string + description: branch to be created from the master + stream_version: + type: string + description: new stream version of satellite + +jobs: + check-group-membership: + runs-on: ubuntu-latest + outputs: + member: ${{steps.check_membership.outputs.member}} + + steps: + - name: Check if the user is a member of repository-admins group + id: check_membership + run: | + # Use GitHub API to check if the user triggering the workflow is a member of satellite-admin group + MEMBER=$(curl -s -H "Authorization: token ${{ secrets._REPO_ADMIN_TOKEN }}" \ + "https://api.github.com/orgs/satelliteQE/teams/repository-admins/memberships/${{ github.actor }}") + if [[ $(echo "$MEMBER" | jq -r '.state') == "active" ]]; then + echo "User is a member of satellite-admin group." + echo "member=true" >> $GITHUB_OUTPUT + else + echo "User is not a member of satellite-admin group." + echo "member=false" >> $GITHUB_OUTPUT + exit 1 + fi + + auto-branching-new-downstream-release: + name: ${{ github.event.inputs.target_branch }} - raise PR with changes + runs-on: ubuntu-latest + needs: check-group-membership + if: ${{ needs.check-group-membership.outputs.member == 'true' }} + + steps: + - uses: actions/checkout@v4 + + - name: Create the ${{ github.event.inputs.target_branch }} branch + id: create-branch + uses: peterjgrainger/action-create-branch@v2.2.0 + env: + GITHUB_TOKEN: ${{ secrets._REPO_ADMIN_TOKEN }} + with: + branch: ${{ github.event.inputs.target_branch }} + + - name: Create label for the ${{ github.event.inputs.target_branch }} branch + id: create-label + run: | + curl -X POST \ + -H "Authorization: token ${{ secrets._REPO_ADMIN_TOKEN }}" \ + -H "Accept: application/vnd.github.v3+json" \ + https://api.github.com/repos/${{ github.repository }}/labels \ + -d "{\"name\":\"${{ github.event.inputs.target_branch }}\",\"color\":\"fbca04\"}" + + - name: Switch to ${{ github.event.inputs.target_branch }} branch + run: git checkout -b "${{ github.event.inputs.target_branch }}" + + - name: Checkout from ${{ github.event.inputs.target_branch }} branch for auto-branching changes + id: checkout-to-auto-branch + run: | + branch_name="auto-branching-${{ github.event.inputs.target_branch }}-$(date '+%s')" + git checkout -b "$branch_name" + echo "branch_name=$branch_name" >> $GITHUB_OUTPUT + + - name: Update target branch label in dependabot yml file + id: update-dependabot + run: | + # Read the dependabot.yml file + FILE_PATH="./.github/dependabot.yml" + TARGET_BRANCH="${{ github.event.inputs.target_branch }}" + # Append the target branch label to the labels node + awk -v target="'$TARGET_BRANCH'" '/^ *labels:/ {$0 = $0 "\n - " target} 1' "$FILE_PATH" > temp.yml && mv temp.yml "$FILE_PATH" + + - name: Update repository URLs in requirements.txt + id: update-repo-urls + run: | + # Define the file path + FILE_PATH="./requirements.txt" + # Define the replacement strings + replacements=( + "airgun @ git+https://github.com/SatelliteQE/airgun.git@master#egg=airgun|airgun @ git+https://github.com/SatelliteQE/airgun.git@${{ github.event.inputs.target_branch }}#egg=airgun" + "nailgun @ git+https://github.com/SatelliteQE/nailgun.git@master#egg=nailgun|nailgun @ git+https://github.com/SatelliteQE/nailgun.git@${{ github.event.inputs.target_branch }}#egg=nailgun" + ) + # Create a temporary file + TEMP_FILE=$(mktemp) + # Perform replacements using a for loop + for replacement in "${replacements[@]}"; do + old_url=$(echo "$replacement" | cut -d'|' -f1) + new_url=$(echo "$replacement" | cut -d'|' -f2) + sed "s|${old_url}|${new_url}|g" "$FILE_PATH" > "$TEMP_FILE" && mv "$TEMP_FILE" "$FILE_PATH" + done + + - name: Remove the dispatch release GHA + id: remove-dispatch-release-gha + run: | + rm -rf ./.github/workflows/dispatch_release.yml + rm -rf ./.github/workflows/auto_branching.yml + + - name: Remove the CODEOWNERS file + id: remove-code-owners + run: | + rm -rf ./.github/CODEOWNERS + + - name: Remove lines with @pytest.mark.stream + id: remove-mark-stream + run: | + # Loop through files in the folder + grep -rl "tests/foreman" -e '@pytest\.mark\.stream' | while IFS= read -r file; do + awk '!/@pytest\.mark\.stream/' "$file" > temp && mv temp "$file" + done + + - name: Update version in setup.py + run: sed -i "s/version=['\"][0-9.]*['\"]\+/version='${{ github.event.inputs.target_branch }}'/" setup.py + + - name: Update the Constants in __init__.py file + run: | + old_url="https://raw.githubusercontent.com/SatelliteQE/robottelo/master/tests/foreman/data/uri.sh" + new_url="https://raw.githubusercontent.com/SatelliteQE/robottelo/${{ github.event.inputs.target_branch }}/tests/foreman/data/uri.sh" + FILE_PATH="./robottelo/constants/__init__.py" + awk '/SAT_NON_GA_VERSIONS =/ { sub(/\[[^,]*, /, "[", $0) } 1' "$FILE_PATH" > temp && mv temp "$FILE_PATH" + sed -i.bak "s|${old_url}|${new_url}|" "$FILE_PATH" + rm "$FILE_PATH.bak" + + - name: git status + run: git status + + - name: git diff + run: git diff + + - name: Commit changes + run: | + git config --local user.email Satellite-QE.satqe.com && git config --local user.name Satellite-QE + git add setup.py ./tests/foreman ./robottelo/* ./requirements.txt ./.github/* + git commit -m "Changes for ${{ github.event.inputs.target_branch }} new branch" + git push origin ${{steps.checkout-to-auto-branch.outputs.branch_name}} + + - name: Create pull request + id: create_pr + run: | + title="[${{ github.event.inputs.target_branch }}]: Changes for ${{ github.event.inputs.target_branch }} new branch" + body=" + ### Problem Statement + New ${{ github.event.inputs.target_branch }} branch + ### Solution + - Dependabot labels are updated for new branch + - Removed dispatch release GHA from ${{ github.event.inputs.target_branch }} as we are releasing only master changes + - Airgun and Nailgun Requirements uses ${{ github.event.inputs.target_branch }} branch + - Constants are using new version now + - Stream tests removed + - Setup.py uses new version + " + pr_number=$(gh pr create --title "$title" --body "$body" --base "${{ github.event.inputs.target_branch }}" | awk -F'/' '{print $NF}') + echo "$pr_number" + echo "pr_number=$pr_number" >> $GITHUB_OUTPUT + env: + GITHUB_TOKEN: ${{ secrets._REPO_ADMIN_TOKEN }} + + - name: Add the prt comment for running the sanity tests + id: add-parent-prt-comment + uses: thollander/actions-comment-pull-request@v2 + with: + message: | + trigger: test-robottelo + pr_number: ${{ steps.create_pr.outputs.pr_number }} + GITHUB_TOKEN: ${{ secrets._REPO_ADMIN_TOKEN }} + + - name: add the no-cherrypick label + uses: actions/github-script@v7 + with: + github-token: ${{ secrets._REPO_ADMIN_TOKEN }} + script: | + github.rest.issues.addLabels({ + issue_number: ${{ steps.create_pr.outputs.pr_number }}, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ["No-CherryPick"] + }) + + branch-protection: + runs-on: ubuntu-latest + needs: auto-branching-new-downstream-release + if: success() + steps: + - name: Create branch protection + run: | + TOKEN=${{ secrets._REPO_ADMIN_TOKEN }} + OWNER=${{ github.repository_owner }} + REPO=${{ github.event.repository.name }} + BRANCH="${{ github.event.inputs.target_branch }}" # Adjust branch name as needed + # Branch protection payload + PROTECTION_PAYLOAD='{ + "required_status_checks": { + "strict": true, + "contexts": ["Code Quality (3.10)", "Code Quality (3.11)", "Code Quality (3.12)", "Enforcing cherrypick labels"] + }, + "required_linear_history": true, + "enforce_admins": null, + "required_pull_request_reviews": null, + "restrictions": null, + "allow_force_pushes": null, + "allow_deletions": null + }' + # Call GitHub API to update branch protection + PROTECTION_RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" \ + -X PUT \ + -H "Accept: application/vnd.github.luke-cage-preview+json" \ + -H "Authorization: token $TOKEN" \ + -d "$PROTECTION_PAYLOAD" \ + "https://api.github.com/repos/$OWNER/$REPO/branches/$BRANCH/protection") + + if [[ $PROTECTION_RESPONSE -eq 200 ]]; then + echo "Branch protection successfully updated." + echo "protection-outcome=success" >> "$GITHUB_OUTPUT" + else + echo "Failed to update branch protection. HTTP status code: $PROTECTION_RESPONSE" + echo "protection-outcome=failure" >> "$GITHUB_OUTPUT" + exit 1 + fi + + auto-branching-master: + name: master - raise PR with changes + runs-on: ubuntu-latest + needs: check-group-membership + if: ${{ needs.check-group-membership.outputs.member == 'true' }} + + steps: + - name: Checkout Robottelo + uses: actions/checkout@v4 + + - name: Update target branch label in dependabot yml file + id: update-dependabot + run: | + # Read the dependabot.yml file + FILE_PATH="./.github/dependabot.yml" + TARGET_BRANCH="${{ github.event.inputs.target_branch }}" + # Append the target branch label to the labels node + awk -v target="'$TARGET_BRANCH'" '/^ *labels:/ {$0 = $0 "\n - " target} 1' "$FILE_PATH" > temp.yml && mv temp.yml "$FILE_PATH" + + - name: Remove lines with @pytest.mark.stream + id: remove-mark-stream + run: | + # Loop through files in the folder + grep -rl "tests/foreman" -e '@pytest\.mark\.stream' | while IFS= read -r file; do + awk '!/@pytest\.mark\.stream/' "$file" > temp && mv temp "$file" + done + + - name: Update the Constants in __init__.py file + run: | + version="${{ github.event.inputs.target_branch }}" + ga_version="${{ github.event.inputs.ga_version }}" + old_stream_version="${version%.z}" + new_stream_version="${{ github.event.inputs.stream_version }}" + non_ga_versions="['$old_stream_version', '$new_stream_version']" + FILE_PATH="./robottelo/constants/__init__.py" + # update the version + sed -i.bak "s/SATELLITE_VERSION = \"$old_stream_version\"/SATELLITE_VERSION = \"$new_stream_version\"/" "$FILE_PATH" + sed -i.bak "s/ SATELLITE_VERSION: \"$old_stream_version\"/ SATELLITE_VERSION: \"$new_stream_version\"/" ./conf/robottelo.yaml.template + sed -i.bak "s/SAT_NON_GA_VERSIONS = \[.*\]/SAT_NON_GA_VERSIONS = $non_ga_versions/" "$FILE_PATH" + rm "$FILE_PATH.bak" "./conf/robottelo.yaml.template.bak" + + - name: git status + run: git status + + - name: git diff + run: git diff + + - name: Commit changes + run: | + git config --local user.email "action@github.com" + git config --local user.name "GitHub Action" + branch_name="auto-branching-${{ github.event.inputs.target_branch }}-$(date '+%s')" + git checkout -b "$branch_name" + git add setup.py ./tests/foreman ./robottelo/* ./requirements.txt ./.github/* ./conf/robottelo.yaml.template + git commit -m "Changes for new ${{ github.event.inputs.target_branch }} branch" + git remote -vvv + git push origin "$branch_name" + + - name: Create pull request + id: create_pr + run: | + title="[master]: Changes for new ${{ github.event.inputs.target_branch }} branch" + body=" + ### Problem Statement + New ${{ github.event.inputs.target_branch }} downstream and master points to stream that is ${{ github.event.inputs.stream_version }} + ### Solution + - Dependabot.yaml cherrypicks to ${{ github.event.inputs.target_branch }} + - Robottelo conf and constants now uses ${{ github.event.inputs.stream_version }} and ${{ github.event.inputs.target_branch }} satellite versions + " + pr_number=$(gh pr create --title "$title" --body "$body" --base "master" | awk -F'/' '{print $NF}') + echo "$pr_number" + echo "pr_number=$pr_number" >> $GITHUB_OUTPUT + env: + GITHUB_TOKEN: ${{ secrets._REPO_ADMIN_TOKEN }} + + - name: Add the prt comment for running the sanity tests + id: add-parent-prt-comment + uses: thollander/actions-comment-pull-request@v2 + with: + message: | + trigger: test-robottelo + pr_number: ${{ steps.create_pr.outputs.pr_number }} + GITHUB_TOKEN: ${{ secrets._REPO_ADMIN_TOKEN }} + + - name: add the no-cherrypick label + uses: actions/github-script@v7 + with: + github-token: ${{ secrets._REPO_ADMIN_TOKEN }} + script: | + github.rest.issues.addLabels({ + issue_number: ${{ steps.create_pr.outputs.pr_number }}, + owner: context.repo.owner, + repo: context.repo.repo, + labels: ["No-CherryPick"] + })