Skip to content

Commit

Permalink
Initial commit
Browse files Browse the repository at this point in the history
  • Loading branch information
MrThearMan committed Sep 23, 2024
0 parents commit be90528
Show file tree
Hide file tree
Showing 31 changed files with 3,290 additions and 0 deletions.
Empty file added .git-blame-ignore-revs
Empty file.
57 changes: 57 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# Default behavior
# ============
* text=auto

# Source files
# ============
*manage.py text diff=python eol=lf
*.py text diff=python
*.pxd text diff=python
*.py3 text diff=python
*.pyw text diff=python
*.pyx text diff=python
*.pyz text diff=python
*.pyi text diff=python

# Plain text
# ============
*.js text diff=js
*.css text diff=css
*.html text diff=html
*.md text diff=markdown
*.json text diff=json eol=lf
*.toml text diff=toml eol=lf
*.yml text diff=yaml eol=lf
*.yaml text diff=yaml eol=lf

# Archives
# ============
*.7z filter=lfs diff=lfs merge=lfs -text
*.br filter=lfs diff=lfs merge=lfs -text
*.gz filter=lfs diff=lfs merge=lfs -text
*.tar filter=lfs diff=lfs merge=lfs -text
*.zip filter=lfs diff=lfs merge=lfs -text

# Binary files
# ============
*.db binary
*.p binary
*.pkl binary
*.pickle binary
*.pyc binary export-ignore
*.pyo binary export-ignore
*.pyd binary
*.png binary
*.jpg binary

# Unix specific
# ============
*.sh text eol=lf
*.bash text eol=lf
*.fish text eol=lf

# Windows specific
# ============
*.cmd text eol=crlf
*.bat text eol=crlf
*.ps1 text eol=crlf
29 changes: 29 additions & 0 deletions .github/workflows/fast-forward.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Fast-forward

on:
issue_comment:
types:
- created
- edited

jobs:
fast-forward:

name: Fast-forward
runs-on: ubuntu-latest

permissions:
contents: write # Allows merging the PR.
pull-requests: write # Writing comments on PRs.
issues: write # Also required for posting comments on PRs.

# Only run if the comment is one of the defined keywords.
if: >-
${{
github.event.issue.pull_request
&& contains(fromJSON('["/fast-forward", "/ff"]'), github.event.comment.body)
}}
steps:
- name: Fast-forward
uses: MrThearMan/prff@main
23 changes: 23 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
name: Test

on:
push:
branches:
- main
pull_request:
workflow_dispatch:

jobs:
test:

name: Test
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'

- run: pip install pytest
- run: pytest -s -vv --log-cli-level=DEBUG --disable-warnings .
Empty file added .gitignore
Empty file.
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) Matti Lamppu

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
45 changes: 45 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Fast-forward merge for pull requests

This action adds the ability to fast-forward merge pull requests by
commenting a predefined comment on the pull request.

Example:

```yaml
name: Fast-forward

on:
issue_comment:
types:
- created
- edited

jobs:
fast-forward:

name: Fast-forward
runs-on: ubuntu-latest

permissions:
contents: write # Allows merging the PR.
pull-requests: write # Writing comments on PRs.
issues: write # Also required for posting comments on PRs.

# Only run if the comment is one of the defined keywords.
if: >-
${{
github.event.issue.pull_request
&& contains(fromJSON('["/fast-forward", "/ff"]'), github.event.comment.body)
}}
steps:
- name: Fast-forward
uses: MrThearMan/prff@v1
```
With this, by commenting either `/ff` or `/fast-forward` on the pull request, the action
will run to check if the fast forward merge is possible, and that the commenter has
the requird permissions to make that merge, and then make the merge if those checks pass.

If this operation is unsuccessful, a comment will be added to the pull request
with the error. A detailed report is also written to the actions summary.
25 changes: 25 additions & 0 deletions action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Fast-forward
description: Fast-forward merge a pull request.

inputs:
github_token:
description: GitHub Actions token.
default: ${{ github.token }}

runs:
using: "composite"
steps:
- uses: actions/setup-python@v5
with:
python-version: '3.12'

