2.4.x release branch #66
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Create release branches in plugins | |
| description: | | |
| This workflow creates release branches (release/vault-<version>) for Vault plugins based on the specified version. | |
| It also creates labels (backport/vault-<version>) in each plugin repository for the specified version. | |
| run-name: ${{ inputs.version }} release branch | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: 'Release branch version to create with *NO* "v", e.g., 1.19.x' | |
| required: true | |
| type: string | |
| plugin_branch_shas: | |
| description: 'Optional: Specify git SHAs to fork from. Format: "plugin1-name:sha1,plugin2-name:sha2,etc" (comma-separated). Plugin names must match plugins.yaml keys exactly. Default behavior will fork from main.' | |
| required: false | |
| type: string | |
| jobs: | |
| create-release-branches: | |
| name: Create release branches | |
| runs-on: ubuntu-latest | |
| env: | |
| GITHUB_TOKEN: ${{ secrets.PRATHIC_GH_TOKEN }} | |
| steps: | |
| - name: Checkout current repository | |
| uses: actions/checkout@0ad4b8fadaa221de15dcec353f45205ec38ea70b # v4.1.4 | |
| with: | |
| token: ${{ secrets.PRATHIC_GH_TOKEN }} | |
| - name: Validate version | |
| shell: bash | |
| run: | | |
| version="${{ inputs.version }}" | |
| regex='^[0-9]+\.[0-9]+\.([0-9]+|x)([^\ ]+)?$' | |
| if ! [[ "${version}" =~ ${regex} ]]; then | |
| echo "::error::Version '${version}' is invalid, must match the pattern '${regex}'" | |
| exit 1 | |
| fi | |
| - name: Validate plugin_branch_shas input | |
| shell: bash | |
| run: | | |
| plugin_shas="${{ inputs.plugin_branch_shas }}" | |
| if [[ -n "$plugin_shas" ]]; then | |
| # Validate format: plugin1:sha1,plugin2:sha2 | |
| regex='^([^:, ]+):[^:, ]+(,([^:, ]+):[^:, ]+)*$' | |
| if ! [[ "$plugin_shas" =~ $regex ]]; then | |
| echo "::error::Invalid format. Expected: 'key1:value1,key2:value2'" | |
| echo "::error::Received: '$plugin_shas'" | |
| exit 1 | |
| fi | |
| # Confirm plugin name exists in plugins.yaml (case-insensitive) | |
| valid_plugins=$(yq eval 'keys | .[]' plugins.yaml | awk '{print tolower($0)}') | |
| IFS=',' read -ra PAIRS <<< "$plugin_shas" | |
| for pair in "${PAIRS[@]}"; do | |
| key="${pair%%:*}" | |
| key_lc=$(echo "$key" | awk '{print tolower($0)}') | |
| if ! echo "$valid_plugins" | grep -qx "$key_lc"; then | |
| echo "::error::Plugin '$key' does not exist in plugins.yaml (case-insensitive check)" | |
| echo "::error::Valid plugins are:" | |
| echo "$valid_plugins" | sed 's/^/::error:: - /' | |
| exit 1 | |
| fi | |
| done | |
| fi | |
| - name: Configure Git | |
| run: git config --global url."https://${{ secrets.PRATHIC_GH_TOKEN }}:@github.com".insteadOf "https://github.com" | |
| - name: Install yq | |
| run: | | |
| sudo wget -qO /usr/local/bin/yq https://github.com/mikefarah/yq/releases/latest/download/yq_linux_amd64 | |
| sudo chmod +x /usr/local/bin/yq | |
| - name: Create labels in repositories | |
| id: labels | |
| run: | | |
| plabel="backport/vault-${{ inputs.version }}" | |
| # Initialize arrays to track results | |
| created_labels=() | |
| skipped_labels=() | |
| failed_labels=() | |
| # Go through all the plugins from plugins.yaml (lowercase keys) | |
| plugins=$(yq eval 'keys | .[]' plugins.yaml | awk '{print tolower($0)}') | |
| for plugin in $plugins; do | |
| if [[ -z "$plugin" ]]; then | |
| continue | |
| fi | |
| echo "::debug::Processing plugin for label creation: $plugin" | |
| repo=$(yq eval "to_entries | map(select(.key | downcase == \"$plugin\")) | .[0].value.repository" plugins.yaml) | |
| # If repository is not specified or is null, use the plugin name as fallback | |
| if [[ "$repo" == "null" ]] || [[ -z "$repo" ]]; then | |
| repo="prathic-hashicorp/$plugin" | |
| else | |
| repo="prathic-hashicorp/$repo" | |
| fi | |
| echo "::debug::Creating label in repository: $repo" | |
| # Check if label already exists | |
| if gh label list --repo "$repo" --search "$plabel" | grep -q "$plabel"; then | |
| skipped_labels+=("$repo") | |
| else | |
| # Create the label using gh CLI | |
| if gh label create "$plabel" --repo "$repo" --description "Backport label for vault ${{ inputs.version }}" --color "0e8a16"; then | |
| created_labels+=("$repo") | |
| else | |
| echo "::error::Label creation failed for repository $repo" | |
| failed_labels+=("$repo") | |
| fi | |
| fi | |
| done | |
| # Save results to step outputs | |
| echo "created_labels=${created_labels[*]}" >> $GITHUB_OUTPUT | |
| echo "skipped_labels=${skipped_labels[*]}" >> $GITHUB_OUTPUT | |
| echo "failed_labels=${failed_labels[*]}" >> $GITHUB_OUTPUT | |
| - name: Create release branches | |
| id: branches | |
| run: | | |
| git config user.name prathic-hashicorp | |
| git config user.email prathic.sundararajan@hashicorp.com | |
| pbranch="release/vault-${{ inputs.version }}" | |
| # Initialize arrays to track branch results | |
| created_branches=() | |
| skipped_branches=() | |
| failed_branches=() | |
| # Parse input key-value pairs for plugin-specific git SHAs (lowercase keys) | |
| declare -A plugin_shas | |
| if [[ -n "${{ inputs.plugin_branch_shas }}" ]]; then | |
| IFS=',' read -ra PAIRS <<< "${{ inputs.plugin_branch_shas }}" | |
| for pair in "${PAIRS[@]}"; do | |
| key="${pair%%:*}" | |
| value="${pair#*:}" | |
| key_lc=$(echo "$key" | awk '{print tolower($0)}') | |
| plugin_shas["$key_lc"]="$value" | |
| echo "::notice::Plugin $key_lc will use SHA: $value" | |
| done | |
| fi | |
| # Go through all the plugins from plugins.yaml (lowercase keys) | |
| plugins=$(yq eval 'keys | .[]' plugins.yaml | awk '{print tolower($0)}') | |
| for plugin in $plugins; do | |
| if [[ -z "$plugin" ]]; then | |
| continue | |
| fi | |
| echo "::debug::Processing plugin for branch creation: $plugin" | |
| repo=$(yq eval "to_entries | map(select(.key | downcase == \"$plugin\")) | .[0].value.repository" plugins.yaml) | |
| # If repository is not specified or is null, use the plugin name as fallback | |
| if [[ "$repo" == "null" ]] || [[ -z "$repo" ]]; then | |
| repo="prathic-hashicorp/$plugin" | |
| else | |
| repo="prathic-hashicorp/$repo" | |
| fi | |
| echo "::debug::Using repository: $repo" | |
| # Check if branch already exists in the remote repository first | |
| if git ls-remote --exit-code --heads "https://github.com/$repo.git" "$pbranch" > /dev/null 2>&1; then | |
| skipped_branches+=("$repo") | |
| continue | |
| fi | |
| temp_dir="temp_$(basename $repo)" | |
| rm -rf "$temp_dir" | |
| echo "::debug::Cloning $repo repository" | |
| if ! git clone --depth 1 "https://github.com/$repo.git" "$temp_dir"; then | |
| echo "::error::Clone failed for repository $repo" | |
| failed_branches+=("$repo") | |
| continue | |
| fi | |
| cd "$temp_dir" | |
| # Configure git to suppress verbose output | |
| git config advice.detachedHead false | |
| # Use specific SHA if provided, otherwise use default branch | |
| if [[ -n "${plugin_shas[$plugin]}" ]]; then | |
| target_sha="${plugin_shas[$plugin]}" | |
| echo "::debug::Checking out specific SHA $target_sha for $plugin" | |
| # Fetch the specific commit to ensure the SHA is reachable | |
| if ! git fetch origin "$target_sha" || ! git checkout "$target_sha"; then | |
| echo "::error::SHA checkout failed for repository $repo (target SHA: $target_sha)" | |
| failed_branches+=("$repo") | |
| cd .. | |
| rm -rf "$temp_dir" | |
| continue | |
| fi | |
| else | |
| echo "::debug::Checking out default branch for $plugin" | |
| if ! (git checkout main || git checkout master) || ! git pull; then | |
| echo "::error::Default branch checkout failed for repository $repo" | |
| failed_branches+=("$repo") | |
| cd .. | |
| rm -rf "$temp_dir" | |
| continue | |
| fi | |
| fi | |
| echo "::debug::Creating branch $pbranch for $plugin (repo: $repo)" | |
| if git checkout -b "$pbranch" && git push origin "$pbranch"; then | |
| created_branches+=("$repo") | |
| else | |
| echo "::error::Branch creation/push failed for repository $repo" | |
| failed_branches+=("$repo") | |
| fi | |
| cd .. | |
| rm -rf "$temp_dir" | |
| done | |
| # Save branch results to step outputs | |
| echo "created_branches=${created_branches[*]}" >> $GITHUB_OUTPUT | |
| echo "skipped_branches=${skipped_branches[*]}" >> $GITHUB_OUTPUT | |
| echo "failed_branches=${failed_branches[*]}" >> $GITHUB_OUTPUT | |
| # Exit with error if there were any failures | |
| if [[ ${#failed_branches[@]} -gt 0 ]]; then | |
| echo "::error::Some branch operations failed. Check the summary below." | |
| exit 1 | |
| fi | |
| - name: Generate summary report | |
| if: always() | |
| run: | | |
| # Read results from previous step outputs | |
| IFS=' ' read -ra created_labels <<< "${{ steps.labels.outputs.created_labels }}" | |
| IFS=' ' read -ra skipped_labels <<< "${{ steps.labels.outputs.skipped_labels }}" | |
| IFS=' ' read -ra failed_labels <<< "${{ steps.labels.outputs.failed_labels }}" | |
| IFS=' ' read -ra created_branches <<< "${{ steps.branches.outputs.created_branches }}" | |
| IFS=' ' read -ra skipped_branches <<< "${{ steps.branches.outputs.skipped_branches }}" | |
| IFS=' ' read -ra failed_branches <<< "${{ steps.branches.outputs.failed_branches }}" | |
| # Get plugins list for table generation | |
| plugins=$(yq eval 'keys | .[]' plugins.yaml | awk '{print tolower($0)}') | |
| # Combined summary table | |
| echo "# Release Branch and Label Creation Summary" >> $GITHUB_STEP_SUMMARY | |
| echo "" >> $GITHUB_STEP_SUMMARY | |
| echo "| Plugin Repository | Labels | Branches |" >> $GITHUB_STEP_SUMMARY | |
| echo "|-------------------|--------|----------|" >> $GITHUB_STEP_SUMMARY | |
| # Create status maps for the combined table | |
| declare -A label_status_map | |
| declare -A branch_status_map | |
| # Populate label status map | |
| for repo in "${created_labels[@]}"; do | |
| label_status_map["$repo"]="✅ Successful" | |
| done | |
| for repo in "${skipped_labels[@]}"; do | |
| label_status_map["$repo"]="⏭️ Skipped" | |
| done | |
| for repo in "${failed_labels[@]}"; do | |
| label_status_map["$repo"]="❌ Failed" | |
| done | |
| # Populate branch status map | |
| for repo in "${created_branches[@]}"; do | |
| branch_status_map["$repo"]="✅ Successful" | |
| done | |
| for repo in "${skipped_branches[@]}"; do | |
| branch_status_map["$repo"]="⏭️ Skipped" | |
| done | |
| for repo in "${failed_branches[@]}"; do | |
| branch_status_map["$repo"]="❌ Failed" | |
| done | |
| # Generate combined table | |
| for plugin in $plugins; do | |
| if [[ -z "$plugin" ]]; then | |
| continue | |
| fi | |
| repo=$(yq eval "to_entries | map(select(.key | downcase == \"$plugin\")) | .[0].value.repository" plugins.yaml) | |
| if [[ "$repo" == "null" ]] || [[ -z "$repo" ]]; then | |
| repo="prathic-hashicorp/$plugin" | |
| else | |
| repo="prathic-hashicorp/$repo" | |
| fi | |
| label_status="${label_status_map[$repo]:-❓ Unknown}" | |
| branch_status="${branch_status_map[$repo]:-❓ Unknown}" | |
| echo "| \`$repo\` | $label_status | $branch_status |" >> $GITHUB_STEP_SUMMARY | |
| done | |
| echo "" >> $GITHUB_STEP_SUMMARY |