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

How to commit a folder and open a Pull Request via Github API?

我想通过 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"
  })
  
}

这感觉像很多API调用都是为了提交一个文件并打开一个PR,我怀疑我做错了什么?

当我创建新的提交时,它不允许我添加任何路径或将 .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 
    }
  ]
})

以下代码正确使用GitHubAPI

// 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)。我认为这一步将是最复杂的,因为对于每个创建的 'folder' 树,还需要创建一个父 'folder' 树来包含新创建的 'folder' 树。所以如果你想修改.github/workflows文件夹,你首先必须在.github/workflows的基础上创建一个新树。假设 tree 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 + {您要添加的文件有多深}

希望这对您有所帮助,我正在研究同一个主题,我需要通过 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
});

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