Automerge backed Markdown editor for the Atmosphere
Something went wrong. Try again.
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162import { mkdir, mkdtemp, rename, rm, writeFile } from 'node:fs/promises'import path from 'node:path'import { fileURLToPath } from 'node:url'
/** Stage a complete release before replacing an existing source fixture. */export async function fetchPluginRelease(repository, tag, directory, fetchRelease = fetch) { if (!/^[\w.-]+\/[\w.-]+$/.test(repository || '') || !/^[\w.-]+$/.test(tag || '')) { throw new Error('Expected owner/repo and a pinned release tag') } const response = await fetchRelease(`https://api.github.com/repos/${repository}/releases/tags/${tag}`) if (!response.ok) throw new Error(`GitHub release lookup: HTTP ${response.status}`) const release = await response.json() await mkdir(path.dirname(directory), { recursive: true }) const staged = await mkdtemp(path.join(path.dirname(directory), '.plugin-release-')) const previous = `${staged}-previous` let backedUp = false let published = false try { for (const file of ['manifest.json', 'main.js', 'styles.css']) { const asset = release.assets.find(asset => asset.name === file) if (!asset) { if (file === 'styles.css') continue throw new Error(`Release missing ${file}`) } const download = await fetchRelease(asset.browser_download_url) if (!download.ok) throw new Error(`${file}: HTTP ${download.status}`) const bytes = new Uint8Array(await download.arrayBuffer()) if (file === 'manifest.json') { const manifest = JSON.parse(new TextDecoder().decode(bytes)) if (typeof manifest.id !== 'string' || typeof manifest.version !== 'string') throw new Error('Invalid release manifest') } await writeFile(path.join(staged, file), bytes) } let license for (const name of ['LICENSE', 'LICENSE.txt', 'LICENSE.md']) { const response = await fetchRelease(`https://raw.githubusercontent.com/${repository}/${tag}/${name}`) if (response.ok) { license = await response.text(); break } } if (!license) throw new Error('Release license unavailable; obtain the upstream license before publishing') await writeFile(path.join(staged, 'LICENSE'), license) await writeFile(path.join(staged, 'DOWNLOAD.md'), `# Upstream release\n\nSource: ${release.html_url}\n\nFetched without modification. Not admitted to the catalog until browser validation.\n`) try { await rename(directory, previous); backedUp = true } catch (error) { if (error.code !== 'ENOENT') throw error } try { await rename(staged, directory); published = true } catch (error) { if (backedUp) { await rename(previous, directory); backedUp = false } throw error } } finally { await rm(staged, { recursive: true, force: true }) if (published && backedUp) await rm(previous, { recursive: true, force: true }) }}
if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) { const [repository, tag, fixture] = process.argv.slice(2) if (!/^[\w-]+$/.test(fixture || '')) throw new Error('Usage: node scripts/fetch-plugin-release.mjs owner/repo release-tag fixture-name') const directory = path.resolve(import.meta.dirname, '../public/test-plugins', fixture) await fetchPluginRelease(repository, tag, directory) console.log(`Fetched ${repository}@${tag} into ${directory}`)}