lgeiger / black-action

A GitHub action that runs black code formatter for Python
MIT License
190 stars 35 forks source link

Would it be possible to write reformatted code back into the PR? #1

Closed cclauss closed 3 years ago

cclauss commented 5 years ago

Use case: I make a modification of a repo's (that I do not own) code and send in a PR. Black-action runs on that PR and instead of failing the run, it replaces the code in my PR with Black formatted code. This would make Black formatting automatic instead of forcing committers to do it manually each time that tests fail.

action "Lint" {
  uses = "lgeiger/black-action@master"
  with:
    repo-token: "${{ secrets.GITHUB_TOKEN }}"
  args = ". --check"
}

https://developer.github.com/actions/managing-workflows/storing-secrets/#github-token-secret

git diff master -U0 | black --diff # would provide a diff for each file For each Python file that had a diff, we could commit the black changes back into the PR.

lgeiger commented 5 years ago

I would really like to have this feature, unfortunately I don't really have time to implement this any time soon. PRs would be very welcome. I'd be happy to help, if you have any questions.

cclauss commented 5 years ago

I will try to get to it. In general, I do this with a pre-commit hook but encapsulating all that complexity into a GitHub Action would be a superpower.

lgeiger commented 5 years ago

@cclauss Thanks for taking a look. An other workflow would be to listen to /black format comments in the PR and push a fix directly to the branch. That way the action doesn't introduce merge conflicts by accident.

jneeven commented 5 years ago

I'm also looking into this, see this fork. It's currently broken, but should hopefully be finished soon. I can't give a timeline though, so feel free to take any code from there and use it in a PR here.

jneeven commented 5 years ago

@lgeiger @cclauss I have finished a minimal working product and submitted a pull request. Please let me know what you think!

peter-evans commented 5 years ago

I have written an action that will raise a pull request for local changes to the Actions workspace. You can check it out here. https://github.com/peter-evans/create-pull-request

There is also an example here where I'm formatting code with autopep8 and using create-pull-request action to raise a PR. https://github.com/peter-evans/autopep8

@cclauss Just to clarify further for your use-case. The create-pull-request action will raise a PR to merge into the branch that triggered the workflow. So if you were to push code with bad formatting to a feature branch, Black-action could run to make fixes locally and then a PR would be created to merge back to that same branch. It's maybe not quite as automatic as you wanted because you need to click "Merge" on the PR that create-pull-request action creates for the changes to be committed to your feature branch.

peter-evans commented 5 years ago

I now have an on: pull_request workflow example for direct push to the pull request branch working. If black-action could expose an exit-code to know whether or not changes were made, a similar workflow could probably be used.

https://github.com/peter-evans/autopep8#direct-push-with-on-pull_request-workflows

cclauss commented 5 years ago

Could you get a status code from black —check . and then run black . if that status code is nonzero?

peter-evans commented 5 years ago

@cclauss I think that could work, yes. However, I figured out a generic solution that should work for any git-tracked modified files during an on: pull_request workflow. Use a repo scoped token instead of GITHUB_TOKEN if you have other pull request checks.

This is the example for Black-action.

name: auto-format
on: pull_request
jobs:
  format:
    # Check if the PR is not from a fork
    if: github.event.pull_request.head.repo.full_name == github.repository
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v1
      - name: black
        uses: lgeiger/black-action@v1.0.1
        with:
          args: .
      - name: Check for modified files
        id: git-check
        run: echo ::set-output name=modified::$(if git diff-index --quiet HEAD --; then echo "false"; else echo "true"; fi)
      - name: Push changes
        if: steps.git-check.outputs.modified == 'true'
        run: |
          git config --global user.name 'Peter Evans'
          git config --global user.email 'peter-evans@users.noreply.github.com'
          git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/$GITHUB_REPOSITORY
          git checkout $GITHUB_HEAD_REF
          git commit -am "Automated changes"
          git push

