mirror of
https://gitea.toothfairyai.com/ToothFairyAI/tf_code.git
synced 2026-03-30 05:43:55 +00:00
237 lines
7.3 KiB
TypeScript
237 lines
7.3 KiB
TypeScript
#!/usr/bin/env bun
|
|
|
|
import { $ } from "bun"
|
|
import fs from "fs"
|
|
import path from "path"
|
|
import { fileURLToPath } from "url"
|
|
import pkg from "../package.json"
|
|
|
|
const __dirname = path.dirname(fileURLToPath(import.meta.url))
|
|
const dir = path.resolve(__dirname, "..")
|
|
process.chdir(dir)
|
|
|
|
const TFCODE_VERSION = pkg.version
|
|
const GITEA_HOST = process.env.GITEA_HOST || "gitea.toothfairyai.com"
|
|
const GITEA_TOKEN = process.env.GITEA_TOKEN
|
|
const GITEA_REPO = process.env.GITEA_REPO || "ToothFairyAI/tf_code"
|
|
|
|
// Collect binaries
|
|
const binaries: Record<string, string> = {}
|
|
for (const filepath of new Bun.Glob("*/package.json").scanSync({ cwd: "./dist" })) {
|
|
const pkg = await Bun.file(`./dist/${filepath}`).json()
|
|
if (pkg.name.startsWith("@toothfairyai/tfcode-")) {
|
|
binaries[pkg.name] = pkg.version
|
|
}
|
|
}
|
|
|
|
console.log("Binaries:", binaries)
|
|
|
|
// Upload to Gitea release
|
|
async function uploadToGitea() {
|
|
if (!GITEA_TOKEN) {
|
|
console.error("GITEA_TOKEN is required")
|
|
process.exit(1)
|
|
}
|
|
|
|
// Check if release exists
|
|
const releaseUrl = `https://${GITEA_HOST}/api/v1/repos/${GITEA_REPO}/releases/tags/v${TFCODE_VERSION}`
|
|
let releaseId: string
|
|
|
|
try {
|
|
const res = await fetch(releaseUrl, {
|
|
headers: { Authorization: `token ${GITEA_TOKEN}` },
|
|
})
|
|
if (res.ok) {
|
|
const release = await res.json()
|
|
releaseId = release.id
|
|
console.log(`Release v${TFCODE_VERSION} exists, updating...`)
|
|
} else {
|
|
throw new Error("Not found")
|
|
}
|
|
} catch {
|
|
// Create new release
|
|
const createUrl = `https://${GITEA_HOST}/api/v1/repos/${GITEA_REPO}/releases`
|
|
const res = await fetch(createUrl, {
|
|
method: "POST",
|
|
headers: {
|
|
Authorization: `token ${GITEA_TOKEN}`,
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({
|
|
tag_name: `v${TFCODE_VERSION}`,
|
|
name: `v${TFCODE_VERSION}`,
|
|
body: `tfcode v${TFCODE_VERSION}\n\nSee CHANGELOG.md for details.`,
|
|
draft: false,
|
|
prerelease: TFCODE_VERSION.includes("-"),
|
|
}),
|
|
})
|
|
|
|
if (!res.ok) {
|
|
console.error("Failed to create release:", await res.text())
|
|
process.exit(1)
|
|
}
|
|
|
|
const release = await res.json()
|
|
releaseId = release.id
|
|
console.log(`Created release v${TFCODE_VERSION}`)
|
|
}
|
|
|
|
// Upload assets
|
|
const assets = await fs.promises.readdir("./dist")
|
|
for (const asset of assets) {
|
|
if (asset.endsWith(".tar.gz") || asset.endsWith(".zip")) {
|
|
const assetPath = `./dist/${asset}`
|
|
const file = Bun.file(assetPath)
|
|
const uploadUrl = `https://${GITEA_HOST}/api/v1/repos/${GITEA_REPO}/releases/${releaseId}/assets?name=${asset}`
|
|
|
|
console.log(`Uploading ${asset}...`)
|
|
const res = await fetch(uploadUrl, {
|
|
method: "POST",
|
|
headers: {
|
|
Authorization: `token ${GITEA_TOKEN}`,
|
|
"Content-Type": "application/octet-stream",
|
|
},
|
|
body: file,
|
|
})
|
|
|
|
if (!res.ok) {
|
|
console.error(`Failed to upload ${asset}:`, await res.text())
|
|
} else {
|
|
console.log(`Uploaded ${asset}`)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create main npm package
|
|
async function createMainPackage() {
|
|
await $`mkdir -p ./dist/tfcode`
|
|
await $`cp -r ./bin ./dist/tfcode/bin`
|
|
await Bun.file(`./dist/tfcode/postinstall.mjs`).write(await Bun.file("./script/postinstall-tfcode.mjs").text())
|
|
await Bun.file(`./dist/tfcode/LICENSE`).write(await Bun.file("../../LICENSE").text())
|
|
|
|
await Bun.file(`./dist/tfcode/package.json`).write(
|
|
JSON.stringify(
|
|
{
|
|
name: "@toothfairyai/tfcode",
|
|
version: TFCODE_VERSION,
|
|
bin: { tfcode: "./bin/tfcode" },
|
|
scripts: { postinstall: "node ./postinstall.mjs" },
|
|
license: pkg.license,
|
|
optionalDependencies: binaries,
|
|
homepage: "https://toothfairyai.com/developers/tfcode",
|
|
repository: {
|
|
type: "git",
|
|
url: `https://${GITEA_HOST}/${GITEA_REPO}.git`,
|
|
},
|
|
},
|
|
null,
|
|
2,
|
|
),
|
|
)
|
|
|
|
// Pack and publish
|
|
if (process.platform !== "win32") {
|
|
await $`chmod -R 755 ./dist/tfcode`
|
|
}
|
|
|
|
for (const [name, version] of Object.entries(binaries)) {
|
|
console.log(`Publishing ${name}...`)
|
|
await $`cd ./dist/${name.replace("@toothfairyai/", "")} && bun pm pack && npm publish *.tgz --access public --tag beta`
|
|
}
|
|
|
|
console.log("Publishing main package...")
|
|
await $`cd ./dist/tfcode && bun pm pack && npm publish *.tgz --access public --tag beta`
|
|
}
|
|
|
|
// Create Homebrew formula
|
|
async function createHomebrewFormula() {
|
|
const arm64Sha = await $`sha256sum ./dist/tfcode-linux-arm64.tar.gz | cut -d' ' -f1`.text().then((x) => x.trim())
|
|
const x64Sha = await $`sha256sum ./dist/tfcode-linux-x64.tar.gz | cut -d' ' -f1`.text().then((x) => x.trim())
|
|
const macX64Sha = await $`sha256sum ./dist/tfcode-darwin-x64.zip | cut -d' ' -f1`.text().then((x) => x.trim())
|
|
const macArm64Sha = await $`sha256sum ./dist/tfcode-darwin-arm64.zip | cut -d' ' -f1`.text().then((x) => x.trim())
|
|
|
|
const formula = [
|
|
"# typed: false",
|
|
"# frozen_string_literal: true",
|
|
"",
|
|
"class Tfcode < Formula",
|
|
` desc "ToothFairyAI's official AI coding agent"`,
|
|
` homepage "https://toothfairyai.com/developers/tfcode"`,
|
|
` version "${TFCODE_VERSION.split("-")[0]}"`,
|
|
"",
|
|
` depends_on "ripgrep"`,
|
|
"",
|
|
" on_macos do",
|
|
" if Hardware::CPU.intel?",
|
|
` url "https://${GITEA_HOST}/${GITEA_REPO}/releases/download/v${TFCODE_VERSION}/tfcode-darwin-x64.zip"`,
|
|
` sha256 "${macX64Sha}"`,
|
|
"",
|
|
" def install",
|
|
' bin.install "tfcode"',
|
|
" end",
|
|
" end",
|
|
" if Hardware::CPU.arm?",
|
|
` url "https://${GITEA_HOST}/${GITEA_REPO}/releases/download/v${TFCODE_VERSION}/tfcode-darwin-arm64.zip"`,
|
|
` sha256 "${macArm64Sha}"`,
|
|
"",
|
|
" def install",
|
|
' bin.install "tfcode"',
|
|
" end",
|
|
" end",
|
|
" end",
|
|
"",
|
|
" on_linux do",
|
|
" if Hardware::CPU.intel? and Hardware::CPU.is_64_bit?",
|
|
` url "https://${GITEA_HOST}/${GITEA_REPO}/releases/download/v${TFCODE_VERSION}/tfcode-linux-x64.tar.gz"`,
|
|
` sha256 "${x64Sha}"`,
|
|
" def install",
|
|
' bin.install "tfcode"',
|
|
" end",
|
|
" end",
|
|
" if Hardware::CPU.arm? and Hardware::CPU.is_64_bit?",
|
|
` url "https://${GITEA_HOST}/${GITEA_REPO}/releases/download/v${TFCODE_VERSION}/tfcode-linux-arm64.tar.gz"`,
|
|
` sha256 "${arm64Sha}"`,
|
|
" def install",
|
|
' bin.install "tfcode"',
|
|
" end",
|
|
" end",
|
|
" end",
|
|
"end",
|
|
"",
|
|
].join("\n")
|
|
|
|
await Bun.file("./dist/tfcode.rb").write(formula)
|
|
console.log("Created Homebrew formula: ./dist/tfcode.rb")
|
|
console.log("To publish: Create a tap repo and push this formula")
|
|
}
|
|
|
|
// Run
|
|
if (process.argv.includes("--upload")) {
|
|
await uploadToGitea()
|
|
}
|
|
|
|
if (process.argv.includes("--npm")) {
|
|
await createMainPackage()
|
|
}
|
|
|
|
if (process.argv.includes("--brew")) {
|
|
await createHomebrewFormula()
|
|
}
|
|
|
|
if (!process.argv.includes("--upload") && !process.argv.includes("--npm") && !process.argv.includes("--brew")) {
|
|
console.log(`
|
|
Usage:
|
|
bun run publish.ts --upload Upload binaries to Gitea release
|
|
bun run publish.ts --npm Publish to npm
|
|
bun run publish.ts --brew Create Homebrew formula
|
|
bun run publish.ts --all Do all of the above
|
|
`)
|
|
}
|
|
|
|
if (process.argv.includes("--all")) {
|
|
await uploadToGitea()
|
|
await createMainPackage()
|
|
await createHomebrewFormula()
|
|
}
|