如何通过Github API提交文件夹并打开Pull Request?

3
我希望通过Github API为用户提交一个流水线配置。
到目前为止,我只能将名为main.yaml的文件提交到仓库的根目录,但我需要该文件位于.github/workflows/main.yaml中。
我目前所拥有的代码如下:
const commitWorkflowConfig = async ({
  ownerName,
  repoName
}) => {

  const pipelineConfig = fs.readFileSync(__dirname + '/../../../templates/main.yaml', { encoding: "utf-8" })

  // Create Blob from the content
  const blob = await axios.post(`https://api.github.com/repos/${ownerName}/${repoName}/git/blobs`, {
    content: pipelineConfig,
    encoding: "utf-8"
  })

  // Get last commit hash of Master
  const {
    commit: {
      sha: masterSha
    }
  } = await axios.get(`https://api.github.com/repos/${ownerName}/${repoName}/branches/master`)

  // Create new branch from master
  const branch = await axios.post(`https://api.github.com/repos/${ownerName}/${repoName}/git/refs`, 
  {
    "ref": "refs/heads/workflow-pipeline",
    "sha": masterSha
  })

  // Create commit to new branch
  const commit = await axios.put(`https://api.github.com/repos/${ownerName}/${repoName}/contents/main.yaml`, {
    message: "New commit",
    content: pipelineConfig,
    sha: blob.sha,
    branch: "workflow-pipeline"
  })

  // Open Pull Request
  const response = await axios.post(`https://api.github.com/repos/${ownerName}/${repoName}/pulls`, {
    title: "New PR",
    head: "workflow-pipeline",
    base: "master"
  })
  
}    const commitWorkflowConfig = async ({
  ownerName,
  repoName
}) => {

  const pipelineConfig = fs.readFileSync(__dirname + '/../../../templates/main.yaml', { encoding: "utf-8" })

  // Create Blob from the content
  const blob = await axios.post(`https://api.github.com/repos/${ownerName}/${repoName}/git/blobs`, {
    content: pipelineConfig,
    encoding: "utf-8"
  })

  // Get last commit hash of Master
  const {
    commit: {
      sha: masterSha
    }
  } = await axios.get(`https://api.github.com/repos/${ownerName}/${repoName}/branches/master`)

  // Create new branch from master
  const branch = await axios.post(`https://api.github.com/repos/${ownerName}/${repoName}/git/refs`, 
  {
    "ref": "refs/heads/workflow-pipeline",
    "sha": masterSha
  })

  // Create commit to new branch
  const commit = await axios.put(`https://api.github.com/repos/${ownerName}/${repoName}/contents/main.yaml`, {
    message: "New commit",
    content: pipelineConfig,
    sha: blob.sha,
    branch: "workflow-pipeline"
  })

  // Open Pull Request
  const response = await axios.post(`https://api.github.com/repos/${ownerName}/${repoName}/pulls`, {
    title: "New PR",
    head: "workflow-pipeline",
    base: "master"
  })
  
}

这感觉像是为了提交一个文件和打开PR而进行了许多API调用,我怀疑我做错了什么?
当我创建新提交时,它不允许我添加任何路径或将.github/workflows/main.yaml添加到URL的末尾,因为我会得到404错误。有没有办法更新此提交以提交到文件夹而不是根目录?
总之,我如何将.github/workflows/main.yaml简单提交到新分支并为其打开PR?
创建树示例:
const tree = await this.post(`https://api.github.com/repos/${ownerName}/${repoName}/git/trees`, {
  base_tree: masterSha,
  tree: [
    {
      path: ".github",
      mode: "040000",
      type: "tree"
    },
    {
      path: ".github/workflow",
      mode: "040000",
      type: "tree"
    },
    {
      path: ".github/workflow/main.yaml",
      mode: "100755",
      type: "tree",
      content: pipelineConfig
    },
  ]
})

创建树
const { tree } = await axios.post(`https://api.github.com/repos/${ownerName}/${repoName}/git/trees`, {
  base_tree: masterSha,
  tree: [
    {
      path: "workflows/main.yaml",
      mode: "100644", 
      type: "blob",
      sha
    }
  ]
})

const workflowTree = tree.find(t => t.path === "workflows");

await axios.post(`https://api.github.com/repos/${ownerName}/${repoName}/git/trees`, {
  base_tree: masterSha,
  tree: [
    {
      path: ".github/workflows",
      mode: workflowTree.mode, // "040000"
      type: workflowTree.type, // "tree"
      sha: workflowTree.sha 
    }
  ]
})

“这感觉像是为了提交一个文件和打开PR而进行了很多API调用。” 当我使用GitHub API时,我也有同样的感觉。另一种选择是只生成git命令。 - programmerRaj
2个回答

3
以下代码并没有正确使用GitHub API。
// Create commit to new branch
  const commit = await axios.put(`https://api.github.com/repos/${ownerName}/${repoName}/contents/main.yaml`, {
    message: "New commit",
    content: pipelineConfig,
    sha: blob.sha,
    branch: "workflow-pipeline"
  })

