83 lines · 3.7 KB
import { createServerFn } from "@tanstack/react-start";
import { requireViewer, viewerMiddleware } from "./middleware";
import { WORKFLOW_DIR } from "~/server/ci/spec";
import { badRequest } from "~/server/errors";
import { listRefs, resolveRevision } from "~/server/git/refs";
import { withStore } from "~/server/git/store";
import type { GitStore } from "~/server/git/store";
import { commitFileChanges } from "~/server/git/web-commit";
import { limitWrite } from "~/server/limits";
import { requireRepoAccess } from "~/server/repos";
import { repoRef } from "~/lib/validation";
import type { RepoRow } from "~/lib/types";

export const GITHUB_WORKFLOW_DIR = ".github/workflows";
const YAML_FILE = /\.ya?ml$/;

async function workflowNames(store: GitStore, treeSha: string, directory: string): Promise<Map<string, string>> {
  const entry = await store.entryAtPath(treeSha, directory);
  if (!entry || entry.type !== "tree") return new Map();
  const files = (await store.listTree(entry.sha, directory)).filter(
    (file) => file.type === "blob" && YAML_FILE.test(file.name),
  );
  return new Map(files.map((file) => [file.name, file.sha]));
}

/** GitHub workflow files on the default branch that have no same-named file in `.gitfor/workflows` yet. */
async function importable(repo: RepoRow) {
  const revision = resolveRevision(await listRefs(repo), repo.default_branch);
  if (!revision) return { headSha: null, files: [] as Array<{ name: string; sha: string }> };
  return withStore(repo.artifacts_name, async (store) => {
    const commit = await store.commit(revision.sha);
    if (!commit) return { headSha: revision.sha, files: [] };
    const [github, existing] = await Promise.all([
      workflowNames(store, commit.treeSha, GITHUB_WORKFLOW_DIR),
      workflowNames(store, commit.treeSha, WORKFLOW_DIR),
    ]);
    const files = [...github].filter(([name]) => !existing.has(name)).map(([name, sha]) => ({ name, sha }));
    return { headSha: revision.sha, files };
  });
}