Skip to content

Commit ada896b

Browse files
authored
feat: git release bundle (#61)
1 parent 3400fce commit ada896b

File tree

6 files changed

+130
-41
lines changed

6 files changed

+130
-41
lines changed

.github/workflows/build.yml

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ jobs:
2020
- name: Generate docs
2121
run: npm run generate-docs
2222
- name: Download latest llama.cpp release
23-
run: node ./dist/cli/cli.js download --release latest --skipBuild --updateBinariesReleaseMetadata
23+
env:
24+
CI: true
25+
run: node ./dist/cli/cli.js download --release latest --skipBuild --updateBinariesReleaseMetadataAndSaveGitBundle
2426
- name: Upload build artifact
2527
uses: actions/upload-artifact@v3
2628
with:
@@ -41,6 +43,11 @@ jobs:
4143
with:
4244
name: "llama.cpp"
4345
path: "llama/llama.cpp"
46+
- name: Upload gitRelease.bundle artifact
47+
uses: actions/upload-artifact@v3
48+
with:
49+
name: "gitReleaseBundle"
50+
path: "llama/gitRelease.bundle"
4451

4552
build-binaries:
4653
name: Build binaries - ${{ matrix.config.name }}
@@ -243,6 +250,9 @@ jobs:
243250
rm -f ./llama/binariesGithubRelease.json
244251
mv artifacts/binariesGithubRelease/binariesGithubRelease.json ./llama/binariesGithubRelease.json
245252
253+
rm -f ./llama/gitRelease.bundle
254+
mv artifacts/gitReleaseBundle/gitRelease.bundle ./llama/gitRelease.bundle
255+
246256
echo "Built binaries:"
247257
ls llamaBins
248258
- name: Add "postinstall" script to package.json

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ node_modules
1111

1212
/llama/compile_commands.json
1313
/llama/llama.cpp
14+
/llama/gitRelease.bundle
1415
/llama/.temp
1516
/llama/build
1617
/llama/Release

src/cli/commands/DownloadCommand.ts

Lines changed: 9 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,17 @@ import {CommandModule} from "yargs";
33
import {Octokit} from "octokit";
44
import fs from "fs-extra";
55
import chalk from "chalk";
6-
import cliProgress from "cli-progress";
7-
import simpleGit from "simple-git";
86
import {
9-
defaultLlamaCppCudaSupport, defaultLlamaCppGitHubRepo, defaultLlamaCppMetalSupport, defaultLlamaCppRelease, llamaCppDirectory
7+
defaultLlamaCppCudaSupport, defaultLlamaCppGitHubRepo, defaultLlamaCppMetalSupport, defaultLlamaCppRelease, isCI, llamaCppDirectory
108
} from "../../config.js";
119
import {compileLlamaCpp} from "../../utils/compileLLamaCpp.js";
1210
import withOra from "../../utils/withOra.js";
1311
import {clearTempFolder} from "../../utils/clearTempFolder.js";
1412
import {setBinariesGithubRelease} from "../../utils/binariesGithubRelease.js";
1513
import {downloadCmakeIfNeeded} from "../../utils/cmake.js";
1614
import withStatusLogs from "../../utils/withStatusLogs.js";
15+
import {saveCurrentRepoAsReleaseBundle} from "../../utils/gitReleaseBundles.js";
16+
import {cloneLlamaCppRepo} from "../../utils/cloneLlamaCppRepo.js";
1717

1818
type DownloadCommandArgs = {
1919
repo: string,
@@ -23,7 +23,7 @@ type DownloadCommandArgs = {
2323
metal: boolean,
2424
cuda: boolean,
2525
skipBuild?: boolean,
26-
updateBinariesReleaseMetadata?: boolean
26+
updateBinariesReleaseMetadataAndSaveGitBundle?: boolean
2727
};
2828

2929
export const DownloadCommand: CommandModule<object, DownloadCommandArgs> = {
@@ -68,7 +68,7 @@ export const DownloadCommand: CommandModule<object, DownloadCommandArgs> = {
6868
default: false,
6969
description: "Skip building llama.cpp after downloading it"
7070
})
71-
.option("updateBinariesReleaseMetadata", {
71+
.option("updateBinariesReleaseMetadataAndSaveGitBundle", {
7272
type: "boolean",
7373
hidden: true, // this for the CI to use
7474
default: false,
@@ -79,7 +79,7 @@ export const DownloadCommand: CommandModule<object, DownloadCommandArgs> = {
7979
};
8080

8181
export async function DownloadLlamaCppCommand({
82-
repo, release, arch, nodeTarget, metal, cuda, skipBuild, updateBinariesReleaseMetadata
82+
repo, release, arch, nodeTarget, metal, cuda, skipBuild, updateBinariesReleaseMetadataAndSaveGitBundle
8383
}: DownloadCommandArgs) {
8484
const octokit = new Octokit();
8585
const [githubOwner, githubRepo] = repo.split("/");
@@ -143,7 +143,7 @@ export async function DownloadLlamaCppCommand({
143143
});
144144

145145
console.log(chalk.blue("Cloning llama.cpp"));
146-
await cloneTag(githubOwner, githubRepo, githubRelease!.data.tag_name, llamaCppDirectory);
146+
await cloneLlamaCppRepo(githubOwner, githubRepo, githubRelease!.data.tag_name);
147147

148148
if (!skipBuild) {
149149
await downloadCmakeIfNeeded(true);
@@ -163,8 +163,9 @@ export async function DownloadLlamaCppCommand({
163163
});
164164
}
165165

166-
if (updateBinariesReleaseMetadata) {
166+
if (isCI && updateBinariesReleaseMetadataAndSaveGitBundle) {
167167
await setBinariesGithubRelease(githubRelease!.data.tag_name);
168+
await saveCurrentRepoAsReleaseBundle();
168169
}
169170

170171
console.log();
@@ -174,35 +175,3 @@ export async function DownloadLlamaCppCommand({
174175
console.log();
175176
console.log(chalk.green("Done"));
176177
}
177-
178-
179-
async function cloneTag(githubOwner: string, githubRepo: string, tag: string, directory: string) {
180-
const progressBar = new cliProgress.Bar({
181-
clearOnComplete: false,
182-
hideCursor: true,
183-
autopadding: true,
184-
format: `${chalk.bold("Clone {repo}")} ${chalk.yellow("{percentage}%")} ${chalk.cyan("{bar}")} ${chalk.grey("{eta_formatted}")}`
185-
}, cliProgress.Presets.shades_classic);
186-
187-
progressBar.start(100, 0, {
188-
speed: "",
189-
repo: `${githubOwner}/${githubRepo}`
190-
});
191-
192-
try {
193-
await simpleGit({
194-
progress({progress, total, processed}) {
195-
const totalProgress = (processed / 100) + (progress / total);
196-
197-
progressBar.update(Math.floor(totalProgress * 10000) / 100);
198-
}
199-
}).clone(`https://github.com/${githubOwner}/${githubRepo}.git`, directory, {
200-
"--depth": 1,
201-
"--branch": tag,
202-
"--quiet": null
203-
});
204-
} finally {
205-
progressBar.update(100);
206-
progressBar.stop();
207-
}
208-
}

src/config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,15 @@ export const llamaCppGrammarsDirectory = path.join(llamaDirectory, "llama.cpp",
2020
export const tempDownloadDirectory = path.join(os.tmpdir(), "node-llama-cpp", uuid.v4());
2121
export const usedBinFlagJsonPath = path.join(llamaDirectory, "usedBin.json");
2222
export const binariesGithubReleasePath = path.join(llamaDirectory, "binariesGithubRelease.json");
23+
export const currentReleaseGitBundlePath = path.join(llamaDirectory, "gitRelease.bundle");
2324
export const xpackDirectory = path.join(llamaDirectory, "xpack");
2425
export const localXpacksStoreDirectory = path.join(xpackDirectory, "store");
2526
export const localXpacksCacheDirectory = path.join(xpackDirectory, "cache");
2627
export const xpmVersion = "^0.16.3";
2728

29+
export const isCI = env.get("CI")
30+
.default("false")
31+
.asBool();
2832
export const defaultLlamaCppGitHubRepo = env.get("NODE_LLAMA_CPP_REPO")
2933
.default("ggerganov/llama.cpp")
3034
.asString();

src/utils/cloneLlamaCppRepo.ts

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import simpleGit, {SimpleGit} from "simple-git";
2+
import cliProgress from "cli-progress";
3+
import chalk from "chalk";
4+
import fs from "fs-extra";
5+
import {llamaCppDirectory} from "../config.js";
6+
import {getGitBundlePathForRelease} from "./gitReleaseBundles.js";
7+
8+
9+
export async function cloneLlamaCppRepo(githubOwner: string, githubRepo: string, tag: string) {
10+
const gitBundleForTag = await getGitBundlePathForRelease(githubOwner, githubRepo, tag);
11+
const remoteGitUrl = `https://github.com/${githubOwner}/${githubRepo}.git`;
12+
13+
async function withGitCloneProgress<T>(cloneName: string, callback: (gitWithCloneProgress: SimpleGit) => Promise<T>): Promise<T> {
14+
const progressBar = new cliProgress.Bar({
15+
clearOnComplete: false,
16+
hideCursor: true,
17+
autopadding: true,
18+
format: `${chalk.bold("Clone {repo}")} ${chalk.yellow("{percentage}%")} ${chalk.cyan("{bar}")} ${chalk.grey("{eta_formatted}")}`
19+
}, cliProgress.Presets.shades_classic);
20+
21+
progressBar.start(100, 0, {
22+
speed: "",
23+
repo: `${githubOwner}/${githubRepo} (${cloneName})`
24+
});
25+
26+
const gitWithCloneProgress = simpleGit({
27+
progress({progress, total, processed}) {
28+
const totalProgress = (processed / 100) + (progress / total);
29+
30+
progressBar.update(Math.floor(totalProgress * 10000) / 100);
31+
}
32+
});
33+
34+
try {
35+
const res = await callback(gitWithCloneProgress);
36+
37+
progressBar.update(100);
38+
39+
return res;
40+
} finally {
41+
progressBar.stop();
42+
}
43+
}
44+
45+
if (gitBundleForTag != null) {
46+
try {
47+
await withGitCloneProgress("local bundle", async (gitWithCloneProgress) => {
48+
await gitWithCloneProgress.clone(gitBundleForTag, llamaCppDirectory, {
49+
"--quiet": null
50+
});
51+
52+
await simpleGit(llamaCppDirectory)
53+
.removeRemote("origin");
54+
await simpleGit(llamaCppDirectory)
55+
.addRemote("origin", remoteGitUrl);
56+
});
57+
return;
58+
} catch (err) {
59+
await fs.remove(llamaCppDirectory);
60+
console.error("Failed to clone git bundle, cloning from GitHub instead", err);
61+
}
62+
}
63+
64+
await withGitCloneProgress("GitHub", async (gitWithCloneProgress) => {
65+
await gitWithCloneProgress.clone(remoteGitUrl, llamaCppDirectory, {
66+
"--depth": 1,
67+
"--branch": tag,
68+
"--quiet": null
69+
});
70+
});
71+
}

src/utils/gitReleaseBundles.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
import fs from "fs-extra";
2+
import simpleGit from "simple-git";
3+
import {currentReleaseGitBundlePath, defaultLlamaCppGitHubRepo, llamaCppDirectory} from "../config.js";
4+
import {getBinariesGithubRelease} from "./binariesGithubRelease.js";
5+
6+
7+
export async function saveCurrentRepoAsReleaseBundle() {
8+
if (!(await fs.pathExists(llamaCppDirectory)))
9+
throw new Error("llama.cpp directory does not exist");
10+
11+
if (await fs.pathExists(currentReleaseGitBundlePath))
12+
await fs.remove(currentReleaseGitBundlePath);
13+
14+
await simpleGit(llamaCppDirectory).raw(["bundle", "create", currentReleaseGitBundlePath, "HEAD"]);
15+
}
16+
17+
export async function getGitBundlePathForRelease(githubOwner: string, githubRepo: string, release: string) {
18+
const [defaultGithubOwner, defaultGithubRepo] = defaultLlamaCppGitHubRepo.split("/");
19+
if (githubOwner !== defaultGithubOwner || githubRepo !== defaultGithubRepo)
20+
return null;
21+
22+
const currentBundleRelease = await getBinariesGithubRelease();
23+
24+
if (currentBundleRelease === "latest")
25+
return null;
26+
27+
if (currentBundleRelease !== release)
28+
return null;
29+
30+
if (!(await fs.pathExists(currentReleaseGitBundlePath)))
31+
return null;
32+
33+
return currentReleaseGitBundlePath;
34+
}

0 commit comments

Comments
 (0)