Skip to content

2.4.x release branch #66

2.4.x release branch

2.4.x release branch #66

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