diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 89fcd134..3a617db8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -20,7 +20,7 @@ jobs: - name: Download latest llama.cpp release env: CI: true - run: node ./dist/cli/cli.js download --release latest --skipBuild --updateBinariesReleaseMetadataAndSaveGitBundle + run: node ./dist/cli/cli.js download --release latest --skipBuild --noBundle --updateBinariesReleaseMetadataAndSaveGitBundle - name: Upload build artifact uses: actions/upload-artifact@v3 with: diff --git a/src/cli/commands/DownloadCommand.ts b/src/cli/commands/DownloadCommand.ts index d740b4a0..f98f1716 100644 --- a/src/cli/commands/DownloadCommand.ts +++ b/src/cli/commands/DownloadCommand.ts @@ -13,7 +13,7 @@ import {setBinariesGithubRelease} from "../../utils/binariesGithubRelease.js"; import {downloadCmakeIfNeeded} from "../../utils/cmake.js"; import withStatusLogs from "../../utils/withStatusLogs.js"; import {getIsInDocumentationMode} from "../../state.js"; -import {saveCurrentRepoAsReleaseBundle} from "../../utils/gitReleaseBundles.js"; +import {unshallowAndSquashCurrentRepoAndSaveItAsReleaseBundle} from "../../utils/gitReleaseBundles.js"; import {cloneLlamaCppRepo} from "../../utils/cloneLlamaCppRepo.js"; type DownloadCommandArgs = { @@ -24,6 +24,7 @@ type DownloadCommandArgs = { metal: boolean, cuda: boolean, skipBuild?: boolean, + noBundle?: boolean, updateBinariesReleaseMetadataAndSaveGitBundle?: boolean }; @@ -71,6 +72,12 @@ export const DownloadCommand: CommandModule = { default: false, description: "Skip building llama.cpp after downloading it" }) + .option("noBundle", { + alias: "nb", + type: "boolean", + default: false, + description: "Download a llama.cpp only from GitHub, even if a local git bundle exists for the release" + }) .option("updateBinariesReleaseMetadataAndSaveGitBundle", { type: "boolean", hidden: true, // this for the CI to use @@ -82,7 +89,7 @@ export const DownloadCommand: CommandModule = { }; export async function DownloadLlamaCppCommand({ - repo, release, arch, nodeTarget, metal, cuda, skipBuild, updateBinariesReleaseMetadataAndSaveGitBundle + repo, release, arch, nodeTarget, metal, cuda, skipBuild, noBundle, updateBinariesReleaseMetadataAndSaveGitBundle }: DownloadCommandArgs) { const octokit = new Octokit(); const [githubOwner, githubRepo] = repo.split("/"); @@ -146,7 +153,7 @@ export async function DownloadLlamaCppCommand({ }); console.log(chalk.blue("Cloning llama.cpp")); - await cloneLlamaCppRepo(githubOwner, githubRepo, githubRelease!.data.tag_name); + await cloneLlamaCppRepo(githubOwner, githubRepo, githubRelease!.data.tag_name, noBundle != true); if (!skipBuild) { await downloadCmakeIfNeeded(true); @@ -168,7 +175,7 @@ export async function DownloadLlamaCppCommand({ if (isCI && updateBinariesReleaseMetadataAndSaveGitBundle) { await setBinariesGithubRelease(githubRelease!.data.tag_name); - await saveCurrentRepoAsReleaseBundle(); + await unshallowAndSquashCurrentRepoAndSaveItAsReleaseBundle(); } console.log(); diff --git a/src/utils/cloneLlamaCppRepo.ts b/src/utils/cloneLlamaCppRepo.ts index 96110dd4..5ae0b54e 100644 --- a/src/utils/cloneLlamaCppRepo.ts +++ b/src/utils/cloneLlamaCppRepo.ts @@ -6,8 +6,8 @@ import {llamaCppDirectory} from "../config.js"; import {getGitBundlePathForRelease} from "./gitReleaseBundles.js"; -export async function cloneLlamaCppRepo(githubOwner: string, githubRepo: string, tag: string) { - const gitBundleForTag = await getGitBundlePathForRelease(githubOwner, githubRepo, tag); +export async function cloneLlamaCppRepo(githubOwner: string, githubRepo: string, tag: string, useBundles: boolean = true) { + const gitBundleForTag = !useBundles ? null : await getGitBundlePathForRelease(githubOwner, githubRepo, tag); const remoteGitUrl = `https://github.com/${githubOwner}/${githubRepo}.git`; async function withGitCloneProgress(cloneName: string, callback: (gitWithCloneProgress: SimpleGit) => Promise): Promise { @@ -49,10 +49,7 @@ export async function cloneLlamaCppRepo(githubOwner: string, githubRepo: string, "--quiet": null }); - await simpleGit(llamaCppDirectory) - .removeRemote("origin"); - await simpleGit(llamaCppDirectory) - .addRemote("origin", remoteGitUrl); + await simpleGit(llamaCppDirectory).removeRemote("origin"); }); return; } catch (err) { diff --git a/src/utils/gitReleaseBundles.ts b/src/utils/gitReleaseBundles.ts index e892f28a..7946f1af 100644 --- a/src/utils/gitReleaseBundles.ts +++ b/src/utils/gitReleaseBundles.ts @@ -4,13 +4,50 @@ import {currentReleaseGitBundlePath, defaultLlamaCppGitHubRepo, llamaCppDirector import {getBinariesGithubRelease} from "./binariesGithubRelease.js"; -export async function saveCurrentRepoAsReleaseBundle() { +export async function unshallowAndSquashCurrentRepoAndSaveItAsReleaseBundle() { if (!(await fs.pathExists(llamaCppDirectory))) throw new Error("llama.cpp directory does not exist"); if (await fs.pathExists(currentReleaseGitBundlePath)) await fs.remove(currentReleaseGitBundlePath); + await simpleGit(llamaCppDirectory).addConfig("user.name", "node-llama-cpp-ci"); + await simpleGit(llamaCppDirectory).addConfig("user.email", "node-llama-cpp-ci@node-llama-cpp-ci.node-llama-cpp-ci"); + + const currentBranch = await getCurrentTagOrBranch(); + + await simpleGit(llamaCppDirectory).fetch(["--unshallow"]); + + const lastCommit = await simpleGit(llamaCppDirectory).log(["-1"]); + const lastCommitMessage: string | null = lastCommit?.all?.[0]?.message; + const newCommitMessage = "## SQUASHED ##\n\n" + (lastCommitMessage ?? ""); + + const newCommitSha = await simpleGit(llamaCppDirectory).raw(["commit-tree", "HEAD^{tree}", "-m", newCommitMessage]); + await simpleGit(llamaCppDirectory).reset(["--hard", newCommitSha.trim()]); + + const tags = await simpleGit(llamaCppDirectory).tags(); + for (const tag of tags.all) { + await simpleGit(llamaCppDirectory).tag(["--delete", tag]); + } + + const branches = await simpleGit(llamaCppDirectory).branch(); + for (const branch of branches.all) { + try { + await simpleGit(llamaCppDirectory).branch(["--delete", branch]); + } catch (err) { + // If the branch is not found, it's fine + // this happens as when there are no branches git returnes an output saying so, and `simpleGit` parses it as a branch, + // so the list may contain branches that do not exist. + // Right now, the non-existent branch name returned called `(no`, but I wouldn't want to rely on this specific text, + // as this is a bug in `simpleGit`. + } + } + + if (currentBranch != null) + await simpleGit(llamaCppDirectory).tag([currentBranch]); + + await simpleGit(llamaCppDirectory).raw(["gc", "--aggressive", "--prune=all"]); + await simpleGit(llamaCppDirectory).raw(["bundle", "create", currentReleaseGitBundlePath, "HEAD"]); } @@ -32,3 +69,18 @@ export async function getGitBundlePathForRelease(githubOwner: string, githubRepo return currentReleaseGitBundlePath; } + +async function getCurrentTagOrBranch() { + const branch = await simpleGit(llamaCppDirectory).revparse(["--abbrev-ref", "HEAD"]); + + if (branch !== "HEAD") + return branch; + + const tags = await simpleGit(llamaCppDirectory).tag(["--points-at", "HEAD"]); + const tagArray = tags.split("\n").filter(Boolean); + + if (tagArray.length > 0) + return tagArray[0]; + + return null; +}