Skip to content

Commit f9304c8

Browse files
committed
workflow and verify assets updated by copilot
1 parent 9d96e62 commit f9304c8

File tree

2 files changed

+116
-88
lines changed

2 files changed

+116
-88
lines changed

.github/workflows/release.yml

Lines changed: 49 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
# .github/workflows/release.yml
2-
31
# Need to write to repo contents to upload the app to GitHub Release
42
# See: https://www.electronforge.io/config/publishers/github#authentication
53
permissions:
@@ -30,9 +28,6 @@ jobs:
3028
{ name: "macos", image: "macos-latest" },
3129
]
3230
runs-on: ${{ matrix.os.image }}
33-
# env:
34-
# CSC_LINK: ${{ secrets.CSC_LINK }}
35-
# CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
3631
steps:
3732
- name: Github checkout
3833
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
@@ -55,35 +50,6 @@ jobs:
5550
if: contains(matrix.os.name, 'macos')
5651
- run: npm rebuild @rollup/rollup-win32-x64-msvc || true
5752
if: contains(matrix.os.name, 'windows')
58-
# - name: add macos cert
59-
# if: contains(matrix.os.name, 'macos') && secrets.MACOS_CERT_P12
60-
# env:
61-
# MACOS_CERT_P12: ${{ secrets.MACOS_CERT_P12 }}
62-
# MACOS_CERT_PASSWORD: ${{ secrets.MACOS_CERT_PASSWORD }}
63-
# run: chmod +x tools/add-macos-cert.sh && . ./tools/add-macos-cert.sh
64-
# Windows only
65-
# - name: Set up certificate
66-
# if: contains(matrix.os.name, 'windows')
67-
# run: |
68-
# echo "${{ secrets.SM_CLIENT_CERT_FILE_B64 }}" | base64 --decode > /d/Certificate_pkcs12.p12
69-
# shell: bash
70-
# - name: Set variables
71-
# if: contains(matrix.os.name, 'windows')
72-
# id: variables
73-
# run: |
74-
# echo "SM_HOST=${{ secrets.SM_HOST }}" >> "$GITHUB_ENV"
75-
# echo "SM_API_KEY=${{ secrets.SM_API_KEY }}" >> "$GITHUB_ENV"
76-
# echo "SM_CLIENT_CERT_FILE=D:\\Certificate_pkcs12.p12" >> "$GITHUB_ENV"
77-
# echo "SM_CLIENT_CERT_PASSWORD=${{ secrets.SM_CLIENT_CERT_PASSWORD }}" >> "$GITHUB_ENV"
78-
# shell: bash
79-
# - name: Code signing with Software Trust Manager
80-
# if: contains(matrix.os.name, 'windows')
81-
# uses: digicert/[email protected]
82-
# - name: Sync certificate (Windows)
83-
# if: contains(matrix.os.name, 'windows')
84-
# run: |
85-
# smctl windows certsync --keypair-alias=${{ secrets.DIGICERT_KEYPAIR_ALIAS }}
86-
# shell: bash
8753
# Publish (all platforms)
8854
- name: Publish app
8955
env:
@@ -93,9 +59,51 @@ jobs:
9359
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
9460
APPLE_ID: ${{ secrets.APPLE_ID }}
9561
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
96-
# CSC_LINK: ${{ secrets.CSC_LINK }}
97-
# CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
98-
run: npm run publish
62+
run: |
63+
set -e
64+
echo "=== Running publish (npm run publish) ==="
65+
npm run publish 2>&1 | sed -n '1,500p'
66+
shell: bash
67+
68+
- name: Wait for GitHub to register release uploads
69+
# Some publishers upload files asynchronously; wait and then list assets
70+
env:
71+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
72+
run: |
73+
set -e
74+
VERSION=$(node -e "console.log(require('./package.json').version)")
75+
TAG="v${VERSION}"
76+
echo "Waiting for GitHub to register release ${TAG} assets..."
77+
# Poll releases/tags endpoint for up to 90s (9 attempts)
78+
attempts=0
79+
max=9
80+
sleep_interval=10
81+
while [ $attempts -lt $max ]; do
82+
echo "Attempt $((attempts+1))/${max}..."
83+
# Use the tag endpoint to retrieve the release by tag
84+
resp=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" -H "Accept: application/vnd.github.v3+json" "https://api.github.com/repos/${{ github.repository }}/releases/tags/${TAG}" || true)
85+
if echo "$resp" | grep -q "\"message\": \"Not Found\""; then
86+
echo "Release ${TAG} not found yet."
87+
else
88+
echo "Release found; printing release summary:"
89+
echo "$resp" | jq -r '. | {name: .name, tag_name: .tag_name, draft: .draft, published_at: .published_at, html_url: .html_url, assets_count: .assets | length}'
90+
echo "Assets list (name | size | url):"
91+
echo "$resp" | jq -r '.assets[] | "\(.name) | \(.size) | \(.browser_download_url)"' || true
92+
assets_count=$(echo "$resp" | jq '.assets | length')
93+
if [ "$assets_count" -gt 0 ]; then
94+
echo "Assets are present (count=$assets_count)"
95+
break
96+
else
97+
echo "No assets yet; will retry."
98+
fi
99+
fi
100+
attempts=$((attempts+1))
101+
sleep $sleep_interval
102+
done
103+
if [ $attempts -eq $max ]; then
104+
echo "Warning: Assets did not appear within the expected time. The verify job may fail."
105+
fi
106+
shell: bash
99107

