From ecaff901909e02e8725990d4dc166ab303d66101 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Fri, 5 Jun 2020 17:21:23 +0200 Subject: [PATCH 01/37] =?UTF-8?q?=F0=9F=93=9D=20Fix=20format=20in=20exampl?= =?UTF-8?q?e=20YAMLs?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 102 +++++++++++++++++++++++++++--------------------------- 1 file changed, 51 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index a787d3e..cb289ee 100644 --- a/README.md +++ b/README.md @@ -26,10 +26,10 @@ jobs: issue-manager: runs-on: ubuntu-latest steps: - - uses: tiangolo/issue-manager@0.2.0 + - uses: tiangolo/issue-manager@0.2.0 with: - token: ${{ secrets.GITHUB_TOKEN }} - config: '{"answered": {}}' + token: ${{ secrets.GITHUB_TOKEN }} + config: '{"answered": {}}' ``` Then, you can answer an issue and add the label from the config, in this case, `answered`. @@ -209,24 +209,24 @@ jobs: issue-manager: runs-on: ubuntu-latest steps: - - uses: tiangolo/issue-manager@0.2.0 + - uses: tiangolo/issue-manager@0.2.0 with: - token: ${{ secrets.GITHUB_TOKEN }} - config: > - { - "answered": { - "delay": "P3DT12H30M5S", - "message": "It seems the issue was answered, I'll close this now." - }, - "validated": { - "delay": 300, - "message": "The issue could not be validated after 5 minutes. Closing now." - }, - "waiting": { - "delay": 691200, - "message": "Closing after 8 days of waiting for the additional info requested." - } + token: ${{ secrets.GITHUB_TOKEN }} + config: > + { + "answered": { + "delay": "P3DT12H30M5S", + "message": "It seems the issue was answered, I'll close this now." + }, + "validated": { + "delay": 300, + "message": "The issue could not be validated after 5 minutes. Closing now." + }, + "waiting": { + "delay": 691200, + "message": "Closing after 8 days of waiting for the additional info requested." } + } ``` ### Edit your own config @@ -271,40 +271,40 @@ jobs: issue-manager: runs-on: ubuntu-latest steps: - - uses: tiangolo/issue-manager@0.2.0 + - uses: tiangolo/issue-manager@0.2.0 with: - token: ${{ secrets.GITHUB_TOKEN }} - config: > - { - "$schema": "https://raw.githubusercontent.com/tiangolo/issue-manager/master/schema.json", - "answered": { - "users": [ - "tiangolo", - "dmontagu" - ], - "delay": "P3DT12H30M5S", - "message": "It seems the issue was answered, I'll close this now.", - "remove_label": false - }, - "validated": { - "users": [ - "tiangolo", - "samuelcolvin" - ], - "delay": 300, - "message": "The issue could not be validated after 5 minutes. Closing now.", - "remove_label": true - }, - "waiting": { - "users": [ - "tomchristie", - "dmontagu" - ], - "delay": 691200, - "message": "Closing after 8 days of waiting for the additional info requested.", - "remove_label": true - } + token: ${{ secrets.GITHUB_TOKEN }} + config: > + { + "$schema": "https://raw.githubusercontent.com/tiangolo/issue-manager/master/schema.json", + "answered": { + "users": [ + "tiangolo", + "dmontagu" + ], + "delay": "P3DT12H30M5S", + "message": "It seems the issue was answered, I'll close this now.", + "remove_label": false + }, + "validated": { + "users": [ + "tiangolo", + "samuelcolvin" + ], + "delay": 300, + "message": "The issue could not be validated after 5 minutes. Closing now.", + "remove_label": true + }, + "waiting": { + "users": [ + "tomchristie", + "dmontagu" + ], + "delay": 691200, + "message": "Closing after 8 days of waiting for the additional info requested.", + "remove_label": true } + } ``` ## GitHub Action triggers From a21808ee6be24d7658f98a69c2ffffa6deb2ef90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sat, 6 Jun 2020 11:54:08 +0200 Subject: [PATCH 02/37] =?UTF-8?q?=F0=9F=94=A7=20Update=20internal=20issue-?= =?UTF-8?q?manager=20config=20to=20test=20cron?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/issue-manager.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/issue-manager.yml b/.github/workflows/issue-manager.yml index 4f30a59..5e3518d 100644 --- a/.github/workflows/issue-manager.yml +++ b/.github/workflows/issue-manager.yml @@ -2,7 +2,7 @@ name: Issue Manager on: schedule: - - cron: "0 0 * * *" + - cron: "* * * * *" issue_comment: types: - created @@ -22,7 +22,7 @@ jobs: config: > { "answered":{ - "delay": 10, + "delay": 300, "users": ["mariacamilagl"], "message": "This issue shall be closed." } From 42170009544be70c43e603e530a78d147957bb40 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sat, 6 Jun 2020 11:58:29 +0200 Subject: [PATCH 03/37] =?UTF-8?q?=F0=9F=94=A7=20Update=20internal=20config?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/issue-manager.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/issue-manager.yml b/.github/workflows/issue-manager.yml index 5e3518d..44ef282 100644 --- a/.github/workflows/issue-manager.yml +++ b/.github/workflows/issue-manager.yml @@ -2,7 +2,7 @@ name: Issue Manager on: schedule: - - cron: "* * * * *" + - cron: "* * * * *" issue_comment: types: - created From 1b74270cb48a1f1fd2e149ced534c833c209a8d4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sat, 6 Jun 2020 12:13:25 +0200 Subject: [PATCH 04/37] =?UTF-8?q?=F0=9F=94=A7=20Update=20internal=20issue-?= =?UTF-8?q?manager=20use?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/issue-manager.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/issue-manager.yml b/.github/workflows/issue-manager.yml index 44ef282..a5a8430 100644 --- a/.github/workflows/issue-manager.yml +++ b/.github/workflows/issue-manager.yml @@ -2,7 +2,7 @@ name: Issue Manager on: schedule: - - cron: "* * * * *" + - cron: "0 0 * * *" issue_comment: types: - created From ab985b74fedc7402f9bd692ec7cb679e414c0083 Mon Sep 17 00:00:00 2001 From: Sofie Van Landeghem Date: Mon, 8 Jun 2020 11:53:09 +0200 Subject: [PATCH 05/37] =?UTF-8?q?=F0=9F=90=9B=20Fix=20readme=20(#6)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * avoid using ' in the msg * further removing ' * further fix --- README.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index cb289ee..ff8e970 100644 --- a/README.md +++ b/README.md @@ -68,7 +68,7 @@ Imagine this JSON config: { "answered": { "delay": "P3DT12H30M5S", - "message": "It seems the issue was answered, I'll close this now." + "message": "It seems the issue was answered, closing this now." }, "validated": { "delay": 300, @@ -90,7 +90,7 @@ In this case, if: ...the GitHub action would close the issue with a message of: ```markdown -It seems the issue was answered, I'll close this now. +It seems the issue was answered, closing this now. ``` But if there was a new comment created _after_ the label was added, by default, it would remove the label. @@ -216,7 +216,7 @@ jobs: { "answered": { "delay": "P3DT12H30M5S", - "message": "It seems the issue was answered, I'll close this now." + "message": "It seems the issue was answered, closing this now." }, "validated": { "delay": 300, @@ -283,7 +283,7 @@ jobs: "dmontagu" ], "delay": "P3DT12H30M5S", - "message": "It seems the issue was answered, I'll close this now.", + "message": "It seems the issue was answered, closing this now.", "remove_label": false }, "validated": { @@ -346,7 +346,7 @@ Moreover, if I closed the issue prematurely, there's a smaller chance that I (or But then, if I leave the issue open after giving an answer, in many cases, the issue will keep open until I come back to close it, after many days. -Then, after that time (10 days, 30 days) and after seeing that there are no new comments, I write "I assume the problem is solved, I'll close this issue now". +Then, after that time (10 days, 30 days) and after seeing that there are no new comments, I write "I assume the problem is solved, closing this issue now". But that requires me going through all the open issues again, one by one, check where I (or someone else) have already answered, typing that message, etc. From f13ad2bc6441012221eb315aed53d1dd6a3aff0b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Mon, 8 Jun 2020 11:55:44 +0200 Subject: [PATCH 06/37] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index ff8e970..d95b74c 100644 --- a/README.md +++ b/README.md @@ -377,6 +377,8 @@ It will also run after each comment or label added, with the specific issue that ### Latest Changes +* Fix using single quote (`'`) in README examples. PR [#6](https://github.com/tiangolo/issue-manager/pull/6) by [@svlandeg](https://github.com/svlandeg). + ### 0.2.0 * Add support for running immediately with each specific issue after a new comment or label is added. From d432df227e76282e40588341a36ccb81d4c0901d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Mon, 8 Jun 2020 14:45:50 +0200 Subject: [PATCH 07/37] =?UTF-8?q?=F0=9F=94=A7=20Add=20new=20label?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/issue-manager.yml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.github/workflows/issue-manager.yml b/.github/workflows/issue-manager.yml index a5a8430..b442015 100644 --- a/.github/workflows/issue-manager.yml +++ b/.github/workflows/issue-manager.yml @@ -25,5 +25,8 @@ jobs: "delay": 300, "users": ["mariacamilagl"], "message": "This issue shall be closed." + }, + "more-info-needed": { + "delay": 120 } } From 136bbda5002548b7423db82e7128be177f0d0d38 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Mon, 8 Jun 2020 14:54:56 +0200 Subject: [PATCH 08/37] =?UTF-8?q?=F0=9F=94=A7=20Run=20every=205=20min?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/issue-manager.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/issue-manager.yml b/.github/workflows/issue-manager.yml index b442015..36dea09 100644 --- a/.github/workflows/issue-manager.yml +++ b/.github/workflows/issue-manager.yml @@ -2,7 +2,7 @@ name: Issue Manager on: schedule: - - cron: "0 0 * * *" + - cron: "* * * * *" issue_comment: types: - created From d756d82c2a45d9cc10da7694e03db849f9ceb316 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Mon, 8 Jun 2020 15:22:09 +0200 Subject: [PATCH 09/37] =?UTF-8?q?=F0=9F=90=9B=20Handle=20renamed=20labels?= =?UTF-8?q?=20that=20don't=20show=20up=20in=20the=20labels=20for=20an=20is?= =?UTF-8?q?sue=20(#9)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/main.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/app/main.py b/app/main.py index 7cd89cf..7c6242c 100644 --- a/app/main.py +++ b/app/main.py @@ -61,10 +61,10 @@ def get_labeled_events(events: List[IssueEvent]) -> List[IssueEvent]: def get_last_event_for_label( *, labeled_events: List[IssueEvent], label: str -) -> IssueEvent: +) -> Optional[IssueEvent]: last_event: Optional[IssueEvent] = None for event in labeled_events: - if event.label.name == label: + if event.label and event.label.name == label: if not last_event: last_event = event continue @@ -83,7 +83,7 @@ def close_issue(*, issue: Issue, keyword_meta: KeywordMeta) -> None: def process_issue(*, issue: Issue, settings: Settings, owner: NamedUser) -> None: logging.info(f"Processing issue: #{issue.number}") - label_strs = set([l.name for l in issue.get_labels()]) + label_strs = set([label.name for label in issue.get_labels()]) events = list(issue.get_events()) labeled_events = get_labeled_events(events) last_comment = get_last_comment(issue) @@ -101,7 +101,11 @@ def process_issue(*, issue: Issue, settings: Settings, owner: NamedUser) -> None keyword_event = get_last_event_for_label( labeled_events=labeled_events, label=keyword ) - if last_comment and last_comment.created_at > keyword_event.created_at: + if ( + last_comment + and keyword_event + and last_comment.created_at > keyword_event.created_at + ): logging.info( f"Not closing as the last comment was written after adding the " f'label: "{keyword}"' @@ -149,4 +153,4 @@ def process_issue(*, issue: Issue, settings: Settings, owner: NamedUser) -> None else: for issue in repo.get_issues(state="open"): process_issue(issue=issue, settings=settings, owner=owner) - logging.info(f"Finished") + logging.info("Finished") From 71e60c5590f8ef919678eddba75995384a519c7e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Mon, 8 Jun 2020 15:23:42 +0200 Subject: [PATCH 10/37] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index d95b74c..dd5e990 100644 --- a/README.md +++ b/README.md @@ -377,6 +377,7 @@ It will also run after each comment or label added, with the specific issue that ### Latest Changes +* Avoid crashing when a label has been edited _after_ added to the issue. PR [#9](https://github.com/tiangolo/issue-manager/pull/9). * Fix using single quote (`'`) in README examples. PR [#6](https://github.com/tiangolo/issue-manager/pull/6) by [@svlandeg](https://github.com/svlandeg). ### 0.2.0 From 87efbec33b87d8fbf580cdc602f49f2ddf3bf67e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Mon, 8 Jun 2020 15:24:02 +0200 Subject: [PATCH 11/37] =?UTF-8?q?=F0=9F=94=96=20Release=20version=200.2.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index dd5e990..5657a75 100644 --- a/README.md +++ b/README.md @@ -377,6 +377,8 @@ It will also run after each comment or label added, with the specific issue that ### Latest Changes +### 0.2.1 + * Avoid crashing when a label has been edited _after_ added to the issue. PR [#9](https://github.com/tiangolo/issue-manager/pull/9). * Fix using single quote (`'`) in README examples. PR [#6](https://github.com/tiangolo/issue-manager/pull/6) by [@svlandeg](https://github.com/svlandeg). From 6039d7f7dd0ba58edb740ab9d66af85cf25e2b4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Mon, 8 Jun 2020 15:35:30 +0200 Subject: [PATCH 12/37] =?UTF-8?q?=F0=9F=94=A7=20Revert=20cron=20to=20daily?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/issue-manager.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/issue-manager.yml b/.github/workflows/issue-manager.yml index 36dea09..b442015 100644 --- a/.github/workflows/issue-manager.yml +++ b/.github/workflows/issue-manager.yml @@ -2,7 +2,7 @@ name: Issue Manager on: schedule: - - cron: "* * * * *" + - cron: "0 0 * * *" issue_comment: types: - created From bb934f4502aab0b4b3b36b163aa48068d8ddca17 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Thu, 11 Jun 2020 13:31:56 +0200 Subject: [PATCH 13/37] =?UTF-8?q?=E2=9C=A8=20Add=20option=20to=20remove=20?= =?UTF-8?q?label=20on=20close=20(#10)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/main.py | 28 ++++++++++++++++++++++------ 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/app/main.py b/app/main.py index 7c6242c..8e14d9e 100644 --- a/app/main.py +++ b/app/main.py @@ -1,7 +1,7 @@ from datetime import datetime, timedelta import logging from pathlib import Path -from typing import Dict, List, Optional +from typing import Dict, List, Optional, Set from github import Github from github.Issue import Issue @@ -15,7 +15,8 @@ class KeywordMeta(BaseModel): delay: timedelta = timedelta(days=10) users: List[str] = [] message: str = "Assuming the original issue was solved, it will be automatically closed now." - remove_label: bool = True + remove_label_on_comment: bool = True + remove_label_on_close: bool = False class Settings(BaseSettings): @@ -73,12 +74,17 @@ def get_last_event_for_label( return last_event -def close_issue(*, issue: Issue, keyword_meta: KeywordMeta) -> None: +def close_issue( + *, issue: Issue, keyword_meta: KeywordMeta, keyword: str, label_strs: Set[str] +) -> None: logging.info( f"Clossing issue: #{issue.number} with message: {keyword_meta.message}" ) issue.create_comment(keyword_meta.message) issue.edit(state="closed") + if keyword_meta.remove_label_on_close: + if keyword in label_strs: + issue.remove_from_labels(keyword) def process_issue(*, issue: Issue, settings: Settings, owner: NamedUser) -> None: @@ -110,11 +116,16 @@ def process_issue(*, issue: Issue, settings: Settings, owner: NamedUser) -> None f"Not closing as the last comment was written after adding the " f'label: "{keyword}"' ) - if keyword_meta.remove_label: + if keyword_meta.remove_label_on_comment: logging.info(f'Removing label: "{keyword}"') issue.remove_from_labels(keyword) elif closable_delay: - close_issue(issue=issue, keyword_meta=keyword_meta) + close_issue( + issue=issue, + keyword_meta=keyword_meta, + keyword=keyword, + label_strs=label_strs, + ) break # Check HTML comments by allowed users if ( @@ -127,7 +138,12 @@ def process_issue(*, issue: Issue, settings: Settings, owner: NamedUser) -> None f'Last comment by user: "{last_comment.user.login}" had HTML keyword ' f'comment: "{keyword}" and there\'s a closable delay.' ) - close_issue(issue=issue, keyword_meta=keyword_meta) + close_issue( + issue=issue, + keyword_meta=keyword_meta, + keyword=keyword, + label_strs=label_strs, + ) break From 834e9d9eda6121f234ed4f9edb1540ee403ecdf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Thu, 11 Jun 2020 13:33:25 +0200 Subject: [PATCH 14/37] =?UTF-8?q?=F0=9F=9A=80=20Update=20internal=20issue-?= =?UTF-8?q?manager=20to=20test=20remove=20on=20close?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/issue-manager.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/issue-manager.yml b/.github/workflows/issue-manager.yml index b442015..0b7724d 100644 --- a/.github/workflows/issue-manager.yml +++ b/.github/workflows/issue-manager.yml @@ -2,7 +2,7 @@ name: Issue Manager on: schedule: - - cron: "0 0 * * *" + - cron: "* * * * *" issue_comment: types: - created @@ -27,6 +27,7 @@ jobs: "message": "This issue shall be closed." }, "more-info-needed": { - "delay": 120 + "delay": 120, + "remove_label_on_close": true } } From 740ac912b5d983b518e13a1add7209ef154b4d0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Thu, 11 Jun 2020 13:41:14 +0200 Subject: [PATCH 15/37] =?UTF-8?q?=F0=9F=94=A7=20Update=20JSON=20Schema?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- schema.json | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/schema.json b/schema.json index 09f022a..ba584e2 100644 --- a/schema.json +++ b/schema.json @@ -24,10 +24,15 @@ "default": "Assuming the original issue was solved, it will be automatically closed now.", "type": "string" }, - "remove_label": { - "title": "Remove Label", + "remove_label_on_comment": { + "title": "Remove Label On Comment", "default": true, "type": "boolean" + }, + "remove_label_on_close": { + "title": "Remove Label On Close", + "default": false, + "type": "boolean" } } } From 4841118d16b2aa8339affc79811579e8c3e1c42f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Thu, 11 Jun 2020 13:42:16 +0200 Subject: [PATCH 16/37] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 5657a75..02f268c 100644 --- a/README.md +++ b/README.md @@ -377,6 +377,8 @@ It will also run after each comment or label added, with the specific issue that ### Latest Changes +* Add option to remove a label automatically after closing the issue. PR [#10](https://github.com/tiangolo/issue-manager/pull/10). + ### 0.2.1 * Avoid crashing when a label has been edited _after_ added to the issue. PR [#9](https://github.com/tiangolo/issue-manager/pull/9). From 09642cedd8fa3dd4d9238fb4f33da029466d388c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Thu, 11 Jun 2020 13:42:42 +0200 Subject: [PATCH 17/37] =?UTF-8?q?=F0=9F=94=96=20Release=20version=200.3.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 02f268c..12242c0 100644 --- a/README.md +++ b/README.md @@ -377,6 +377,8 @@ It will also run after each comment or label added, with the specific issue that ### Latest Changes +### 0.3.0 + * Add option to remove a label automatically after closing the issue. PR [#10](https://github.com/tiangolo/issue-manager/pull/10). ### 0.2.1 From d11e463046d1831b45a4c00917f229275f9c2395 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Thu, 11 Jun 2020 13:48:04 +0200 Subject: [PATCH 18/37] =?UTF-8?q?=F0=9F=93=9D=20Update=20README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 12242c0..ef4862f 100644 --- a/README.md +++ b/README.md @@ -159,13 +159,19 @@ In this case, the GitHub action will only close the issue if: * it was written by a user in the `users` list in the `config` (or the owner of the repo) * the time delay since the last comment is enough -### Remove label +### Remove label on comment -You can also pass a config `remove_label` per keyword. By default it's `true`. +You can also pass a config `remove_label_on_comment` per keyword. By default it's `true`. When someone adds a comment _after_ the label was added, then this GitHub action won't close the issue. -On top of not closing the issue, by default, it will remove the label. You can disable removing the label by setting `remove_label` to `false`. +On top of not closing the issue, by default, it will remove the label. You can disable removing the label by setting `remove_label_on_comment` to `false`. + +### Remove label on close + +After this GitHub action closes an issue it can also automatically remove the label from the issue when you pass the config `remove_label_on_close` set to `true`. + +By default it is false, and doesn't remove the label from the issue. ### Defaults @@ -179,7 +185,8 @@ By default, any config has: Assuming the original issue was solved, it will be automatically closed now. ``` -* `remove_label`: True. If someone adds a comment after you added the label, it will remove the label from the issue. +* `remove_label_on_comment`: True. If someone adds a comment after you added the label, it will remove the label from the issue. +* `remove_label_on_close`: False. After this GitHub action closes the issue it would also remove the label from the issue. ### Config in the action @@ -284,7 +291,8 @@ jobs: ], "delay": "P3DT12H30M5S", "message": "It seems the issue was answered, closing this now.", - "remove_label": false + "remove_label_on_comment": false, + "remove_label_on_close": false }, "validated": { "users": [ @@ -293,7 +301,8 @@ jobs: ], "delay": 300, "message": "The issue could not be validated after 5 minutes. Closing now.", - "remove_label": true + "remove_label_on_comment": true, + "remove_label_on_close": false }, "waiting": { "users": [ @@ -302,7 +311,8 @@ jobs: ], "delay": 691200, "message": "Closing after 8 days of waiting for the additional info requested.", - "remove_label": true + "remove_label_on_comment": true, + "remove_label_on_close": true } } ``` From 40a83f7174f1d975cb5b97d4ddd3cdc1696cae22 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Thu, 11 Jun 2020 16:26:55 +0200 Subject: [PATCH 19/37] =?UTF-8?q?=F0=9F=94=A7=20Run=20issue-manager=20ever?= =?UTF-8?q?y=20night?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/issue-manager.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/issue-manager.yml b/.github/workflows/issue-manager.yml index 0b7724d..309e4d1 100644 --- a/.github/workflows/issue-manager.yml +++ b/.github/workflows/issue-manager.yml @@ -2,7 +2,7 @@ name: Issue Manager on: schedule: - - cron: "* * * * *" + - cron: "0 0 * * *" issue_comment: types: - created From b038c0ee4c17df589d781a67c5a94c61d78fac4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sat, 13 Jun 2020 09:21:18 +0200 Subject: [PATCH 20/37] =?UTF-8?q?=F0=9F=93=9D=20Update=20version=20used=20?= =?UTF-8?q?in=20README?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index ef4862f..81b81f9 100644 --- a/README.md +++ b/README.md @@ -26,7 +26,7 @@ jobs: issue-manager: runs-on: ubuntu-latest steps: - - uses: tiangolo/issue-manager@0.2.0 + - uses: tiangolo/issue-manager@0.3.0 with: token: ${{ secrets.GITHUB_TOKEN }} config: '{"answered": {}}' @@ -216,7 +216,7 @@ jobs: issue-manager: runs-on: ubuntu-latest steps: - - uses: tiangolo/issue-manager@0.2.0 + - uses: tiangolo/issue-manager@0.3.0 with: token: ${{ secrets.GITHUB_TOKEN }} config: > @@ -278,7 +278,7 @@ jobs: issue-manager: runs-on: ubuntu-latest steps: - - uses: tiangolo/issue-manager@0.2.0 + - uses: tiangolo/issue-manager@0.3.0 with: token: ${{ secrets.GITHUB_TOKEN }} config: > From 076a3b90a273b6bb2ececc53b2f2c3f7af03e988 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sun, 16 May 2021 20:12:54 +0200 Subject: [PATCH 21/37] =?UTF-8?q?=E2=9C=A8=20Add=20support=20for=20managin?= =?UTF-8?q?g=20PRs=20and=20remove=20support=20for=20HTML=20comments=20to?= =?UTF-8?q?=20avoid=20rate=20limits=20(#12)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 🔥 Remove support for HTML comments to avoid rate limits and because that feature was not used much after having support for labels * 🔊 Add not closing logging * 📌 Pin ranges of PyGitHub and pydantic better * ✨ Enable processing PRs * ✨ Add support for reading PR event data * 📝 Update docs supporting PRs * 📝 Update README with new version --- Dockerfile | 2 +- README.md | 103 +++++++++++++++++++++++++--------------------------- app/main.py | 56 ++++++++++++---------------- 3 files changed, 74 insertions(+), 87 deletions(-) diff --git a/Dockerfile b/Dockerfile index dc37b72..c2a9724 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,6 +1,6 @@ FROM python:3.7 -RUN pip install PyGithub "pydantic==1.5.1" +RUN pip install "PyGithub>=1.55,<2.0" "pydantic>=v1.8.2,<2.0" COPY ./app /app diff --git a/README.md b/README.md index 81b81f9..6e0106c 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Issue Manager -Automatically close issues that have a **label**, after a **custom delay**, if no one replies back. +Automatically close issues or Pull Requests that have a **label**, after a **custom delay**, if no one replies back. ## How to use @@ -17,32 +17,35 @@ on: issue_comment: types: - created - - edited issues: types: - labeled + pull_request_target: + types: + - labeled + workflow_dispatch: jobs: issue-manager: runs-on: ubuntu-latest steps: - - uses: tiangolo/issue-manager@0.3.0 + - uses: tiangolo/issue-manager@0.4.0 with: token: ${{ secrets.GITHUB_TOKEN }} config: '{"answered": {}}' ``` -Then, you can answer an issue and add the label from the config, in this case, `answered`. +Then, you can answer an issue or PR and add the label from the config, in this case, `answered`. After 10 days, if no one has added a new comment, the GitHub action will write: ```markdown -Assuming the original issue was solved, it will be automatically closed now. +Assuming the original need was handled, this will be automatically closed now. ``` And then it will close the issue. -But if someone adds a comment _after_ you added the label, it will remove the label. +But if someone adds a comment _after_ you added the label, this GitHub Action will remove the label so that you can come back and check it instead of closing it. ## Config @@ -77,6 +80,10 @@ Imagine this JSON config: "waiting": { "delay": 691200, "message": "Closing after 8 days of waiting for the additional info requested." + }, + "needs-tests": { + "delay": 691200, + "message": "This PR will be closed after waiting 8 days for tests to be added. Please create a new one with tests." } } ``` @@ -113,11 +120,11 @@ And also, if there was a new comment created _after_ the label was added, by def --- -And in the last case, if: +Then, if: * the issue has a label `waiting` * the label was added _after_ the last comment -* the last comment was addded more than `691200` seconds (10 days) ago +* the last comment was addded more than `691200` seconds (8 days) ago ...the GitHub action would close the issue with: @@ -127,41 +134,31 @@ Closing after 10 days of waiting for the additional info requested. And again, by default, removing the label if there was a new comment written after adding the label. -### Delay - -The delay can be configured using [anything supported by Pydantic's `datetime`](https://pydantic-docs.helpmanual.io/usage/types/#datetime-types). - -So, it can be an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) period format (like `P3DT12H30M5S`), or the amount of seconds between the two dates (like `691200`, or 10 days) plus other options. - -### Users and HTML comments - -Before supporting labels, this GitHub action used HTML comments, so, you would write a comment like: +--- -```markdown -Ah, you have to use a JSON string in the config. +And finally, if: - -``` +* a PR has a label `needs-tests` +* the label was added _after_ the last comment +* the last comment was addded more than `691200` seconds (8 days) ago -Then the comment would only show: +...the GitHub action would close the PR with: ```markdown -Ah, you have to use a JSON string in the config. +This PR will be closed after waiting 8 days for tests to be added. Please create a new one with tests. ``` -And the GitHub action would read the label/keyword from that HTML comment. +**Note**: in this last example the process is applied to a PR instead of an issue. The same logic applies to both issues and PRs. If you want a label to only apply to issues, you should use that label only with issues, and the same with PRs. -To support external users adding these comments (even if they can't add labels to your repo), you can add a config `users` with a list of usernames allowed to add these HTML keyword comments. +### Delay -In this case, the GitHub action will only close the issue if: +The delay can be configured using [anything supported by Pydantic's `datetime`](https://pydantic-docs.helpmanual.io/usage/types/#datetime-types). -* the _last_ comment has the keyword/label -* it was written by a user in the `users` list in the `config` (or the owner of the repo) -* the time delay since the last comment is enough +So, it can be an [ISO 8601](https://en.wikipedia.org/wiki/ISO_8601) period format (like `P3DT12H30M5S`), or the number of seconds between the two dates (like `691200`, or 10 days) plus other options. ### Remove label on comment -You can also pass a config `remove_label_on_comment` per keyword. By default it's `true`. +You can also pass a config `remove_label_on_comment` per keyword. By default, it's `true`. When someone adds a comment _after_ the label was added, then this GitHub action won't close the issue. @@ -177,7 +174,6 @@ By default it is false, and doesn't remove the label from the issue. By default, any config has: -* `users`: No users, only the repository owner (only applies to HTML comments). * `delay`: A delay of 10 days. * `message`: A message of: @@ -211,12 +207,16 @@ on: issues: types: - labeled + pull_request_target: + types: + - labeled + workflow_dispatch: jobs: issue-manager: runs-on: ubuntu-latest steps: - - uses: tiangolo/issue-manager@0.3.0 + - uses: tiangolo/issue-manager@0.4.0 with: token: ${{ secrets.GITHUB_TOKEN }} config: > @@ -238,7 +238,7 @@ jobs: ### Edit your own config -If you have [Visual Studio Code](https://code.visualstudio.com) or other modern editor, you can create your JSON config by creating a JSON file, e.g. `config.json`. +If you have [Visual Studio Code](https://code.visualstudio.com) or another modern editor, you can create your JSON config by creating a JSON file, e.g. `config.json`. Then writing the contents of your config in that file, and then copying the results. @@ -252,13 +252,13 @@ You can start your JSON config file with: } ``` -And then after you write a keyword and start its config, like `"answered": {}`, it will autocomplete the internal config keys, like `delay`, `users`, `message`. And will validate its contents. +And then after you write a keyword and start its config, like `"answered": {}`, it will autocomplete the internal config keys, like `delay`, `message`. And will validate its contents. It's fine to leave the `$schema` in the `config` on the `.yml` file, it will be discarded and won't be used as a label. ### A complete example -**Note**: you probably don't need all the configs, the examples above should suffice for most cases. But if you want to allow other users to use keywords/labels in HTML comments, or want to make the GitHub action _not_ remove the labels if someone adds a new comment, this can help as an example: +**Note**: you probably don't need all the configs, the examples above should suffice for most cases. But if you want to make the GitHub action _not_ remove the labels if someone adds a new comment, this can help as an example: ```yml name: Issue Manager @@ -273,42 +273,34 @@ on: issues: types: - labeled + pull_request_target: + types: + - labeled + workflow_dispatch: jobs: issue-manager: runs-on: ubuntu-latest steps: - - uses: tiangolo/issue-manager@0.3.0 + - uses: tiangolo/issue-manager@0.4.0 with: token: ${{ secrets.GITHUB_TOKEN }} config: > { "$schema": "https://raw.githubusercontent.com/tiangolo/issue-manager/master/schema.json", "answered": { - "users": [ - "tiangolo", - "dmontagu" - ], "delay": "P3DT12H30M5S", "message": "It seems the issue was answered, closing this now.", "remove_label_on_comment": false, "remove_label_on_close": false }, "validated": { - "users": [ - "tiangolo", - "samuelcolvin" - ], "delay": 300, "message": "The issue could not be validated after 5 minutes. Closing now.", "remove_label_on_comment": true, "remove_label_on_close": false }, "waiting": { - "users": [ - "tomchristie", - "dmontagu" - ], "delay": 691200, "message": "Closing after 8 days of waiting for the additional info requested.", "remove_label_on_comment": true, @@ -332,6 +324,10 @@ on: issues: types: - labeled + pull_request_target: + types: + - labeled + workflow_dispatch: ``` * The `cron` option means that the GitHub action will be run every day at 00:00 UTC. @@ -339,14 +335,17 @@ on: * This way, if there's a new comment, it can immediately remove any label that was added before the new comment. * The `issues` option with a type of `label` will run it with each specific issue when you add a label. * This way you can add a label to an issue that was answered long ago, and if the configured delay since the last comment is enough the GitHub action will close the issue right away. +* The `pull_request_target` option with a type of `label` will run it with each specific Pull Request made to your repo when you add a label. + * This way you can add a label to a PR that was answered long ago, or that was waiting for more comments from the author, etc. And if the configured delay since the last comment is enough the GitHub action will close the issue right away. +* The `workflow_dispatch` option allows you to run the action manually from the GitHub Actions tab for your repo. ## Motivation ### Closing early -When I answer an issue, I like to give the original user some time to respond, and give them the chance to close the issue before doing it myself. +When I answer an issue, I like to give the original user some time to respond and give them the chance to close the issue before doing it myself. -Or some times, I have to request additional info. +Or sometimes, I have to request additional info. Sometimes, my answer didn't respond the real question/problem, and if I closed the issue immediately, it would end up feeling "impolite" to the user. @@ -364,7 +363,7 @@ But that requires me going through all the open issues again, one by one, check One option would be to use a tool that closes stale issues, like [probot/stale](https://github.com/probot/stale), or the [Close Stale Issues Action](https://github.com/marketplace/actions/close-stale-issues). -But if the user came back explaining that my answer didn't respond to his/her problem, or giving the extra info requested, but I couldn't respond on time, the issue would still go "stale" and be closed. +But if the user came back explaining that my answer didn't respond to his/her problem or giving the extra info requested, but I couldn't respond on time, the issue would still go "stale" and be closed. ## What Issue Manager does @@ -379,8 +378,6 @@ Then, this action, by running every night (or however you configure it) will, fo * Then, if all that matches, it will add a comment with a message (configurable). * And then it will close the issue. -Also, all that with the optional alternative using HTML comments. - It will also run after each comment or label added, with the specific issue that has the new comment or label (if you used the example configurations from above). ## Release Notes diff --git a/app/main.py b/app/main.py index 8e14d9e..18703af 100644 --- a/app/main.py +++ b/app/main.py @@ -7,14 +7,12 @@ from github.Issue import Issue from github.IssueComment import IssueComment from github.IssueEvent import IssueEvent -from github.NamedUser import NamedUser from pydantic import BaseModel, BaseSettings, SecretStr, validator class KeywordMeta(BaseModel): delay: timedelta = timedelta(days=10) - users: List[str] = [] - message: str = "Assuming the original issue was solved, it will be automatically closed now." + message: str = "Assuming the original need was handled, this will be automatically closed now." remove_label_on_comment: bool = True remove_label_on_close: bool = False @@ -39,6 +37,7 @@ class PartialGitHubEventIssue(BaseModel): class PartialGitHubEvent(BaseModel): issue: Optional[PartialGitHubEventIssue] = None + pull_request: Optional[PartialGitHubEventIssue] = None def get_last_comment(issue: Issue) -> Optional[IssueComment]: @@ -87,7 +86,7 @@ def close_issue( issue.remove_from_labels(keyword) -def process_issue(*, issue: Issue, settings: Settings, owner: NamedUser) -> None: +def process_issue(*, issue: Issue, settings: Settings) -> None: logging.info(f"Processing issue: #{issue.number}") label_strs = set([label.name for label in issue.get_labels()]) events = list(issue.get_events()) @@ -95,12 +94,10 @@ def process_issue(*, issue: Issue, settings: Settings, owner: NamedUser) -> None last_comment = get_last_comment(issue) for keyword, keyword_meta in settings.input_config.items(): # Check closable delay, if enough time passed and the issue could be closed - closable_delay = False - if ( + closable_delay = ( last_comment is None or (datetime.utcnow() - keyword_meta.delay) > last_comment.created_at - ): - closable_delay = True + ) # Check label, optionally removing it if there's a comment after adding it if keyword in label_strs: logging.info(f'Keyword: "{keyword}" in issue labels') @@ -127,24 +124,10 @@ def process_issue(*, issue: Issue, settings: Settings, owner: NamedUser) -> None label_strs=label_strs, ) break - # Check HTML comments by allowed users - if ( - last_comment - and f"" in last_comment.body - and closable_delay - and last_comment.user.login in keyword_meta.users + [owner.login] - ): - logging.info( - f'Last comment by user: "{last_comment.user.login}" had HTML keyword ' - f'comment: "{keyword}" and there\'s a closable delay.' - ) - close_issue( - issue=issue, - keyword_meta=keyword_meta, - keyword=keyword, - label_strs=label_strs, - ) - break + else: + logging.info( + f"Not clossing issue: #{issue.number} as the delay hasn't been reached: {keyword_meta.delay}" + ) if __name__ == "__main__": @@ -153,20 +136,27 @@ def process_issue(*, issue: Issue, settings: Settings, owner: NamedUser) -> None logging.info(f"Using config: {settings.json()}") g = Github(settings.input_token.get_secret_value()) repo = g.get_repo(settings.github_repository) - owner: NamedUser = repo.owner github_event: Optional[PartialGitHubEvent] = None if settings.github_event_path.is_file(): contents = settings.github_event_path.read_text() github_event = PartialGitHubEvent.parse_raw(contents) if ( settings.github_event_name == "issues" + or settings.github_event_name == "pull_request_target" or settings.github_event_name == "issue_comment" ): - if github_event and github_event.issue: - issue = repo.get_issue(github_event.issue.number) - if issue.state == "open": - process_issue(issue=issue, settings=settings, owner=owner) + if github_event: + issue_number: Optional[int] = None + if github_event.issue: + issue_number = github_event.issue.number + elif github_event.pull_request: + issue_number = github_event.pull_request.number + if issue_number is not None: + issue = repo.get_issue(issue_number) + if issue.state == "open": + process_issue(issue=issue, settings=settings) else: - for issue in repo.get_issues(state="open"): - process_issue(issue=issue, settings=settings, owner=owner) + for keyword, keyword_meta in settings.input_config.items(): + for issue in repo.get_issues(state="open", labels=[keyword]): + process_issue(issue=issue, settings=settings) logging.info("Finished") From 026fba9e0eb3683b84bbebd5a9b7b6ed905f930a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sun, 16 May 2021 20:15:31 +0200 Subject: [PATCH 22/37] =?UTF-8?q?=F0=9F=91=B7=20Add=20Latest=20Changes=20G?= =?UTF-8?q?itHub=20Action=20(#13)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/latest-changes.yml | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 .github/workflows/latest-changes.yml diff --git a/.github/workflows/latest-changes.yml b/.github/workflows/latest-changes.yml new file mode 100644 index 0000000..02a0e92 --- /dev/null +++ b/.github/workflows/latest-changes.yml @@ -0,0 +1,23 @@ +name: Latest Changes + +on: + pull_request_target: + branches: + - master + types: + - closed + # For manually triggering it + workflow_dispatch: + inputs: + number: + description: PR number + required: true + +jobs: + latest-changes: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - uses: docker://tiangolo/latest-changes:0.0.3 + with: + token: ${{ secrets.GITHUB_TOKEN }} From d76f72694b3dbcb090bac5497d9c3eb4a46d6f9f Mon Sep 17 00:00:00 2001 From: github-actions Date: Sun, 16 May 2021 18:16:03 +0000 Subject: [PATCH 23/37] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 6e0106c..ab9ed90 100644 --- a/README.md +++ b/README.md @@ -384,6 +384,7 @@ It will also run after each comment or label added, with the specific issue that ### Latest Changes +* 👷 Add Latest Changes GitHub Action. PR [#13](https://github.com/tiangolo/issue-manager/pull/13) by [@tiangolo](https://github.com/tiangolo). ### 0.3.0 * Add option to remove a label automatically after closing the issue. PR [#10](https://github.com/tiangolo/issue-manager/pull/10). From f7f9cca09826b5d6870e41b10c29b1a9b6a582f9 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sun, 16 May 2021 18:17:02 +0000 Subject: [PATCH 24/37] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index ab9ed90..232b59d 100644 --- a/README.md +++ b/README.md @@ -384,6 +384,7 @@ It will also run after each comment or label added, with the specific issue that ### Latest Changes +* ✨ Add support for managing PRs and remove support for HTML comments to avoid rate limits. PR [#12](https://github.com/tiangolo/issue-manager/pull/12) by [@tiangolo](https://github.com/tiangolo). * 👷 Add Latest Changes GitHub Action. PR [#13](https://github.com/tiangolo/issue-manager/pull/13) by [@tiangolo](https://github.com/tiangolo). ### 0.3.0 From 4d1b7e05935a404dc8337d30bd23be46be8bb8e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sun, 16 May 2021 20:18:02 +0200 Subject: [PATCH 25/37] =?UTF-8?q?=F0=9F=94=96=20Release=20version=200.4.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 232b59d..c98cea3 100644 --- a/README.md +++ b/README.md @@ -384,8 +384,12 @@ It will also run after each comment or label added, with the specific issue that ### Latest Changes + +### 0.4.0 + * ✨ Add support for managing PRs and remove support for HTML comments to avoid rate limits. PR [#12](https://github.com/tiangolo/issue-manager/pull/12) by [@tiangolo](https://github.com/tiangolo). * 👷 Add Latest Changes GitHub Action. PR [#13](https://github.com/tiangolo/issue-manager/pull/13) by [@tiangolo](https://github.com/tiangolo). + ### 0.3.0 * Add option to remove a label automatically after closing the issue. PR [#10](https://github.com/tiangolo/issue-manager/pull/10). From 401738f38679ecd2b0b979f12e9084c81c019a3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sat, 4 Nov 2023 10:02:11 +0400 Subject: [PATCH 26/37] =?UTF-8?q?=F0=9F=91=B7=20Add=20latest-changes=20Git?= =?UTF-8?q?Hub=20Action=20(#16)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/workflows/latest-changes.yml | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/.github/workflows/latest-changes.yml b/.github/workflows/latest-changes.yml index 02a0e92..c5625bc 100644 --- a/.github/workflows/latest-changes.yml +++ b/.github/workflows/latest-changes.yml @@ -6,18 +6,24 @@ on: - master types: - closed - # For manually triggering it workflow_dispatch: inputs: number: description: PR number required: true + debug_enabled: + description: 'Run the build with tmate debugging enabled (https://github.com/marketplace/actions/debugging-with-tmate)' + required: false + default: 'false' jobs: latest-changes: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 - - uses: docker://tiangolo/latest-changes:0.0.3 + - uses: actions/checkout@v4 + with: + token: ${{ secrets.ISSUE_MANAGER_LATEST_CHANGES }} + - uses: docker://tiangolo/latest-changes:0.2.0 + # - uses: tiangolo/latest-changes@main with: token: ${{ secrets.GITHUB_TOKEN }} From 161cf89f1992ab3b582b4264721e4189408542e1 Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 4 Nov 2023 06:02:27 +0000 Subject: [PATCH 27/37] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index c98cea3..9c4cf6b 100644 --- a/README.md +++ b/README.md @@ -384,6 +384,9 @@ It will also run after each comment or label added, with the specific issue that ### Latest Changes +#### Internal + +* 👷 Add latest-changes GitHub Action. PR [#16](https://github.com/tiangolo/issue-manager/pull/16) by [@tiangolo](https://github.com/tiangolo). ### 0.4.0 From 9a2072c93aa838bf79bf222b6f7fbee69b1cf60d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sat, 4 Nov 2023 10:03:09 +0400 Subject: [PATCH 28/37] =?UTF-8?q?=F0=9F=91=B7=20Update=20dependabot=20(#17?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/dependabot.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 .github/dependabot.yml diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 0000000..cd972a0 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,16 @@ +version: 2 +updates: + # GitHub Actions + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + commit-message: + prefix: ⬆ + # Python + - package-ecosystem: "pip" + directory: "/" + schedule: + interval: "daily" + commit-message: + prefix: ⬆ From ef48e4eb3d7bb22c3dd87a218376fdaa016b3d7a Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 4 Nov 2023 06:03:32 +0000 Subject: [PATCH 29/37] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9c4cf6b..9199cb3 100644 --- a/README.md +++ b/README.md @@ -386,6 +386,7 @@ It will also run after each comment or label added, with the specific issue that #### Internal +* 👷 Update dependabot. PR [#17](https://github.com/tiangolo/issue-manager/pull/17) by [@tiangolo](https://github.com/tiangolo). * 👷 Add latest-changes GitHub Action. PR [#16](https://github.com/tiangolo/issue-manager/pull/16) by [@tiangolo](https://github.com/tiangolo). ### 0.4.0 From 9564d6b0eb3f3edbb8442c68e01b210004458d00 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Sat, 4 Nov 2023 10:04:01 +0400 Subject: [PATCH 30/37] =?UTF-8?q?=F0=9F=94=A7=20Add=20funding=20(#18)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .github/FUNDING.yml | 1 + 1 file changed, 1 insertion(+) create mode 100644 .github/FUNDING.yml diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml new file mode 100644 index 0000000..0ffc101 --- /dev/null +++ b/.github/FUNDING.yml @@ -0,0 +1 @@ +github: [tiangolo] From 62a7f8e035b9731ef95c8401ad5cb81b00b3941e Mon Sep 17 00:00:00 2001 From: github-actions Date: Sat, 4 Nov 2023 06:04:18 +0000 Subject: [PATCH 31/37] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 9199cb3..9113d1c 100644 --- a/README.md +++ b/README.md @@ -386,6 +386,7 @@ It will also run after each comment or label added, with the specific issue that #### Internal +* 🔧 Add funding. PR [#18](https://github.com/tiangolo/issue-manager/pull/18) by [@tiangolo](https://github.com/tiangolo). * 👷 Update dependabot. PR [#17](https://github.com/tiangolo/issue-manager/pull/17) by [@tiangolo](https://github.com/tiangolo). * 👷 Add latest-changes GitHub Action. PR [#16](https://github.com/tiangolo/issue-manager/pull/16) by [@tiangolo](https://github.com/tiangolo). From cc4fdd28920eece04bd913639f838b1a22a99651 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Tue, 9 Jan 2024 00:16:25 +0400 Subject: [PATCH 32/37] =?UTF-8?q?=F0=9F=90=9B=20Fix=20datetime=20compariso?= =?UTF-8?q?n=20(#19)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Dockerfile | 12 ++++++++---- app/main.py | 8 +++++--- requirements.txt | 3 +++ 3 files changed, 16 insertions(+), 7 deletions(-) create mode 100644 requirements.txt diff --git a/Dockerfile b/Dockerfile index c2a9724..d8b60d3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,11 @@ -FROM python:3.7 +FROM python:3.10 -RUN pip install "PyGithub>=1.55,<2.0" "pydantic>=v1.8.2,<2.0" +COPY ./requirements.txt /code/requirements.txt -COPY ./app /app +RUN pip install -r /code/requirements.txt -CMD ["python", "/app/main.py"] +COPY ./app /code/app + +ENV PYTHONPATH=/code/app + +CMD ["python", "/code/app/main.py"] diff --git a/app/main.py b/app/main.py index 18703af..4a5ea63 100644 --- a/app/main.py +++ b/app/main.py @@ -1,4 +1,4 @@ -from datetime import datetime, timedelta +from datetime import datetime, timedelta, timezone import logging from pathlib import Path from typing import Dict, List, Optional, Set @@ -7,7 +7,8 @@ from github.Issue import Issue from github.IssueComment import IssueComment from github.IssueEvent import IssueEvent -from pydantic import BaseModel, BaseSettings, SecretStr, validator +from pydantic import BaseModel, SecretStr, validator +from pydantic_settings import BaseSettings class KeywordMeta(BaseModel): @@ -92,11 +93,12 @@ def process_issue(*, issue: Issue, settings: Settings) -> None: events = list(issue.get_events()) labeled_events = get_labeled_events(events) last_comment = get_last_comment(issue) + now = datetime.now(timezone.utc) for keyword, keyword_meta in settings.input_config.items(): # Check closable delay, if enough time passed and the issue could be closed closable_delay = ( last_comment is None - or (datetime.utcnow() - keyword_meta.delay) > last_comment.created_at + or (now - keyword_meta.delay) > last_comment.created_at ) # Check label, optionally removing it if there's a comment after adding it if keyword in label_strs: diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..65e67cf --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +PyGithub +pydantic>=2.5.3,<3.0.0 +pydantic-settings>=2.1.0,<3.0.0 From 99cc645dde14d2d5cf5c9a231193b080fa3b8880 Mon Sep 17 00:00:00 2001 From: github-actions Date: Mon, 8 Jan 2024 20:16:45 +0000 Subject: [PATCH 33/37] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 9113d1c..8862a12 100644 --- a/README.md +++ b/README.md @@ -384,6 +384,10 @@ It will also run after each comment or label added, with the specific issue that ### Latest Changes +#### Fixes + +* 🐛 Fix datetime comparison. PR [#19](https://github.com/tiangolo/issue-manager/pull/19) by [@tiangolo](https://github.com/tiangolo). + #### Internal * 🔧 Add funding. PR [#18](https://github.com/tiangolo/issue-manager/pull/18) by [@tiangolo](https://github.com/tiangolo). From 38b626d4643014d3655bbe26228e4c5f0e28b9dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Mon, 8 Jan 2024 21:17:32 +0100 Subject: [PATCH 34/37] =?UTF-8?q?=F0=9F=94=96=20Release=20version=200.4.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 8862a12..f7164bb 100644 --- a/README.md +++ b/README.md @@ -384,6 +384,8 @@ It will also run after each comment or label added, with the specific issue that ### Latest Changes +### 0.4.1 + #### Fixes * 🐛 Fix datetime comparison. PR [#19](https://github.com/tiangolo/issue-manager/pull/19) by [@tiangolo](https://github.com/tiangolo). From 6022ac0a51cb36a2722a79a1c348d88e7693c791 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Tue, 30 Jan 2024 19:37:56 +0100 Subject: [PATCH 35/37] =?UTF-8?q?=E2=9C=A8=20Add=20first-class=20support?= =?UTF-8?q?=20for=20PRs,=20including=20reviews,=20review=20comments=20(#20?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 6 +++--- app/main.py | 52 ++++++++++++++++++++++++++++++++++------------------ 2 files changed, 37 insertions(+), 21 deletions(-) diff --git a/README.md b/README.md index f7164bb..e13c94c 100644 --- a/README.md +++ b/README.md @@ -37,7 +37,7 @@ jobs: Then, you can answer an issue or PR and add the label from the config, in this case, `answered`. -After 10 days, if no one has added a new comment, the GitHub action will write: +After 10 days, if no one has added a new comment (or in the case of PRs, a new review or commit), the GitHub action will write: ```markdown Assuming the original need was handled, this will be automatically closed now. @@ -140,7 +140,7 @@ And finally, if: * a PR has a label `needs-tests` * the label was added _after_ the last comment -* the last comment was addded more than `691200` seconds (8 days) ago +* the last comment was added more than `691200` seconds (8 days) ago ...the GitHub action would close the PR with: @@ -336,7 +336,7 @@ on: * The `issues` option with a type of `label` will run it with each specific issue when you add a label. * This way you can add a label to an issue that was answered long ago, and if the configured delay since the last comment is enough the GitHub action will close the issue right away. * The `pull_request_target` option with a type of `label` will run it with each specific Pull Request made to your repo when you add a label. - * This way you can add a label to a PR that was answered long ago, or that was waiting for more comments from the author, etc. And if the configured delay since the last comment is enough the GitHub action will close the issue right away. + * This way you can add a label to a PR that was answered long ago, or that was waiting for more comments from the author, reviews, commits, etc. And if the configured delay since the last comment is enough the GitHub action will close the issue right away. * The `workflow_dispatch` option allows you to run the action manually from the GitHub Actions tab for your repo. ## Motivation diff --git a/app/main.py b/app/main.py index 4a5ea63..300b5ca 100644 --- a/app/main.py +++ b/app/main.py @@ -1,11 +1,10 @@ -from datetime import datetime, timedelta, timezone import logging +from datetime import datetime, timedelta, timezone from pathlib import Path from typing import Dict, List, Optional, Set from github import Github from github.Issue import Issue -from github.IssueComment import IssueComment from github.IssueEvent import IssueEvent from pydantic import BaseModel, SecretStr, validator from pydantic_settings import BaseSettings @@ -13,7 +12,9 @@ class KeywordMeta(BaseModel): delay: timedelta = timedelta(days=10) - message: str = "Assuming the original need was handled, this will be automatically closed now." + message: str = ( + "Assuming the original need was handled, this will be automatically closed now." + ) remove_label_on_comment: bool = True remove_label_on_close: bool = False @@ -41,15 +42,31 @@ class PartialGitHubEvent(BaseModel): pull_request: Optional[PartialGitHubEventIssue] = None -def get_last_comment(issue: Issue) -> Optional[IssueComment]: - last_comment: Optional[IssueComment] = None - comment: IssueComment - for comment in issue.get_comments(): - if not last_comment: - last_comment = comment - elif comment.created_at > last_comment.created_at: - last_comment = comment - return last_comment +def get_last_interaction_date(issue: Issue) -> Optional[datetime]: + last_date: Optional[datetime] = None + comments = list(issue.get_comments()) + if issue.pull_request: + pr = issue.as_pull_request() + commits = list(pr.get_commits()) + reviews = list(pr.get_reviews()) + pr_comments = list(pr.get_comments()) + interactions = comments + pr_comments + interaction_dates = [ + interaction.created_at for interaction in interactions + ] + interaction_dates.extend( + [commit.commit.author.date for commit in commits] + ) + interaction_dates.extend([review.submitted_at for review in reviews]) + else: + interactions = comments + interaction_dates = [interaction.created_at for interaction in interactions] + for item_date in interaction_dates: + if not last_date: + last_date = item_date + elif item_date > last_date: + last_date = item_date + return last_date def get_labeled_events(events: List[IssueEvent]) -> List[IssueEvent]: @@ -92,13 +109,12 @@ def process_issue(*, issue: Issue, settings: Settings) -> None: label_strs = set([label.name for label in issue.get_labels()]) events = list(issue.get_events()) labeled_events = get_labeled_events(events) - last_comment = get_last_comment(issue) + last_date = get_last_interaction_date(issue) now = datetime.now(timezone.utc) for keyword, keyword_meta in settings.input_config.items(): # Check closable delay, if enough time passed and the issue could be closed closable_delay = ( - last_comment is None - or (now - keyword_meta.delay) > last_comment.created_at + last_date is None or (now - keyword_meta.delay) > last_date ) # Check label, optionally removing it if there's a comment after adding it if keyword in label_strs: @@ -107,9 +123,9 @@ def process_issue(*, issue: Issue, settings: Settings) -> None: labeled_events=labeled_events, label=keyword ) if ( - last_comment + last_date and keyword_event - and last_comment.created_at > keyword_event.created_at + and last_date > keyword_event.created_at ): logging.info( f"Not closing as the last comment was written after adding the " @@ -141,7 +157,7 @@ def process_issue(*, issue: Issue, settings: Settings) -> None: github_event: Optional[PartialGitHubEvent] = None if settings.github_event_path.is_file(): contents = settings.github_event_path.read_text() - github_event = PartialGitHubEvent.parse_raw(contents) + github_event = PartialGitHubEvent.model_validate_json(contents) if ( settings.github_event_name == "issues" or settings.github_event_name == "pull_request_target" From 59b530ae75a0b917bcbaa355b5b1b041e23cd2f0 Mon Sep 17 00:00:00 2001 From: github-actions Date: Tue, 30 Jan 2024 18:38:16 +0000 Subject: [PATCH 36/37] =?UTF-8?q?=F0=9F=93=9D=20Update=20release=20notes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index e13c94c..3b50fd5 100644 --- a/README.md +++ b/README.md @@ -384,6 +384,10 @@ It will also run after each comment or label added, with the specific issue that ### Latest Changes +#### Features + +* ✨ Add first-class support for PRs, including reviews, review comments. PR [#20](https://github.com/tiangolo/issue-manager/pull/20) by [@tiangolo](https://github.com/tiangolo). + ### 0.4.1 #### Fixes From f344760bbc82c5624ea3316a1ca65e9e545279d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sebasti=C3=A1n=20Ram=C3=ADrez?= Date: Tue, 30 Jan 2024 19:41:15 +0100 Subject: [PATCH 37/37] =?UTF-8?q?=F0=9F=94=96=20Release=20version=200.5.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 3b50fd5..8d77614 100644 --- a/README.md +++ b/README.md @@ -384,6 +384,8 @@ It will also run after each comment or label added, with the specific issue that ### Latest Changes +### 0.5.0 + #### Features * ✨ Add first-class support for PRs, including reviews, review comments. PR [#20](https://github.com/tiangolo/issue-manager/pull/20) by [@tiangolo](https://github.com/tiangolo).