diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 7655a71..cba7f5f 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -14,7 +14,7 @@ jobs: if: github.event.action != 'labeled' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 # Bump version on merging Pull Requests with specific labels. # (bump:major,bump:minor,bump:patch) @@ -42,7 +42,7 @@ jobs: # Create release. - name: Release - uses: softprops/action-gh-release@v1 + uses: softprops/action-gh-release@v2 if: steps.tag.outputs.value != '' with: tag_name: ${{ steps.tag.outputs.value }} @@ -52,6 +52,6 @@ jobs: if: github.event.action == 'labeled' runs-on: ubuntu-latest steps: - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 - name: Post bumpr status comment uses: zckv/action-bumpr@v1 diff --git a/.github/workflows/test_action.yml b/.github/workflows/test_action.yml index 2685a54..ab2ba7a 100644 --- a/.github/workflows/test_action.yml +++ b/.github/workflows/test_action.yml @@ -8,15 +8,12 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + psr-version: ["8.7.0", "9.21.2", "10.6.1"] steps: - - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: path: ./action - - uses: actions/checkout@v4 + - uses: actions/checkout@v7 with: token: ${{ secrets.PAT_TOKEN }} fetch-depth: 0 @@ -31,4 +28,4 @@ jobs: push: false changelog: false vcs_release: false - psr_version: 8.7.0 + psr_version: ${{ matrix.psr-version }} diff --git a/action.sh b/action.sh index 7dd073c..5809b36 100755 --- a/action.sh +++ b/action.sh @@ -1,135 +1,183 @@ -bool() { +#!/bin/bash + +args_bool() { input="$1" option1="$2" option2="$3" if [ -z "$input" ]; then - echo "" + return elif [ "$input" = "true" ]; then - echo "$option1" + ARGS+=("$option1") elif [ "$input" = "false" ]; then - echo "$option2" + ARGS+=("$option2") else - echo "$option1" + ARGS+=("$option1") fi } -set -eux -export ARGS="" -case $(echo "$INPUT_PSR_VERSION" | cut -d '.' -f1) in -"9") - ARGS+="$(bool "$INPUT_PRERELEASE" "--as-prerelease " "")" - ARGS+="$(bool "$INPUT_COMMIT" "--commit " "--no-commit ")" - ARGS+="$(bool "$INPUT_TAG" "--tag " "--no-tag ")" - ARGS+="$(bool "$INPUT_PUSH" "--push " "--no-push ")" - ARGS+="$(bool "$INPUT_CHANGELOG" "--changelog " "--no-changelog ")" - ARGS+="$(bool "$INPUT_VCS_RELEASE" "--vcs-release " "--no-vcs-release ")" - ARGS+="$(bool "$INPUT_BUILD" "" "--skip-build ")" + +add_args_from_env_v10(){ + args_bool "$INPUT_PRERELEASE" "--as-prerelease" "" + args_bool "$INPUT_COMMIT" "--commit" "--no-commit" + args_bool "$INPUT_TAG" "--tag" "--no-tag" + args_bool "$INPUT_PUSH" "--push" "--no-push" + args_bool "$INPUT_CHANGELOG" "--changelog" "--no-changelog" + args_bool "$INPUT_VCS_RELEASE" "--vcs-release" "--no-vcs-release" + args_bool "$INPUT_BUILD" "" "--skip-build" + + force_levels=("prerelease" "patch" "minor" "major") + if [ -z "$INPUT_FORCE" ]; then + : + elif [[ " ${force_levels[*]} " == *"$INPUT_FORCE"* ]]; then + ARGS+=("--$INPUT_FORCE") + else + echo "Error: Input 'force' must be one of: %s" >&2 + echo "${force_levels[@]}" >&2 + fi + + if [ -n "$INPUT_BUILD_METADATA" ]; then + ARGS+=("--build-metadata" "$INPUT_BUILD_METADATA") + fi + + if [ -n "$INPUT_PRERELEASE_TOKEN" ]; then + ARGS+=("--prerelease-token" "$INPUT_PRERELEASE_TOKEN") + fi +} + +add_args_from_env_v9(){ + args_bool "$INPUT_PRERELEASE" "--as-prerelease" "" + args_bool "$INPUT_COMMIT" "--commit" "--no-commit" + args_bool "$INPUT_TAG" "--tag" "--no-tag" + args_bool "$INPUT_PUSH" "--push" "--no-push" + args_bool "$INPUT_CHANGELOG" "--changelog" "--no-changelog" + args_bool "$INPUT_VCS_RELEASE" "--vcs-release" "--no-vcs-release" + args_bool "$INPUT_BUILD" "" "--skip-build" + force_levels=("prerelease" "patch" "minor" "major") if [ -z "$INPUT_FORCE" ]; then - true # do nothing if 'force' input is not set - elif - echo '%s\0' "${force_levels[@]}" | grep -Fxzq "$INPUT_FORCE" - then - ARGS+="--$INPUT_FORCE " + : + elif [[ " ${force_levels[*]} " == *"$INPUT_FORCE"* ]]; then + ARGS+=("--$INPUT_FORCE") else - echo "Error: Input 'force' must be one of: %s" >&2 - echo "${force_levels[@]}" >&2 + echo "Error: Input 'force' must be one of: %s" >&2 + echo "${force_levels[@]}" >&2 fi if [ -n "$INPUT_BUILD_METADATA" ]; then - ARGS+="--build-metadata $INPUT_BUILD_METADATA " + ARGS+=("--build-metadata" "$INPUT_BUILD_METADATA") fi if [ -n "$INPUT_PRERELEASE_TOKEN" ]; then - ARGS+=("--prerelease-token $INPUT_PRERELEASE_TOKEN") + ARGS+=("--prerelease-token" "$INPUT_PRERELEASE_TOKEN") fi - ;; -"8") - ARGS+="$(bool "$INPUT_PRERELEASE" "--as-prerelease " "")" - ARGS+="$(bool "$INPUT_COMMIT" "--commit " "--no-commit ")" - ARGS+="$(bool "$INPUT_PUSH" "--push " "--no-push ")" - ARGS+="$(bool "$INPUT_CHANGELOG" "--changelog " "--no-changelog ")" - ARGS+="$(bool "$INPUT_VCS_RELEASE" "--vcs-release " "--no-vcs-release ")" +} + +add_args_from_env_v8(){ + args_bool "$INPUT_PRERELEASE" "--as-prerelease" "" + args_bool "$INPUT_COMMIT" "--commit" "--no-commit" + args_bool "$INPUT_PUSH" "--push" "--no-push" + args_bool "$INPUT_CHANGELOG" "--changelog" "--no-changelog" + args_bool "$INPUT_VCS_RELEASE" "--vcs-release" "--no-vcs-release" + force_levels=("patch" "minor" "major") if [ -z "$INPUT_FORCE" ]; then - true # do nothing if 'force' input is not set - elif - echo '%s\0' "${force_levels[@]}" | grep -Fxzq "$INPUT_FORCE" - then - args+="--$input_force " + : + elif [[ " ${force_levels[*]} " == *"$INPUT_FORCE"* ]]; then + ARGS+=("--$INPUT_FORCE") else - echo "Error: Input 'force' must be one of: %s" >&2 - echo "${force_levels[@]}" >&2 + echo "Error: Input 'force' must be one of: %s" >&2 + echo "${force_levels[@]}" >&2 fi if [ -n "$INPUT_BUILD_METADATA" ]; then - ARGS+="--build-metadata $INPUT_BUILD_METADATA " + ARGS+=("--build-metadata" "$INPUT_BUILD_METADATA") + fi +} + +detect_version(){ + read -r version< <(semantic-release --version | grep -Po "(?<=version )[0-9]+") + echo "Semantic-Release version: $version" +} + +setup_git(){ + # Set Git details + if ! [ "${INPUT_GIT_COMMITTER_NAME:="-"}" = "-" ]; then + git config user.name "$INPUT_GIT_COMMITTER_NAME" fi - ;; -esac - - -# Change to configured directory -cd "${INPUT_DIRECTORY}" - -# Set Git details -if ! [ "${INPUT_GIT_COMMITTER_NAME:="-"}" = "-" ]; then - git config user.name "$INPUT_GIT_COMMITTER_NAME" -fi -if ! [ "${INPUT_GIT_COMMITTER_EMAIL:="-"}" = "-" ]; then - git config user.email "$INPUT_GIT_COMMITTER_EMAIL" -fi -if ( - [ "${INPUT_GIT_COMMITTER_NAME:="-"}" != "-" ] && - [ "${INPUT_GIT_COMMITTER_EMAIL:="-"}" != "-" ] - ); -then - # Must export this value to the environment for PSR to consume the override - export GIT_COMMIT_AUTHOR="$INPUT_GIT_COMMITTER_NAME <$INPUT_GIT_COMMITTER_EMAIL>" -fi - -# See https://github.com/actions/runner-images/issues/6775#issuecomment-1409268124 -# and https://github.com/actions/runner-images/issues/6775#issuecomment-1410270956 -# git config --system --add safe.directory "*" - -if ( - [[ -n "$INPUT_SSH_PUBLIC_SIGNING_KEY" && - -n "$INPUT_SSH_PRIVATE_SIGNING_KEY" ]] -); -then - echo "SSH Key pair found, configuring signing..." - - # Write keys to disk - mkdir -vp ~/.ssh - echo -e "$INPUT_SSH_PUBLIC_SIGNING_KEY" >>~/.ssh/signing_key.pub - cat ~/.ssh/signing_key.pub - echo -e "$INPUT_SSH_PRIVATE_SIGNING_KEY" >>~/.ssh/signing_key - # DO NOT CAT private key for security reasons - sha256sum ~/.ssh/signing_key - # Ensure read only private key - chmod 400 ~/.ssh/signing_key - - # Enable ssh-agent & add signing key - eval "$(ssh-agent -s)" - ssh-add ~/.ssh/signing_key - - # Create allowed_signers file for git - if [ "${INPUT_GIT_COMMITTER_EMAIL:="-"}" = "-" ]; then - echo >&2 "git_committer_email must be set to use SSH key signing!" - exit 1 + if ! [ "${INPUT_GIT_COMMITTER_EMAIL:="-"}" = "-" ]; then + git config user.email "$INPUT_GIT_COMMITTER_EMAIL" + fi + if [ "${INPUT_GIT_COMMITTER_NAME:="-"}" != "-" ] && [ "${INPUT_GIT_COMMITTER_EMAIL:="-"}" != "-" ]; then + # Must export this value to the environment for PSR to consume the override + export GIT_COMMIT_AUTHOR="$INPUT_GIT_COMMITTER_NAME <$INPUT_GIT_COMMITTER_EMAIL>" + fi + + # See https://github.com/actions/runner-images/issues/6775#issuecomment-1409268124 + # and https://github.com/actions/runner-images/issues/6775#issuecomment-1410270956 + # git config --system --add safe.directory "*" + + if [[ -n "$INPUT_SSH_PUBLIC_SIGNING_KEY" && -n "$INPUT_SSH_PRIVATE_SIGNING_KEY" ]]; then + echo "SSH Key pair found, configuring signing..." + + # Write keys to disk + mkdir -vp ~/.ssh + echo -e "$INPUT_SSH_PUBLIC_SIGNING_KEY" >>~/.ssh/signing_key.pub + cat ~/.ssh/signing_key.pub + echo -e "$INPUT_SSH_PRIVATE_SIGNING_KEY" >>~/.ssh/signing_key + # DO NOT CAT private key for security reasons + sha256sum ~/.ssh/signing_key + # Ensure read only private key + chmod 400 ~/.ssh/signing_key + + # Enable ssh-agent & add signing key + eval "$(ssh-agent -s)" + ssh-add ~/.ssh/signing_key + + # Create allowed_signers file for git + if [ "${INPUT_GIT_COMMITTER_EMAIL:="-"}" = "-" ]; then + echo >&2 "git_committer_email must be set to use SSH key signing!" + exit 1 + fi + touch ~/.ssh/allowed_signers + echo "$INPUT_GIT_COMMITTER_EMAIL $INPUT_SSH_PUBLIC_SIGNING_KEY" >~/.ssh/allowed_signers + + # Configure git for signing + git config gpg.format ssh + git config gpg.ssh.allowedSignersFile ~/.ssh/allowed_signers + git config user.signingKey ~/.ssh/signing_key + git config commit.gpgsign true + git config tag.gpgsign true fi - touch ~/.ssh/allowed_signers - echo "$INPUT_GIT_COMMITTER_EMAIL $INPUT_SSH_PUBLIC_SIGNING_KEY" >~/.ssh/allowed_signers - - # Configure git for signing - git config gpg.format ssh - git config gpg.ssh.allowedSignersFile ~/.ssh/allowed_signers - git config user.signingKey ~/.ssh/signing_key - git config commit.gpgsign true - git config tag.gpgsign true -fi - -# Copy inputs into correctly-named environment variables -export GH_TOKEN="${INPUT_GITHUB_TOKEN}" - -source ~/semantic-release/.venv/bin/activate -semantic-release $INPUT_ROOT_OPTIONS version $ARGS +} + +main(){ + local -a ARGS=() + local version + + detect_version + + case ${version} in + "10") + add_args_from_env_v10;; + "9") + add_args_from_env_v9;; + "8") + add_args_from_env_v8;; + *) + echo "Semantic release version not supported ($version)" >&2 + exit 2 + esac + + setup_git + + # Change to configured directory + cd "${INPUT_DIRECTORY}" + + # Copy inputs into correctly-named environment variables + export GH_TOKEN="${INPUT_GITHUB_TOKEN}" + + # shellcheck disable=SC2086 + semantic-release $INPUT_ROOT_OPTIONS version "${ARGS[@]}" +} + +set -eux +main diff --git a/action.yml b/action.yml index 0bdaa49..850e0e6 100644 --- a/action.yml +++ b/action.yml @@ -149,21 +149,7 @@ runs: shell: bash env: INPUT_PSR_VERSION: ${{ inputs.psr_version }} - run: | - set -eux - if ! command -v python $> /dev/null - then - echo "Python not found." - exit 1 - fi - python -m venv ~/semantic-release/.venv - source ~/semantic-release/.venv/bin/activate - if [ -z "$INPUT_PSR_VERSION" ] - then - pip install python-semantic-release - else - pip install "python-semantic-release==$INPUT_PSR_VERSION" - fi + run: psr_install.sh - name: PSR action script id: semrel shell: bash diff --git a/psr_install.sh b/psr_install.sh new file mode 100755 index 0000000..22761d4 --- /dev/null +++ b/psr_install.sh @@ -0,0 +1,36 @@ +#!/bin/bash + +set -eux + +install_pipx(){ + read -r os_like< <(grep -Po "(?<=ID_LIKE=).*" /etc/os-release) + case ${os_like} in + debian) + apt install pipx;; + fedora) + dnf install pipx;; + *) + echo "Runner OS not supported, BEST EFFORT" + pip install pipx;; + esac +} + +install_psr(){ + if ! command -v pipx &> /dev/null; then + install_pipx + fi + + if [ -z "$INPUT_PSR_VERSION" ]; then + pipx install python-semantic-release + else + pipx install "python-semantic-release==$INPUT_PSR_VERSION" + fi + + pipx ensurepath +} + + +if ! command -v semantic-release &> /dev/null; then + echo "Semantic-release not found, will try to install using pipx" + install_psr +fi