217 lines · 8.4 KB
import { env } from "cloudflare:workers";
import { GitStore } from "../git/store";
import { objectKey, STATIC_LIMITS } from "./config";
import type { ManifestFile } from "./config";

export type PlannedFile = { path: string; sha: string };
export type CollectedFiles = Record<string, ManifestFile>;

const TAR_BLOCK = 512;
const encoder = new TextEncoder();
const decoder = new TextDecoder();

export class DeployError extends Error {}

function normalizeFolder(folder: string): string {
  return folder.replace(/^\/+|\/+$/g, "");
}

/** Normalizes an archive path relative to `folder`; null when outside it or unsafe. */
function relativePath(raw: string, folder: string): string | null {
  const parts = raw
    .replace(/^\.?\/+/, "")
    .split("/")
    .filter((part) => part && part !== ".");
  if (parts.some((part) => part === "..")) return null;
  const path = parts.join("/");
  if (!folder) return path || null;
  return path.startsWith(`${folder}/`) ? path.slice(folder.length + 1) : null;
}

/** Lists the files of a branch folder, bounded by the deploy file cap. */
export async function planGitFiles(
  artifactsName: string,
  sha: string,
  folder: string,
  maxFiles: number = STATIC_LIMITS.maxFiles,
): Promise<PlannedFile[]> {
  const store = await GitStore.open(artifactsName);
  try {
    const commit = await store.commit(sha);