2022-08-01 22:43:40 +03:00
|
|
|
|
#!/usr/bin/env -S tea -E
|
|
|
|
|
|
|
|
|
|
/*---
|
|
|
|
|
args:
|
|
|
|
|
- deno
|
|
|
|
|
- run
|
|
|
|
|
- --allow-net
|
2022-09-08 23:40:35 +03:00
|
|
|
|
- --allow-read
|
2022-08-17 15:22:22 +03:00
|
|
|
|
- --allow-env
|
2022-08-01 22:43:40 +03:00
|
|
|
|
- --import-map={{ srcroot }}/import-map.json
|
|
|
|
|
---*/
|
|
|
|
|
|
|
|
|
|
import { S3 } from "s3"
|
2022-08-26 02:59:37 +03:00
|
|
|
|
import { PackageRequirement, Path } from "types"
|
2022-08-01 22:43:40 +03:00
|
|
|
|
import useCache from "hooks/useCache.ts"
|
|
|
|
|
import { Package, parsePackageRequirement, SemVer, semver } from "types"
|
2022-08-17 15:22:22 +03:00
|
|
|
|
import useFlags from "hooks/useFlags.ts"
|
|
|
|
|
|
|
|
|
|
useFlags()
|
2022-08-01 22:43:40 +03:00
|
|
|
|
|
|
|
|
|
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",
|
|
|
|
|
})
|
|
|
|
|
|
2022-09-12 14:48:12 +03:00
|
|
|
|
const bucket = s3.getBucket(Deno.env.get("AWS_S3") ?? Deno.env.get("S3_BUCKET")!)
|
2022-08-01 22:43:40 +03:00
|
|
|
|
|
2022-08-26 02:59:37 +03:00
|
|
|
|
const encode = (() => { const e = new TextEncoder(); return e.encode.bind(e) })()
|
|
|
|
|
|
|
|
|
|
const bottles = new Set<PackageRequirement>()
|
|
|
|
|
const checksums = new Set<string>()
|
|
|
|
|
|
|
|
|
|
for (const filename of Deno.args) {
|
|
|
|
|
const path = new Path(filename).isFile()
|
|
|
|
|
|
|
|
|
|
if (!path) { throw new Error(`${filename} is missing`)}
|
|
|
|
|
|
|
|
|
|
if (path.basename() == "files.txt") { continue } // We don't need to upload this
|
|
|
|
|
|
|
|
|
|
const match = path.basename().match(/(.*)-([0-9]+\.[0-9]+\.[0-9]+)\+.*\.tar\.gz.*/)
|
|
|
|
|
|
|
|
|
|
if (!match) { throw new Error(`${filename} doesn't appear to be our bottle/checksum`) }
|
|
|
|
|
|
|
|
|
|
const req = parsePackageRequirement(`${match[1]}@${match[2]}`)
|
|
|
|
|
|
2022-09-08 23:40:35 +03:00
|
|
|
|
if (path.basename().match(/\.sha256sum$/)) {
|
|
|
|
|
checksums.add(`${req.project}@${req.constraint.raw}`)
|
|
|
|
|
} else {
|
|
|
|
|
bottles.add(req)
|
|
|
|
|
}
|
2022-08-26 02:59:37 +03:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Ensure our sets are the same:
|
|
|
|
|
if (bottles.size !== checksums.size || ![...bottles].every(b => checksums.has(`${b.project}@${b.constraint.raw}`))) {
|
|
|
|
|
throw new Error("bottles and checksums don't align")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
for (const rq of bottles) {
|
2022-08-31 22:39:16 +03:00
|
|
|
|
// Packages should be a fixed version, so this should be fine:
|
|
|
|
|
const version = semver.parse(rq.constraint.raw)
|
|
|
|
|
if (!version) { throw new Error(`Incomplete package version: ${rq.constraint.raw}`)}
|
|
|
|
|
const pkg = { project: rq.project, version }
|
2022-08-01 22:43:40 +03:00
|
|
|
|
const key = useCache().s3Key(pkg)
|
|
|
|
|
const bottle = useCache().bottle(pkg)
|
2022-08-26 02:59:37 +03:00
|
|
|
|
const checksum = new Path(`${bottle.string}.sha256sum`)
|
2022-08-01 22:43:40 +03:00
|
|
|
|
|
|
|
|
|
console.log({ key });
|
|
|
|
|
|
|
|
|
|
//FIXME stream it to S3
|
2022-09-08 00:16:01 +03:00
|
|
|
|
const [basename, dirname] = (split => [split.pop(), split.join("/")])(key.split("/"))
|
2022-08-01 22:43:40 +03:00
|
|
|
|
const bottle_contents = await Deno.readFile(bottle.string)
|
2022-09-08 00:16:01 +03:00
|
|
|
|
const checksum_contents = fixup_checksum(await Deno.readFile(checksum.string), basename!)
|
2022-08-01 22:43:40 +03:00
|
|
|
|
const versions = await get_versions(pkg)
|
|
|
|
|
|
|
|
|
|
await bucket.putObject(key, bottle_contents)
|
2022-08-26 02:59:37 +03:00
|
|
|
|
await bucket.putObject(`${key}.sha256sum`, checksum_contents)
|
2022-08-01 22:43:40 +03:00
|
|
|
|
await bucket.putObject(`${dirname}/versions.txt`, encode(versions.join("\n")))
|
|
|
|
|
|
|
|
|
|
console.log({ uploaded: key })
|
|
|
|
|
}
|
|
|
|
|
|
2022-08-26 02:59:37 +03:00
|
|
|
|
//end
|
|
|
|
|
|
2022-08-04 18:28:23 +03:00
|
|
|
|
import { dirname, basename } from "deno/path/mod.ts"
|
|
|
|
|
|
2022-08-01 22:43:40 +03:00
|
|
|
|
async function get_versions(pkg: Package): Promise<SemVer[]> {
|
2022-08-04 18:28:23 +03:00
|
|
|
|
const prefix = dirname(useCache().s3Key(pkg))
|
2022-08-01 22:43:40 +03:00
|
|
|
|
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
|
2022-08-02 05:50:33 +03:00
|
|
|
|
const got = rsp
|
2022-08-01 22:43:40 +03:00
|
|
|
|
?.contents
|
2022-08-04 18:28:23 +03:00
|
|
|
|
?.compactMap(x => x.key)
|
|
|
|
|
.map(x => basename(x))
|
2022-08-01 22:43:40 +03:00
|
|
|
|
.compactMap(semver.coerce) //FIXME coerce is too loose
|
2022-08-02 05:50:33 +03:00
|
|
|
|
?? []
|
|
|
|
|
|
|
|
|
|
// have to add pkg.version as put and get are not atomic
|
|
|
|
|
return [...new Set([...got, pkg.version])].sort()
|
2022-08-01 22:43:40 +03:00
|
|
|
|
}
|
2022-09-08 00:16:01 +03:00
|
|
|
|
|
|
|
|
|
// 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}`)
|
|
|
|
|
}
|