Compare commits
4 commits
Author | SHA1 | Date | |
---|---|---|---|
Cat /dev/Nulo | 30b2482ed5 | ||
Cat /dev/Nulo | b22f2b5741 | ||
Cat /dev/Nulo | d44e35d40e | ||
Cat /dev/Nulo | d64787ddc3 |
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "@nulo/isogit-lfs",
|
"name": "@nulo/isogit-lfs",
|
||||||
"version": "0.2.8",
|
"version": "0.3.1",
|
||||||
"description": "LFS helpers for Isomorphic Git",
|
"description": "LFS helpers for Isomorphic Git",
|
||||||
"main": "dist/index.js",
|
"main": "dist/index.js",
|
||||||
"repository": "git@github.com:riboseinc/isogit-lfs.git",
|
"repository": "git@github.com:riboseinc/isogit-lfs.git",
|
||||||
|
|
|
@ -3,7 +3,7 @@ import { Buffer } from "buffer";
|
||||||
|
|
||||||
import { bodyToBuffer, getAuthHeader, isWriteable } from "./util";
|
import { bodyToBuffer, getAuthHeader, isWriteable } from "./util";
|
||||||
import { Pointer } from "./pointers";
|
import { Pointer } from "./pointers";
|
||||||
import { HTTPRequest } from "./types";
|
import { HTTPRequest, BasicAuth } from "./types";
|
||||||
import { PromiseFsClient } from "isomorphic-git";
|
import { PromiseFsClient } from "isomorphic-git";
|
||||||
|
|
||||||
interface LFSInfoResponse {
|
interface LFSInfoResponse {
|
||||||
|
@ -28,23 +28,27 @@ function isValidLFSInfoResponseData(
|
||||||
* Uses already cached object, if size matches.
|
* Uses already cached object, if size matches.
|
||||||
*/
|
*/
|
||||||
export default async function downloadBlobFromPointer(
|
export default async function downloadBlobFromPointer(
|
||||||
{ promises: fs }: PromiseFsClient,
|
fs: PromiseFsClient | null,
|
||||||
{ http: { request }, headers = {}, url, auth }: HTTPRequest,
|
url: string,
|
||||||
|
auth: BasicAuth | {},
|
||||||
{ info, objectPath }: Pointer
|
{ info, objectPath }: Pointer
|
||||||
): Promise<Buffer> {
|
): Promise<Blob> {
|
||||||
try {
|
if (fs) {
|
||||||
const cached = await fs.readFile(objectPath);
|
try {
|
||||||
if (cached.byteLength === info.size) {
|
const cached = await fs?.promises.readFile(objectPath);
|
||||||
return cached;
|
if (cached.byteLength === info.size) {
|
||||||
}
|
return cached;
|
||||||
} catch (e) {
|
}
|
||||||
// Silence file not found errors (implies cache miss)
|
} catch (e) {
|
||||||
if ((e as any).code !== "ENOENT") {
|
// Silence file not found errors (implies cache miss)
|
||||||
throw e;
|
if ((e as any).code !== "ENOENT") {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const authHeaders: Record<string, string> = auth ? getAuthHeader(auth) : {};
|
const authHeaders: Record<string, string> =
|
||||||
|
"username" in auth ? getAuthHeader(auth) : {};
|
||||||
|
|
||||||
// Request LFS transfer
|
// Request LFS transfer
|
||||||
|
|
||||||
|
@ -54,29 +58,18 @@ export default async function downloadBlobFromPointer(
|
||||||
objects: [info],
|
objects: [info],
|
||||||
};
|
};
|
||||||
|
|
||||||
const { body: lfsInfoBody } = await request({
|
const lfsInfoRes = await fetch(`${url}/info/lfs/objects/batch`, {
|
||||||
url: `${url}/info/lfs/objects/batch`,
|
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers: {
|
headers: {
|
||||||
// Github LFS doesn’t seem to accept this UA, but works fine without any
|
// Github LFS doesn’t seem to accept this UA, but works fine without any
|
||||||
// 'User-Agent': `git/isomorphic-git@${git.version()}`,
|
// 'User-Agent': `git/isomorphic-git@${git.version()}`,
|
||||||
...headers,
|
|
||||||
...authHeaders,
|
...authHeaders,
|
||||||
Accept: "application/vnd.git-lfs+json",
|
Accept: "application/vnd.git-lfs+json",
|
||||||
"Content-Type": "application/vnd.git-lfs+json",
|
"Content-Type": "application/vnd.git-lfs+json",
|
||||||
},
|
},
|
||||||
body: [Buffer.from(JSON.stringify(lfsInfoRequestData))],
|
body: JSON.stringify(lfsInfoRequestData),
|
||||||
});
|
});
|
||||||
|
const lfsInfoResponseData = await lfsInfoRes.json();
|
||||||
const lfsInfoResponseRaw = (await bodyToBuffer(lfsInfoBody)).toString();
|
|
||||||
let lfsInfoResponseData: any;
|
|
||||||
try {
|
|
||||||
lfsInfoResponseData = JSON.parse(lfsInfoResponseRaw);
|
|
||||||
} catch (e) {
|
|
||||||
throw new Error(
|
|
||||||
`Unexpected structure received from LFS server: unable to parse JSON ${lfsInfoResponseRaw}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isValidLFSInfoResponseData(lfsInfoResponseData)) {
|
if (isValidLFSInfoResponseData(lfsInfoResponseData)) {
|
||||||
// Request the actual blob
|
// Request the actual blob
|
||||||
|
@ -85,24 +78,21 @@ export default async function downloadBlobFromPointer(
|
||||||
const lfsObjectDownloadURL = downloadAction.href;
|
const lfsObjectDownloadURL = downloadAction.href;
|
||||||
const lfsObjectDownloadHeaders = downloadAction.header ?? {};
|
const lfsObjectDownloadHeaders = downloadAction.header ?? {};
|
||||||
|
|
||||||
const dlHeaders = {
|
const lfsObjectRes = await fetch(lfsObjectDownloadURL, {
|
||||||
...headers,
|
|
||||||
...authHeaders,
|
|
||||||
...lfsObjectDownloadHeaders,
|
|
||||||
};
|
|
||||||
|
|
||||||
const { body: lfsObjectBody } = await request({
|
|
||||||
url: lfsObjectDownloadURL,
|
|
||||||
method: "GET",
|
method: "GET",
|
||||||
headers: dlHeaders,
|
headers: {
|
||||||
|
...authHeaders,
|
||||||
|
...lfsObjectDownloadHeaders,
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
const blob = await lfsObjectRes.blob();
|
||||||
|
|
||||||
const blob = await bodyToBuffer(lfsObjectBody);
|
if (fs) {
|
||||||
|
// Write LFS cache for this object, if cache path is accessible.
|
||||||
// Write LFS cache for this object, if cache path is accessible.
|
if (await isWriteable(fs, objectPath)) {
|
||||||
if (await isWriteable({ promises: fs }, objectPath)) {
|
await fs.promises.mkdir(path.dirname(objectPath), { recursive: true });
|
||||||
await fs.mkdir(path.dirname(objectPath), { recursive: true });
|
await fs.promises.writeFile(objectPath, blob);
|
||||||
await fs.writeFile(objectPath, blob);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return blob;
|
return blob;
|
||||||
|
|
|
@ -75,11 +75,7 @@ export default async function populateCache(
|
||||||
total: 10,
|
total: 10,
|
||||||
});
|
});
|
||||||
|
|
||||||
await downloadBlobFromPointer(
|
await downloadBlobFromPointer(fs, remoteURL, {}, pointer);
|
||||||
fs,
|
|
||||||
{ http, url: remoteURL },
|
|
||||||
pointer
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "es2018",
|
"target": "es2015",
|
||||||
|
"lib": ["es2020", "DOM"],
|
||||||
"module": "esnext",
|
"module": "esnext",
|
||||||
"moduleResolution": "node",
|
"moduleResolution": "node",
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue