Release #17
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Release | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| bump_level: | |
| description: "Version increment level" | |
| type: choice | |
| options: | |
| - patch | |
| - minor | |
| - major | |
| default: minor | |
| workflow_run: | |
| branches: [master] | |
| workflows: ["Tests"] | |
| types: [completed] | |
| permissions: | |
| contents: write | |
| concurrency: | |
| group: ${{ github.workflow }} | |
| cancel-in-progress: true | |
| jobs: | |
| release: | |
| if: > | |
| (github.event_name == 'workflow_run' && github.event.workflow_run.conclusion == 'success') || | |
| github.event_name == 'workflow_dispatch' | |
| runs-on: ubuntu-latest | |
| env: | |
| PACKAGE_JSON: package.json | |
| steps: | |
| - name: Resolve target | |
| id: target | |
| shell: bash | |
| run: | | |
| if [[ "${{ github.event_name }}" == "workflow_run" ]]; then | |
| echo "ref=${{ github.event.workflow_run.head_branch }}" >> "$GITHUB_OUTPUT" | |
| echo "sha=${{ github.event.workflow_run.head_sha }}" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "ref=${{ github.ref_name }}" >> "$GITHUB_OUTPUT" | |
| echo "sha=" >> "$GITHUB_OUTPUT" | |
| fi | |
| - uses: actions/checkout@v4 | |
| with: | |
| ref: ${{ steps.target.outputs.sha || steps.target.outputs.ref }} | |
| fetch-depth: 0 | |
| - name: Establish baseline | |
| id: base | |
| shell: bash | |
| run: | | |
| if [[ -n "${{ steps.target.outputs.sha }}" ]]; then | |
| echo "sha=${{ steps.target.outputs.sha }}" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" | |
| fi | |
| echo "ref=${{ steps.target.outputs.ref }}" >> "$GITHUB_OUTPUT" | |
| - name: Cancel if branch advanced | |
| id: guard | |
| shell: bash | |
| run: | | |
| ref="${{ steps.base.outputs.ref }}" | |
| git fetch origin "$ref" --quiet | |
| remote_sha="$(git rev-parse "origin/$ref")" | |
| base_sha="${{ steps.base.outputs.sha }}" | |
| if [[ "$remote_sha" != "$base_sha" ]]; then | |
| echo "::notice::Remote branch '$ref' advanced from $base_sha to $remote_sha; cancelling release." | |
| echo "changed=true" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "changed=false" >> "$GITHUB_OUTPUT" | |
| fi | |
| - name: Skipping release due to new commits | |
| if: steps.guard.outputs.changed == 'true' | |
| shell: bash | |
| run: | | |
| { | |
| echo "### Release skipped" | |
| echo | |
| echo "The branch has advanced after the workflow was started." | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| - name: Get previous tag | |
| if: steps.guard.outputs.changed == 'false' | |
| id: prev | |
| shell: bash | |
| run: | | |
| prev_tag="$(git describe --tags --abbrev=0 2>/dev/null || true)" | |
| echo "tag=$prev_tag" >> "$GITHUB_OUTPUT" | |
| - name: Read bump level | |
| if: steps.guard.outputs.changed == 'false' | |
| id: bump | |
| shell: bash | |
| run: | | |
| if [[ "${{ github.event_name }}" == "workflow_dispatch" ]]; then | |
| echo "level=${{ inputs.bump_level }}" >> "$GITHUB_OUTPUT" | |
| else | |
| echo "level=patch" >> "$GITHUB_OUTPUT" | |
| fi | |
| echo "Selected bump level: ${{ steps.bump.outputs.level }}" | |
| - name: Bump package.json | |
| if: steps.guard.outputs.changed == 'false' | |
| id: ver | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| if [ ! -f "$PACKAGE_JSON" ]; then | |
| echo "Package file not found at: $PACKAGE_JSON" | |
| exit 1 | |
| fi | |
| current="$(jq -r '.version' "$PACKAGE_JSON")" | |
| if [[ ! "$current" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then | |
| echo "Version '$current' is not SemVer (x.y.z)" | |
| exit 1 | |
| fi | |
| IFS='.' read -r major minor patch <<<"$current" | |
| level="${{ steps.bump.outputs.level }}" | |
| case "$level" in | |
| major) | |
| major=$((major+1)); minor=0; patch=0 | |
| ;; | |
| minor) | |
| minor=$((minor+1)); patch=0 | |
| ;; | |
| patch) | |
| patch=$((patch+1)) | |
| ;; | |
| *) | |
| echo "Unsupported bump level: '$level' (expected: major|minor|patch)" | |
| exit 1 | |
| ;; | |
| esac | |
| new="$major.$minor.$patch" | |
| if [[ "$new" == "$current" ]]; then | |
| echo "Version unchanged; nothing to do." | |
| exit 0 | |
| fi | |
| tmp="$PACKAGE_JSON.tmp" | |
| jq --arg v "$new" '.version=$v' "$PACKAGE_JSON" > "$tmp" | |
| mv "$tmp" "$PACKAGE_JSON" | |
| echo "old=$current" >> "$GITHUB_OUTPUT" | |
| echo "new=$new" >> "$GITHUB_OUTPUT" | |
| - name: Commit and tag | |
| if: steps.guard.outputs.changed == 'false' && steps.ver.outputs.new != '' | |
| id: push | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| echo "pushed=false" >> "$GITHUB_OUTPUT" | |
| git config user.name "github-actions[bot]" | |
| git config user.email "41898282+github-actions[bot]@users.noreply.github.com" | |
| git add "$PACKAGE_JSON" | |
| git commit -m "${{ github.event.repository.name }} ${{ steps.ver.outputs.new }} [release] [skip ci]" | |
| ref="${{ steps.base.outputs.ref }}" | |
| base_sha="${{ steps.base.outputs.sha }}" | |
| git fetch origin "$ref" --quiet | |
| remote_sha="$(git rev-parse "origin/$ref")" | |
| if [[ "$remote_sha" != "$base_sha" ]]; then | |
| echo "::notice::Remote branch '$ref' advanced to $remote_sha; cancelling release." | |
| exit 0 | |
| fi | |
| if ! git push origin "HEAD:${{ steps.target.outputs.ref }}"; then | |
| echo "::notice::Non-fast-forward push rejected; cancelling release." | |
| exit 0 | |
| fi | |
| git tag "v${{ steps.ver.outputs.new }}" | |
| git push origin "v${{ steps.ver.outputs.new }}" | |
| echo "pushed=true" >> "$GITHUB_OUTPUT" | |
| echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT" | |
| - name: Create or update latest release | |
| if: steps.ver.outputs.new != '' && steps.push.outputs.pushed == 'true' | |
| env: | |
| GH_TOKEN: ${{ github.token }} | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| tag="latest" | |
| upper="${{ steps.push.outputs.sha }}" | |
| declare -A since=() | |
| prev_tag="${{ steps.prev.outputs.tag }}" | |
| if [[ -n "${prev_tag:-}" ]]; then | |
| while read -r s; do | |
| [[ -n "$s" ]] && since["$s"]=1 | |
| done < <(git rev-list "${prev_tag}..$upper") | |
| fi | |
| notes="release-notes.md" | |
| date_utc="$(date -u '+%d-%m-%Y')" | |
| cat > "$notes" <<'MD' | |
| This is a rolling release. Whenever all tests pass, it is automatically updated | |
| to point to the latest release tag. | |
| Installation | |
| -------------- | |
| Compatibility: ***Unity 2022.3.12f1 or newer*** | |
| Recommended method: | |
| - Window ⟶ Package Manager ⟶ `+` ⟶ "Install package from git URL" | |
| - Paste the URL (the `release` branch which points at the latest commit with all tests passing) | |
| ```md | |
| https://github.com/apkd/Medicine.git#release | |
| ``` | |
| Latest changes | |
| -------------- | |
| | Author | Release | Commit | SHA | | |
| |---|---|---|---| | |
| MD | |
| limit=12 | |
| count=0 | |
| mapfile -t rel_tags < <(git for-each-ref --sort=creatordate --format='%(refname:short)' refs/tags | grep -E '^v[0-9]+\.[0-9]+\.[0-9]+$' || true) | |
| git log -n 24 "$upper" --pretty=format:%H%x09%s%x09%an%x09%ae | while IFS=$'\t' read -r sha msg author email; do | |
| lc_author="$(printf '%s' "$author" | tr '[:upper:]' '[:lower:]')" | |
| lc_email="$(printf '%s' "$email" | tr '[:upper:]' '[:lower:]')" | |
| if [[ "$lc_author" == "github-actions[bot]" || "$lc_email" == "41898282+github-actions[bot]@users.noreply.github.com" ]]; then | |
| continue | |
| fi | |
| short="${sha:0:7}" | |
| shortlink="[$short](https://github.com/${{ github.repository }}/commit/$sha)" | |
| msg="${msg//|/\\|}" | |
| new_prefix="" | |
| if [[ -n "${since[$sha]:-}" ]]; then | |
| new_prefix="✨ " | |
| fi | |
| info="$(gh api repos/$GITHUB_REPOSITORY/commits/$sha --jq '{email: .commit.author.email, login: (.author.login // .committer.login)}')" | |
| login="$(jq -r '.login // empty' <<<"$info")" | |
| if [[ -n "$login" && "$login" != "null" ]]; then | |
| avatar="https://wsrv.nl/?url=github.com/${login}.png&w=48&h=48&mask=circle&fit=cover&maxage=1w" | |
| else | |
| email_lookup="$(jq -r '.email // empty' <<<"$info" | tr '[:upper:]' '[:lower:]')" | |
| if [[ -n "$email_lookup" ]]; then | |
| hash="$(printf '%s' "$email_lookup" | md5sum | awk '{print $1}')" | |
| base="www.gravatar.com/avatar/$hash?d=identicon" | |
| else | |
| base="www.gravatar.com/avatar/?d=mp" | |
| fi | |
| avatar="https://wsrv.nl/?url=${base}&w=48&h=48&mask=circle&fit=cover&maxage=1w" | |
| fi | |
| author_text="$author" | |
| author_text="${author_text//|/\\|}" | |
| if [[ -n "$login" && "$login" != "null" ]]; then | |
| author_cell="<a href=\"https://github.com/$login\"><img src=\"$avatar\" width=\"24\" height=\"24\" /></a> @$login" | |
| else | |
| author_cell="<img src=\"$avatar\" width=\"24\" height=\"24\" /> $author_text" | |
| fi | |
| version="-" | |
| for t in "${rel_tags[@]}"; do | |
| tcommit="$(git rev-list -n 1 "$t")" | |
| if git merge-base --is-ancestor "$sha" "$tcommit"; then | |
| version="$t" | |
| break | |
| fi | |
| done | |
| version="${new_prefix}${version}" | |
| printf '| %s | %s | %s | %s |\n' "$author_cell" "$version" "$msg" "$shortlink" >> "$notes" | |
| count=$((count+1)) | |
| if [[ $count -ge $limit ]]; then | |
| break | |
| fi | |
| done | |
| printf '| | | [View diff](https://github.com/%s/compare/v%s...latest) | |\n' "$GITHUB_REPOSITORY" "${{ steps.ver.outputs.old }}" >> "$notes" | |
| printf '| | | [More recent commits](https://github.com/%s/commits/master/) | |\n' "$GITHUB_REPOSITORY" >> "$notes" | |
| git tag -fa "$tag" "$upper" -m "Latest rolling release" | |
| git push -f origin "refs/tags/$tag" | |
| if gh release view "$tag" >/dev/null 2>&1; then | |
| gh release edit "$tag" --notes-file "$notes" --title "v${{ steps.ver.outputs.new }} ($date_utc)" --latest | |
| else | |
| gh release create "$tag" --title "v${{ steps.ver.outputs.new }} ($date_utc)" --notes-file "$notes" --target "$upper" --latest | |
| fi | |
| { | |
| echo "## ${{ steps.ver.outputs.new }}" | |
| echo | |
| cat "$notes" | |
| } >> "$GITHUB_STEP_SUMMARY" | |
| - name: Update latest branch | |
| if: steps.ver.outputs.new != '' && steps.push.outputs.pushed == 'true' | |
| shell: bash | |
| run: | | |
| set -euo pipefail | |
| git push -f origin HEAD:refs/heads/release |