简体   繁体   English

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

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

I want to commit a pipeline config for a user via the Github API.我想通过 Github API 为用户提交管道配置。

So far I am able to commit just a file named main.yaml into the root directory of the repo but I need the file to sit within .github/workflows/main.yaml .到目前为止,我只能将一个名为main.yaml的文件提交到 repo 的根目录中,但我需要该文件位于.github/workflows/main.yaml中。

The code I have so far is this:到目前为止我的代码是这样的:

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"
  })
  
}

This feels like a lot of API calls all for just committing one file and opening a PR, I suspect I am doing something wrong?这感觉就像很多 API 调用都是为了提交一个文件并打开一个 PR,我怀疑我做错了什么?

When I create the new commit, it doesn't allow me to add any path or add .github/workflows/main.yaml to the end of the URL as I get a 404. Is there any way I can update this commit to commit to a folder instead of the root directory?当我创建新的提交时,它不允许我添加任何路径或将.github/workflows/main.yaml添加到 URL 的末尾,因为我得到了 404。有什么方法可以更新此提交以提交到文件夹而不是根目录?

To summarise, How can I make a simple commit of .github/workflows/main.yaml to a new branch and open a PR for it?总而言之,如何将.github/workflows/main.yaml简单提交到新分支并为其打开 PR?

Create tree example:创建树示例:

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
    },
  ]
})

Creating the tree创建树

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 
    }
  ]
})

The following code does not use the GitHub API properly以下代码没有正确使用 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"
  })

You cannot directly edit the contents of a file.您不能直接编辑文件的内容。 You need to use the trees API to create an entirely new tree based off of the original tree.您需要使用树 API 在原始树的基础上创建一棵全新的树。

Steps脚步

  1. Create the blob for the new file ( https://docs.github.com/en/rest/reference/git#create-a-blob ).为新文件创建 blob ( https://docs.github.com/en/rest/reference/git#create-a-blob )。 You already did this step good job.您已经很好地完成了这一步。

  2. Get the tree that you want to make your new branch based off of ( https://docs.github.com/en/rest/reference/repos#get-a-branch ).获取您想要创建新分支的树( https://docs.github.com/en/rest/reference/repos#get-a-branch )。 Note that you have to get the tree sha , not the commit sha .请注意,您必须获取树 sha ,而不是提交 sha

  3. Create a new tree with that file added ( https://docs.github.com/en/rest/reference/git#create-a-tree ).使用添加的文件创建一个新树 ( https://docs.github.com/en/rest/reference/git#create-a-tree )。 I think this step will be the most complicated, because for each 'folder' tree created, a parent 'folder' tree will also need to be created to contain the newly created 'folder' tree.我认为这一步将是最复杂的,因为对于每个创建的“文件夹”树,还需要创建一个父“文件夹”树以包含新创建的“文件夹”树。 So if you want to modify the .github/workflows folder, you'll first have to create a new tree based on .github/workflows .所以如果你想修改.github/workflows文件夹,你首先必须创建一个基于.github/workflows的新树。 Let's say that tree sha was abc... .比方说 tree sha 是abc... Then you'll need to create a new tree based on the .github folder, and make the workflows dir abc... , and not the old one.然后,您需要基于.github文件夹创建一棵新树,并将workflows目录设为abc... ,而不是旧目录。

  4. Create a commit ( https://docs.github.com/en/rest/reference/git#create-a-commit ).创建一个提交( https://docs.github.com/en/rest/reference/git#create-a-commit )。 Use the sha of the root tree you created in the previous step.使用您在上一步中创建的根树的 sha。

  5. Create a new branch ( https://docs.github.com/en/rest/reference/git#create-a-reference ).创建一个新分支 ( https://docs.github.com/en/rest/reference/git#create-a-reference )。 In the question code, you created it before you created the commit, which doesn't make sense.在问题代码中,您在创建提交之前创建了它,这是没有意义的。 You need to create it after you create the commit, so that it's head will point to the sha of the commit you created.您需要在创建提交创建它,以便它的头部指向您创建的提交的 sha。

  6. Create the pull request ( https://docs.github.com/en/rest/reference/pulls#create-a-pull-request ).创建拉取请求 ( https://docs.github.com/en/rest/reference/pulls#create-a-pull-request )。 You already have this step in your code.您的代码中已有此步骤。

Here is a visual that explains the steps 2 and 3 for adding a main.yml file to .github/workflows :这是一个视觉效果,解释了将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)
          ...
      ...
  ...

There are three --> s in the visual.视觉对象中有三个--> Each --> is a request to make.每个-->都是一个请求。

  1. Start with creating the ^c tree, which is based off of the c tree and has the added main.yml file.从创建^c树开始,它基于c树并添加了main.yml文件。
  2. Create the ^b tree, which is based off of b but has ^c in it.创建^b树,它基于b但其中包含^c
  3. Create the ^a tree, which is based off of a but has ^b in it.创建^a树,它基于a但其中包含^b

And those are the steps to creating a simple simple, but complicated to create pull request.这些是创建 简单 但复杂的拉取请求的步骤。

It's surprising how many API calls are needed for this.令人惊讶的是为此需要多少次 API 调用。 5 + {how deep the file you want to add is} 5 + {你要添加的文件有多深}

I hope this can help you, I am working on the same topic where I need to update files inside .github/workflows folder by API and, I think I found a better and easy way.我希望这可以帮助你,我正在研究同一个主题,我需要通过 API 更新.github/workflows文件夹中的文件,我想我找到了一个更好更简单的方法。

I don't know since when but, with this endpoint https://docs.github.com/en/rest/reference/repos#create-or-update-file-contents with the right permissions (scope workflow in your authToken), you can create and update files into the .github/workflows folder.我不知道从什么时候开始,但是,这个端点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
});

With this, I removed a lot of lines of code and solved the issue on my side, I hope is helpful for you too.有了这个,我删除了很多代码行并解决了我这边的问题,希望对你也有帮助。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM