diff --git a/.github/PULL_REQUEST_TEMPLATE/major_release_request.md b/.github/PULL_REQUEST_TEMPLATE/major_release_request.md index 8ba4b4c..21f6c24 100644 --- a/.github/PULL_REQUEST_TEMPLATE/major_release_request.md +++ b/.github/PULL_REQUEST_TEMPLATE/major_release_request.md @@ -2,20 +2,20 @@ Release Description --> +## ❗ Breaking Changes + + ## 🆕 Features ## 🐞 Fixes - -## ❗ Breaking Changes - diff --git a/.github/PULL_REQUEST_TEMPLATE/minor_release_request.md b/.github/PULL_REQUEST_TEMPLATE/minor_release_request.md index 03665e3..d010cc4 100644 --- a/.github/PULL_REQUEST_TEMPLATE/minor_release_request.md +++ b/.github/PULL_REQUEST_TEMPLATE/minor_release_request.md @@ -4,13 +4,13 @@ ## 🆕 Features ## 🐞 Fixes diff --git a/.github/PULL_REQUEST_TEMPLATE/patch_release_request.md b/.github/PULL_REQUEST_TEMPLATE/patch_release_request.md index 4c61c6a..08cdff6 100644 --- a/.github/PULL_REQUEST_TEMPLATE/patch_release_request.md +++ b/.github/PULL_REQUEST_TEMPLATE/patch_release_request.md @@ -4,7 +4,6 @@ ## 🐞 Fixes diff --git a/.github/PULL_REQUEST_TEMPLATE/prerelease_request.md b/.github/PULL_REQUEST_TEMPLATE/prerelease_request.md index dd8bbba..dc23805 100644 --- a/.github/PULL_REQUEST_TEMPLATE/prerelease_request.md +++ b/.github/PULL_REQUEST_TEMPLATE/prerelease_request.md @@ -2,20 +2,20 @@ Prerelease Description --> +## ❗ Breaking Changes + + ## 🆕 Features ## 🐞 Fixes - -## ❗ Breaking Changes - diff --git a/.github/workflows/i.yml b/.github/workflows/i.yml new file mode 100644 index 0000000..84b6620 --- /dev/null +++ b/.github/workflows/i.yml @@ -0,0 +1,109 @@ +name: Synchronize Pointag + +on: + workflow_call: + +defaults: + run: + shell: bash + +jobs: + synchronize-pointag: + runs-on: ubuntu-22.04 + if: ${{ (github.event_name != 'push' || startsWith(github.event.ref, 'refs/tags/')) && (github.event_name != 'delete' || github.event.ref_type == 'tag') }} + env: + CONTINUE_JOB: 1 + permissions: + contents: write + timeout-minutes: 2 + + steps: + - name: Extract information + id: extract + env: + EVENT_REF: ${{ github.event.ref }} + IS_PUSH_EVENT: ${{ github.event_name == 'push' }} + IS_MISC_EVENT: ${{ github.event_name != 'push' && github.event_name != 'delete' }} + run: | + if [[ $IS_MISC_EVENT == 'true' ]] ; then + echo '::error::The workflow only supports the on.push and the on.delete triggers.' + exit 1 + fi + + if [[ $IS_PUSH_EVENT == 'true' ]] ; then + EVENT_REF="${EVENT_REF#refs/tags/}" + fi + if [[ $EVENT_REF == */* ]] ; then + tag_directory="${EVENT_REF%/*}/" + fi + + version="${EVENT_REF##*/}" + if [[ $version =~ ^v[0-9]$ ]] ; then + echo '::notice::Refusing to overwrite user-initiated update of a pointag.' + echo 'CONTINUE_JOB=0' >> "$GITHUB_ENV" + exit 0 + elif [[ ! $version =~ ^v[0-9]+\.[0-9] ]] ; then + echo '::error::Incorrect version format.' + exit 1 + fi + + major_pointag="$tag_directory${version%%.*}" + echo "tag-directory=$tag_directory" >> "$GITHUB_OUTPUT" + echo "major-pointag=$major_pointag" >> "$GITHUB_OUTPUT" + echo "major-version=$version" >> "$GITHUB_OUTPUT" + + - name: Update or delete pointag + if: ${{ env.CONTINUE_JOB == '1' }} + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + IS_DELETE_EVENT: ${{ github.event_name == 'delete' }} + + MAJOR_POINTAG: ${{ steps.extract.outputs.major-pointag }} + MAJOR_VERSION: ${{ steps.extract.outputs.major-version }} + TAG_DIRECTORY: ${{ steps.extract.outputs.tag-directory }} + + REPOSITORY_URL: ${{ github.server_url }}/${{ github.repository }}.git + run: | + git -c init.defaultBranch=default-branch init + git remote add origin "$REPOSITORY_URL" + git config "http.$REPOSITORY_URL.extraheader" "Authorization: Basic $(echo -n "x-access-token:$GH_TOKEN" | base64 -w0)" + + tags_under_major_version="$(git ls-remote --sort=v:refname --tags origin "refs/tags/$MAJOR_POINTAG*")" + major_pointag_information="$(head -1 <<< "$tags_under_major_version")" + highest_tag_information="$(tail -1 <<< "$tags_under_major_version")" + highest_tag_reference="$(cut -s -f 2 <<< "$highest_tag_information")" + + if [[ -z $major_pointag_information ]] ; then + if [[ $IS_DELETE_EVENT == 'true' ]] ; then + echo '::notice::User-initated deletion of major pointag.' + exit 0 + else + echo "::error::Failed to find any tags under $MAJOR_VERSION." + exit 1 + fi + elif [[ $major_pointag_information == $highest_tag_information ]] ; then + if [[ $highest_tag_reference == "refs/tags/$MAJOR_POINTAG" ]] ; then + echo '::notice::Deleting dangling pointag.' + git push -f origin ":refs/tags/$MAJOR_POINTAG" || true + exit 0 + else + create_pointag='true' + fi + fi + + highest_tag_revision="$(cut -s -f 1 <<< "$highest_tag_information")" + + if [[ $create_pointag != 'true' ]] ; then + major_pointag_revision="$(cut -s -f 1 <<< "$major_pointag_information")" + if [[ $major_pointag_revision == $highest_tag_revision ]] ; then + echo "$MAJOR_POINTAG is already pointing at the highest tag under it." + exit 0 + fi + + echo "Setting $MAJOR_POINTAG to $highest_tag_revision ($highest_tag_reference)." + else + echo "Creating $MAJOR_POINTAG and setting it to $highest_tag_revision ($highest_tag_reference)." + fi + + git fetch --depth 1 origin "$highest_tag_revision" + git push -f origin "FETCH_HEAD:refs/tags/$MAJOR_POINTAG" diff --git a/.github/workflows/run-synchronize-pointag.yml b/.github/workflows/run-synchronize-pointag.yml new file mode 100644 index 0000000..763ee3a --- /dev/null +++ b/.github/workflows/run-synchronize-pointag.yml @@ -0,0 +1,14 @@ +name: Synchronize Pointag + +on: + delete: + + push: + tags: + - '**' + +jobs: + synchronize-pointag: + permissions: + contents: write + uses: ./.github/workflows/i.yml diff --git a/.github/workflows/run-update-major-tag.yml b/.github/workflows/run-update-major-tag.yml deleted file mode 100644 index b2469b2..0000000 --- a/.github/workflows/run-update-major-tag.yml +++ /dev/null @@ -1,16 +0,0 @@ -name: Update v Tag -run-name: | - Update major tag: ${{ github.event_name }}${{ github.event_name == 'push' && (github.event.created && ' created' || ' deleted') || '' }} ${{ github.event.ref }} - -on: - delete: - - push: - tags: - - '**' - -jobs: - update-tag: - permissions: - contents: write - uses: ./.github/workflows/update-major-tag.yml diff --git a/.github/workflows/update-major-tag.yml b/.github/workflows/update-major-tag.yml deleted file mode 100644 index b8d5ffd..0000000 --- a/.github/workflows/update-major-tag.yml +++ /dev/null @@ -1,121 +0,0 @@ -name: Update v Tag -run-name: | - Update major tag: ${{ github.event_name }}${{ github.event_name == 'push' && (github.event.created && ' created' || ' deleted') || '' }} ${{ github.event.ref }} - -on: - workflow_call: - -env: - CONTINUE_WORKFLOW: 1 - TAG_CREATED: ${{ (github.event_name != 'push' && github.event_name != 'delete' || github.event.created) && '1' || '' }} - -jobs: - update-tag: - if: ${{ contains(github.event.ref, '.') && (github.event_name != 'delete' || github.event.ref_type == 'tag') }} - permissions: - contents: write - runs-on: ubuntu-22.04 - timeout-minutes: 2 - - steps: - - name: Extract Major Tag - id: get-major - run: | - tag=${{ github.event.ref }} - if ${{ github.event_name == 'push' }} ; then - tag=${tag#refs/tags/} - fi - if [[ $tag == */* ]] ; then - tag_path=${tag%/*}/ - fi - - version=${tag#**/} - if [[ $version != *.* ]] ; then - echo 'Stopping workflow. Updated tag is a major tag.' - echo 'CONTINUE_WORKFLOW=' >> "$GITHUB_ENV" - exit 0 - elif [[ ! $version =~ ^v[0-9]+\.[0-9] ]] ; then - echo 'Invalid version "'"$version"'".' - exit 1 - fi - - major_tag="$tag_path${version%%.*}" - - echo "tag-path=$tag_path" >> "$GITHUB_OUTPUT" - echo "major-tag=$major_tag" >> "$GITHUB_OUTPUT" - cat << EOF >> "$GITHUB_STEP_SUMMARY" - ## Extracted Data - - Major Tag: \`$major_tag\` - - Tag: \`$tag\` - - Tag Path: $([ -z "$tag_path" ] && echo 'None' || echo "\`$tag_path\`") - - Version: \`$version\` - - ## Operation Logs - EOF - - if ${{ github.event_name == 'push' }} ; then - if ${{ !startsWith(github.event.ref, 'refs/tags/') }} ; then - echo 'Stopping workflow. `${{ github.event.ref }}` is not a tag.' - echo 'CONTINUE_WORKFLOW=' >> "$GITHUB_ENV" - exit 0 - fi - fi - - - name: Checkout Repository - if: ${{ env.CONTINUE_WORKFLOW }} - uses: actions/checkout@v4 - with: - fetch-depth: 0 - ref: ${{ github.event.repository.default_branch }} - - - name: Upgrade Major Tag - if: ${{ env.CONTINUE_WORKFLOW && env.TAG_CREATED }} - run: | - latest_tag_under_major="$(git tag -l '${{ steps.get-major.outputs.major-tag }}.*' | sort -V | tail -1)" - - if [ -z "$latest_tag_under_major" ] ; then - echo '- Exiting. No tags under major.' | tee -a $GITHUB_STEP_SUMMARY - exit 1 - fi - - major_tag_rev="$(git rev-list -1 "refs/tags/${{ steps.get-major.outputs.major-tag }}" || true)" - latest_rev_under_major="$(git rev-list -1 "refs/tags/$latest_tag_under_major")" - - if [ -n major_tag_rev ] && [ "$major_tag_rev" == "$latest_rev_under_major" ] ; then - echo '- Short-circuiting. Major tag `${{ steps.get-major.outputs.major-tag }}`(`'"$major_tag_rev"'`) equals latest tag under major version `'"$latest_tag_under_major"'`(`'"$latest_rev_under_major"'`).' | tee -a "$GITHUB_STEP_SUMMARY" - exit 0 - fi - - echo '- Upgrading major tag `${{ steps.get-major.outputs.major-tag }}`('"$([ -z "$major_tag_rev" ] && echo 'N/A' || echo "\`$major_tag_rev\`")"') to `'"$latest_tag_under_major"'`(`'"$latest_rev_under_major"'`)' | tee -a "$GITHUB_STEP_SUMMARY" - - git tag -f "${{ steps.get-major.outputs.major-tag }}" "$latest_tag_under_major" - git push -f origin "${{ steps.get-major.outputs.major-tag }}" - - - name: Downgrade Major Tag - if: ${{ env.CONTINUE_WORKFLOW && !env.TAG_CREATED }} - run: | - major_tag_rev="$(git rev-list -1 "refs/tags/${{ steps.get-major.outputs.major-tag }}" || true)" - if [ -z $major_tag_rev ] ; then - echo '- Short-circuiting. Major tag doesn'"'"'t exist.' | tee -a "$GITHUB_STEP_SUMMARY" - exit 0 - fi - - tags_under_major="$(git tag -l '${{ steps.get-major.outputs.major-tag }}.*')" - - if [ -z "$tags_under_major" ] ; then - echo '- Deleting major tag. Tag deletion resulted in no tags under major version.' | tee -a "$GITHUB_STEP_SUMMARY" - git push origin :refs/tags/${{ steps.get-major.outputs.major-tag }} - else - latest_tag_under_major=$(echo "$tags_under_major" | sort -V | tail -1) - latest_rev_under_major="$(git rev-list -1 "refs/tags/$latest_tag_under_major")" - - if [ "$major_tag_rev" == "$latest_rev_under_major" ] ; then - echo '- Short-circuiting. Major tag `${{ steps.get-major.outputs.major-tag }}`(`'"$major_tag_rev"'`) equals latest tag under major version `'"$latest_tag_under_major"'`(`'"$latest_rev_under_major"'`).' | tee -a "$GITHUB_STEP_SUMMARY" - exit 0 - fi - - echo '- Downgrading major tag `${{ steps.get-major.outputs.major-tag }}`(`'"$major_tag_rev"'`) to `'"$latest_tag_under_major"'`(`'"$latest_rev_under_major"'`)' | tee -a "$GITHUB_STEP_SUMMARY" - - git tag -f ${{ steps.get-major.outputs.major-tag }} $latest_tag_under_major - git push -f origin ${{ steps.get-major.outputs.major-tag }} - fi diff --git a/LICENSE b/LICENSE index b749846..2a5f961 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT No Attribution -Copyright 2023 Arthri +Copyright 2023, 2024 Arthryxate Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index 8492638..bbfb0b7 100644 --- a/README.md +++ b/README.md @@ -1,12 +1,10 @@ -# update-major-tag -A reusable workflow which automatically updates the major tag(for example, `v1`, `Test.App/v2`) when a new tag is created. +# synchronize-pointag +A reusable workflow which automatically creates and synchronizes pointer tags ("pointags") when tags are created, updated, or deleted. ## Installation -Add a new workflow under `.github/workflows/` with the following contents. +Add a new workflow under `.github/workflows/` with the following contents, ```yml -name: Update v Tag -run-name: | - Update major tag: ${{ github.event_name }}${{ github.event_name == 'push' && (github.event.created && ' created' || ' deleted') || '' }} ${{ github.event.ref }} +name: Synchronize Pointag on: delete: @@ -16,13 +14,30 @@ on: - '**' jobs: - update-tag: + synchronize-pointag: permissions: contents: write - uses: Arthri/update-major-tag/.github/workflows/update-major-tag.yml@v1 - + uses: Arthri/synchronize-pointag/.github/workflows/i.yml@v2 ``` +> [!NOTE] +> `on.push` is used over `on.create` primarily for two reasons: +> 1. `on.create` is noisier because it does not support filtering by tags or branches, and consequently, always triggers when new branches are pushed. +> 1. `on.create` is not triggered by tag updates. +> +> Furthermore, `on.delete` is used because `on.push` is not triggered by tag deletions. Unfortunately, it does not support filtering similar to `on.create` and triggers even when branches are deleted. + ## Usage -1. Create a new tag. For example, `v1.4.2`, `Test.App/v2.5.3`. -1. Expect the workflow to run and create a tag such as `v1` or `Test.App/v1` in a few moments. +1. Create and push a new tag. For example, `v1.4.2`, `Test.App/v2.5.3`. +1. Expect the workflow to run and create a tag such as `v1` or `Test.App/v2` in a few moments. + +A list of tags and their corresponding pointags are provided for reference below. +- `v1.12.0` → `v1` +- `v1.56.8` → `v1` +- `v2.11` → `v2` +- `TagDirectory/v3.4.0` → `TagDirectory/v3` +- `Tag/With/Path/v3.11.0` → `Tag/With/Path/v3` +- `Tag/With/Path/v3.56` → `Tag/With/Path/v3` + +> [!WARNING] +> Although discouraged, pointags may be manually updated. The workflow will detect the update but will terminate and do nothing. diff --git a/docs/Test_List.md b/docs/Test_List.md deleted file mode 100644 index 81a057b..0000000 --- a/docs/Test_List.md +++ /dev/null @@ -1,110 +0,0 @@ -# Test List -A list of steps for testing the workflow's features. - -## Deleting branch skips workflow - -### Steps -1. Create a new branch. -1. Delete the branch. - -### Output -1. Workflow triggers. -1. Workflow skipped. - -## Creating a new tag creates a corresponding major tag - -### Steps -1. Create a new tag in the format `v.[.]`. For example, `v0.1.0`, `v0.2.0`, `v1.0.2`. - -### Output -1. Workflow triggers. -1. Workflow creates a new major tag with a revision matching the tag created earlier. - -## Creating a tag that is not the latest short-circuits - -### Steps -1. Create another tag based on the one created earlier. - -### Output -1. Workflow triggers. -1. Upgrade major tag short-circuits because of equality. - -## Deleting major tag skips workflow - -### Steps -1. Delete the major tag created earlier. - -### Output -1. Workflow triggers. -1. Workflow skipped. - -## Deleting tag while major does not exist short-circuits - -### Steps -1. Delete one of the tags created earlier. - -### Output -1. Workflow triggers. -1. Downgrade major tag short-circuits because major tag doesn't exist. - -## Cleanup - -### Steps -1. Create new tags under one major version. -1. Create another tag based on the latest tag. - -## Deleting tag which results in latest revision not changing short-circuits - -### Steps -1. Delete one of the tags created earlier based on the latest tag. - -### Outputs -1. Workflow triggers. -1. Downgrade major tag short-circuits because the major tag's revision will not change. - -## Deleting latest tag downgrades major tag to the next latest tag - -### Steps -1. Delete the latest tag of one major version. - -### Output -1. Workflow triggers. -1. Downgrade major tag updates major tag to next latest version. - -## Deleting all tags under a major version deletes the major tag - -### Steps -1. Delete all tags under a major version. - -### Output -1. Workflow triggers. -1. Major tag is deleted because no versions exist under that major version. - -## Check extraction of major tag is correct - -### Steps -1. Find logs of the following. - - Creation of a tag through pushing. Example information. - - Major tag: `v0` - - Tag: `v0.1.0` - - Tag Path: None - - Version: `v0.1.0` - - Deletion of a tag through pushing. Example information. - - Major tag: `v1` - - Tag: `v1.2.0` - - Tag Path: None - - Version: `v1.2.0` - - Deletion of a tag through GitHub GUI. Example information. - - Major tag: `a/b/v2` - - Tag: `a/b/v2.3.0` - - Tag Path: `a/b/` - - Version: `v2.3.0` -1. Confirm information is correct. - -### Outputs -N/A - -## Test tags with directories - -### Steps -1. Repeat the previous tests but with tags inside directories. Name one of the directories with one or more periods; for example, `Test.App/v1.2.0` diff --git a/scripts/test.sh b/scripts/test.sh new file mode 100644 index 0000000..9b96dac --- /dev/null +++ b/scripts/test.sh @@ -0,0 +1,278 @@ +set -eo pipefail + +LAST_WORKFLOW_RUN_ID='' + +if ! WORKFLOW_RUN_ID="$(gh run list -L 1 --json databaseId -q '.[].databaseId')" ; then + ec=$? + echo '::error::Failed to probe workflow runs.' + exit "$ec" +else + LAST_WORKFLOW_RUN_ID="$WORKFLOW_RUN_ID" +fi + +function expect_no_workflow_run { + sleep 4 + if ! WORKFLOW_RUN_ID="$(gh run list -L 1 --json databaseId -q '.[].databaseId')" ; then + ec=$? + echo '::error::Failed to probe workflow runs.' + exit "$ec" + elif [[ $LAST_WORKFLOW_RUN_ID != $WORKFLOW_RUN_ID ]] ; then + echo '::error::Expected no workflows to trigger.' + exit 1 + fi +} + +function expect_workflow_conclusion { + sleep 4 + if ! WORKFLOW_RUN_ID="$(gh run list -L 1 --json databaseId -q '.[].databaseId')" ; then + ec=$? + echo '::error::Failed to probe workflow runs.' + exit "$ec" + elif [[ -z $WORKFLOW_RUN_ID || $WORKFLOW_RUN_ID == $LAST_WORKFLOW_RUN_ID ]] ; then + echo '::error::Expected a new workflow run, but no new workflow runs triggered.' + exit 1 + else + LAST_WORKFLOW_RUN_ID="$WORKFLOW_RUN_ID" + echo "Waiting for workflow run $WORKFLOW_RUN_ID to finish..." + gh run watch "$WORKFLOW_RUN_ID" -i 1 > /dev/null + if ! WORKFLOW_CONCLUSION="$(gh run view "$WORKFLOW_RUN_ID" --json conclusion -q .conclusion)" ; then + ec=$? + echo '::error::Failed to retrieve conclusion for the workflow run.' + exit "$ec" + elif [[ $WORKFLOW_CONCLUSION != $1 ]] ; then + echo "::error::Expected the workflow run's conclusion to be \"$1\", but it was instead \"$WORKFLOW_CONCLUSION\"." + exit 1 + fi + fi +} + +function expect_tag_exists { + if [[ -z $(git ls-remote --tags origin "refs/tags/$1") ]] ; then + echo "::error::Expected $1 to exist." + exit 1 + fi +} + +function expect_tag_not_exists { + if [[ -n $(git ls-remote --tags origin "refs/tags/$1") ]] ; then + echo "::error::Expected $1 to not exist." + exit 1 + fi +} + +function expect_tags_equal { + if ! REMOTE_TAGS="$(git ls-remote --tags origin)" ; then + ec=$? + echo '::error::Failed to fetch list of tags from origin.' + exit "$?" + else + local tag1_information + local tag2_information + if ! tag1_information="$(grep $'\t'"refs/tags/$1$" <<< "$REMOTE_TAGS")" ; then + echo "::error::Failed to find $1 on remote." + exit 1 + elif ! tag2_information="$(grep $'\t'"refs/tags/$2$" <<< "$REMOTE_TAGS")" ; then + echo "::error::Failed to find $2 on remote." + exit 1 + elif [[ $(cut -s -d ' ' -f 1 <<< "$tag1_information") != $(cut -s -d ' ' -f 1 <<< "$tag2_information") ]] ; then + echo "::error::Expected $1 and $2 to point at the same revision." + exit 1 + fi + fi +} + + + +# Setup +git switch --detach + + + +echo '::group::Expect a skipped workflow run after deleting branches' + +git push origin @:refs/heads/feature1 +expect_no_workflow_run +sleep 2 +git push origin :refs/heads/feature1 +expect_workflow_conclusion skipped + +echo '::endgroup::' + + + +echo '::group::Expect successful workflow runs with new pointags after pushing new tags.' + +git push origin @:refs/tags/v0.0.5 +expect_workflow_conclusion success +expect_tags_equal v0 v0.0.5 + +git push origin @:refs/tags/v1.1.2 +expect_workflow_conclusion success +expect_tags_equal v1 v1.1.2 + +git push origin @:refs/tags/v2.63.12 +expect_workflow_conclusion success +expect_tags_equal v2 v2.63.12 + +git push origin @:refs/tags/v3.94 +expect_workflow_conclusion success +expect_tags_equal v3 v3.94 + +git push origin @:refs/tags/proj.ect/v4.12.5 +expect_workflow_conclusion success +expect_tags_equal proj.ect/v4 proj.ect/v4.12.5 + +git push origin @:refs/tags/proj.ect/v5.96.1 +expect_workflow_conclusion success +expect_tags_equal proj.ect/v5 proj.ect/v5.96.1 + +git push origin @:refs/tags/proj.ect/v6.13.7 +expect_workflow_conclusion success +expect_tags_equal proj.ect/v6 proj.ect/v6.13.7 + +git push origin @:refs/tags/dir/pro.ject/v7.16.1 +expect_workflow_conclusion success +expect_tags_equal dir/pro.ject/v7 dir/pro.ject/v7.16.1 + +git push origin @:refs/tags/dir/pro.ject/v8.6.23 +expect_workflow_conclusion success +expect_tags_equal dir/pro.ject/v8 dir/pro.ject/v8.6.23 + +git push origin @:refs/tags/dir/pro.ject/v9.4.23 +expect_workflow_conclusion success +expect_tags_equal dir/pro.ject/v9 dir/pro.ject/v9.4.23 + +echo '::endgroup::' + + + +echo '::group::Expect successful but no-op workflow runs after pushing non-highest tags.' +git commit --allow-empty --only -m c2 + +git push origin @:refs/tags/v0.0.4 +expect_workflow_conclusion success +expect_tags_equal v0 v0.0.5 + +git push origin @:refs/tags/v1.0.2 +expect_workflow_conclusion success +expect_tags_equal v1 v1.1.2 + +git push origin @:refs/tags/v2.50.12 +expect_workflow_conclusion success +expect_tags_equal v2 v2.63.12 + +git push origin @:refs/tags/proj.ect/v4.1.2 +expect_workflow_conclusion success +expect_tags_equal proj.ect/v4 proj.ect/v4.12.5 + +git push origin @:refs/tags/proj.ect/v5.23.47 +expect_workflow_conclusion success +expect_tags_equal proj.ect/v5 proj.ect/v5.96.1 + +git push origin @:refs/tags/proj.ect/v6.9.1 +expect_workflow_conclusion success +expect_tags_equal proj.ect/v6 proj.ect/v6.13.7 + +git push origin @:refs/tags/dir/pro.ject/v7.4.7 +expect_workflow_conclusion success +expect_tags_equal dir/pro.ject/v7 dir/pro.ject/v7.16.1 + +git push origin @:refs/tags/dir/pro.ject/v8.4.12 +expect_workflow_conclusion success +expect_tags_equal dir/pro.ject/v8 dir/pro.ject/v8.6.23 + +git push origin @:refs/tags/dir/pro.ject/v9.2.15 +expect_workflow_conclusion success +expect_tags_equal dir/pro.ject/v9 dir/pro.ject/v9.4.23 + +echo '::endgroup::' + + + +echo '::group::Expect a successful but no-op workflow run after deleting a pointag.' + +git push origin :refs/tags/v0 +expect_workflow_conclusion success +expect_tag_not_exists v0 + +git push origin :refs/tags/proj.ect/v4 +expect_workflow_conclusion success +expect_tag_not_exists proj.ect/v4 + +git push origin :refs/tags/dir/pro.ject/v7 +expect_workflow_conclusion success +expect_tag_not_exists dir/pro.ject/v7 + +echo '::endgroup::' + + + +echo '::group::Expect a successful but no-op workflow run after deleting tags with no associated pointags.' + +git push --atomic origin :refs/tags/v0.0.5 :refs/tags/v0.0.4 +expect_workflow_conclusion success +expect_tag_not_exists v0 + +git push --atomic origin :refs/tags/proj.ect/v4.12.5 :refs/tags/proj.ect/v4.1.2 +expect_workflow_conclusion success +expect_tag_not_exists proj.ect/v4 + +git push --atomic origin :refs/tags/dir/pro.ject/v7.16.1 :refs/tags/dir/pro.ject/v7.4.7 +expect_workflow_conclusion success +expect_tag_not_exists dir/pro.ject/v7 + +echo '::endgroup::' + + + +echo '::group::Expect a successful but no-op workflow run after deleting a non-highest tag.' + +git push origin :refs/tags/v1.0.2 +expect_workflow_conclusion success +expect_tags_equal v1 v1.1.2 + +git push origin :refs/tags/proj.ect/v5.23.47 +expect_workflow_conclusion success +expect_tags_equal proj.ect/v5 proj.ect/v5.96.1 + +git push origin :refs/tags/dir/pro.ject/v8.4.12 +expect_workflow_conclusion success +expect_tags_equal dir/pro.ject/v8 dir/pro.ject/v8.6.23 + +echo '::endgroup::' + + + +echo '::group::Expect a successful workflow run which updates the pointag to the next highest tag after deleting the highest tag.' + +git push origin :refs/tags/v2.63.12 +expect_workflow_conclusion success +expect_tags_equal v2 v2.50.12 + +git push origin :refs/tags/proj.ect/v6.13.7 +expect_workflow_conclusion success +expect_tags_equal proj.ect/v6 proj.ect/v6.9.1 + +git push origin :refs/tags/dir/pro.ject/v9.4.23 +expect_workflow_conclusion success +expect_tags_equal dir/pro.ject/v9 dir/pro.ject/v9.2.15 + +echo '::endgroup::' + + + +echo '::group::Expect a successful workflow run which deletes the pointag after deleting every tag under the associated major version.' + +git push origin :refs/tags/v1.1.2 +expect_workflow_conclusion success +expect_tag_not_exists v1 + +git push origin :refs/tags/proj.ect/v5.96.1 +expect_workflow_conclusion success +expect_tag_not_exists proj.ect/v5 + +git push origin :refs/tags/dir/pro.ject/v8.6.23 +expect_workflow_conclusion success +expect_tag_not_exists dir/pro.ject/v8 + +echo '::endgroup::'