OverviewFilesCommitsIssuesDocsReleasesPeers

pear-git commits / e06e180

Arch-specific archive downloads for pgh app install
Full release branches can be large (1.4 GB for hyper-tps with Godot binaries + node_modules). The install should only download what the current platform needs. Changes: - generateArchive(): accepts opts.arch — passes path filter to git archive so only by-arch/{host}/ + root launcher files are included - Archive route: accepts ?arch= query param for arch-specific tarballs - buildArchiveUrl(): auto-detects host and adds ?arch= to URL - Archive file naming: release-linux-x64.tar.gz (arch-specific cache) For a hyper-tps release with by-arch layout, this reduces the download from 1.4 GB (full) to ~200 MB (platform-specific). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
e06e180b63dade1d0dbb4e1c3bde5896dcd7e85f
by plankatron <fleeky@gmail.com> · 2026-04-07T12:50:50.000Z

Changed files (3) +22 -10

lib/archive.js +12 -4
……@@ -41,7 +41,7 @@ export function getArchivePath (archiveDir, repoName, format) {
4141 * @param {string} format - 'tar.gz' or 'zip'
4242 * @returns {Promise<{ path: string, size: number, format: string, commitHash: string }>}
4343 */
44export async function generateArchive (bareRepoPath, repoName, archiveDir, format) {
44export async function generateArchive (bareRepoPath, repoName, archiveDir, format, opts = {}) {
4545 // 1. Validate format
4646 if (!VALID_FORMATS.includes(format)) {
4747 throw new Error(`Invalid archive format "${format}". Must be one of: ${VALID_FORMATS.join(', ')}`)
……@@ -62,15 +62,23 @@ export async function generateArchive (bareRepoPath, repoName, archiveDir, forma
6262 await fs.promises.mkdir(outDir, { recursive: true })
6363
6464 // 4. Run git archive
65 const outFile = path.join(outDir, `release.${format}`)
65 const archSuffix = opts.arch ? `-${opts.arch}` : ''
66 const outFile = path.join(outDir, `release${archSuffix}.${format}`)
6667
67 await execGit(bareRepoPath, [
68 const archiveArgs = [
6869 'archive',
6970 `--format=${format}`,
7071 `--prefix=${repoName}/`,
7172 '-o', outFile,
7273 'release'
73 ])
74 ]
75
76 // Arch-specific: only include by-arch/{host}/ + root launcher files
77 if (opts.arch) {
78 archiveArgs.push('--', `by-arch/${opts.arch}/`, 'index.js', 'package.json', 'version.txt', 'daemon/')
79 }
80
81 await execGit(bareRepoPath, archiveArgs)
7482
7583 // 5. Get file size and return metadata
7684 const stat = await fs.promises.stat(outFile)
lib/cli/commands/app.js +3 -2
……@@ -571,9 +571,10 @@ async function appRemove (name, flags) {
571571 * @param {string} repoName - The repository name
572572 * @returns {string} Full archive URL
573573 */
574export function buildArchiveUrl (serverUrl, repoName) {
574export function buildArchiveUrl (serverUrl, repoName, opts = {}) {
575575 const base = serverUrl.replace(/\/$/, '')
576 return `${base}/tabertron/${repoName}/archive/release.tar.gz`
576 const arch = opts.arch || detectHost()
577 return `${base}/tabertron/${repoName}/archive/release.tar.gz?arch=${arch}`
577578}
578579
579580/**
lib/routes/archive.js +7 -4
……@@ -81,10 +81,13 @@ async function handleArchiveDownload (req, res, server) {
8181 }
8282 }
8383
84 // 4. Resolve archive path
84 // 4. Resolve archive path (arch-specific if ?arch= query param)
85 const arch = req.query ? req.query.arch : null
8586 const storagePath = path.resolve(storage || './pear-git-storage')
8687 const archiveDir = path.join(storagePath, 'archives')
87 const archivePath = getArchivePath(archiveDir, repoName, format)
88 const archSuffix = arch ? `-${arch}` : ''
89 const archiveSubdir = path.join(archiveDir, repoName)
90 const archivePath = path.join(archiveSubdir, `release${archSuffix}.${format}`)
8891
8992 // 5. Check if pre-built archive exists
9093 let stat = null
……@@ -101,9 +104,9 @@ async function handleArchiveDownload (req, res, server) {
101104 }
102105
103106 try {
104 const result = await generateArchive(repo.path, repoName, archiveDir, format)
107 const result = await generateArchive(repo.path, repoName, archiveDir, format, { arch })
105108 stat = fs.statSync(result.path)
106 console.log(`[Archive] Generated ${format} for ${repoName} (${stat.size} bytes)`)
109 console.log(`[Archive] Generated ${format}${arch ? ' (' + arch + ')' : ''} for ${repoName} (${(stat.size / 1024 / 1024).toFixed(1)} MB)`)
107110 } catch (err) {
108111 if (err.message.includes('has no release branch')) {
109112 notFound(res, 'No release branch found. Deploy first to create one.')