Skip to content

Commit

Permalink
fix(2694): tailcall server run via npx command (#2733)
Browse files Browse the repository at this point in the history
Co-authored-by: Tushar Mathur <[email protected]>
  • Loading branch information
webbdays and tusharmath authored Sep 25, 2024
1 parent dbf10b4 commit 04e7a6a
Show file tree
Hide file tree
Showing 6 changed files with 118 additions and 130 deletions.
38 changes: 19 additions & 19 deletions npm/gen-root.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import * as fs from "fs/promises"
import {resolve, dirname} from "path"
import * as yml from "yaml"
import {fileURLToPath} from "url"
import {parse} from "ts-command-line-args"
import {PackageJson as IPackageJSON} from "type-fest"
import YML from "yaml"

const __dirname = dirname(fileURLToPath(import.meta.url))

Expand All @@ -17,36 +17,29 @@ const options = parse<ICLI>({
name: {alias: "n", type: String},
})

async function getBuildDefinitions(): Promise<string[]> {
async function get_build_matrix() {
const ciYMLPath = resolve(__dirname, "../.github/workflows/build_matrix.yml")
const ciYML = await fs.readFile(ciYMLPath, "utf8").then(yml.parse)
const ciYML = await fs.readFile(ciYMLPath, "utf8").then(YML.parse)
const steps = ciYML.jobs["setup-matrix"].steps

for (const step of steps) {
const matrix = step?.with?.matrix

if (matrix) {
// Parse yaml again since matrix is defined as string inside setup-matrix
return yml.parse(matrix).build
return YML.parse(matrix)
}
}

throw new Error("Cannot find matrix definition in workflow file")
}

async function genServerPackage(buildDefinitions: string[]) {
async function genServerPackage() {
const packageVersion = options.version || "0.1.0"
const name = options.name || "@tailcallhq/tailcall"

console.log(`Generating package.json with version ${packageVersion}`)

// Construct the optionalDependencies object with the provided version
const optionalDependencies: Record<string, string> = {}

for (const buildDef of buildDefinitions) {
optionalDependencies[`@tailcallhq/core-${buildDef}`] = packageVersion
}

const packageJson = await fs.readFile(resolve(__dirname, "./package.json"), "utf8")
const basePackage = JSON.parse(packageJson) as IPackageJSON
const {description, license, repository, homepage, keywords} = basePackage
Expand All @@ -60,42 +53,49 @@ async function genServerPackage(buildDefinitions: string[]) {
name: name,
type: "module",
version: packageVersion,
optionalDependencies,
scarfSettings: {
defaultOptIn: true,
allowTopLevel: true,
},
dependencies: {
"detect-libc": "^2.0.2",
"@scarf/scarf": "^1.3.0",
yaml: "^2.3.3",
axios: "^1.7.4",
},
scripts: {
postinstall: "node ./scripts/post-install.js",
preinstall: "node ./scripts/pre-install.js",
},
bin: {
tailcall: "bin/tailcall", // will replace with respective platform binary later.
},
}

// Define the directory path where the package.json should be created
const directoryPath = resolve(__dirname, "@tailcallhq/tailcall")
const scriptsPath = resolve(directoryPath, "./scripts")
const binPath = resolve(directoryPath, "./bin")

await fs.mkdir(scriptsPath, {recursive: true})
await fs.mkdir(binPath, {recursive: true})
await fs.mkdir(directoryPath, {recursive: true})

const postInstallScript = await fs.readFile(resolve(__dirname, "./post-install.js"), "utf8")
const preInstallScript = await fs.readFile(resolve(__dirname, "./pre-install.js"), "utf8")
const utilsScript = await fs.readFile(resolve(__dirname, "./utils.js"), "utf8")
const stringified_yaml = YML.stringify(await get_build_matrix())

const postInstallScriptContent = `const version = "${packageVersion}";\n${postInstallScript}`
const preInstallScriptContent = `const optionalDependencies = ${JSON.stringify(
optionalDependencies,
)};\n${preInstallScript}`

await fs.writeFile(resolve(scriptsPath, "post-install.js"), postInstallScriptContent, "utf8")
await fs.writeFile(resolve(scriptsPath, "pre-install.js"), preInstallScriptContent, "utf8")
await fs.writeFile(resolve(scriptsPath, "pre-install.js"), preInstallScript, "utf8")
await fs.writeFile(resolve(scriptsPath, "utils.js"), utilsScript, "utf8")
await fs.writeFile(resolve(directoryPath, "./build-matrix.yaml"), stringified_yaml, "utf8")

await fs.writeFile(resolve(directoryPath, "./package.json"), JSON.stringify(tailcallPackage, null, 2), "utf8")

await fs.copyFile(resolve(__dirname, "../README.md"), resolve(directoryPath, "./README.md"))
}

const buildDefinitions = await getBuildDefinitions()
await genServerPackage(buildDefinitions)
await genServerPackage()
94 changes: 1 addition & 93 deletions npm/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions npm/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@
"dependencies": {
"ts-command-line-args": "^2.5.1",
"type-fest": "^4.7.1",
"yaml": "^2.3.3",
"yml": "^1.0.0"
"yaml": "^2.3.3"
},
"devDependencies": {
"tsx": "^4.1.0"
Expand Down
70 changes: 57 additions & 13 deletions npm/post-install.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,69 @@
import {familySync, GLIBC, MUSL} from "detect-libc"
import {exec} from "child_process"
import util from "util"
import get_matched_platform from "./utils.js"
import fs from "fs"
import axios from "axios"
import {resolve, dirname} from "path"
import {fileURLToPath} from "url"

const execa = util.promisify(exec)
const platform = process.platform
const os = process.platform
const arch = process.arch

const libcFamily = familySync()
let libc
if (platform === "win32") {
libc = "-msvc"

let libc = ""
if (os === "win32") {
libc = "msvc"
} else {
libc = libcFamily === GLIBC ? "-gnu" : libcFamily === MUSL ? "-musl" : ""
libc = libcFamily === GLIBC ? "gnu" : libcFamily === MUSL ? "musl" : ""
}

const pkg = `@tailcallhq/core-${platform}-${arch}${libc}`
const matched_platform = get_matched_platform(os, arch, libc)
if (matched_platform != null) {
const targetPlatform = matched_platform

let targetPlatformExt = ""
if (targetPlatform.get("ext") != undefined) {
targetPlatformExt = targetPlatform.get("ext")
}

const pkg_download_base_url = "https://github.com/tailcallhq/tailcall/releases/download/"
const specific_url = `v${version}/tailcall-${targetPlatform.get("target")}${targetPlatformExt}`
const full_url = pkg_download_base_url + specific_url

console.log(`Downloading Tailcall for ${targetPlatform.get("target")}${targetPlatformExt} ,\nUrl - ${full_url} ...`)

const output_path = `bin/tailcall-${targetPlatform.get("target")}${targetPlatformExt}`
await download_binary(full_url, output_path)
}

async function download_binary(full_url, output_path) {
try {
const file = fs.createWriteStream(output_path)
console.log("bin path -", output_path)
const response = await axios({
url: full_url,
method: "GET",
responseType: "stream",
})

response.data.pipe(file)
response.data.on("error", (error) => {
console.error("Error with resp data - ", error)
})

try {
// @ts-ignore
const {stdout, stderr} = await execa(`npm install ${pkg}@${version} --no-save`)
stderr ? console.log(stderr) : console.log(`Successfully installed optional dependency: ${pkg}`, stdout)
} catch (error) {
console.error(`Failed to install optional dependency: ${pkg}`, error.stderr)
file.on("close", async () => {
const packageJsonString = await fs.promises.readFile("package.json", "utf8")
const packageJson = JSON.parse(packageJsonString)
packageJson.bin = {tailcall: output_path}
await fs.promises.writeFile("package.json", JSON.stringify(packageJson, null, 2), "utf8")
console.log("Tailcall binary downloaded successfully")
})
file.on("error", (error) => {
console.error("Error while writing to a file - ", error)
})
} catch (error) {
console.error("Error downloading", error.message)
}
}
19 changes: 16 additions & 3 deletions npm/pre-install.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,23 @@
// @ts-check
import {familySync, GLIBC, MUSL} from "detect-libc"
import get_matched_platform from "./utils.js"

const os = process.platform
const arch = process.arch
const libcFamily = familySync()

let libc = ""
if (os === "win32") {
libc = "msvc"
} else {
libc = libcFamily === GLIBC ? "gnu" : libcFamily === MUSL ? "musl" : ""
}

const matched_platform = get_matched_platform(os, arch, libc)

const dependency = Object.keys(optionalDependencies).find((name) => name.includes(`${os}-${arch}`))
if (!dependency) {
if (matched_platform == null) {
const redColor = "\x1b[31m"
const resetColor = "\x1b[0m"
console.error(`${redColor} Tailcall does not support platform ${os} arch ${arch} ${resetColor}`)
console.error(`${redColor} Tailcall does not support platform - ${os}, arch - ${arch}, libc - ${libc} ${resetColor}`)
process.exit(1)
}
Loading

1 comment on commit 04e7a6a

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Running 30s test @ http://localhost:8000/graphql

4 threads and 100 connections

Thread Stats Avg Stdev Max +/- Stdev
Latency 11.73ms 5.10ms 191.42ms 91.39%
Req/Sec 2.17k 265.58 3.26k 87.50%

259451 requests in 30.04s, 1.30GB read

Requests/sec: 8638.24

Transfer/sec: 44.34MB

Please sign in to comment.