100108
verify-assets:
101109
name: Verify Release Assets
@@ -116,4 +124,7 @@ jobs:
116124
- name: Verify all release assets are uploaded
117125
env:
118126
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
119-
run: node scripts/verify-release-assets.js
127+
run: |
128+
echo "Running release asset verification..."
129+
node scripts/verify-release-assets.js
130+
shell: bash

scripts/verify-release-assets.js

Lines changed: 67 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
const fs = require("fs");
44
const path = require("path");
5+
const fetch = require("node-fetch");
56

67
/**
78
* Verifies that all expected binary assets are present in the GitHub release
@@ -11,6 +12,10 @@ async function verifyReleaseAssets() {
1112
try {
1213
// Read version from package.json
1314
const packagePath = path.join(__dirname, "..", "package.json");
15+
if (!fs.existsSync(packagePath)) {
16+
console.error("❌ package.json not found at", packagePath);
17+
process.exit(1);
18+
}
1419
const packageJson = JSON.parse(fs.readFileSync(packagePath, "utf8"));
1520
const version = packageJson.version;
1621

@@ -28,10 +33,9 @@ async function verifyReleaseAssets() {
2833

2934
const inGitHubActions = process.env.GITHUB_ACTIONS === "true";
3035

31-
// ✅ Skip token validation in GitHub Actions (since built-in token is limited)
3236
if (!inGitHubActions) {
3337
console.log("🔐 Checking GITHUB_TOKEN permissions...");
34-
38+
// Validate token by calling /user
3539
const userCheck = await fetch("https://api.github.com/user", {
3640
headers: {
3741
Authorization: `token ${token}`,
@@ -52,48 +56,40 @@ async function verifyReleaseAssets() {
5256
console.log(`✅ Authenticated as: ${userData.login}`);
5357
} else {
5458
console.log("🏃 Running inside GitHub Actions — no user authentication check needed");
59+
// quick repo check to ensure token can access repo
60+
const appCheck = await fetch(`https://api.github.com/repos/${owner}/${repo}`, {
61+
headers: {
62+
Authorization: `token ${token}`,
63+
Accept: "application/vnd.github.v3+json",
64+
"User-Agent": "alifullstack-release-verifier",
65+
},
66+
});
5567

56-
// Test API access by fetching org/user info
57-
try {
58-
const appCheck = await fetch(`https://api.github.com/repos/${owner}/${repo}`, {
59-
headers: {
60-
Authorization: `token ${token}`,
61-
Accept: "application/vnd.github.v3+json",
62-
"User-Agent": "alifullstack-release-verifier",
63-
},
64-
});
65-
66-
if (!appCheck.ok) {
67-
const body = await appCheck.text();
68-
console.error("❌ Token authentication failed!");
69-
console.error(`Status: ${appCheck.status} ${appCheck.statusText}`);
70-
console.error(`Response body: ${body}`);
71-
process.exit(1);
72-
}
73-
74-
const repoData = await appCheck.json();
75-
console.log(`✅ Token authenticated for repository: ${repoData.full_name}`);
76-
} catch (error) {
77-
console.error("❌ Error testing token authentication:", error.message);
68+
if (!appCheck.ok) {
69+
const body = await appCheck.text();
70+
console.error("❌ Token authentication failed on repo check!");
71+
console.error(`Status: ${appCheck.status} ${appCheck.statusText}`);
72+
console.error(`Response body: ${body}`);
7873
process.exit(1);
7974
}
75+
76+
const repoData = await appCheck.json();
77+
console.log(`✅ Token authenticated for repository: ${repoData.full_name}`);
8078
}
8179

82-
// --- Fetch releases with retry logic ---
8380
const tagName = `v${version}`;
84-
const maxRetries = 5;
81+
const maxRetries = 8;
8582
const baseDelay = 10000; // 10 seconds
8683
let release = null;
8784
let lastError = null;
8885

86+
// Try to fetch the release by tag name. This avoids scanning the entire releases list.
8987
for (let attempt = 1; attempt <= maxRetries; attempt++) {
9088
try {
91-
console.log(
92-
`📡 Attempt ${attempt}/${maxRetries}: Fetching releases to find: ${tagName}`,
93-
);
89+
console.log(`📡 Attempt ${attempt}/${maxRetries}: Fetching release by tag: ${tagName}`);
9490

95-
const allReleasesUrl = `https://api.github.com/repos/${owner}/${repo}/releases`;
96-
const response = await fetch(allReleasesUrl, {
91+
const releaseUrl = `https://api.github.com/repos/${owner}/${repo}/releases/tags/${tagName}`;
92+
const response = await fetch(releaseUrl, {
9793
headers: {
9894
Authorization: `token ${token}`,
9995
Accept: "application/vnd.github.v3+json",
@@ -102,29 +98,36 @@ async function verifyReleaseAssets() {
10298
});
10399

104100
if (!response.ok) {
105-
console.error(`❌ GitHub API error: ${response.status} ${response.statusText}`);
106-
const errorBody = await response.text();
107-
console.error(`Response Body: ${errorBody}`);
108-
throw new Error(`GitHub API returned ${response.status}`);
109-
}
110-
111-
const allReleases = await response.json();
112-
113-
const releaseExists = allReleases.some((r) => r.tag_name === tagName);
114-
if (!releaseExists) {
115-
console.warn(`⚠️ Release ${tagName} not found. Retrying...`);
101+
const body = await response.text();
102+
console.warn(`⚠️ GitHub API returned ${response.status} ${response.statusText}`);
103+
console.warn("Response body:", body);
104+
if (response.status === 404) {
105+
console.warn(`⚠️ Release ${tagName} not found (404). Will retry.`);
106+
} else {
107+
console.warn("⚠️ Non-404 response; will retry after delay.");
108+
}
116109
if (attempt < maxRetries) {
117110
const delay = baseDelay * attempt;
118111
console.log(`⏳ Waiting ${delay / 1000}s before retry...`);
119112
await new Promise((r) => setTimeout(r, delay));
113+
continue;
114+
} else {
115+
throw new Error(`Failed to fetch release: ${response.status}`);
120116
}
121-
continue;
122117
}
123118

124-
release = allReleases.find((r) => r.tag_name === tagName);
125-
console.log(
126-
`✅ Found release: ${release.tag_name} (${release.draft ? "DRAFT" : "PUBLISHED"})`,
127-
);
119+
release = await response.json();
120+
121+
console.log(`✅ Found release: ${release.tag_name} (${release.draft ? "DRAFT" : "PUBLISHED"})`);
122+
// If release exists but has zero assets, wait and retry (registrations can be delayed)
123+
const assets = release.assets || [];
124+
console.log(`📦 Found ${assets.length} assets in release ${tagName}`);
125+
if (assets.length === 0 && attempt < maxRetries) {
126+
const delay = baseDelay * attempt;
127+
console.log(`⚠️ No assets present yet. Waiting ${delay / 1000}s before retry...`);
128+
await new Promise((r) => setTimeout(r, delay));
129+
continue;
130+
}
128131
break;
129132
} catch (err) {
130133
lastError = err;
@@ -147,6 +150,7 @@ async function verifyReleaseAssets() {
147150

148151
console.log(`📦 Found ${assets.length} assets in release ${tagName}`);
149152
console.log(`📄 Release status: ${release.draft ? "DRAFT" : "PUBLISHED"}`);
153+
console.log("");
150154

151155
// --- Define expected assets ---
152156
const normalizeVersionForPlatform = (version, platform) => {
@@ -179,14 +183,27 @@ async function verifyReleaseAssets() {
179183

180184
const actualAssets = assets.map((a) => a.name);
181185
console.log("📋 Actual assets:");
182-
actualAssets.forEach((a) => console.log(` - ${a}`));
186+
if (actualAssets.length === 0) {
187+
console.log("(none)");
188+
} else {
189+
actualAssets.forEach((a) => console.log(` - ${a}`));
190+
}
183191
console.log("");
184192

185193
// --- Compare assets ---
186194
const missingAssets = expectedAssets.filter((a) => !actualAssets.includes(a));
187195
if (missingAssets.length > 0) {
188196
console.error("❌ VERIFICATION FAILED! Missing assets:");
189197
missingAssets.forEach((a) => console.error(` - ${a}`));
198+
console.error("");
199+
// For debugging, emit the full release JSON to help identify naming differences
200+
console.error("🔎 Full release JSON preview (first 2000 chars):");
201+
try {
202+
const releaseJson = JSON.stringify(release, null, 2);
203+
console.error(releaseJson.substring(0, 2000));
204+
} catch (_) {
205+
// ignore
206+
}
190207
process.exit(1);
191208
}
192209

@@ -206,10 +223,10 @@ async function verifyReleaseAssets() {
206223
console.log(` Published: ${release.published_at}`);
207224
console.log(` URL: ${release.html_url}`);
208225
} catch (error) {
209-
console.error("❌ Error verifying release assets:", error.message);
226+
console.error("❌ Error verifying release assets:", error && error.message ? error.message : error);
210227
process.exit(1);
211228
}
212229
}
213230

214231
// Run the verification
215-
verifyReleaseAssets();
232+
verifyReleaseAssets();

0 commit comments

Comments
 (0)