Skip to content

chore(ci): add versioning workflow for pre-release #2006

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 12 commits into from
Feb 6, 2024
92 changes: 92 additions & 0 deletions .github/actions/create-pr/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
name: "Create PR custom action"
description: "Create a PR and a temporary branch, close duplicates"

# PROCESS
#
# 1. Setup git client using Powertools for AWS Lambda bot username
# 2. Pushes staged files to a temporary branch
# 3. Creates a PR from temporary branch against a target branch (typically trunk: develop, main, etc.)
# 4. Searches for duplicate PRs with the same title
# 5. If duplicates are found, link to the most recent one, close and delete their branches so we keep a single PR
# 6. In the event of failure, we delete the now orphaned branch (if any), and propagate the failure

# REQUIREMENTS
# You need to enable "Allow GitHub Actions to create and approve pull requests" in your repository settings
# You need to have "skip-changelog" label in your repository to skip changelog generation.

# USAGE
#
# - name: Create PR
# id: create-pr
# uses: ./.github/actions/create-pr
# with:
# files: "CHANGELOG.md"
# temp_branch_prefix: "ci-changelog"
# pull_request_title: "chore(ci): changelog rebuild"
# github_token: ${{ secrets.GITHUB_TOKEN }}
# - name: Step to demonstrate how to access outputs (no need for this)
# run: |
# echo "PR number: ${PR_ID}"
# echo "Branch: ${BRANCH}"
# env:
# PR_ID: ${{ steps.create-pr.outputs.pull_request_id}}
# BRANCH: ${{ steps.create-pr.outputs.temp_branch}}

inputs:
files:
description: "Files to add separated by space"
required: true
temp_branch_prefix:
description: "Prefix for temporary git branch to be created, e.g, ci-docs"
required: true
pull_request_title:
description: "Pull Request title to use"
required: true
github_token:
description: "GitHub token for GitHub CLI"
required: true
target_branch:
description: "Branch to target when creating a PR against (main, by default)"
required: false
default: main

outputs:
pull_request_id:
description: "Pull request ID created"
value: ${{ steps.create-pr.outputs.pull_request_id }}
temp_branch:
description: "Temporary branch created with staged changed"
value: ${{ steps.create-pr.outputs.temp_branch }}

runs:
using: "composite"
steps:
- id: adjust-path
run: echo "${{ github.action_path }}" >> $GITHUB_PATH
shell: bash
- id: setup-git
name: Git client setup and refresh tip
run: |
git config --global user.name 'aws-powertools-bot'
git config --global user.email '[email protected]'
git config pull.rebase true
git config remote.origin.url >&-
shell: bash
- id: create-pr
working-directory: ${{ env.GITHUB_WORKSPACE }}
run: create_pr_for_staged_changes.sh "${FILES}"
env:
FILES: ${{ inputs.files }}
TEMP_BRANCH_PREFIX: ${{ inputs.temp_branch_prefix }}
PR_TITLE: ${{ inputs.pull_request_title }}
BASE_BRANCH: ${{ inputs.target_branch }}
GH_TOKEN: ${{ inputs.github_token }}
shell: bash
- id: cleanup
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: ${{ inputs.temp_branch_prefix }}
GITHUB_RUN_ID: ${{ github.run_id }}
shell: bash
148 changes: 148 additions & 0 deletions .github/actions/create-pr/create_pr_for_staged_changes.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
#!/bin/bash
set -uo pipefail # 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, detect and close duplicate PRs. All PRs will be omitted from Release Notes and Changelogs
#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 :PR_TITLE, TEMP_BRANCH_PREFIX, GH_TOKEN
#==============================================================================

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

function debug() {
TIMESTAMP=$(date -u "+%FT%TZ") # 2023-05-10T07:53:59Z
echo ""${TIMESTAMP}" - $1"
}

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

function start_span() {
echo "::group::$1"
}

function end_span() {
echo "::endgroup::"
}

function has_required_config() {
start_span "Validating required config"
test -z "${TEMP_BRANCH_PREFIX}" && error "TEMP_BRANCH_PREFIX env must be set to create a PR"
test -z "${PR_TITLE}" && error "PR_TITLE env must be set"
test -z "${GH_TOKEN}" && error "GH_TOKEN env must be set for GitHub CLI"

# Default GitHub Actions Env Vars: https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables
debug "Are we running in GitHub Action environment?"
test -z "${GITHUB_RUN_ID}" && error "GITHUB_RUN_ID env must be set to trace Workflow Run ID back to PR"
test -z "${GITHUB_SERVER_URL}" && error "GITHUB_SERVER_URL env must be set to trace Workflow Run ID back to PR"
test -z "${GITHUB_REPOSITORY}" && error "GITHUB_REPOSITORY env must be set to trace Workflow Run ID back to PR"

debug "Config validated successfully!"
set_environment_variables
end_span
}

