summaryrefslogtreecommitdiffhomepage
path: root/packages/desktop/scripts/finalize-latest-json.ts
blob: 855c6a3878cfa115c7f3fc2645de6259874dac7e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
#!/usr/bin/env bun

import { Buffer } from "node:buffer"
import { $ } from "bun"

const { values } = parseArgs({
  args: Bun.argv.slice(2),
  options: {
    "dry-run": { type: "boolean", default: false },
  },
})

const dryRun = values["dry-run"]

import { parseArgs } from "node:util"

const repo = process.env.GH_REPO
if (!repo) throw new Error("GH_REPO is required")

const releaseId = process.env.OPENCODE_RELEASE
if (!releaseId) throw new Error("OPENCODE_RELEASE is required")

const version = process.env.OPENCODE_VERSION
if (!version) throw new Error("OPENCODE_VERSION is required")

const token = process.env.GH_TOKEN ?? process.env.GITHUB_TOKEN
if (!token) throw new Error("GH_TOKEN or GITHUB_TOKEN is required")

const apiHeaders = {
  Authorization: `token ${token}`,
  Accept: "application/vnd.github+json",
}

const releaseRes = await fetch(`https://api.github.com/repos/${repo}/releases/${releaseId}`, {
  headers: apiHeaders,
})

if (!releaseRes.ok) {
  throw new Error(`Failed to fetch release: ${releaseRes.status} ${releaseRes.statusText}`)
}

type Asset = {
  name: string
  url: string
}

type Release = {
  tag_name?: string
  assets?: Asset[]
}

const release = (await releaseRes.json()) as Release
const assets = release.assets ?? []
const assetByName = new Map(assets.map((asset) => [asset.name, asset]))

const latestAsset = assetByName.get("latest.json")
if (!latestAsset) {
  console.log("latest.json not found, skipping tauri finalization")
  process.exit(0)
}

const latestRes = await fetch(latestAsset.url, {
  headers: {
    Authorization: `token ${token}`,
    Accept: "application/octet-stream",
  },
})

if (!latestRes.ok) {
  throw new Error(`Failed to fetch latest.json: ${latestRes.status} ${latestRes.statusText}`)
}

const latestText = new TextDecoder().decode(await latestRes.arrayBuffer())
const latest = JSON.parse(latestText)
const base = { ...latest }
delete base.platforms

const fetchSignature = async (asset: Asset) => {
  const res = await fetch(asset.url, {
    headers: {
      Authorization: `token ${token}`,
      Accept: "application/octet-stream",
    },
  })

  if (!res.ok) {
    throw new Error(`Failed to fetch signature: ${res.status} ${res.statusText}`)
  }

  return Buffer.from(await res.arrayBuffer()).toString()
}

const entries: Record<string, { url: string; signature: string }> = {}
const add = (key: string, asset: Asset, signature: string) => {
  if (entries[key]) return
  entries[key] = {
    url: `https://github.com/${repo}/releases/download/v${version}/${asset.name}`,
    signature,
  }
}

const targets = [
  { key: "linux-x86_64-deb", asset: "opencode-desktop-linux-amd64.deb" },
  { key: "linux-x86_64-rpm", asset: "opencode-desktop-linux-x86_64.rpm" },
  { key: "linux-aarch64-deb", asset: "opencode-desktop-linux-arm64.deb" },
  { key: "linux-aarch64-rpm", asset: "opencode-desktop-linux-aarch64.rpm" },
  { key: "windows-aarch64-nsis", asset: "opencode-desktop-windows-arm64.exe" },
  { key: "windows-x86_64-nsis", asset: "opencode-desktop-windows-x64.exe" },
  { key: "darwin-x86_64-app", asset: "opencode-desktop-darwin-x64.app.tar.gz" },
  {
    key: "darwin-aarch64-app",
    asset: "opencode-desktop-darwin-aarch64.app.tar.gz",
  },
]

for (const target of targets) {
  const asset = assetByName.get(target.asset)
  if (!asset) continue

  const sig = assetByName.get(`${target.asset}.sig`)
  if (!sig) continue

  const signature = await fetchSignature(sig)
  add(target.key, asset, signature)
}

const alias = (key: string, source: string) => {
  if (entries[key]) return
  const entry = entries[source]
  if (!entry) return
  entries[key] = entry
}

alias("linux-x86_64", "linux-x86_64-deb")
alias("linux-aarch64", "linux-aarch64-deb")
alias("windows-aarch64", "windows-aarch64-nsis")
alias("windows-x86_64", "windows-x86_64-nsis")
alias("darwin-x86_64", "darwin-x86_64-app")
alias("darwin-aarch64", "darwin-aarch64-app")

const platforms = Object.fromEntries(
  Object.keys(entries)
    .sort()
    .map((key) => [key, entries[key]]),
)
const output = {
  ...base,
  platforms,
}

const dir = process.env.RUNNER_TEMP ?? "/tmp"
const file = `${dir}/latest.json`
await Bun.write(file, JSON.stringify(output, null, 2))

const tag = release.tag_name
if (!tag) throw new Error("Release tag not found")

if (dryRun) {
  console.log(`dry-run: wrote latest.json for ${tag} to ${file}`)
  process.exit(0)
}
await $`gh release upload ${tag} ${file} --clobber --repo ${repo}`

console.log(`finalized latest.json for ${tag}`)