mirror of
https://github.com/ditkrg/todo-to-issue-action.git
synced 2026-01-22 22:06:43 +00:00
Initial commit
This commit is contained in:
commit
bf153fe2df
17
.github/workflows/workflow.yaml
vendored
Normal file
17
.github/workflows/workflow.yaml
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
name: "Workflow"
|
||||
on: ["push"]
|
||||
jobs:
|
||||
build:
|
||||
runs-on: "ubuntu-latest"
|
||||
steps:
|
||||
- uses: "actions/checkout@master"
|
||||
- name: "TODO to Issue"
|
||||
uses: "alstr/todo-to-issue-action@master"
|
||||
with:
|
||||
REPO: ${{ github.repository }}
|
||||
HEAD: ${{ github.event.head }}
|
||||
BEFORE: ${{ github.event.before }}
|
||||
SHA: ${{ github.sha }}
|
||||
TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
LABEL: "# TODO"
|
||||
id: "todo"
|
||||
11
Dockerfile
Normal file
11
Dockerfile
Normal file
@ -0,0 +1,11 @@
|
||||
FROM python:3-slim AS builder
|
||||
ADD . /app
|
||||
WORKDIR /app
|
||||
|
||||
RUN pip install --target=/app requests
|
||||
|
||||
FROM gcr.io/distroless/python3-debian10
|
||||
COPY --from=builder /app /app
|
||||
WORKDIR /app
|
||||
ENV PYTHONPATH /app
|
||||
CMD ["/app/main.py"]
|
||||
79
README.md
Normal file
79
README.md
Normal file
@ -0,0 +1,79 @@
|
||||
# TODO to Issue Action
|
||||
|
||||
This action will convert your `# TODO` comments to GitHub issues when a new commit is pushed.
|
||||
|
||||
It will also close an issue when a `# TODO` is removed in a pushed commit.
|
||||
|
||||
The `# TODO` comment is commonly used in Python, but this can be customised to whatever you want.
|
||||
|
||||
## Usage
|
||||
|
||||
Create a workflow file in your .github/workflows directory as follows:
|
||||
|
||||
### workflow.yaml
|
||||
|
||||
name: "Workflow"
|
||||
on: ["push"]
|
||||
jobs:
|
||||
build:
|
||||
runs-on: "ubuntu-latest"
|
||||
steps:
|
||||
- uses: "actions/checkout@master"
|
||||
- name: "TODO to Issue"
|
||||
uses: "alstr/todo-to-issue-action@master"
|
||||
with:
|
||||
REPO: ${{ github.repository }}
|
||||
BEFORE: ${{ github.event.before }}
|
||||
SHA: ${{ github.sha }}
|
||||
TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
LABEL: "# TODO"
|
||||
id: "todo"
|
||||
|
||||
### Inputs
|
||||
|
||||
| Input | Description |
|
||||
|----------|-------------|
|
||||
| `REPO` | The path to the repository where the action will be used, e.g. 'alstr/my-repo' (automatically set) |
|
||||
| `BEFORE` | The SHA of the last pushed commit (automatically set) |
|
||||
| `SHA` | The SHA of the latest commit (automatically set) |
|
||||
| `TOKEN` | The GitHub access token to allow us to retrieve, create and update issues (automatically set) |
|
||||
| `LABEL` | The label that will be used to identify TODO comments (by default this is `# TODO` for Python) |
|
||||
|
||||
## Examples
|
||||
|
||||
### Adding TODOs
|
||||
|
||||
def hello_world():
|
||||
# TODO Come up with a more imaginative greeting
|
||||
print('Hello world!')
|
||||
|
||||
This will create an issue called "Come up with a more imaginative greeting".
|
||||
|
||||
**The action expects a space to follow the `TODO` label.**
|
||||
|
||||
Should the title be longer than 50 characters, it will be truncated for the issue title.
|
||||
|
||||
The full title will be included in the issue body and a `todo` label will be attached to the issue.
|
||||
|
||||
### Removing TODOs
|
||||
|
||||
def hello_world():
|
||||
print('Hello world!')
|
||||
|
||||
Removing the `# TODO` comment will close the issue on push.
|
||||
|
||||
### Updating TODOs
|
||||
|
||||
def hello_world():
|
||||
# TODO Come up with a more imaginative greeting, like "Greetings world!"
|
||||
print('Hello world!')
|
||||
|
||||
Should you change the `# TODO` text, this will currently create a new issue, so bear that in mind.
|
||||
|
||||
This may be updated in future.
|
||||
|
||||
## Thanks
|
||||
|
||||
Thanks to Jacob Tomlinson for his handy overview of GitHub Actions:
|
||||
|
||||
https://www.jacobtomlinson.co.uk/posts/2019/creating-github-actions-in-python/
|
||||
6
action.yaml
Normal file
6
action.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
name: "TODO to Issue"
|
||||
description: "Converts IDE TODO comments to GitHub issues"
|
||||
author: "Alastair Mooney"
|
||||
runs:
|
||||
using: "docker"
|
||||
image: "Dockerfile"
|
||||
97
main.py
Normal file
97
main.py
Normal file
@ -0,0 +1,97 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Convert IDE TODOs to GitHub issues."""
|
||||
|
||||
import os
|
||||
import requests
|
||||
import re
|
||||
import json
|
||||
from time import sleep
|
||||
|
||||
|
||||
base_url = 'https://api.github.com/repos/'
|
||||
|
||||
|
||||
def main():
|
||||
repo = os.getenv('INPUT_REPO')
|
||||
before = os.getenv('INPUT_BEFORE')
|
||||
sha = os.getenv('INPUT_SHA')
|
||||
label = os.getenv('INPUT_LABEL')
|
||||
params = {
|
||||
'access_token': os.getenv('INPUT_TOKEN')
|
||||
}
|
||||
|
||||
# Let's compare the last two pushed commits.
|
||||
diff_url = f'{base_url}{repo}/compare/{before}...{sha}'
|
||||
diff_headers = {
|
||||
'Accept': 'application/vnd.github.v3.diff'
|
||||
}
|
||||
|
||||
diff_request = requests.get(url=diff_url, headers=diff_headers, params=params)
|
||||
if diff_request.status_code == 200:
|
||||
diff = diff_request.text
|
||||
|
||||
# Check for additions in the diff.
|
||||
addition_pattern = re.compile(r'(?<=^\+).*', re.MULTILINE)
|
||||
additions = addition_pattern.findall(diff)
|
||||
new_issues = []
|
||||
|
||||
# Filter the additions down to newly added TODOs.
|
||||
for addition in additions:
|
||||
todo_pattern = re.compile(r'(?<=' + label + r'\s).*')
|
||||
todos = todo_pattern.search(addition)
|
||||
if todos:
|
||||
new_issues.append(todos.group(0))
|
||||
|
||||
# Create new issues for any newly added TODOs.
|
||||
issues_url = f'{base_url}{repo}/issues'
|
||||
issue_headers = {
|
||||
'Content-Type': 'application/json',
|
||||
}
|
||||
for issue in new_issues:
|
||||
title = issue
|
||||
# Truncate the title if it's longer than 50 chars.
|
||||
if len(title) > 50:
|
||||
title = issue[:50] + '...'
|
||||
new_issue_body = {'title': title, 'body': issue, 'labels': ['todo']}
|
||||
requests.post(url=issues_url, headers=issue_headers, params=params, data=json.dumps(new_issue_body))
|
||||
# Don't add too many issues too quickly.
|
||||
sleep(1)
|
||||
|
||||
# Check for deletions in the diff.
|
||||
deletion_pattern = re.compile(r'(?<=^-).*', re.MULTILINE)
|
||||
deletions = deletion_pattern.findall(diff)
|
||||
closed_issues = []
|
||||
|
||||
# Filter the deletions down to removed TODOs.
|
||||
for deletion in deletions:
|
||||
todo_pattern = re.compile(r'(?<=' + label + r'\s).*')
|
||||
todos = todo_pattern.search(deletion)
|
||||
if todos:
|
||||
closed_issues.append(todos.group(0))
|
||||
|
||||
if len(closed_issues) > 0:
|
||||
# Get the list of current issues.
|
||||
list_issues_request = requests.get(issues_url, headers=issue_headers, params=params)
|
||||
if list_issues_request.status_code == 200:
|
||||
current_issues = list_issues_request.json()
|
||||
for closed_issue in closed_issues:
|
||||
title = closed_issue
|
||||
if len(title) > 50:
|
||||
title = closed_issue[:50] + '...'
|
||||
|
||||
# Compare the title of each closed issue with each issue in the issues list.
|
||||
for current_issue in current_issues:
|
||||
if current_issue['title'] == title:
|
||||
# The titles match, so we will try and close the issue.
|
||||
issue_number = current_issue['number']
|
||||
|
||||
update_issue_url = f'{base_url}{repo}/issues/{issue_number}'
|
||||
body = {'state': 'closed'}
|
||||
requests.patch(update_issue_url, headers=issue_headers, params=params,
|
||||
data=json.dumps(body))
|
||||
# Don't update too many issues too quickly.
|
||||
sleep(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
Loading…
Reference in New Issue
Block a user