Edits history of script submission #23949 for ' Commit files (gitlab)'

  • bunnative
    One script reply has been approved by the moderators
    Ap­pro­ved
    //native
    
    /**
     * Commit files
     * Create or update one or more files on a branch in a single commit. Each file is created when it doesn't exist on the branch and updated otherwise. When the branch doesn't exist, set start_branch to create it from that branch.
     */
    export async function main(
      auth: RT.Gitlab,
      project: string,
      branch: string,
      commit_message: string,
      files: { file_path: string; content: string; encoding?: "text" | "base64" }[],
      start_branch: string | undefined,
      author_name: string | undefined,
      author_email: string | undefined
    ) {
      const api = `${(auth.baseUrl || "https://gitlab.com").replace(/\/+$/, "")}/api/v4/projects/${encodeURIComponent(project)}/repository`
      const paths = files.map((f) => f?.file_path)
      const repeated = paths.find((path, i) => paths.indexOf(path) !== i)
      if (!files.length || paths.some((path) => !path) || repeated) {
        throw new Error(
          repeated
            ? `${repeated} is listed twice in files`
            : "Every file needs a file_path"
        )
      }
      const headers = {
        Authorization: `Bearer ${auth.token}`,
        Accept: "application/json",
      }
      // A 404 is a missing branch only when GitLab says so: it also means a project the token can't see.
      const branchResponse = await fetch(
        `${api}/branches/${encodeURIComponent(branch)}`,
        { headers }
      )
      const branchText = await branchResponse.text()
      const branchExists = branchResponse.ok
      if (!branchExists && !branchText.includes("Branch Not Found")) {
        throw new Error(`${branchResponse.status} ${branchText}`)
      }
      if (!branchExists && !start_branch) {
        throw new Error(
          `Branch ${branch} does not exist: set start_branch to create it`
        )
      }
      // Each file is created or updated against the branch, or start_branch for a new branch.
      const ref = branchExists ? branch : start_branch!
      // Ten checks at a time, so a large commit doesn't burst the files API rate limit.
      const actions = []
      for (let i = 0; i < files.length; i += 10) {
        actions.push(
          ...(await Promise.all(
            files.slice(i, i + 10).map(async (f) => {
              const head = await fetch(
                `${api}/files/${encodeURIComponent(f.file_path)}?ref=${encodeURIComponent(ref)}`,
                { method: "HEAD", headers }
              )
              if (!head.ok && head.status !== 404) {
                throw new Error(`${head.status} checking ${f.file_path} on ${ref}`)
              }
              return {
                action: head.ok ? "update" : "create",
                file_path: f.file_path,
                content: f.content,
                encoding: f.encoding || "text",
              }
            })
          ))
        )
      }
    
      const response = await fetch(`${api}/commits`, {
        method: "POST",
        headers: { ...headers, "Content-Type": "application/json" },
        body: JSON.stringify({
          branch,
          start_branch: branchExists ? undefined : start_branch,
          commit_message,
          actions,
          author_name: author_name || undefined,
          author_email: author_email || undefined,
        }),
      })
    
      if (!response.ok) {
        throw new Error(`${response.status} ${await response.text()}`)
      }
    
      return await response.json()
    }
    

    Submitted by hugo989 8 hours ago