Skip to content

chore(ci): create pull request on changelog update #2224

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 114 additions & 0 deletions .github/scripts/create_pr_for_staged_changes.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#!/bin/bash
set -uxo pipefail # enable debugging, prevent accessing unset env vars, prevent masking pipeline errors to the next command

#docs
#title :create_pr_for_staged_changes.sh
#description :This script will create a PR for staged changes and detect and close duplicate PRs.
#author :@heitorlessa
#date :May 8th 2023
#version :0.1
#usage :bash create_pr_for_staged_changes.sh {git_staged_files_or_directories_separated_by_space}
#notes :Meant to use in GitHub Actions only. Temporary branch will be named $TEMP_BRANCH_PREFIX-$GITHUB_RUN_ID
#os_version :Ubuntu 22.04.2 LTS
#required_env_vars :COMMIT_MSG, PR_TITLE, TEMP_BRANCH_PREFIX, GH_TOKEN, GITHUB_RUN_ID, GITHUB_SERVER_URL, GITHUB_REPOSITORY
#==============================================================================

PR_BODY="This is an automated PR created from the following workflow"
FILENAME=".github/scripts/$(basename "$0")"
readonly PR_BODY
readonly FILENAME

# Sets GitHub Action with error message to ease troubleshooting
function raise_validation_error() {
echo "::error file=${FILENAME}::$1"
exit 1
}

function debug() {
echo "::debug::$1"
}

function notice() {
echo "::notice file=${FILENAME}::$1"
}

function has_required_config() {
# Default GitHub Actions Env Vars: https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables
debug "Do we have required environment variables?"
test -z "${TEMP_BRANCH_PREFIX}" && raise_validation_error "TEMP_BRANCH_PREFIX env must be set to create a PR"
test -z "${GH_TOKEN}" && raise_validation_error "GH_TOKEN env must be set for GitHub CLI"
test -z "${GITHUB_RUN_ID}" && raise_validation_error "GITHUB_RUN_ID env must be set to trace Workflow Run ID back to PR"
test -z "${GITHUB_SERVER_URL}" && raise_validation_error "GITHUB_SERVER_URL env must be set to trace Workflow Run ID back to PR"
test -z "${GITHUB_REPOSITORY}" && raise_validation_error "GITHUB_REPOSITORY env must be set to trace Workflow Run ID back to PR"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Worth it adding the COMMIT_MSG and PR_TITLE variables in this check?

#required_env_vars :COMMIT_MSG, PR_TITLE, TEMP_BRANCH_PREFIX, GH_TOKEN, GITHUB_RUN_ID, GITHUB_SERVER_URL, GITHUB_REPOSITORY

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

YES, good catch! Doing it now

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DONE


set_environment_variables
}

function set_environment_variables() {
WORKFLOW_URL="${GITHUB_SERVER_URL}"/"${GITHUB_REPOSITORY}"/actions/runs/"${GITHUB_RUN_ID}" # e.g., heitorlessa/aws-lambda-powertools-test/actions/runs/4913570678
TEMP_BRANCH="${TEMP_BRANCH_PREFIX}"-"${GITHUB_RUN_ID}" # e.g., ci-changelog-4894658712

export readonly WORKFLOW_URL
export readonly TEMP_BRANCH
}

function has_anything_changed() {
debug "Is there an update to the source code?"
HAS_ANY_SOURCE_CODE_CHANGED="$(git status --porcelain)"

test -z "${HAS_ANY_SOURCE_CODE_CHANGED}" && echo "Nothing to update" && exit 0
}

function create_temporary_branch_with_changes() {
debug "Creating branch ${TEMP_BRANCH}"
git checkout -b "${TEMP_BRANCH}"

debug "Committing staged files: $*"
git add "$@"
git commit -m "${COMMIT_MSG}"

debug "Creating branch remotely"
git push origin "${TEMP_BRANCH}"
}

function create_pr() {
debug "Creating PR against ${BRANCH} branch"
NEW_PR_URL=$(gh pr create --title "${PR_TITLE}" --body "${PR_BODY}: ${WORKFLOW_URL}" --base "${BRANCH}") # e.g, https://github.com/awslabs/aws-lambda-powertools/pull/13

# greedy remove any string until the last URL path, including the last '/'. https://opensource.com/article/17/6/bash-parameter-expansion
NEW_PR_ID="${NEW_PR_URL##*/}" # 13
export NEW_PR_URL
export NEW_PR_ID
}

function close_duplicate_prs() {
debug "Do we have any duplicate PRs?"
DUPLICATE_PRS=$(gh pr list --search "${PR_TITLE}" --json number --jq ".[] | select(.number != ${NEW_PR_ID}) | .number") # e.g, 13\n14

debug "Closing duplicated PRs if any"
echo "${DUPLICATE_PRS}" | xargs -L1 gh pr close --delete-branch --comment "Superseded by #${NEW_PR_ID}"
export readonly DUPLICATE_PRS
}
Comment on lines +86 to +93
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Super cool this check to avoid orphans branches in the repository and getting messy. :D


function report_summary() {
debug "Creating job summary"
echo "### Pull request created successfully :rocket: #${NEW_PR_URL} <br/><br/> Closed duplicated PRs (if any): ${DUPLICATE_PRS}" >>"$GITHUB_STEP_SUMMARY"

notice "PR_URL is ${NEW_PR_URL}"
notice "PR_BRANCH is ${TEMP_BRANCH}"
notice "PR_DUPLICATES are ${DUPLICATE_PRS}"
}

function main() {
# Sanity check
has_anything_changed
has_required_config

create_temporary_branch_with_changes "$@"
create_pr
close_duplicate_prs

report_summary
}

main "$@"
27 changes: 16 additions & 11 deletions .github/workflows/reusable_publish_changelog.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,6 @@ name: Build and publish latest changelog
on:
workflow_call:

permissions:
contents: write

env:
BRANCH: develop

Expand All @@ -16,6 +13,9 @@ jobs:
concurrency:
group: changelog-build
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- name: Checkout repository # reusable workflows start clean, so we need to checkout again
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab # v3.5.2
Expand All @@ -30,11 +30,16 @@ jobs:
git pull origin "${BRANCH}"
- name: "Generate latest changelog"
run: make changelog
- name: Update Changelog in trunk
run: |
HAS_CHANGE=$(git status --porcelain)
test -z "${HAS_CHANGE}" && echo "Nothing to update" && exit 0
git add CHANGELOG.md
git commit -m "update changelog with latest changes"
git pull origin "${BRANCH}" # prevents concurrent branch update failing push
git push origin HEAD:refs/heads/"${BRANCH}"
- name: Create PR
run: bash .github/scripts/create_pr_for_staged_changes.sh CHANGELOG.md
env:
COMMIT_MSG: "chore(ci): update changelog with latest changes"
PR_TITLE: "chore(ci): changelog rebuild"
TEMP_BRANCH_PREFIX: "ci-changelog"
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Cleanup orphaned branch
if: failure()
run: git push origin --delete "${TEMP_BRANCH_PREFIX}-${GITHUB_RUN_ID}" || echo "Must have failed before creating temporary branch; no cleanup needed."
env:
TEMP_BRANCH_PREFIX: "ci-changelog"
GITHUB_RUN_ID: ${{ github.run_id }}