function set_environment_variables() {
start_span "Setting environment variables"
export readonly WORKFLOW_URL="${GITHUB_SERVER_URL}"/"${GITHUB_REPOSITORY}"/actions/runs/"${GITHUB_RUN_ID}" # e.g., heitorlessa/aws-lambda-powertools-test/actions/runs/4913570678
export readonly TEMP_BRANCH="${TEMP_BRANCH_PREFIX}"-"${GITHUB_RUN_ID}" # e.g., ci-changelog-4894658712
export readonly BASE_BRANCH="${BASE_BRANCH:-main}" # e.g., main, defaults to develop if missing
export readonly PR_BODY="This is an automated PR created from the following workflow"
export readonly FILENAME=".github/scripts/$(basename "$0")"
export readonly NO_DUPLICATES_MESSAGE="No duplicated PRs found"
export readonly SKIP_LABEL="skip-changelog"

end_span
}

function has_anything_changed() {
start_span "Validating git staged files"
HAS_ANY_SOURCE_CODE_CHANGED="$(git status --porcelain)"

test -z "${HAS_ANY_SOURCE_CODE_CHANGED}" && debug "Nothing to update; exitting early" && exit 0
end_span
}

function create_temporary_branch_with_changes() {
start_span "Creating temporary branch: "${TEMP_BRANCH}""
git checkout -b "${TEMP_BRANCH}"

debug "Committing staged files: $*"
echo "$@" | xargs -n1 git add || error "Failed to add staged changes: "$@""
git commit -m "${PR_TITLE}"

git push origin "${TEMP_BRANCH}" || error "Failed to create new temporary branch"
end_span
}

function create_pr() {
start_span "Creating PR against ${TEMP_BRANCH} branch"
# TODO: create label
NEW_PR_URL=$(gh pr create --title "${PR_TITLE}" --body "${PR_BODY}: ${WORKFLOW_URL}" --base "${BASE_BRANCH}" --label "${SKIP_LABEL}" || error "Failed to create PR") # e.g, https://github.com/aws-powertools/powertools-lambda-python/pull/13

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

function close_duplicate_prs() {
start_span "Searching for duplicate PRs"
DUPLICATE_PRS=$(gh pr list --search "${PR_TITLE}" --json number --jq ".[] | select(.number != ${NEW_PR_ID}) | .number") # e.g, 13\n14

if [ -z "${DUPLICATE_PRS}" ]; then
debug "No duplicate PRs found"
DUPLICATE_PRS="${NO_DUPLICATES_MESSAGE}"
else
debug "Closing duplicated PRs: "${DUPLICATE_PRS}""
echo "${DUPLICATE_PRS}" | xargs -L1 gh pr close --delete-branch --comment "Superseded by #${NEW_PR_ID}"
fi

export readonly DUPLICATE_PRS
end_span
}

function report_job_output() {
start_span "Updating job outputs"
echo pull_request_id="${NEW_PR_ID}" >>"$GITHUB_OUTPUT"
echo temp_branch="${TEMP_BRANCH}" >>"$GITHUB_OUTPUT"
end_span
}

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

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

function main() {
# Sanity check
has_anything_changed
has_required_config

create_temporary_branch_with_changes "$@"
create_pr
close_duplicate_prs

report_job_output
report_summary
}

main "$@"
47 changes: 47 additions & 0 deletions .github/workflows/make-version.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
name: Make Version

on:
workflow_dispatch: { }


env:
RELEASE_COMMIT: ${{ github.sha }}

jobs:
bump-version:
permissions:
id-token: write
contents: write
pull-requests: write
runs-on: ubuntu-latest
outputs:
RELEASE_VERSION: ${{ steps.set-release-version.outputs.RELEASE_VERSION }}
steps:
- name: Checkout code
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
with:
ref: ${{ github.ref }}
- name: Setup NodeJS
uses: actions/setup-node@b39b52d1213e96004bfcb1c61a8a6fa8ab84f3e8 # v4.0.1
with:
node-version: "20"
cache: "npm"
- name: Setup dependencies
uses: ./.github/actions/cached-node-modules
- name: Version
id: bump-version
run: |
npx lerna version --conventional-commits --no-git-tag-version --no-push --no-commit-hooks --yes
git add .
- name: Set release version
id: set-release-version
run: |
VERSION=$(cat lerna.json | jq .version -r)
echo RELEASE_VERSION="$VERSION" >> "$GITHUB_OUTPUT"
- name: Create PR
id: create-pr
uses: ./.github/actions/create-pr
with:
temp_branch_prefix: "ci-bump"
pull_request_title: "chore(ci): bump version to ${{ steps.set-release-version.outputs.RELEASE_VERSION }}"
github_token: ${{ secrets.GITHUB_TOKEN }}