mirror of
https://github.com/ivabus/pantry
synced 2024-11-23 17:05:07 +03:00
115 lines
No EOL
3.2 KiB
TypeScript
Executable file
115 lines
No EOL
3.2 KiB
TypeScript
Executable file
#!/usr/bin/env -S tea -E
|
||
|
||
/*---
|
||
args:
|
||
- deno
|
||
- run
|
||
- --allow-net
|
||
- --allow-read
|
||
- --allow-env
|
||
- --import-map={{ srcroot }}/import-map.json
|
||
---*/
|
||
|
||
import { S3 } from "s3"
|
||
import { pkg as pkgutils } from "utils"
|
||
import { useFlags, useOffLicense } from "hooks"
|
||
import { Package, PackageRequirement } from "types"
|
||
import SemVer, * as semver from "semver"
|
||
import { dirname, basename } from "deno/path/mod.ts"
|
||
import Path from "path"
|
||
|
||
useFlags()
|
||
|
||
if (Deno.args.length === 0) throw new Error("no args supplied")
|
||
|
||
const s3 = new S3({
|
||
accessKeyID: Deno.env.get("AWS_ACCESS_KEY_ID")!,
|
||
secretKey: Deno.env.get("AWS_SECRET_ACCESS_KEY")!,
|
||
region: "us-east-1",
|
||
})
|
||
|
||
const bucket = s3.getBucket(Deno.env.get("AWS_S3_BUCKET")!)
|
||
const encode = (() => { const e = new TextEncoder(); return e.encode.bind(e) })()
|
||
|
||
const pkgs = args_get("pkgs").map(pkgutils.parse).map(assert_pkg)
|
||
const bottles = args_get("bottles")
|
||
const checksums = args_get("checksums")
|
||
|
||
|
||
function args_get(key: string): string[] {
|
||
const it = Deno.args[Symbol.iterator]()
|
||
while (true) {
|
||
const { value, done } = it.next()
|
||
if (done) throw new Error()
|
||
if (value === `--${key}`) break
|
||
}
|
||
const rv: string[] = []
|
||
while (true) {
|
||
const { value, done } = it.next()
|
||
if (done) return rv
|
||
if (value.startsWith('--')) return rv
|
||
rv.push(value)
|
||
}
|
||
}
|
||
|
||
for (const [index, pkg] of pkgs.entries()) {
|
||
const bottle = new Path(bottles[index])
|
||
const checksum = checksums[index]
|
||
const compression = bottle.extname() == '.tar.gz' ? 'gz' : 'xz'
|
||
const key = useOffLicense('s3').key({
|
||
pkg,
|
||
type: 'bottle',
|
||
compression,
|
||
})
|
||
|
||
//FIXME stream it to S3
|
||
const bottle_contents = await Deno.readFile(bottle.string)
|
||
const checksum_contents = fixup_checksum(await Deno.readFile(checksum), bottle.basename())
|
||
const versions = await get_versions(key, pkg)
|
||
|
||
console.log({ uploading: key })
|
||
|
||
await bucket.putObject(key, bottle_contents)
|
||
await bucket.putObject(`${key}.sha256sum`, checksum_contents)
|
||
await bucket.putObject(`${dirname(key)}/versions.txt`, encode(versions.join("\n")))
|
||
|
||
console.log({ uploaded: key })
|
||
}
|
||
|
||
//end
|
||
|
||
async function get_versions(key: string, pkg: Package): Promise<SemVer[]> {
|
||
const prefix = dirname(key)
|
||
const rsp = await bucket.listObjects({ prefix })
|
||
|
||
//FIXME? API isn’t clear if these nulls indicate failure or not
|
||
//NOTE if this is a new package then some empty results is expected
|
||
const got = rsp
|
||
?.contents
|
||
?.compact(x => x.key)
|
||
.map(x => basename(x))
|
||
.filter(x => x.match(/v.*\.tar\.gz$/))
|
||
.map(x => x.replace(/v(.*)\.tar\.gz/, "$1"))
|
||
.compact(semver.parse)
|
||
?? []
|
||
|
||
// have to add pkg.version as put and get are not atomic
|
||
return [...new Set([...got, pkg.version])].sort()
|
||
}
|
||
|
||
// Somewhat hacky. We call the bottle on thing locally, and another on the server.
|
||
function fixup_checksum(data: Uint8Array, new_file_name: string) {
|
||
const checksum = new TextDecoder().decode(data).split(" ")[0]
|
||
return new TextEncoder().encode(`${checksum} ${new_file_name}`)
|
||
}
|
||
|
||
function assert_pkg(pkg: Package | PackageRequirement) {
|
||
if ("version" in pkg) {
|
||
return pkg
|
||
} else {
|
||
return {
|
||
project: pkg.project,
|
||
version: new SemVer(pkg.constraint)
|
||
}
|
||
}
|
||
} |