This is a continuation of what I posted the other day.
In this exercise – I needed to be able to automatically copy workflow files from one repository to another.
There is this known reported issue that despite adding a GitHub Actions Workflow file – unless it’s on the main/master branch – you can’t use the workflow_dispatch popup on the UI so build can be manually triggered (if needed).
So I did some investigation and created an automation to copy a workflow to the main/master branch.
As a result, I studied the GitHub API and wrote up the below snippet – it does the following:
- Disables Branch Protections for the Target Repository and Branch
- Checks if the Workflow exists on the Target Repository
- If True on “Update if Exists” – Copies the Workflow from the Source Repository to the Target
- Useful if wanting to do bulk updates across many repositories but not wanting to overwrite an existing actions build that has already been created on the Target
- Enables Branch Protections for the Target Repository and Branch
File Name – .github/workflows/copy_workflow.yaml
name: Copy Workflow from Source to Target Repository
on:
workflow_dispatch:
inputs:
owner:
description: 'Source/Target Organization'
required: true
default: 'my-organization-name'
target_repo:
description: 'Target Repository (Place to Copy Workflow File TO)'
required: true
default: 'ExampleRepositoryName'
target_branch:
description: 'Target Branch (Place to Copy Workflow File TO)'
required: true
default: 'main'
source_repo:
description: 'Source Repository (Place to Copy Workflow File FROM)'
required: true
default: 'ExampleRepositoryName'
source_branch:
description: 'Source Branch (Place to Copy Workflow File FROM)'
required: true
default: 'master'
workflow_file:
description: 'Workflow File to Copy'
required: true
default: 'build.yaml'
update_if_exists:
description: 'Update Workflow File if it already exists in the target repository'
required: true
default: 'false'
jobs:
update_secrets:
name: Copy Workflow from Source to Target Repository
runs-on: ubuntu-latest
steps:
- name: Clone Dependencies - my-actions-repository
uses: actions/checkout@v3
with:
token: ${{secrets.GIT_TOKEN}}
repository: my-organization-name/my-actions-repository
ref: main
path: ${{github.workspace}}/my-actions-repository
- name: Copy Workflow from Source to Target Repository
shell: bash
run: |
chmod 700 ${{github.workspace}}/my-actions-repository/utility/copy-workflow/
${{github.workspace}}/my-actions-repository/utility/copy-workflow/copy-workflow.sh "${{inputs.owner}}" "${{inputs.target_repo}}" "${{inputs.target_branch}}" "${{inputs.source_repo}}" "${{inputs.source_branch}}" "${{inputs.workflow_file}}" "${{inputs.update_if_exists}}"
env:
GITHUB_TOKEN: ${{ secrets.GIT_TOKEN }}
File Name – utility/copy-workflow/copy-workflow.sh
#!/bin/bash
# Get command-line arguments
owner="$1"
target_repo="$2"
target_branch="$3"
source_repo="$4"
source_branch="$5"
workflow_file="$6"
update_if_exists="$7"
echo "Disabling Branch Protection Rules for $target_branch on $target_repo"
gh api repos/$owner/$target_repo/branches/$target_branch/protection \
-H "Accept: application/vnd.github.v3+json" \
-X DELETE || echo "Branch protection rules already disabled or don't exist"
echo "Copying workflow: $workflow_file from '$source_repo' ($source_branch) to '$target_repo' ($target_branch)"
# Check if the source repository exists
echo "Checking source repository: $source_repo"
check_source_repo=$(gh api -X GET "/repos/$owner/$source_repo" -H 'Accept: application/vnd.github.v3+json' 2>&1)
if [[ $? -ne 0 ]]; then
echo "Error: Failed to retrieve information for source repository: $source_repo"
echo "$check_source_repo"
exit 1
fi
# Check if the target repository exists
echo "Checking target repository: $target_repo"
check_target_repo=$(gh api -X GET "/repos/$owner/$target_repo" -H 'Accept: application/vnd.github.v3+json' 2>&1)
if [[ $? -ne 0 ]]; then
echo "Error: Failed to retrieve information for target repository: $target_repo"
echo "$check_target_repo"
exit 1
fi
# Retrieve the workflow file from the source repository
echo "Retrieving workflow file: $workflow_file"
workflow_file_info=$(gh api -X GET "/repos/$owner/$source_repo/contents/.github/workflows/$workflow_file" -H 'Accept: application/vnd.github.v3+json' 2>&1)
if [[ $? -ne 0 ]]; then
echo "Failed to retrieve workflow file: $workflow_file"
echo "$workflow_file_info"
exit 1
fi
workflow_content=$(echo "$workflow_file_info" | jq -r '.content')
workflow_sha=$(echo "$workflow_file_info" | jq -r '.sha')
# Check if the workflow file already exists in the target repository
echo "Checking if workflow file exists in target repository: $workflow_file"
existing_workflow=$(gh api -X GET "/repos/$owner/$target_repo/contents/.github/workflows/$workflow_file" -H 'Accept: application/vnd.github.v3+json' 2>&1)
exists=$?
if [[ $exists -eq 0 ]]; then
existing_workflow_sha=$(echo "$existing_workflow" | jq -r '.sha')
echo "Workflow file exists in the target repository. Grabbing the Sha: $existing_workflow_sha"
else
echo "Workflow file does not exist in the target repository."
fi
# Update - Skip if Exists
if [[ $exists -eq 0 && "$update_if_exists" = false ]]; then
# Update the workflow in the target repository
echo "Skipping Update of Existing Workflow: $workflow_file"
fi
# Update - Update if Exists
if [[ $exists -eq 0 && "$update_if_exists" = true ]]; then
# Update the workflow in the target repository
echo "Updating workflow in target repository: $workflow_file"
update_workflow=$(gh api -X PUT "/repos/$owner/$target_repo/contents/.github/workflows/$workflow_file" \
-H 'Accept: application/vnd.github.v3+json' \
-f branch="$target_branch" \
-f content="$workflow_content" \
-f message="Update workflow: $workflow_file" \
-f sha="$existing_workflow_sha" 2>&1)
fi
# Create
if [[ $exists -eq 1 ]]; then
# Create the workflow in the target repository
echo "Creating workflow in target repository: $workflow_file"
create_workflow=$(gh api -X PUT "/repos/$owner/$target_repo/contents/.github/workflows/$workflow_file" \
-H 'Accept: application/vnd.github.v3+json' \
-f branch="$target_branch" \
-f content="$workflow_content" \
-f message="Copy workflow: $workflow_file" 2>&1)
fi
if [[ $? -ne 0 ]]; then
echo "Failed to copy workflow: $workflow_file"
if [[ $exists -eq 0 && "$update_if_exists" = true ]]; then
echo "$update_workflow"
else
echo "$create_workflow"
fi
exit 1
fi
# Notify the user that the workflow has been copied
echo "Workflow Logic Completed: $workflow_file from '$source_repo' ($source_branch) to '$target_repo' ($target_branch)."
echo "Enabling Branch Protection Rules for $target_branch on $target_repo"
PAYLOAD='{
"required_status_checks": null,
"enforce_admins": true,
"required_pull_request_reviews": {
"dismissal_restrictions": {
"users": [],
"teams": ["my_team_name"]
},
"dismiss_stale_reviews": false,
"require_code_owner_reviews": false,
"required_approving_review_count": 1,
"require_last_push_approval": false,
"bypass_pull_request_allowances": {
"users": ["service_account_username"],
"teams": []
}
},
"restrictions": {
"users": [],
"teams": ["my_team_name"],
"apps": []
},
"required_linear_history": false,
"allow_force_pushes": false,
"allow_deletions": false,
"block_creations": true,
"required_conversation_resolution": true,
"lock_branch": false,
"allow_fork_syncing": true
}'
echo "$PAYLOAD" | gh api repos/$owner/$target_repo/branches/$target_branch/protection \
-H "Accept: application/vnd.github.v3+json" \
-X PUT \
--silent \
--input - || (echo "Failed to enable branch protection rules for $target_branch on $target_repo" && exit 1)