您不能直接编辑文件内容。您需要使用树API基于原始树创建一个全新的树。

步骤

  1. 创建新文件的 blob (https://docs.github.com/en/rest/reference/git#create-a-blob)。你已经完成了这个步骤,干得好。

  2. 获取你想要基于其创建新分支的树(https://docs.github.com/en/rest/reference/repos#get-a-branch)。注意,你需要获取 tree sha,而不是 commit sha

  3. 创建一个包含该文件的新树 (https://docs.github.com/en/rest/reference/git#create-a-tree)。我认为这一步将是最复杂的,因为每次创建“文件夹”树时,还必须创建一个包含新创建的“文件夹”树的父“文件夹”树。因此,如果你想修改 .github/workflows 文件夹,你首先需要基于 .github/workflows 创建一个新的树。假设该树的 sha 是 abc...。然后你需要基于 .github 文件夹创建一个新的树,并将 workflows 目录设置为 abc...,而不是旧目录。

  4. 创建提交记录 (https://docs.github.com/en/rest/reference/git#create-a-commit)。使用你在上一步中创建的根树的 sha。

  5. 创建新分支 (https://docs.github.com/en/rest/reference/git#create-a-reference)。在这个问题中的代码中,你在创建提交记录之前创建了分支,这是没有意义的。你需要在创建提交记录之后创建它,这样它的头部将指向你创建的提交记录的 sha。

  6. 创建拉取请求 (https://docs.github.com/en/rest/reference/pulls#create-a-pull-request)。你已经在你的代码中完成了这一步。

这里有一张图示,解释了将 main.yml 文件添加到 .github/workflows 的步骤2和3:
Original tree        | New Tree (but the '.github' tree references ^b, not b)
- sha: a           --> - sha: ^a
- files:               - files:
  - .github        -->   - .github (but the 'workflows' tree references ^c, not c)
    - sha: b               - sha: ^b
    - files:               - files
      - workflows  -->       - workflows (but with the main.yml)
        - sha: c               - sha: ^c
        - files:               - files:
                                 - main.yml (reference the sha of the blob you created)
          ...
      ...
  ...


在视觉上有三个-->。每个-->都是一个请求。
  1. 首先创建基于c树的^c树,并添加main.yml文件。
  2. 创建基于b^b树,其中包含^c
  3. 创建基于a^a树,其中包含^b
这些是创建一个简单简单但难以创建的拉取请求的步骤。
令人惊讶的是,需要多少API调用。5 + {要添加的文件的深度}

好的,那个流程很有道理。关于创建树形结构,我已经在我的原始帖子中添加了一个示例片段,但是将我的base_tree作为主SHA哈希值是否正确?而且每个树对象似乎需要包含sha或内容,对于子目录树中的这些值,我应该使用什么? - Stretch0
你的 base_tree 已经接近正确了。只需使用 tree hash 而不是 commit hash。我更新了答案,包括获取基本树 sha 的步骤。 - programmerRaj
我注意到你在创建树时使用了 path: ".github/workflow/main.yaml"。我认为你一次只能创建一个“文件夹”,但如果你的方法有效,请告诉我,这样我就可以更新答案以使用你的方法。 - programmerRaj
我的方法不起作用。我认为你是对的,一次只能创建一个文件夹。 - Stretch0
那么这是否意味着我需要对 /repos/${ownerName}/${repoName}/git/trees 进行两次 API 调用?一次用于创建目录,另一次用于创建文件?如果是空目录,负载中的 sha 或 content 字段应该是什么? - Stretch0
显示剩余3条评论

0

希望这可以帮到你,我正在处理同样的主题,需要通过API更新.github/workflows文件夹中的文件,我认为我找到了更好、更简单的方法。

我不知道从什么时候开始,但是使用此端点https://docs.github.com/en/rest/reference/repos#create-or-update-file-contents和正确的权限(在您的authToken中使用workflow范围),您可以创建和更新.github/workflows文件夹中的文件。

// to create a new file
await octokit.request('PUT /repos/{owner}/{repo}/contents/{path}', {
  owner: 'octocat',
  repo: 'hello-world',
  path: '.github/workflows/your-file.yml',
  message: 'Your commit message',
  content: 'contentInBase64'
});

// to update a file

// get the sha of the file that you want to update
const { 
  data: contentData 
} = await octokit.request('GET /repos/{owner}/{repo}/contents/{path}', {
  owner: 'octocat',
  repo: 'hello-world',
  path: '.github/workflows/your-file.yml',
});

const shaOfCurrentFileToUpdate = contentData.sha;

await octokit.request('PUT /repos/{owner}/{repo}/contents/{path}', {
  owner: 'octocat',
  repo: 'hello-world',
  path: '.github/workflows/your-file.yml',
  message: 'Your new commit message',
  content: 'newContentInBase64'
  sha: shaOfCurrentFileToUpdate
});

通过这个方法,我删除了很多代码行,并解决了我的问题,希望对你也有所帮助。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接