From a578fa814e1e93009a3bcbddaa3450cb36a28fb1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Pantale=C3=A3o?= <5808343+bgoncal@users.noreply.github.com> Date: Thu, 23 Apr 2026 13:35:32 +0200 Subject: [PATCH 1/6] Automate github mac releases --- .github/workflows/release_macos.yml | 312 ++++++++++++++++++++++++++++ 1 file changed, 312 insertions(+) create mode 100644 .github/workflows/release_macos.yml diff --git a/.github/workflows/release_macos.yml b/.github/workflows/release_macos.yml new file mode 100644 index 000000000..2a1c74185 --- /dev/null +++ b/.github/workflows/release_macos.yml @@ -0,0 +1,312 @@ +name: Release macOS + +on: + push: + tags: + - 'release/*/*' + +permissions: + actions: read + contents: write + +concurrency: + group: macos-release-${{ github.ref_name }} + cancel-in-progress: false + +jobs: + release: + runs-on: ubuntu-latest + timeout-minutes: 45 + steps: + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + fetch-depth: 0 + + - name: Parse release tag + id: parse_tag + run: | + tag="${GITHUB_REF_NAME}" + + case "$tag" in + release/*/*) ;; + *) + echo "Unexpected tag format: $tag" + exit 1 + ;; + esac + + version="${tag#release/}" + version="${version%%/*}" + build="${tag##*/}" + display_build="${build##*.}" + + echo "tag=$tag" >> "$GITHUB_OUTPUT" + echo "version=$version" >> "$GITHUB_OUTPUT" + echo "build=$build" >> "$GITHUB_OUTPUT" + echo "display_build=$display_build" >> "$GITHUB_OUTPUT" + + - name: Ensure tag points to main + run: | + git fetch origin main + + if ! git branch -r --contains "$GITHUB_SHA" | grep -Eq 'origin/main$'; then + echo "Tag ${GITHUB_REF_NAME} does not point to a commit on origin/main" + exit 1 + fi + + - name: Wait for successful distribute run + id: find_run + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + with: + script: | + const owner = context.repo.owner; + const repo = context.repo.repo; + const workflowId = 'distribute.yml'; + const headSha = context.sha; + const pollIntervalMs = 60 * 1000; + const deadline = Date.now() + (30 * 60 * 1000); + + while (Date.now() < deadline) { + const { data } = await github.request( + 'GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs', + { + owner, + repo, + workflow_id: workflowId, + head_sha: headSha, + branch: 'main', + event: 'push', + per_page: 20, + } + ); + + const runs = [...data.workflow_runs].sort((lhs, rhs) => + new Date(rhs.created_at) - new Date(lhs.created_at) + ); + const successfulRun = runs.find((run) => run.conclusion === 'success'); + + if (successfulRun) { + core.info( + `Using successful Distribute run #${successfulRun.run_number} (${successfulRun.html_url})` + ); + core.setOutput('run_id', String(successfulRun.id)); + core.setOutput('run_url', successfulRun.html_url); + return; + } + + const pendingRun = runs.find((run) => run.status !== 'completed'); + if (pendingRun) { + core.info( + `Waiting for Distribute run #${pendingRun.run_number} (${pendingRun.status})` + ); + await new Promise((resolve) => setTimeout(resolve, pollIntervalMs)); + continue; + } + + const latestRun = runs[0]; + if (latestRun) { + core.setFailed( + `No successful Distribute run found for ${headSha}. Latest run concluded with ${latestRun.conclusion}.` + ); + return; + } + + core.info(`No Distribute run found for ${headSha} yet.`); + await new Promise((resolve) => setTimeout(resolve, pollIntervalMs)); + } + + core.setFailed( + `Timed out waiting for a successful Distribute run for ${headSha} on main.` + ); + + - name: Resolve artifact + id: find_artifact + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + DISTRIBUTE_RUN_ID: ${{ steps.find_run.outputs.run_id }} + with: + script: | + const owner = context.repo.owner; + const repo = context.repo.repo; + const runId = Number(process.env.DISTRIBUTE_RUN_ID); + + const { data } = await github.request( + 'GET /repos/{owner}/{repo}/actions/runs/{run_id}/artifacts', + { + owner, + repo, + run_id: runId, + per_page: 100, + } + ); + + const artifact = data.artifacts.find((candidate) => candidate.name === 'mac-developer-id.zip'); + + if (!artifact) { + core.setFailed(`Artifact mac-developer-id.zip was not found on run ${runId}.`); + return; + } + + core.setOutput('artifact_name', artifact.name); + core.setOutput('artifact_url', artifact.archive_download_url); + + - name: Download release asset + id: download_asset + env: + ARTIFACT_URL: ${{ steps.find_artifact.outputs.artifact_url }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + mkdir -p release-artifact/extracted release-assets + + curl --fail --location \ + --header "Authorization: Bearer ${GITHUB_TOKEN}" \ + --header "Accept: application/vnd.github+json" \ + "${ARTIFACT_URL}" \ + --output release-artifact/mac-developer-id.zip + + unzip -o release-artifact/mac-developer-id.zip -d release-artifact/extracted + + extracted_asset_path="$(find release-artifact/extracted -type f -name 'home-assistant-mac.zip' -print -quit)" + + if [ -z "$extracted_asset_path" ]; then + echo "Expected release asset was not found inside the artifact archive" + exit 1 + fi + + asset_path="release-assets/home-assistant-mac.zip" + cp "$extracted_asset_path" "$asset_path" + + echo "asset_path=$asset_path" >> "$GITHUB_OUTPUT" + echo "asset_name=$(basename "$asset_path")" >> "$GITHUB_OUTPUT" + + - name: Create or update prerelease + id: release + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + TAG_NAME: ${{ steps.parse_tag.outputs.tag }} + RELEASE_NAME: ${{ steps.parse_tag.outputs.version }} (${{ steps.parse_tag.outputs.display_build }}) + with: + script: | + const owner = context.repo.owner; + const repo = context.repo.repo; + const tag = process.env.TAG_NAME; + const releaseName = process.env.RELEASE_NAME; + let release; + + try { + release = ( + await github.request( + 'GET /repos/{owner}/{repo}/releases/tags/{tag}', + { + owner, + repo, + tag, + } + ) + ).data; + + release = ( + await github.request( + 'PATCH /repos/{owner}/{repo}/releases/{release_id}', + { + owner, + repo, + release_id: release.id, + name: releaseName, + target_commitish: context.sha, + prerelease: release.prerelease, + } + ) + ).data; + } catch (error) { + if (error.status !== 404) { + throw error; + } + + release = ( + await github.request( + 'POST /repos/{owner}/{repo}/releases', + { + owner, + repo, + tag_name: tag, + target_commitish: context.sha, + name: releaseName, + prerelease: true, + generate_release_notes: true, + } + ) + ).data; + } + + core.setOutput('release_id', String(release.id)); + core.setOutput('release_url', release.html_url); + core.setOutput('upload_url', release.upload_url); + + - name: Upload macOS asset + uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + ASSET_PATH: ${{ steps.download_asset.outputs.asset_path }} + ASSET_NAME: ${{ steps.download_asset.outputs.asset_name }} + RELEASE_ID: ${{ steps.release.outputs.release_id }} + UPLOAD_URL: ${{ steps.release.outputs.upload_url }} + with: + script: | + const fs = require('fs'); + const owner = context.repo.owner; + const repo = context.repo.repo; + const releaseId = Number(process.env.RELEASE_ID); + const assetPath = process.env.ASSET_PATH; + const assetName = process.env.ASSET_NAME; + const uploadUrl = process.env.UPLOAD_URL.replace( + '{?name,label}', + `?name=${encodeURIComponent(assetName)}` + ); + const data = fs.readFileSync(assetPath); + + const { data: assets } = await github.request( + 'GET /repos/{owner}/{repo}/releases/{release_id}/assets', + { + owner, + repo, + release_id: releaseId, + per_page: 100, + } + ); + + const existingAsset = assets.find((asset) => asset.name === assetName); + if (existingAsset) { + await github.request( + 'DELETE /repos/{owner}/{repo}/releases/assets/{asset_id}', + { + owner, + repo, + asset_id: existingAsset.id, + } + ); + } + + await github.request({ + method: 'POST', + url: uploadUrl, + headers: { + 'content-type': 'application/zip', + 'content-length': data.length, + }, + data, + }); + + - name: Write summary + env: + VERSION: ${{ steps.parse_tag.outputs.version }} + DISPLAY_BUILD: ${{ steps.parse_tag.outputs.display_build }} + DISTRIBUTE_RUN_URL: ${{ steps.find_run.outputs.run_url }} + RELEASE_URL: ${{ steps.release.outputs.release_url }} + run: | + { + echo "## macOS release created" + echo + echo "- Version: ${VERSION} (${DISPLAY_BUILD})" + echo "- Distribution run: ${DISTRIBUTE_RUN_URL}" + echo "- Release: ${RELEASE_URL}" + } >> "$GITHUB_STEP_SUMMARY" From 23e8342b9df9043821ca2fe5ba87b436d8f46483 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Pantale=C3=A3o?= <5808343+bgoncal@users.noreply.github.com> Date: Thu, 23 Apr 2026 13:41:32 +0200 Subject: [PATCH 2/6] Update release_macos.yml --- .github/workflows/release_macos.yml | 31 +++++++++++++++++------------ 1 file changed, 18 insertions(+), 13 deletions(-) diff --git a/.github/workflows/release_macos.yml b/.github/workflows/release_macos.yml index 2a1c74185..a7fd208e7 100644 --- a/.github/workflows/release_macos.yml +++ b/.github/workflows/release_macos.yml @@ -39,11 +39,13 @@ jobs: version="${version%%/*}" build="${tag##*/}" display_build="${build##*.}" + run_number="${display_build}" echo "tag=$tag" >> "$GITHUB_OUTPUT" echo "version=$version" >> "$GITHUB_OUTPUT" echo "build=$build" >> "$GITHUB_OUTPUT" echo "display_build=$display_build" >> "$GITHUB_OUTPUT" + echo "run_number=$run_number" >> "$GITHUB_OUTPUT" - name: Ensure tag points to main run: | @@ -57,12 +59,15 @@ jobs: - name: Wait for successful distribute run id: find_run uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 + env: + EXPECTED_RUN_NUMBER: ${{ steps.parse_tag.outputs.run_number }} with: script: | const owner = context.repo.owner; const repo = context.repo.repo; const workflowId = 'distribute.yml'; const headSha = context.sha; + const expectedRunNumber = Number(process.env.EXPECTED_RUN_NUMBER); const pollIntervalMs = 60 * 1000; const deadline = Date.now() + (30 * 60 * 1000); @@ -83,40 +88,40 @@ jobs: const runs = [...data.workflow_runs].sort((lhs, rhs) => new Date(rhs.created_at) - new Date(lhs.created_at) ); - const successfulRun = runs.find((run) => run.conclusion === 'success'); + const matchingRun = runs.find((run) => run.run_number === expectedRunNumber); - if (successfulRun) { + if (matchingRun?.conclusion === 'success') { core.info( - `Using successful Distribute run #${successfulRun.run_number} (${successfulRun.html_url})` + `Using successful Distribute run #${matchingRun.run_number} (${matchingRun.html_url})` ); - core.setOutput('run_id', String(successfulRun.id)); - core.setOutput('run_url', successfulRun.html_url); + core.setOutput('run_id', String(matchingRun.id)); + core.setOutput('run_url', matchingRun.html_url); return; } - const pendingRun = runs.find((run) => run.status !== 'completed'); - if (pendingRun) { + if (matchingRun && matchingRun.status !== 'completed') { core.info( - `Waiting for Distribute run #${pendingRun.run_number} (${pendingRun.status})` + `Waiting for Distribute run #${matchingRun.run_number} (${matchingRun.status})` ); await new Promise((resolve) => setTimeout(resolve, pollIntervalMs)); continue; } - const latestRun = runs[0]; - if (latestRun) { + if (matchingRun) { core.setFailed( - `No successful Distribute run found for ${headSha}. Latest run concluded with ${latestRun.conclusion}.` + `Distribute run #${matchingRun.run_number} for ${headSha} concluded with ${matchingRun.conclusion}.` ); return; } - core.info(`No Distribute run found for ${headSha} yet.`); + core.info( + `No Distribute run #${expectedRunNumber} found for ${headSha} yet.` + ); await new Promise((resolve) => setTimeout(resolve, pollIntervalMs)); } core.setFailed( - `Timed out waiting for a successful Distribute run for ${headSha} on main.` + `Timed out waiting for successful Distribute run #${expectedRunNumber} for ${headSha} on main.` ); - name: Resolve artifact From abf97add0409aa4c83af87b478ffdcbcca13df3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Pantale=C3=A3o?= <5808343+bgoncal@users.noreply.github.com> Date: Thu, 23 Apr 2026 13:50:08 +0200 Subject: [PATCH 3/6] Update release_macos.yml --- .github/workflows/release_macos.yml | 32 ++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) diff --git a/.github/workflows/release_macos.yml b/.github/workflows/release_macos.yml index a7fd208e7..ceac7e66f 100644 --- a/.github/workflows/release_macos.yml +++ b/.github/workflows/release_macos.yml @@ -68,6 +68,17 @@ jobs: const workflowId = 'distribute.yml'; const headSha = context.sha; const expectedRunNumber = Number(process.env.EXPECTED_RUN_NUMBER); + + if ( + !Number.isFinite(expectedRunNumber) || + !Number.isInteger(expectedRunNumber) + ) { + core.setFailed( + `Invalid EXPECTED_RUN_NUMBER: "${process.env.EXPECTED_RUN_NUMBER}". The release tag must contain a numeric Distribute run number.` + ); + return; + } + const pollIntervalMs = 60 * 1000; const deadline = Date.now() + (30 * 60 * 1000); @@ -162,12 +173,26 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | mkdir -p release-artifact/extracted release-assets + redirect_headers="$(mktemp)" + trap 'rm -f "$redirect_headers"' EXIT - curl --fail --location \ + curl --fail --silent --show-error \ + --dump-header "$redirect_headers" \ --header "Authorization: Bearer ${GITHUB_TOKEN}" \ --header "Accept: application/vnd.github+json" \ "${ARTIFACT_URL}" \ - --output release-artifact/mac-developer-id.zip + --output /dev/null + + artifact_download_url="$(awk 'BEGIN { IGNORECASE = 1 } /^location:/ { sub(/\r$/, "", $2); print $2; exit }' "$redirect_headers")" + + if [ -z "$artifact_download_url" ]; then + echo "Failed to resolve artifact download redirect URL" + exit 1 + fi + + curl --fail --location \ + --output release-artifact/mac-developer-id.zip \ + "$artifact_download_url" unzip -o release-artifact/mac-developer-id.zip -d release-artifact/extracted @@ -184,7 +209,7 @@ jobs: echo "asset_path=$asset_path" >> "$GITHUB_OUTPUT" echo "asset_name=$(basename "$asset_path")" >> "$GITHUB_OUTPUT" - - name: Create or update prerelease + - name: Create release or sync existing release id: release uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: @@ -219,6 +244,7 @@ jobs: release_id: release.id, name: releaseName, target_commitish: context.sha, + // Preserve the current prerelease flag so reruns don't undo a manual promotion. prerelease: release.prerelease, } ) From 52ad7796d37515a726d3529cee86edf351027482 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Pantale=C3=A3o?= <5808343+bgoncal@users.noreply.github.com> Date: Thu, 23 Apr 2026 13:54:51 +0200 Subject: [PATCH 4/6] Update release_macos.yml --- .github/workflows/release_macos.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/release_macos.yml b/.github/workflows/release_macos.yml index ceac7e66f..b5f245e0f 100644 --- a/.github/workflows/release_macos.yml +++ b/.github/workflows/release_macos.yml @@ -74,7 +74,8 @@ jobs: !Number.isInteger(expectedRunNumber) ) { core.setFailed( - `Invalid EXPECTED_RUN_NUMBER: "${process.env.EXPECTED_RUN_NUMBER}". The release tag must contain a numeric Distribute run number.` + `Invalid EXPECTED_RUN_NUMBER: "${process.env.EXPECTED_RUN_NUMBER}". ` + + 'The release tag must contain a numeric Distribute run number.' ); return; } @@ -183,7 +184,10 @@ jobs: "${ARTIFACT_URL}" \ --output /dev/null - artifact_download_url="$(awk 'BEGIN { IGNORECASE = 1 } /^location:/ { sub(/\r$/, "", $2); print $2; exit }' "$redirect_headers")" + artifact_download_url="$( + awk 'BEGIN { IGNORECASE = 1 } /^location:/ { sub(/\r$/, "", $2); print $2; exit }' \ + "$redirect_headers" + )" if [ -z "$artifact_download_url" ]; then echo "Failed to resolve artifact download redirect URL" From ec4cbec8b1591f62e1f31e003924849cb541e1cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Pantale=C3=A3o?= <5808343+bgoncal@users.noreply.github.com> Date: Thu, 23 Apr 2026 14:55:44 +0200 Subject: [PATCH 5/6] Update release_macos.yml --- .github/workflows/release_macos.yml | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/.github/workflows/release_macos.yml b/.github/workflows/release_macos.yml index b5f245e0f..206686156 100644 --- a/.github/workflows/release_macos.yml +++ b/.github/workflows/release_macos.yml @@ -40,18 +40,22 @@ jobs: build="${tag##*/}" display_build="${build##*.}" run_number="${display_build}" + commit_sha="$(git rev-parse "${tag}^{commit}")" echo "tag=$tag" >> "$GITHUB_OUTPUT" echo "version=$version" >> "$GITHUB_OUTPUT" echo "build=$build" >> "$GITHUB_OUTPUT" echo "display_build=$display_build" >> "$GITHUB_OUTPUT" echo "run_number=$run_number" >> "$GITHUB_OUTPUT" + echo "commit_sha=$commit_sha" >> "$GITHUB_OUTPUT" - name: Ensure tag points to main + env: + TARGET_COMMIT_SHA: ${{ steps.parse_tag.outputs.commit_sha }} run: | git fetch origin main - if ! git branch -r --contains "$GITHUB_SHA" | grep -Eq 'origin/main$'; then + if ! git branch -r --contains "$TARGET_COMMIT_SHA" | grep -Eq 'origin/main$'; then echo "Tag ${GITHUB_REF_NAME} does not point to a commit on origin/main" exit 1 fi @@ -61,12 +65,13 @@ jobs: uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 env: EXPECTED_RUN_NUMBER: ${{ steps.parse_tag.outputs.run_number }} + TARGET_COMMIT_SHA: ${{ steps.parse_tag.outputs.commit_sha }} with: script: | const owner = context.repo.owner; const repo = context.repo.repo; const workflowId = 'distribute.yml'; - const headSha = context.sha; + const headSha = process.env.TARGET_COMMIT_SHA; const expectedRunNumber = Number(process.env.EXPECTED_RUN_NUMBER); if ( @@ -92,7 +97,6 @@ jobs: workflow_id: workflowId, head_sha: headSha, branch: 'main', - event: 'push', per_page: 20, } ); @@ -219,12 +223,14 @@ jobs: env: TAG_NAME: ${{ steps.parse_tag.outputs.tag }} RELEASE_NAME: ${{ steps.parse_tag.outputs.version }} (${{ steps.parse_tag.outputs.display_build }}) + TARGET_COMMIT_SHA: ${{ steps.parse_tag.outputs.commit_sha }} with: script: | const owner = context.repo.owner; const repo = context.repo.repo; const tag = process.env.TAG_NAME; const releaseName = process.env.RELEASE_NAME; + const targetCommitish = process.env.TARGET_COMMIT_SHA; let release; try { @@ -247,7 +253,7 @@ jobs: repo, release_id: release.id, name: releaseName, - target_commitish: context.sha, + target_commitish: targetCommitish, // Preserve the current prerelease flag so reruns don't undo a manual promotion. prerelease: release.prerelease, } @@ -265,7 +271,7 @@ jobs: owner, repo, tag_name: tag, - target_commitish: context.sha, + target_commitish: targetCommitish, name: releaseName, prerelease: true, generate_release_notes: true, From 4bc47f546423f48967f95b3acf82bd11bb014115 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bruno=20Pantale=C3=A3o?= <5808343+bgoncal@users.noreply.github.com> Date: Wed, 13 May 2026 09:32:47 -0300 Subject: [PATCH 6/6] Address macOS release workflow review comments --- .github/workflows/release_macos.yml | 67 ++++++++++++----------------- 1 file changed, 27 insertions(+), 40 deletions(-) diff --git a/.github/workflows/release_macos.yml b/.github/workflows/release_macos.yml index 206686156..35bcdf835 100644 --- a/.github/workflows/release_macos.yml +++ b/.github/workflows/release_macos.yml @@ -89,22 +89,30 @@ jobs: const deadline = Date.now() + (30 * 60 * 1000); while (Date.now() < deadline) { - const { data } = await github.request( - 'GET /repos/{owner}/{repo}/actions/workflows/{workflow_id}/runs', + const runs = await github.paginate( + github.rest.actions.listWorkflowRuns, { owner, repo, workflow_id: workflowId, head_sha: headSha, branch: 'main', - per_page: 20, + per_page: 100, + }, + (response, done) => { + const workflowRuns = response.data.workflow_runs ?? []; + + if (workflowRuns.some((run) => run.run_number === expectedRunNumber)) { + done(); + } + + return workflowRuns; } ); - const runs = [...data.workflow_runs].sort((lhs, rhs) => - new Date(rhs.created_at) - new Date(lhs.created_at) - ); - const matchingRun = runs.find((run) => run.run_number === expectedRunNumber); + const matchingRun = [...runs] + .sort((lhs, rhs) => new Date(rhs.created_at) - new Date(lhs.created_at)) + .find((run) => run.run_number === expectedRunNumber); if (matchingRun?.conclusion === 'success') { core.info( @@ -169,40 +177,19 @@ jobs: } core.setOutput('artifact_name', artifact.name); - core.setOutput('artifact_url', artifact.archive_download_url); - name: Download release asset + uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 + with: + run-id: ${{ steps.find_run.outputs.run_id }} + name: ${{ steps.find_artifact.outputs.artifact_name }} + path: release-artifact/extracted + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Prepare release asset id: download_asset - env: - ARTIFACT_URL: ${{ steps.find_artifact.outputs.artifact_url }} - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} run: | - mkdir -p release-artifact/extracted release-assets - redirect_headers="$(mktemp)" - trap 'rm -f "$redirect_headers"' EXIT - - curl --fail --silent --show-error \ - --dump-header "$redirect_headers" \ - --header "Authorization: Bearer ${GITHUB_TOKEN}" \ - --header "Accept: application/vnd.github+json" \ - "${ARTIFACT_URL}" \ - --output /dev/null - - artifact_download_url="$( - awk 'BEGIN { IGNORECASE = 1 } /^location:/ { sub(/\r$/, "", $2); print $2; exit }' \ - "$redirect_headers" - )" - - if [ -z "$artifact_download_url" ]; then - echo "Failed to resolve artifact download redirect URL" - exit 1 - fi - - curl --fail --location \ - --output release-artifact/mac-developer-id.zip \ - "$artifact_download_url" - - unzip -o release-artifact/mac-developer-id.zip -d release-artifact/extracted + mkdir -p release-assets extracted_asset_path="$(find release-artifact/extracted -type f -name 'home-assistant-mac.zip' -print -quit)" @@ -299,11 +286,11 @@ jobs: const releaseId = Number(process.env.RELEASE_ID); const assetPath = process.env.ASSET_PATH; const assetName = process.env.ASSET_NAME; + const assetSize = fs.statSync(assetPath).size; const uploadUrl = process.env.UPLOAD_URL.replace( '{?name,label}', `?name=${encodeURIComponent(assetName)}` ); - const data = fs.readFileSync(assetPath); const { data: assets } = await github.request( 'GET /repos/{owner}/{repo}/releases/{release_id}/assets', @@ -332,9 +319,9 @@ jobs: url: uploadUrl, headers: { 'content-type': 'application/zip', - 'content-length': data.length, + 'content-length': assetSize, }, - data, + data: fs.createReadStream(assetPath), }); - name: Write summary