diff --git a/README.md b/README.md
index 4b368f4..45611dc 100644
--- a/README.md
+++ b/README.md
@@ -136,6 +136,8 @@ The action supports the following inputs:
Example:
Download all .PNG and .JPEG files: `*.PNG,*.JPEG`
Download release asset with a static name: `myfile-to-download.extension`
+
+ >Downloaded files are verified against the sha256 [asset digest](https://github.blog/changelog/2025-06-03-releases-now-expose-digests-for-release-assets/) reported by the release API. The action fails on a digest mismatch. Assets without a digest (uploaded before April 2025) are downloaded without verification.
**Required:**
*false*
@@ -164,9 +166,11 @@ The action supports the following inputs:
| `node_id` | Release node id |
| `tag_name` | Release tag name |
| `target_commitish` | Target from which the release is created |
+| `name` | Release name |
| `body` | The `body` output from release. It's a multiline output, which is packed in a JSON object.
The output can be used with the `fromJSON` function `(steps..outputs.body)`. |
| `draft` | Shows if the release is a draft release or not (true/false) |
| `prerelease` | Shows if the release is a pre-release (true/false) |
+| `immutable` | Shows if the release is [immutable](https://docs.github.com/en/code-security/concepts/supply-chain-security/immutable-releases) (true/false) - can be `null` on GitHub Enterprise Server versions without immutable releases |
| `created_at` | Shows the release creation date |
| `published_at` | Shows the published date of the release |
| `assets_array_json` | The `assets_array_json` output from release. It's a multiline output, which is packed in a JSON object.
The output can be used with the `fromJSON` function `(steps..outputs.assets_array_json)`. |
diff --git a/action.yml b/action.yml
index 4a36504..a565e7e 100644
--- a/action.yml
+++ b/action.yml
@@ -113,6 +113,9 @@ outputs:
prerelease:
description: 'Shows if the release is a pre-release (true/false)'
value: ${{ steps.release_data.outputs.prerelease }}
+ immutable:
+ description: 'Shows if the release is immutable (true/false)'
+ value: ${{ steps.release_data.outputs.immutable }}
created_at:
description: 'Shows the release creation date'
value: ${{ steps.release_data.outputs.created_at }}
@@ -160,7 +163,7 @@ runs:
gh_api_url="https://api.github.com/repos/${{ inputs.repository }}/releases/tags/${{ inputs.version }}"
fi
- release_json_data=$(curl ${token:+"-H"} ${token:+"Authorization: token ${token}"} "$gh_api_url")
+ release_json_data=$(curl ${token:+"-H"} ${token:+"Authorization: token ${token}"} -H "Accept: application/vnd.github+json" -H "X-GitHub-Api-Version: 2022-11-28" "$gh_api_url")
url=$(jq -r '.url' <<< "$release_json_data")
html_url=$(jq -r '.html_url' <<< "$release_json_data")
@@ -178,6 +181,7 @@ runs:
body_packed_in_json=$(printf '%s' "$body" | jq --raw-input --slurp '.')
draft=$(jq -r '.draft' <<< "$release_json_data")
prerelease=$(jq -r '.prerelease' <<< "$release_json_data")
+ immutable=$(jq -r '.immutable' <<< "$release_json_data")
created_at=$(jq -r '.created_at' <<< "$release_json_data")
published_at=$(jq -r '.published_at' <<< "$release_json_data")
author=$(jq -r '.author' <<< "$release_json_data")
@@ -204,6 +208,7 @@ runs:
echo "body=$body_packed_in_json" >> $GITHUB_OUTPUT
echo "draft=$draft" >> $GITHUB_OUTPUT
echo "prerelease=$prerelease" >> $GITHUB_OUTPUT
+ echo "immutable=$immutable" >> $GITHUB_OUTPUT
echo "created_at=$created_at" >> $GITHUB_OUTPUT
echo "published_at=$published_at" >> $GITHUB_OUTPUT
echo "author_json=$author_packed_in_json" >> $GITHUB_OUTPUT
@@ -242,6 +247,8 @@ runs:
- name: Download release assets
id: release_asset_download
if: ${{ inputs.asset-file != '' }}
+ env:
+ ASSETS_JSON: ${{ steps.release_data.outputs.assets_array_json }}
shell: bash
run: | # shell
echo "::group::Download release artifacts"
@@ -281,9 +288,11 @@ runs:
done
regex_search=${regex_search:1}
- jq -c '.[]' <<< '${{ fromJSON(steps.release_data.outputs.assets_array_json) }}' | while read i; do
+ digest_mismatch=""
+ while IFS= read -r i; do
asset_name=$(jq -r '.name' <<<"$i")
asset_id=$(jq -r '.id' <<<"$i")
+ asset_digest=$(jq -r '.digest // empty' <<<"$i")
should_download=false
if [[ $(echo "$asset_name" | grep -E -o "$regex_search") != "" ]]; then
should_download=true
@@ -294,12 +303,29 @@ runs:
-L \
-H "${auth_header}" \
-H "Accept:application/octet-stream" \
+ -H "X-GitHub-Api-Version: 2022-11-28" \
-o "${{ inputs.asset-output }}/${asset_name}" \
"${download_url}"
+ if [[ "$asset_digest" == sha256:* ]]; then
+ expected_hash="${asset_digest#sha256:}"
+ if [[ "$RUNNER_OS" == "Linux" ]]; then
+ actual_hash=$(sha256sum "${{ inputs.asset-output }}/${asset_name}" | head -c 64)
+ else
+ actual_hash=$(shasum -a 256 "${{ inputs.asset-output }}/${asset_name}" | head -c 64)
+ fi
+ if [[ "$actual_hash" != "$expected_hash" ]]; then
+ digest_mismatch="$digest_mismatch $asset_name"
+ fi
+ fi
fi
- done
+ done < <(printf '%s' "$ASSETS_JSON" | jq -c 'fromjson | .[]')
echo "::$log_token_toggle::"
+ if [[ -n "$digest_mismatch" ]]; then
+ echo "::error title=Asset digest mismatch::Downloaded asset(s) do not match the sha256 digest from the release API:$digest_mismatch"
+ exit 1
+ fi
+
echo "::endgroup::"
- name: show output values
@@ -322,6 +348,7 @@ runs:
echo "INFO: Release body ${{ fromJSON(steps.release_data.outputs.body) }}"
echo "INFO: Release draft ${{ steps.release_data.outputs.draft }}"
echo "INFO: Release prerelease ${{ steps.release_data.outputs.prerelease }}"
+ echo "INFO: Release immutable ${{ steps.release_data.outputs.immutable }}"
echo "INFO: Release created_at ${{ steps.release_data.outputs.created_at }}"
echo "INFO: Release published_at ${{ steps.release_data.outputs.published_at }}"
echo "INFO: Release assets_array_json ${{ fromJSON(steps.release_data.outputs.assets_array_json) }}"