forked from tektoncd/catalog
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add DELETE option to github-add-comment
This will be used as part of tektoncd/plumbing#483 in Tekton's own CI setup, and I think it could be generally useful for replicating behavior such as Prow's job failure comments. Those comments are deleted once the job completes next, with a new comment created for subsequent failures, so that the latest result is always towards the end of the comment list. Signed-off-by: Andrew Bayer <[email protected]>
- Loading branch information
Showing
7 changed files
with
642 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
# Add a comment to an issue or a pull request | ||
|
||
The `github-add-comment` task let you add a comment to a pull request or an | ||
issue. | ||
|
||
## Install the Task | ||
|
||
``` | ||
kubectl apply -f https://api.hub.tekton.dev/v1/resource/tekton/task/github-add-comment/0.7/raw | ||
``` | ||
|
||
## Secrets | ||
|
||
This Task requires access to a GitHub token set via a Kubernetes Secret. By default, the name of this Secret should be `github` and the secret key should be `token`, but you can configure this via the `GITHUB_TOKEN_SECRET_NAME` and `GITHUB_TOKEN_SECRET_KEY` [parameters](#parameters) described below. | ||
|
||
To create such a Secret via `kubectl`: | ||
|
||
``` | ||
kubectl create secret generic github --from-literal token="MY_TOKEN" | ||
``` | ||
|
||
Check [this](https://help.github.com/en/github/authenticating-to-github/creating-a-personal-access-token-for-the-command-line) to get personal access token for `Github`. | ||
|
||
See GitHub's documentation on [Understanding scopes for OAuth Apps](https://developer.github.com/apps/building-oauth-apps/understanding-scopes-for-oauth-apps/) to figure out what scopes you need to give to this token to add comment to an issue or a pull request. | ||
|
||
## Parameters | ||
|
||
- **GITHUB_HOST_URL:**: The GitHub host domain (_default:_ `api.github.com`) | ||
- **API_PATH_PREFIX:**: The GitHub Enterprise has a prefix for the API path. _e.g:_ `/api/v3` | ||
- **REQUEST_URL:**: The GitHub pull request or issue url, _e.g:_ | ||
`https://github.com/tektoncd/catalog/issues/46` | ||
- **COMMENT_OR_FILE:**: The actual comment to add or the filename inside the | ||
optional workspace `comment-file` containing comment to post. _e.g:_ `don't forget to eat your vegetables before commiting.` _or_ `input.txt` | ||
- **COMMENT_TAG:**: An invisible tag to be added into the comment. The tag is | ||
made invisible by embedding in an an HTML comment. The tag allows | ||
for later retrieval of the comment, and it allows replacing an existing comment. _e.g._ `myservice.[commit-sha]`. (_default:_ `""`). | ||
- **REPLACE:**: When a tag is specified, and `REPLACE` is `true`, look for a | ||
comment with a matching tag and replace it with the new comment. (_default:_ `false`). | ||
- **DELETE:**: When a tag is specified and `DELETE` is true, look for a comment | ||
with a matching tag and delete it, instead of adding a new comment. (_default:_ `false`). | ||
- **GITHUB_TOKEN_SECRET_NAME**: The name of the Kubernetes Secret that | ||
contains the GitHub token. (_default:_ `github`). | ||
- **GITHUB_TOKEN_SECRET_KEY**: The key within the Kubernetes Secret that contains the GitHub token. (_default:_ `token`). | ||
|
||
## Results | ||
|
||
- **OLD_COMMENT:**: The old text of the comment, if any. | ||
- **NEW_COMMENT:**: The new text of the comment, if any. | ||
|
||
## Workspaces | ||
|
||
- **comment-file**: The optional workspace containing comment file to be posted. | ||
|
||
## Platforms | ||
|
||
The Task can be run on `linux/amd64`, `linux/s390x` and `linux/ppc64le` platforms. | ||
|
||
## Usage | ||
|
||
This TaskRun add a comment to an issue. | ||
|
||
```yaml | ||
--- | ||
apiVersion: tekton.dev/v1beta1 | ||
kind: TaskRun | ||
metadata: | ||
labels: | ||
tekton.dev/task: github-add-comment | ||
name: github-add-comment-to-pr-22 | ||
spec: | ||
taskRef: | ||
kind: Task | ||
name: github-add-comment | ||
params: | ||
- name: REQUEST_URL | ||
value: https://github.com/chmouel/scratchpad/pull/46 | ||
- name: COMMENT_OR_FILE | ||
value: | | ||
The cat went here and there | ||
And the moon spun round like a top, | ||
And the nearest kin of the moon, | ||
The creeping cat, looked up. | ||
Black Minnaloushe stared at the moon, | ||
For, wander and wail as he would, | ||
The pure cold light in the sky | ||
Troubled his animal blood. | ||
``` | ||
### When passing a comment via file | ||
```yaml | ||
apiVersion: v1 | ||
kind: ConfigMap | ||
metadata: | ||
name: comment-cm | ||
data: | ||
input.txt: | | ||
This is the sample input comment via file. | ||
--- | ||
apiVersion: tekton.dev/v1beta1 | ||
kind: TaskRun | ||
metadata: | ||
labels: | ||
tekton.dev/task: github-add-comment | ||
name: github-add-comment-to-pr-22 | ||
spec: | ||
taskRef: | ||
kind: Task | ||
name: github-add-comment | ||
workspace: | ||
- name: comment-file | ||
configMap: | ||
name: comment-cm | ||
params: | ||
- name: REQUEST_URL | ||
value: https://github.com/chmouel/scratchpad/pull/46 | ||
- name: COMMENT_OR_FILE | ||
value: "input.txt" | ||
``` | ||
### This TaskRun replaces a comment in an issue | ||
```yaml | ||
--- | ||
apiVersion: tekton.dev/v1beta1 | ||
kind: TaskRun | ||
metadata: | ||
name: github-add-comment-to-pr-22 | ||
spec: | ||
taskRef: | ||
kind: Task | ||
name: github-add-comment | ||
params: | ||
- name: REQUEST_URL | ||
value: https://github.com/chmouel/scratchpad/pull/46 | ||
- name: COMMENT_TAG | ||
value: catalog-sha123abc | ||
- name: REPLACE | ||
value: "true" | ||
- name: COMMENT_OR_FILE | ||
value: | | ||
The cat went here and there | ||
And the moon spun round like a top, | ||
And the nearest kin of the moon, | ||
The creeping cat, looked up. | ||
Black Minnaloushe stared at the moon, | ||
For, wander and wail as he would, | ||
The pure cold light in the sky | ||
Troubled his animal blood. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,216 @@ | ||
--- | ||
apiVersion: tekton.dev/v1beta1 | ||
kind: Task | ||
metadata: | ||
name: github-add-comment | ||
labels: | ||
app.kubernetes.io/version: "0.8" | ||
annotations: | ||
tekton.dev/categories: Git | ||
tekton.dev/pipelines.minVersion: "0.17.0" | ||
tekton.dev/tags: github | ||
tekton.dev/displayName: "add github comment" | ||
tekton.dev/platforms: "linux/amd64,linux/s390x,linux/ppc64le" | ||
spec: | ||
description: >- | ||
This Task will add a comment to a pull request or an issue. | ||
It can take either a filename or a comment as input and can | ||
post the comment back to GitHub accordingly. | ||
workspaces: | ||
- name: comment-file | ||
optional: true | ||
description: The optional workspace containing comment file to be posted. | ||
|
||
results: | ||
- name: OLD_COMMENT | ||
description: The old text of the comment, if any. | ||
|
||
- name: NEW_COMMENT | ||
description: The new text of the comment, if any. | ||
|
||
params: | ||
- name: GITHUB_HOST_URL | ||
description: | | ||
The GitHub host, adjust this if you run a GitHub enteprise. | ||
default: "api.github.com" | ||
type: string | ||
|
||
- name: API_PATH_PREFIX | ||
description: | | ||
The API path prefix, GitHub Enterprise has a prefix e.g. /api/v3 | ||
default: "" | ||
type: string | ||
|
||
- name: REQUEST_URL | ||
description: | | ||
The GitHub issue or pull request URL where we want to add a new | ||
comment. | ||
type: string | ||
|
||
- name: COMMENT_OR_FILE | ||
description: | | ||
The actual comment to add or the filename containing comment to post. | ||
type: string | ||
|
||
- name: GITHUB_TOKEN_SECRET_NAME | ||
description: | | ||
The name of the Kubernetes Secret that contains the GitHub token. | ||
type: string | ||
default: github | ||
|
||
- name: GITHUB_TOKEN_SECRET_KEY | ||
description: | | ||
The key within the Kubernetes Secret that contains the GitHub token. | ||
type: string | ||
default: token | ||
|
||
- name: AUTH_TYPE | ||
description: | | ||
The type of authentication to use. You could use the less secure "Basic" for example | ||
type: string | ||
default: Bearer | ||
|
||
- name: COMMENT_TAG | ||
description: | | ||
An invisible tag to be added into the comment. The tag is made | ||
invisible by embedding in an an HTML comment. The tag allows for later | ||
retrieval of the comment, and it allows replacing an existing comment. | ||
type: string | ||
default: "" | ||
|
||
- name: REPLACE | ||
description: | | ||
When a tag is specified, and `REPLACE` is `true`, look for a comment | ||
with a matching tag and replace it with the new comment. | ||
type: string | ||
default: "false" # Alternative value: "true" | ||
|
||
- name: DELETE | ||
description: | | ||
When a tag is specified, and `DELETE` is `true`, look for a comment | ||
with a matching tag and delete it instead of adding a new comment. | ||
type: string | ||
default: "false" # Alternative value: "true" | ||
|
||
steps: | ||
- name: post-comment | ||
workingDir: $(workspaces.comment-file.path) | ||
env: | ||
- name: GITHUBTOKEN | ||
valueFrom: | ||
secretKeyRef: | ||
name: $(params.GITHUB_TOKEN_SECRET_NAME) | ||
key: $(params.GITHUB_TOKEN_SECRET_KEY) | ||
|
||
image: registry.access.redhat.com/ubi8/ubi-minimal:8.2 | ||
script: | | ||
#!/usr/libexec/platform-python | ||
import json | ||
import os | ||
import http.client | ||
import sys | ||
import urllib.parse | ||
authHeader = "$(params.AUTH_TYPE) " + os.environ["GITHUBTOKEN"] | ||
split_url = urllib.parse.urlparse( | ||
"$(params.REQUEST_URL)").path.split("/") | ||
# This will convert https://github.com/foo/bar/pull/202 to | ||
# api url path /repos/foo/issues/ | ||
api_url = "{base}/repos/{package}/issues/{id}".format( | ||
base="$(params.API_PATH_PREFIX)", package="/".join(split_url[1:3]), id=split_url[-1]) | ||
commentParamValue = """$(params.COMMENT_OR_FILE)""" | ||
# check if workspace is bound and parameter passed is a filename or not | ||
if "$(workspaces.comment-file.bound)" == "true" and os.path.exists(commentParamValue): | ||
commentParamValue = open(commentParamValue, "r").read() | ||
# If a tag was specified, append it to the comment | ||
if "$(params.COMMENT_TAG)": | ||
commentParamValue += "<!-- {tag} -->".format(tag="$(params.COMMENT_TAG)") | ||
data = { | ||
"body": commentParamValue, | ||
} | ||
# This is for our fake github server | ||
if "$(params.GITHUB_HOST_URL)".startswith("http://"): | ||
conn = http.client.HTTPConnection("$(params.GITHUB_HOST_URL)".replace("http://", "")) | ||
else: | ||
conn = http.client.HTTPSConnection("$(params.GITHUB_HOST_URL)") | ||
# If REPLACE is true, we need to search for comments first | ||
matching_comment = "" | ||
if "$(params.REPLACE)" == "true" or "$(params.DELETE)" == "true": | ||
if not "$(params.COMMENT_TAG)": | ||
print("REPLACE or DELETE requested but no COMMENT_TAG specified") | ||
sys.exit(1) | ||
r = conn.request( | ||
"GET", | ||
api_url + "/comments", | ||
headers={ | ||
"User-Agent": "TektonCD, the peaceful cat", | ||
"Authorization": authHeader, | ||
}) | ||
resp = conn.getresponse() | ||
if not str(resp.status).startswith("2"): | ||
print("Error: %d" % (resp.status)) | ||
print(resp.read()) | ||
sys.exit(1) | ||
print(resp.status) | ||
comments = json.loads(resp.read()) | ||
print(comments) | ||
# If more than one comment is found take the last one | ||
matching_comment = [x for x in comments if '$(params.COMMENT_TAG)' in x['body']][-1:] | ||
if matching_comment: | ||
with open("$(results.OLD_COMMENT.path)", "w") as result_old: | ||
result_old.write(str(matching_comment[0])) | ||
matching_comment = matching_comment[0]['url'] | ||
if matching_comment: | ||
if "$(params.DELETE)" == "true": | ||
method = "DELETE" | ||
else: | ||
method = "PATCH" | ||
target_url = urllib.parse.urlparse(matching_comment).path | ||
else: | ||
method = "POST" | ||
target_url = api_url + "/comments" | ||
# if DELETE is true, don't send any data. | ||
if method == "DELETE": | ||
print("Deleting comment on GitHub at {}".format(target_url)) | ||
r = conn.request( | ||
method, | ||
target_url, | ||
headers={ | ||
"User-Agent": "TektonCD, the peaceful cat", | ||
"Authorization": authHeader, | ||
}) | ||
else: | ||
print("Sending this data to GitHub with {}: ".format(method)) | ||
print(data) | ||
r = conn.request( | ||
method, | ||
target_url, | ||
body=json.dumps(data), | ||
headers={ | ||
"User-Agent": "TektonCD, the peaceful cat", | ||
"Authorization": authHeader, | ||
}) | ||
resp = conn.getresponse() | ||
if not str(resp.status).startswith("2"): | ||
print("Error: %d" % (resp.status)) | ||
print(resp.read()) | ||
sys.exit(1) | ||
elif method != "DELETE": | ||
with open("$(results.NEW_COMMENT.path)", "wb") as result_new: | ||
result_new.write(resp.read()) | ||
print("a GitHub comment has been {} to $(params.REQUEST_URL)".format( | ||
"updated" if matching_comment else "added")) |
Oops, something went wrong.