mirror of
https://github.com/stablyai/orca.git
synced 2026-09-21 16:02:20 +00:00
electron-builder --publish always was creating a public GitHub release as soon as the first platform uploaded, so /releases/latest could serve a missing Windows exe. Keep the main-repo publisher on draft, pin draft creation to the tag commit, re-draft immediately if anything flips public, and refuse mac publish after the parent cut is cancelled.
117 lines
3.0 KiB
JavaScript
117 lines
3.0 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
import { pathToFileURL } from 'node:url'
|
|
|
|
const API_VERSION = '2022-11-28'
|
|
|
|
function githubHeaders(token) {
|
|
return {
|
|
Accept: 'application/vnd.github+json',
|
|
Authorization: `Bearer ${token}`,
|
|
'X-GitHub-Api-Version': API_VERSION
|
|
}
|
|
}
|
|
|
|
async function githubJson(fetchImpl, url, token, options = {}) {
|
|
const res = await fetchImpl(url, {
|
|
...options,
|
|
headers: {
|
|
...githubHeaders(token),
|
|
...options.headers
|
|
}
|
|
})
|
|
if (!res.ok) {
|
|
const body = await res.text().catch(() => '')
|
|
throw new Error(`GitHub request failed ${res.status} ${res.statusText}: ${body.slice(0, 300)}`)
|
|
}
|
|
return res.json()
|
|
}
|
|
|
|
export function matchingDesktopReleases(releases, tag) {
|
|
const version = tag.startsWith('v') ? tag.slice(1) : tag
|
|
return (releases ?? []).filter((release) => {
|
|
const tagName = release?.tag_name
|
|
const name = release?.name
|
|
return tagName === tag || tagName === version || name === tag || name === version
|
|
})
|
|
}
|
|
|
|
export async function restorePublishedDesktopReleasesToDraft({
|
|
repo,
|
|
tag,
|
|
token,
|
|
fetchImpl = fetch,
|
|
log = console.log
|
|
}) {
|
|
if (!repo) {
|
|
throw new Error('repo is required')
|
|
}
|
|
if (!tag) {
|
|
throw new Error('tag is required')
|
|
}
|
|
if (!token) {
|
|
throw new Error('token is required')
|
|
}
|
|
|
|
const releases = await githubJson(
|
|
fetchImpl,
|
|
`https://api.github.com/repos/${repo}/releases?per_page=100`,
|
|
token
|
|
)
|
|
if (!Array.isArray(releases)) {
|
|
throw new Error(`GitHub releases response for ${repo} was not an array`)
|
|
}
|
|
|
|
const matches = matchingDesktopReleases(releases, tag)
|
|
if (matches.length === 0) {
|
|
throw new Error(`No GitHub release named ${tag} was found after artifact upload`)
|
|
}
|
|
|
|
const restored = []
|
|
for (const release of matches) {
|
|
if (release?.draft === true) {
|
|
continue
|
|
}
|
|
if (!Number.isInteger(release.id)) {
|
|
throw new Error(`Release ${tag} is missing a GitHub release id`)
|
|
}
|
|
const patched = await githubJson(
|
|
fetchImpl,
|
|
`https://api.github.com/repos/${repo}/releases/${release.id}`,
|
|
token,
|
|
{
|
|
method: 'PATCH',
|
|
body: JSON.stringify({ draft: true, make_latest: 'false' })
|
|
}
|
|
)
|
|
log(`Restored GitHub release ${release.id} (${release.tag_name}) to draft.`)
|
|
restored.push(patched)
|
|
}
|
|
return restored
|
|
}
|
|
|
|
async function main() {
|
|
const tag = process.argv[2]
|
|
const token = process.env.GH_TOKEN || process.env.GITHUB_TOKEN
|
|
const repo = process.env.GITHUB_REPOSITORY || 'stablyai/orca'
|
|
const restored = await restorePublishedDesktopReleasesToDraft({
|
|
repo,
|
|
tag,
|
|
token,
|
|
log: (message) => console.error(message)
|
|
})
|
|
if (restored.length > 0) {
|
|
console.error(
|
|
`::error::Release ${tag} was published during artifact upload. Restored ${restored.length} release(s) to draft so /releases/latest does not serve partial assets.`
|
|
)
|
|
process.exit(1)
|
|
}
|
|
}
|
|
|
|
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
main().catch((error) => {
|
|
console.error(error.message)
|
|
process.exit(1)
|
|
})
|
|
}
|