diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index ac959c3..659c6f4 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -103,14 +103,14 @@ jobs:
gpg_fingerprint: ${{ steps.import_gpg.outputs.fingerprint }}
steps:
- - uses: actions/create-github-app-token@v2
+ - uses: actions/create-github-app-token@fee1f7d63c2ff003460e3d139729b119787bc349 # v2
id: app-token
with:
app-id: ${{ secrets.LERIAN_STUDIO_MIDAZ_PUSH_BOT_APP_ID }}
private-key: ${{ secrets.LERIAN_STUDIO_MIDAZ_PUSH_BOT_PRIVATE_KEY }}
- name: Checkout repository
- uses: actions/checkout@v6
+ uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6
with:
fetch-depth: 0
token: ${{ steps.app-token.outputs.token }}
@@ -121,7 +121,7 @@ jobs:
git reset --hard origin/${{ github.ref_name }}
- name: Import GPG key
- uses: crazy-max/ghaction-import-gpg@v7
+ uses: crazy-max/ghaction-import-gpg@2dc316deee8e90f13e1a351ab510b4d5bc0c82cd # v7
id: import_gpg
with:
gpg_private_key: ${{ secrets.LERIAN_CI_CD_USER_GPG_KEY }}
@@ -133,7 +133,7 @@ jobs:
git_commit_gpgsign: true
- name: Setup Node.js
- uses: actions/setup-node@v6
+ uses: actions/setup-node@53b83947a5a98c8d113130e565377fae1a50d02f # v6
with:
node-version: '20'
@@ -147,8 +147,13 @@ jobs:
npm install --save-dev \
@semantic-release/exec
+ # ----------------- Snapshot tags before release -----------------
+ - name: Snapshot tags before release
+ id: pre-tags
+ uses: LerianStudio/github-actions-shared-workflows/src/config/release-tag-snapshot@develop
+
- name: Semantic Release
- uses: cycjimmy/semantic-release-action@v6
+ uses: cycjimmy/semantic-release-action@b12c8f6015dc215fe37bc154d4ad456dd3833c90 # v6
id: semantic
continue-on-error: true
with:
@@ -165,17 +170,32 @@ jobs:
GIT_COMMITTER_NAME: ${{ secrets.LERIAN_CI_CD_USER_NAME }}
GIT_COMMITTER_EMAIL: ${{ secrets.LERIAN_CI_CD_USER_EMAIL }}
+ # ----------------- Detect release via git tag -----------------
+ - name: Detect if release was published
+ if: always() && steps.semantic.outcome == 'failure'
+ id: detect-release
+ uses: LerianStudio/github-actions-shared-workflows/src/config/release-tag-check@develop
+ with:
+ previous-tag: ${{ steps.pre-tags.outputs.latest-tag }}
+
# ----------------- Backmerge Fallback -----------------
- name: Backmerge PR fallback
- if: steps.semantic.outcome == 'failure' && steps.semantic.outputs.new_release_published == 'true'
+ if: |
+ always() && steps.semantic.outcome == 'failure' && (
+ steps.semantic.outputs.new_release_published == 'true' ||
+ steps.detect-release.outputs.release-published == 'true'
+ )
uses: LerianStudio/github-actions-shared-workflows/src/config/backmerge-pr@develop
with:
github-token: ${{ steps.app-token.outputs.token }}
source-branch: ${{ github.ref_name }}
- version: ${{ steps.semantic.outputs.new_release_version }}
+ version: ${{ steps.semantic.outputs.new_release_version || steps.detect-release.outputs.release-version }}
- name: Fail if release itself failed
- if: steps.semantic.outcome == 'failure' && steps.semantic.outputs.new_release_published != 'true'
+ if: |
+ always() && steps.semantic.outcome == 'failure' &&
+ steps.semantic.outputs.new_release_published != 'true' &&
+ steps.detect-release.outputs.release-published != 'true'
run: |
echo "::error::Semantic release failed before publishing a new version"
exit 1
diff --git a/src/config/release-tag-check/README.md b/src/config/release-tag-check/README.md
new file mode 100644
index 0000000..da84492
--- /dev/null
+++ b/src/config/release-tag-check/README.md
@@ -0,0 +1,61 @@
+
+
+  |
+ release-tag-check |
+
+
+
+Compares the current latest semver (`v*`) tag against a snapshot captured by [`release-tag-snapshot`](../release-tag-snapshot/) to detect whether a new release was published. This is useful when the release action exits with failure due to post-release steps (e.g., backmerge plugin) but the release itself was successful.
+
+## Inputs
+
+| Input | Description | Required | Default |
+|-------|-------------|----------|---------|
+| `previous-tag` | The tag captured by `release-tag-snapshot` before the release step | Yes | |
+
+## Outputs
+
+| Output | Description |
+|--------|-------------|
+| `release-published` | `true` if a new tag was created since the snapshot, `false` otherwise |
+| `release-version` | The new release version (without `v` prefix), empty if no new release |
+
+## Usage as composite step
+
+```yaml
+- name: Snapshot tags before release
+ id: pre-tags
+ uses: LerianStudio/github-actions-shared-workflows/src/config/release-tag-snapshot@v1.x.x
+
+- name: Semantic Release
+ uses: cycjimmy/semantic-release-action@ # v6
+ id: semantic
+ continue-on-error: true
+ ...
+
+- name: Check if release was published
+ id: detect-release
+ if: always() && steps.semantic.outcome == 'failure'
+ uses: LerianStudio/github-actions-shared-workflows/src/config/release-tag-check@v1.x.x
+ with:
+ previous-tag: ${{ steps.pre-tags.outputs.latest-tag }}
+
+- name: Backmerge PR fallback
+ if: |
+ always() && steps.semantic.outcome == 'failure' && (
+ steps.semantic.outputs.new_release_published == 'true' ||
+ steps.detect-release.outputs.release-published == 'true'
+ )
+ uses: LerianStudio/github-actions-shared-workflows/src/config/backmerge-pr@v1.x.x
+ with:
+ github-token: ${{ steps.app-token.outputs.token }}
+ source-branch: ${{ github.ref_name }}
+ version: ${{ steps.semantic.outputs.new_release_version || steps.detect-release.outputs.release-version }}
+```
+
+## Required permissions
+
+```yaml
+permissions:
+ contents: read
+```
diff --git a/src/config/release-tag-check/action.yml b/src/config/release-tag-check/action.yml
new file mode 100644
index 0000000..b077a9b
--- /dev/null
+++ b/src/config/release-tag-check/action.yml
@@ -0,0 +1,36 @@
+name: Release Tag Check
+description: "Compares the current latest semver tag against a previous snapshot to detect if a new release was published."
+
+inputs:
+ previous-tag:
+ description: The tag captured by release-tag-snapshot before the release step
+ required: true
+
+outputs:
+ release-published:
+ description: "'true' if a new tag was created since the snapshot, 'false' otherwise"
+ value: ${{ steps.check.outputs.release_published }}
+ release-version:
+ description: The new release version (without v prefix), empty if no new release
+ value: ${{ steps.check.outputs.release_version }}
+
+runs:
+ using: composite
+ steps:
+ - name: Check for new release tag
+ id: check
+ shell: bash
+ env:
+ PREVIOUS_TAG: ${{ inputs.previous-tag }}
+ run: |
+ git fetch --tags origin
+ NEW_TAG=$(git tag -l 'v*' --sort=-v:refname | head -1)
+ if [ "$NEW_TAG" != "$PREVIOUS_TAG" ] && [ -n "$NEW_TAG" ]; then
+ echo "release_published=true" >> "$GITHUB_OUTPUT"
+ VERSION="${NEW_TAG#v}"
+ echo "release_version=${VERSION}" >> "$GITHUB_OUTPUT"
+ echo "::notice::Release ${NEW_TAG} was published despite post-release failure"
+ else
+ echo "release_published=false" >> "$GITHUB_OUTPUT"
+ echo "release_version=" >> "$GITHUB_OUTPUT"
+ fi
diff --git a/src/config/release-tag-snapshot/README.md b/src/config/release-tag-snapshot/README.md
new file mode 100644
index 0000000..1b76f93
--- /dev/null
+++ b/src/config/release-tag-snapshot/README.md
@@ -0,0 +1,46 @@
+
+
+  |
+ release-tag-snapshot |
+
+
+
+Captures the latest semver (`v*`) tag before a release step runs. Used together with [`release-tag-check`](../release-tag-check/) to detect whether a new release was published — even when the release action reports failure due to post-release steps (e.g., backmerge).
+
+## Inputs
+
+None.
+
+## Outputs
+
+| Output | Description |
+|--------|-------------|
+| `latest-tag` | The latest `v*` tag before the release step, or `none` if no tags exist |
+
+## Usage as composite step
+
+```yaml
+- name: Snapshot tags before release
+ id: pre-tags
+ uses: LerianStudio/github-actions-shared-workflows/src/config/release-tag-snapshot@v1.x.x
+
+- name: Semantic Release
+ uses: cycjimmy/semantic-release-action@ # v6
+ id: semantic
+ continue-on-error: true
+ ...
+
+- name: Check if release was published
+ id: detect-release
+ if: always() && steps.semantic.outcome == 'failure'
+ uses: LerianStudio/github-actions-shared-workflows/src/config/release-tag-check@v1.x.x
+ with:
+ previous-tag: ${{ steps.pre-tags.outputs.latest-tag }}
+```
+
+## Required permissions
+
+```yaml
+permissions:
+ contents: read
+```
diff --git a/src/config/release-tag-snapshot/action.yml b/src/config/release-tag-snapshot/action.yml
new file mode 100644
index 0000000..8124b4f
--- /dev/null
+++ b/src/config/release-tag-snapshot/action.yml
@@ -0,0 +1,18 @@
+name: Release Tag Snapshot
+description: "Captures the latest semver tag before a release step runs, enabling post-release comparison."
+
+outputs:
+ latest-tag:
+ description: The latest v* tag before the release step (or 'none' if no tags exist)
+ value: ${{ steps.snapshot.outputs.latest_tag }}
+
+runs:
+ using: composite
+ steps:
+ - name: Snapshot latest semver tag
+ id: snapshot
+ shell: bash
+ run: |
+ git fetch --tags origin
+ LATEST_TAG=$(git tag -l 'v*' --sort=-v:refname | head -1)
+ echo "latest_tag=${LATEST_TAG:-none}" >> "$GITHUB_OUTPUT"