- name: Fast-forward
id: fast-forward
env:
GITHUB_TOKEN: ${{ inputs.github_token }}
PYTHONUNBUFFERED: "1"
PYTHONDONTWRITEBYTECODE: "1"
PYTHONFAULTHANDLER: "1"
PYTHONENCODING: "utf-8"
run: python ${{ github.action_path }}/main.py
shell: bash
33 changes: 33 additions & 0 deletions main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#!/usr/bin/env python

from prff.exception import PullRequestFastForwardError
from prff.fast_forward import fast_forward_pull_request
from prff.github_actions import load_event_data, write_job_summary
from prff.github_api import add_rocket_reaction
from prff.logging import logger

if __name__ == "__main__":
exit_code: int = 0

try:
event_data = load_event_data()
fast_forward_pull_request(
pull_request_url=event_data.pull_request_url,
permissions_url=event_data.permissions_url,
)

except PullRequestFastForwardError as error:
logger.error(error)
exit_code = 1

except Exception as error: # noqa: BLE001
logger.exception("An unexpected error occurred", exc_info=error)
exit_code = 1

else:
add_rocket_reaction(reactions_url=event_data.reactions_url)

finally:
write_job_summary()

raise SystemExit(exit_code)
Empty file added prff/__init__.py
Empty file.
35 changes: 35 additions & 0 deletions prff/command.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
from __future__ import annotations

import subprocess
from dataclasses import dataclass
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from pathlib import Path

__all__ = [
"run_command",
]


@dataclass
class CommandResult:
out: str | None
err: str | None
exit_code: int


def run_command(command: str, *, directory: Path | str | None = None) -> CommandResult:
"""
Run a command in the given directory using subprocess.
:param command: The command string to run.
:param directory: The directory to run the command in.
"""
process = subprocess.Popen(command.split(" "), stdout=subprocess.PIPE, stderr=subprocess.PIPE, cwd=directory)
stdout, stderr = process.communicate()

error = stderr.decode().strip() or None
result = stdout.decode().strip() if not error else None

return CommandResult(out=result, err=error, exit_code=process.returncode)
7 changes: 7 additions & 0 deletions prff/constants.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import os

REPO_PATH = "./clone"
GITHUB_TOKEN = os.environ["GITHUB_TOKEN"]
GITHUB_ACTOR = os.environ["GITHUB_ACTOR"]
SUMMARY_FILE = os.environ["GITHUB_STEP_SUMMARY"]
EVENT_PATH = os.environ["GITHUB_EVENT_PATH"]
7 changes: 7 additions & 0 deletions prff/exception.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
__all__ = [
"PullRequestFastForwardError",
]


class PullRequestFastForwardError(Exception):
"""Base class for exceptions in this library."""
32 changes: 32 additions & 0 deletions prff/fast_forward.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
from __future__ import annotations

from prff.git import clone_repo_at_branch, create_branch, fetch_commit, push_branch_to_commit, validate_fast_forward
from prff.github_api import fetch_user_permissions, post_error_comment, validate_push_permissions
from prff.pull_request import PullRequestData

__all__ = [
"fast_forward_pull_request",
]


def fast_forward_pull_request(*, pull_request_url: str, permissions_url: str) -> None:
pull_request = PullRequestData.from_github(url=pull_request_url)

try:
permissions = fetch_user_permissions(url=permissions_url)
validate_push_permissions(permissions=permissions)

clone_repo_at_branch(repo_url=pull_request.base_clone_url, branch_name=pull_request.base_branch_name)
fetch_commit(repo_url=pull_request.pr_clone_url, commit_sha=pull_request.pr_head_sha)
create_branch(branch_name=pull_request.pr_branch_name, commit_sha=pull_request.pr_head_sha)
pull_request.fix_base_sha()

validate_fast_forward(base_sha=pull_request.base_head_sha, head_sha=pull_request.pr_head_sha)
push_branch_to_commit(branch_name=pull_request.base_branch_name, commit_sha=pull_request.pr_head_sha)

# TODO: Trigger another workflow after merging:
# https://docs.github.com/en/actions/writing-workflows/choosing-when-your-workflow-runs/events-that-trigger-workflows#repository_dispatch

except Exception as error:
post_error_comment(error=str(error), comments_url=pull_request.comments_url)
raise
Loading

0 comments on commit be90528

Please sign in to comment.