diff --git a/.github/workflows/lint-and-test.yml b/.github/workflows/lint-and-test.yml new file mode 100644 index 0000000..67359cf --- /dev/null +++ b/.github/workflows/lint-and-test.yml @@ -0,0 +1,9 @@ +name: Lint & Test +on: pull_request + +jobs: + lint: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - run: make lint \ No newline at end of file diff --git a/Makefile b/Makefile index f73933c..f76d075 100644 --- a/Makefile +++ b/Makefile @@ -4,6 +4,6 @@ test-node: test: test-node lint-shell: - shellcheck prepare-dev/*.sh prepare-dev/src/*.sh + shellcheck prepare-dev/*.sh src/*.sh release-pr/*.sh lint: lint-shell \ No newline at end of file diff --git a/README.md b/README.md index 5a05924..70fb0e9 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,8 @@ # plugin-release-actions GitHub actions for standardized releases for WP plugins and Drupal modules -## Build Tag and Release +## Actions +### Build Tag and Release This action will build a tag and draft a release for a plugin or module. To use this action, create a workflow file in your plugin or module repository (e.g. `.github/workflows/release.yml`) with the following contents: @@ -33,7 +34,7 @@ jobs: draft: "false" ``` -### Inputs +#### Inputs | Name | Description | Default | | --- | --- | --- | @@ -43,7 +44,13 @@ jobs: | `draft` | Whether to make the release a draft or live | `true` | -## Prepare Dev -This action will update the development branch to be ready for the next release after releasing a new version of a plugin or module. +### Prepare Dev +This action will update the development branch to be ready for the next release after releasing a new version of a plugin or module. This action search and replaces the version number across all the top-level files in the repo. +### Release PR +This action will draft a "ship it" PR to the `release` branch when new features are added to the development branch. This action search and replaces the version number in relevant places across all the top-level files in the repo. +This action expects to make use of the label "automation" on the repo. Create it if it does not already exist. + +## Merging Commits +_TBD explain why features should be squashed and releases must be merged._ diff --git a/prepare-dev/prepare-dev.sh b/prepare-dev/prepare-dev.sh index bdb198d..87b70cb 100644 --- a/prepare-dev/prepare-dev.sh +++ b/prepare-dev/prepare-dev.sh @@ -1,6 +1,5 @@ #!/bin/bash set -eou pipefail -set -x IFS=$'\n\t' if [[ "${DRY_RUN:-}" == 1 ]]; then @@ -17,13 +16,13 @@ readonly GIT_USER="bot@getpantheon.com" readonly GIT_NAME="Pantheon Automation" # shellcheck disable=SC1091 -source "${SELF_DIRNAME}/src/functions.sh" +source "${SELF_DIRNAME}/../src/functions.sh" readonly RELEASE_BRANCH="release" readonly DEVELOP_BRANCH="main" main() { - local README_MD="${2:-}" + local README_MD="${1:-}" if [[ -z "$README_MD" ]]; then README_MD=README.MD fi diff --git a/release-pr/action.yml b/release-pr/action.yml new file mode 100644 index 0000000..1238638 --- /dev/null +++ b/release-pr/action.yml @@ -0,0 +1,27 @@ +name: Draft Release PR +description: Create a PR to the release branch with all new features on the development branch. + +inputs: + # All of these should be parameterized but right now are set in the bash script. + # release_branch: + # required: false + # default: "release" + # development_branch: + # required: false + # default: "main" + # git_author_username: + # required: false + # default: "bot@getpantheon.com" + # git_author_username: + # required: false + # default: "Pantheon Automation" + readme_md: # to avoid case sensitivty issues when getting the current version out of readme + required: false + default: "README.MD" + +runs: + using: "composite" + steps: + - name: Draft Release PR + shell: bash + run: bash ${{ github.action_path }}/release-pr.sh ${{ inputs.readme_md }} \ No newline at end of file diff --git a/release-pr/release-pr.sh b/release-pr/release-pr.sh new file mode 100644 index 0000000..c61ba60 --- /dev/null +++ b/release-pr/release-pr.sh @@ -0,0 +1,57 @@ +#!/bin/bash +set -eou pipefail +IFS=$'\n\t' + +# shellcheck disable=SC2155 +readonly SELF_DIRNAME="$(dirname -- "$0")" + +# shellcheck disable=SC1091 +source "${SELF_DIRNAME}/../src/functions.sh" + +main() { + local README_MD="${1:-}" + if [[ -z "$README_MD" ]]; then + README_MD=README.MD + fi + + local CURRENT_VERSION + CURRENT_VERSION="$(grep 'Stable tag:' < "${README_MD}" | awk '{print $3}')" + + local NEW_VERSION="${CURRENT_VERSION%-dev}" + local RELEASE_BRANCH="release-${NEW_VERSION}" + + # if local release branch exists, delete it + if git show-ref --quiet --verify "refs/heads/$RELEASE_BRANCH"; then + echo "> git branch -D ${RELEASE_BRANCH}" + git branch -D "${RELEASE_BRANCH}" + fi + + git checkout -b "${RELEASE_BRANCH}" + echo "Updating ${CURRENT_VERSION} to ${NEW_VERSION}" + # Iterate through each file in the top-level directory + for file in ./*; do + process_file "$file" "${CURRENT_VERSION}" "${NEW_VERSION}" + done + + git_config + + RELEASE_MESSAGE="Release ${NEW_VERSION}" + git commit -m "${RELEASE_MESSAGE}" + git push origin "${RELEASE_BRANCH}" --force + + # Create a draft PR + create_draft_pr RELEASE_MESSAGE + if gh pr view "${RELEASE_BRANCH}"; then + echo_info "PR Already Exists" + return + fi + local PR_TITLE="${RELEASE_MESSAGE}" + local PR_BODY="${RELEASE_MESSAGE}. If CI tests have not run, mark as 'ready for review' or close this PR and re-open it. + +For proper management of git history, merge this PR, do not squash or rebase." + gh pr create --draft --base "release" \ + --title "${PR_TITLE}" --body "${PR_BODY}" \ + --label "automation" +} + +main "$@" \ No newline at end of file diff --git a/prepare-dev/src/functions.sh b/src/functions.sh similarity index 58% rename from prepare-dev/src/functions.sh rename to src/functions.sh index c0b8845..bd0f910 100644 --- a/prepare-dev/src/functions.sh +++ b/src/functions.sh @@ -6,7 +6,7 @@ echo_info(){ echo "[Info] $*" >&2 } -# echo to stderr with info tag +# echo to stderr with error tag echo_error(){ echo "[Error] $*" >&2 } @@ -40,6 +40,7 @@ process_file(){ return fi # Convert the filename to lowercase for case-insensitive comparison + local LC_FILE_PATH LC_FILE_PATH=$(echo "$FILE" | tr '[:upper:]' '[:lower:]') echo "Processing file '${FILE}'..." @@ -63,12 +64,16 @@ process_file(){ echo_info "Alternative readme Processing [${FILE}]." update_readme "${FILE}" "${OLD_VERSION}" "${NEW_VERSION}" echo_info "Skip futher readme sed" - return + else + echo "search-and-replace with sed" + if [[ ${NEW_VERSION} == *"-dev" ]]; then + # if we're going TO a new dev version, don't s/r "@since" in php docs + sed -i.tmp -e '/^\s*\* @since/!s/'"${OLD_VERSION}"'/'"${NEW_VERSION}"'/g' "$FILE" && rm "$FILE.tmp" + else + sed -i.tmp -e "s/${OLD_VERSION}/${NEW_VERSION}/g" "$FILE" && rm "$FILE.tmp" + fi fi - echo "search-and-replace with sed" - sed -i.tmp -e '/^\s*\* @since/!s/'"${OLD_VERSION}"'/'"${NEW_VERSION}"'/g' "$FILE" && rm "$FILE.tmp" - git add "$FILE" } @@ -82,22 +87,33 @@ update_readme(){ fi # Convert the filename to lowercase for case-insensitive comparison + local LC_FILE_PATH LC_FILE_PATH=$(echo "$FILE_PATH" | tr '[:upper:]' '[:lower:]') - if [[ "$LC_FILE_PATH" == *.md ]]; then - echo_info "markdown search-replace" - local new_heading="### ${NEW_VERSION}" - local awk_with_target='/## Changelog/ { print; print ""; print heading; next } 1' + # handle "prepare dev" one way and "draft PR" another. + if [[ ${NEW_VERSION} == *"-dev" ]]; then + # if we're going TO -dev, add a new changelog heading + if [[ "$LC_FILE_PATH" == *.md ]]; then + local new_heading="### ${NEW_VERSION}" + local awk_with_target='/## Changelog/ { print; print ""; print heading; next } 1' + else + local new_heading="= ${NEW_VERSION} =" + local awk_with_target='/== Changelog ==/ { print; print ""; print heading; next } 1' + fi + awk -v heading="$new_heading" "$awk_with_target" "$FILE_PATH" > tmp.md + mv tmp.md "$FILE_PATH" else - echo_info "wp.org txt search-replace" - local new_heading="= ${NEW_VERSION} =" - local awk_with_target='/== Changelog ==/ { print; print ""; print heading; next } 1' + # if we're going FROM -dev, update the changelog. + # TODO: do this instead/again as part of release since PR is unlikely to merge right away + if [[ "$LC_FILE_PATH" == *.md ]]; then + echo_info "updating changelog and adding date to readme.md" + sed -i -e "s/= ${OLD_VERSION}/= ${NEW_VERSION} (${TODAYS_DATE})/g" "$FILE" + else + echo_info "updating changelog and adding date to readme.txt" + sed -i -e "s/= ${OLD_VERSION}/= ${NEW_VERSION} (${TODAYS_DATE})/g" "$FILE" + fi fi - awk -v heading="$new_heading" "$awk_with_target" "$FILE_PATH" > tmp.md - mv tmp.md "$FILE_PATH" - + # Update only the stable tag at the top of the document sed -i.tmp -e "s/Stable tag: ${OLD_VERSION}/Stable tag: ${NEW_VERSION}/g" "$FILE_PATH" && rm "$FILE_PATH.tmp" - - git add "$FILE_PATH" } \ No newline at end of file