name: Release # Adapted from https://github.com/taiki-e/github-actions/blob/HEAD/.github/workflows/action-release.yml. permissions: contents: read on: workflow_dispatch: inputs: target: description: Package to be released required: true type: choice options: - install-action - install-action-manifest-schema version: description: Version to be increased required: true type: choice options: - patch - minor - major defaults: run: shell: bash --noprofile --norc -CeEuxo pipefail {0} concurrency: group: ${{ github.workflow }} cancel-in-progress: false jobs: prepare: if: github.repository_owner == 'taiki-e' && inputs.target == 'install-action' runs-on: ubuntu-latest timeout-minutes: 60 steps: - uses: taiki-e/checkout-action@83ed61bfbe2b8abbb3c66e8b65b1335484c70009 # v1.4.1 - uses: taiki-e/install-action@7bc99eee1f1b8902a125006cf790a1f4c8461e63 # v2.69.8 with: tool: parse-changelog fallback: none - id: check run: | set +x IFS=$'\n\t' trap -- 's=$?; printf >&2 "%s\n" "${0##*/}:${LINENO}: \`${BASH_COMMAND}\` exit with ${s}"; exit ${s}' ERR retry() { for i in {1..10}; do if "$@"; then return 0 else sleep "${i}" fi done "$@" } bail() { printf '::error::%s\n' "$*" exit 1 } if { sed --help 2>&1 || true; } | grep -Eq -e '-i extension'; then in_place=(-i '') else in_place=(-i) fi # shellcheck disable=SC2153 version="${VERSION}" printf '%s\n' "version(input): ${version}" # shellcheck disable=SC2153 tag_prefix="${TAG_PREFIX}" printf '%s\n' "tag_prefix: ${tag_prefix}" # shellcheck disable=SC2153 changelog="${CHANGELOG}" printf '%s\n' "changelog: ${changelog}" # Get the current date. release_date=$(date -u '+%Y-%m-%d') printf '%s\n' "release-date: ${release_date}" printf '%s\n' "release-date=${release_date}" >>"${GITHUB_OUTPUT}" # Get the current revision. rev=$(git rev-parse HEAD) printf '%s\n' "rev: ${rev}" printf '%s\n' "rev=${rev}" >>"${GITHUB_OUTPUT}" prev_version=$(parse-changelog --title-no-link "${changelog}" | cut -d' ' -f1) # Determine the new version number and tag name. case "${version}" in major | minor | patch) if [[ ! "${prev_version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then bail "pre-release/build-metadata" fi major="${prev_version%%.*}" minor_patch="${prev_version#*.}" minor="${minor_patch%%.*}" patch="${minor_patch#*.}" case "${version}" in major) version="$((major+1)).0.0" ;; minor) version="${major}.$((minor+1)).0" ;; patch) version="${major}.${minor}.$((patch+1))" ;; esac ;; *) version="${version#v}" ;; esac if [[ ! "${version}" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z\.-]+)?(\+[0-9A-Za-z\.-]+)?$ ]]; then bail "invalid version format '${version}'" fi printf '%s\n' "version: ${version}" printf '%s\n' "version=${version}" >>"${GITHUB_OUTPUT}" tag="${tag_prefix}${version}" printf '%s\n' "tag: ${tag}" printf '%s\n' "tag=${tag}" >>"${GITHUB_OUTPUT}" # Make sure the same release has not been created in the past. if gh release view "${tag}" &>/dev/null; then bail "tag '${tag}' has already been created and pushed" fi # Make sure that the release was created from an allowed branch. if ! git branch | grep -Eq '\* '"${BRANCH}"'$'; then bail "current branch is not '${BRANCH}'" fi changed_paths=() retry git fetch origin --tags &>/dev/null tags=$(git --no-pager tag | { grep -E "^${tag_prefix}[0-9]+" || true; }) if [[ -n "${tags}" ]]; then printf 'has-tags=true\n' >>"${GITHUB_OUTPUT}" # Make sure the same release does not exist in changelog. if grep -Eq "^## \\[${version//./\\.}\\]" "${changelog}"; then bail "release ${version} already exist in ${changelog}" fi if grep -Eq "^\\[${version//./\\.}\\]: " "${changelog}"; then bail "link to ${version} already exist in ${changelog}" fi # Update changelog. changed_paths+=("${changelog}") remote_url=$(grep -E '^\[Unreleased\]: https://' "${changelog}" | sed -E 's/^\[Unreleased\]: //; s/\.\.\.HEAD$//') prev_tag="${remote_url#*/compare/}" remote_url="${remote_url%/compare/*}" sed -E "${in_place[@]}" \ -e "s/^## \\[Unreleased\\]/## [Unreleased]\\n\\n## [${version}] - ${release_date}/" \ -e "s#^\[Unreleased\]: https://.*#[Unreleased]: ${remote_url}/compare/${tag}...HEAD\\n[${version}]: ${remote_url}/compare/${prev_tag}...${tag}#" "${changelog}" if ! grep -Eq "^## \\[${version//./\\.}\\] - ${release_date}$" "${changelog}"; then bail "failed to update ${changelog}" fi if ! grep -Eq "^\\[${version//./\\.}\\]: " "${changelog}"; then bail "failed to update ${changelog}" fi else # Make sure the release exists in changelog. if ! grep -Eq "^## \\[${version//./\\.}\\] - ${release_date}$" "${changelog}"; then bail "release ${version} does not exist in ${changelog} or has wrong release date" fi if ! grep -Eq "^\\[${version//./\\.}\\]: " "${changelog}"; then bail "link to ${version} does not exist in ${changelog}" fi fi # Make sure that a valid release note for this version exists. # https://github.com/taiki-e/parse-changelog changes=$(parse-changelog "${changelog}" "${version}") if [[ -z "${changes}" ]]; then bail "changelog for ${version} has no body" fi printf '============== CHANGELOG ==============\n' printf '%s\n' "${changes}" printf '=======================================\n' if [[ -n "${tags}" ]]; then git -c color.ui=always diff "${changed_paths[@]}" git add "${changed_paths[@]}" fi # Make sure that there is no unintended change. git add -N . git -c color.ui=always diff --exit-code ( set -x git show HEAD --shortstat ) env: VERSION: ${{ inputs.version }} TAG_PREFIX: v CHANGELOG: CHANGELOG.md BRANCH: main outputs: has-tags: ${{ steps.check.outputs.has-tags }} release-date: ${{ steps.check.outputs.release-date }} rev: ${{ steps.check.outputs.rev }} tag: ${{ steps.check.outputs.tag }} version: ${{ steps.check.outputs.version }} release: if: github.repository_owner == 'taiki-e' && inputs.target == 'install-action' needs: prepare runs-on: ubuntu-latest timeout-minutes: 60 environment: name: release deployment: false permissions: contents: write # for taiki-e/create-gh-release-action steps: - uses: taiki-e/checkout-action@83ed61bfbe2b8abbb3c66e8b65b1335484c70009 # v1.4.1 - uses: taiki-e/install-action@7bc99eee1f1b8902a125006cf790a1f4c8461e63 # v2.69.8 with: tool: parse-changelog fallback: none - uses: actions/create-github-app-token@f8d387b68d61c58ab83c6c016672934102569859 # v3.0.0 id: push-token with: app-id: ${{ secrets.PUSH_TOKEN_APP_CLIENT_ID }} private-key: ${{ secrets.PUSH_TOKEN_APP_PRIVATE_KEY }} - name: Create and push release commit and tag id: push run: | set +x IFS=$'\n\t' trap -- 's=$?; printf >&2 "%s\n" "${0##*/}:${LINENO}: \`${BASH_COMMAND}\` exit with ${s}"; exit ${s}' ERR retry() { for i in {1..10}; do if "$@"; then return 0 else sleep "${i}" fi done "$@" } bail() { printf '::error::%s\n' "$*" exit 1 } if { sed --help 2>&1 || true; } | grep -Eq -e '-i extension'; then in_place=(-i '') else in_place=(-i) fi git config user.name 'Taiki Endo' git config user.email 'te316e89@gmail.com' # shellcheck disable=SC2153 version="${VERSION}" # shellcheck disable=SC2153 tag="${TAG}" # shellcheck disable=SC2153 changelog="${CHANGELOG}" # shellcheck disable=SC2153 release_date="${RELEASE_DATE}" # Make sure the current revision is same as prepare step. # --unshallow is necessary to successfully push the # "releases/${major_version_tag}" branch in the subsequent step. retry git fetch origin --unshallow &>/dev/null rev=$(git rev-parse HEAD) if [[ "${rev}" != "${PREPARE_REV}" ]]; then bail "revision difference between prepare step" fi # Make sure the same release has not been created in the past. if gh release view "${tag}" &>/dev/null; then bail "tag '${tag}' has already been created and pushed" fi # Make sure that the release was created from an allowed branch. if ! git branch | grep -Eq '\* '"${BRANCH}"'$'; then bail "current branch is not '${BRANCH}'" fi changed_paths=() if [[ "${HAS_TAGS}" == "true" ]]; then # Update changelog. changed_paths+=("${changelog}") remote_url=$(grep -E '^\[Unreleased\]: https://' "${changelog}" | sed -E 's/^\[Unreleased\]: //; s/\.\.\.HEAD$//') prev_tag="${remote_url#*/compare/}" remote_url="${remote_url%/compare/*}" sed -E "${in_place[@]}" \ -e "s/^## \\[Unreleased\\]/## [Unreleased]\\n\\n## [${version}] - ${release_date}/" \ -e "s#^\[Unreleased\]: https://.*#[Unreleased]: ${remote_url}/compare/${tag}...HEAD\\n[${version}]: ${remote_url}/compare/${prev_tag}...${tag}#" "${changelog}" if ! grep -Eq "^## \\[${version//./\\.}\\] - ${release_date}$" "${changelog}"; then bail "failed to update ${changelog}" fi if ! grep -Eq "^\\[${version//./\\.}\\]: " "${changelog}"; then bail "failed to update ${changelog}" fi fi changes=$(parse-changelog "${changelog}" "${version}") printf '============== CHANGELOG ==============\n' printf '%s\n' "${changes}" printf '=======================================\n' if [[ "${HAS_TAGS}" == "true" ]]; then # Create a release commit. ( set -x git add "${changed_paths[@]}" git commit -m "Release ${version}" ) fi prev_credential_helper=$(git config get --local credential.helper || true) if [[ -n "${prev_credential_helper}" ]]; then printf 'credential helper is already set (%s)\n' "${prev_credential_helper}" else protocol="${GITHUB_SERVER_URL%%://*}" hostname="${GITHUB_SERVER_URL#*://}" ( set -x git config --local credential.helper cache ) git credential approve <