Also wrote a blog post about this solution with a bunch of other examples. https://peterevans.dev/posts/github-actions-how-to-automate-code-formatting-in-pull-requests/

LuckierDodge commented 5 years ago

@peter-evans, as a personal anecdote, I had to modify your code slightly to get it to work with an on: push hook. Not sure if it directly related to the change in hook, but the as-is example you provided complained about not being on a branch during the final push. Here's the modified version that works for my repository:

name: auto-format
on: push
jobs:
    format:
        runs-on: ubuntu-latest
        steps:
            - uses: actions/checkout@v1
            - name: black
              uses: lgeiger/black-action@master
              with:
                  args: .
            - name: Check for modified files
              id: git-check
              run: echo ::set-output name=modified::$(if git diff-index --quiet HEAD --; then echo "false"; else echo "true"; fi)
            - name: Push changes
              if: steps.git-check.outputs.modified == 'true'
              run: |
                  git config --global user.name 'NIU Rover'
                  git config --global user.email 'niurover@gmail.com'
                  git remote set-url origin https://x-access-token:${{ secrets.GITHUB_TOKEN }}@github.com/$GITHUB_REPOSITORY
                  git checkout `basename $GITHUB_REF`
                  git commit -am "Automated changes"
                  git push origin HEAD:`basename $GITHUB_REF`
cclauss commented 5 years ago

@LuckierDodge it easier on a repo that you own but it is quite difficult on a repo that someone else owns... cclauss/autoblack#8

With the new yml approach, you don’t need to have your own Docker image and you don’t need entrypoint.sh

peter-evans commented 5 years ago

@LuckierDodge Yes, what I posted only works for on: pull_request because it's checking out GITHUB_HEAD_REF which is the merging branch. This variable only makes sense (and is only available) for on: pull_request workflows. For on: push workflows you can just checkout GITHUB_REF as you discovered.

peter-evans commented 5 years ago

It turns out that the workflow I posted here doesn't work on PRs raised from forks. I've updated it to stop it running on those PRs with the following job condition. It limits the usefulness of this workflow considerably, but it looks unavoidable at the moment due to limitations on forks.

jobs:
  format:
    # Check if the PR is not from a fork
    if: github.event.pull_request.head.repo.full_name == github.repository
cclauss commented 4 years ago

Look at the commits in this PR... TheAlgorithms/Python#1779

The change to the first file was made by me in the first commit. The changes to the other three files was made in the second commit by the autoblack GitHub Action.

We use autoblack to add a second, automatic commit to a PR that black formats the code. This only succeeds if the author of the PR has write access to this repo.

rickstaa commented 3 years ago

I do this by using the git-auto-commit-action of stefanzweifel.

name: auto-format
on: push
jobs:
    format:
        runs-on: ubuntu-latest
        steps:
            - uses: actions/checkout@v1
            - name: black
              uses: lgeiger/black-action@master
              with:
                  args: .
      - name: Apply black formatting if reviewdog found formatting errors
        if: "${{ failure() }}"
        run: |
          black .
      - name: Commit black formatting results
        if: "${{ failure() }}"
        uses: stefanzweifel/git-auto-commit-action@v4
        with:
          commit_message: ":art: Format Python code with psf/black push"
cclauss commented 3 years ago

Yes. With actions/checkout@v1 it is possible to write back but that is disabled in actions/checkout@v2.

rickstaa commented 3 years ago

@cclauss Thanks for pointing that out I was not aware of that!

rickstaa commented 3 years ago

@cclauss Judging from the fact that you use v1 in your (very helpful) examples, I assume there currently is no (easy) way to autoformat the code using actions/checkout@v2?

rickstaa commented 3 years ago

@cclauss I did some testing, and for me, the action appears to be working with checkout@v2 when you make sure you resolve the detached head. Stefanzweifel also states something about this in the repository readme.

cclauss commented 3 years ago

A_W_E_S_O_M_E!!! Thanks for the detective work! Closing.