Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .github/PULL_REQUEST_TEMPLATE.md

This file was deleted.

18 changes: 0 additions & 18 deletions .github/workflows/docs_bot.yml

This file was deleted.

55 changes: 55 additions & 0 deletions .github/workflows/pr_comment_bot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

name: comment-bot
on:
pull_request_target:
types: [opened, reopened, edited, ready_for_review, labeled]
status:

concurrency:
group: pr-comment-${{ github.event.number }}-${{ github.event.target_url }}
cancel-in-progress: true

jobs:
run-comment-bot:
if: ${{ github.repository == 'apache/tvm' }}
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Comment bot comment (pr)
if: ${{ github.event.number }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
PR_NUMBER: ${{ github.event.number }}
run: |
set -eux
python ci/scripts/github_pr_comment.py --pr "$PR_NUMBER"
- name: Comment bot comment (status)
if: ${{ github.event.state }}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
URL: ${{ github.event.target_url }}
run: |
set -eux
if [[ "$URL" == *"PR-"* ]]; then
echo "PR status, sending comment"
PR_NUMBER=$(echo $URL | sed 's/.*PR-//g' | sed 's/\/.*//g')
python ci/scripts/github_pr_comment.py --pr "$PR_NUMBER"
else
echo "Not a PR status, skipping"
fi
7 changes: 0 additions & 7 deletions .github/workflows/tag_teams.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,9 @@
# specific language governing permissions and limitations
# under the License.

# GH actions.
# We use it to cover windows and mac builds
# Jenkins is still the primary CI

name: Teams

on:
# See https://docs.github.com/en/actions/using-workflows/events-that-trigger-workflows#pull_request_target
pull_request_target:
types: [opened, reopened, edited, ready_for_review, labeled]
issues:
types: [opened, edited, reopened, labeled]

Expand Down
21 changes: 0 additions & 21 deletions .github/workflows/tests_bot.yml

This file was deleted.

19 changes: 19 additions & 0 deletions ci/scripts/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
"""Package to enable testing of CI scripts"""

from . import github_skipped_tests_comment, github_pr_comment, github_tag_teams, github_docs_comment
60 changes: 51 additions & 9 deletions ci/scripts/git_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,14 @@
import json
import subprocess
import re
import os
import base64
import logging
from urllib import request, error
from typing import Dict, Tuple, Any, Optional, List

DRY_RUN = object()


def compress_query(query: str) -> str:
query = query.replace("\n", "")
Expand All @@ -32,7 +35,7 @@ def compress_query(query: str) -> str:


def post(url: str, body: Optional[Any] = None, auth: Optional[Tuple[str, str]] = None):
print(f"Requesting POST to", url, "with", body)
logging.info(f"Requesting POST to", url, "with", body)
headers = {}
req = request.Request(url, headers=headers, method="POST")
if auth is not None:
Expand All @@ -51,34 +54,63 @@ def post(url: str, body: Optional[Any] = None, auth: Optional[Tuple[str, str]] =
return response.read()


def dry_run_token(is_dry_run: bool) -> Any:
if is_dry_run:
return DRY_RUN
return os.environ["GITHUB_TOKEN"]


class GitHubRepo:
def __init__(self, user, repo, token):
GRAPHQL_URL = "https://api.github.com/graphql"

def __init__(self, user, repo, token, test_data=None):
self.token = token
self.user = user
self.repo = repo
self.test_data = test_data
self.num_calls = 0
self.base = f"https://api.github.com/repos/{user}/{repo}/"

def headers(self):
return {
"Authorization": f"Bearer {self.token}",
}

def dry_run(self) -> bool:
return self.token == DRY_RUN

def graphql(self, query: str, variables: Optional[Dict[str, str]] = None) -> Dict[str, Any]:
query = compress_query(query)
if variables is None:
variables = {}

response = self._request(
"https://api.github.com/graphql",
self.GRAPHQL_URL,
{"query": query, "variables": variables},
method="POST",
)
if self.dry_run():
return self.testing_response("POST", self.GRAPHQL_URL)

if "data" not in response:
msg = f"Error fetching data with query:\n{query}\n\nvariables:\n{variables}\n\nerror:\n{json.dumps(response, indent=2)}"
raise RuntimeError(msg)
return response

def testing_response(self, method: str, url: str) -> Any:
self.num_calls += 1
key = f"[{self.num_calls}] {method} - {url}"
if self.test_data is not None and key in self.test_data:
return self.test_data[key]
logging.info(f"Unknown URL in dry run: {key}")
return {}

def _request(self, full_url: str, body: Dict[str, Any], method: str) -> Dict[str, Any]:
print(f"Requesting {method} to", full_url, "with", body)
if self.dry_run():
logging.info(f"Dry run, would have requested a {method} to {full_url} with {body}")
return self.testing_response(method, full_url)

logging.info(f"Requesting {method} to {full_url} with {body}")
req = request.Request(full_url, headers=self.headers(), method=method.upper())
req.add_header("Content-Type", "application/json; charset=utf-8")
data = json.dumps(body)
Expand Down Expand Up @@ -111,16 +143,22 @@ def post(self, url: str, data: Dict[str, Any]) -> Dict[str, Any]:
return self._request(self.base + url, data, method="POST")

def get(self, url: str) -> Dict[str, Any]:
if self.dry_run():
logging.info(f"Dry run, would have requested a GET to {url}")
return self.testing_response("GET", url)
url = self.base + url
print("Requesting GET to", url)
logging.info(f"Requesting GET to {url}")
req = request.Request(url, headers=self.headers())
with request.urlopen(req) as response:
response = json.loads(response.read())
return response

def delete(self, url: str) -> Dict[str, Any]:
if self.dry_run():
logging.info(f"Dry run, would have requested a DELETE to {url}")
return self.testing_response("DELETE", url)
url = self.base + url
print("Requesting DELETE to", url)
logging.info(f"Requesting DELETE to {url}")
req = request.Request(url, headers=self.headers(), method="DELETE")
with request.urlopen(req) as response:
response = json.loads(response.read())
Expand All @@ -136,18 +174,22 @@ def parse_remote(remote: str) -> Tuple[str, str]:
parts = remote.split("/")
if len(parts) < 2:
raise RuntimeError(f"Unable to parse remote '{remote}'")
return parts[-2], parts[-1].replace(".git", "")
user, repo = parts[-2], parts[-1].replace(".git", "")
else:
# Parse SSH remote
m = re.search(r":(.*)/(.*)\.git", remote)
if m is None or len(m.groups()) != 2:
raise RuntimeError(f"Unable to parse remote '{remote}'")
return m.groups()
user, repo = m.groups()

user = os.getenv("DEBUG_USER", user)
repo = os.getenv("DEBUG_REPO", repo)
return user, repo


def git(command, **kwargs):
command = ["git"] + command
print("Running", command)
logging.info(f"Running {command}")
proc = subprocess.run(command, stdout=subprocess.PIPE, encoding="utf-8", **kwargs)
if proc.returncode != 0:
raise RuntimeError(f"Command failed {command}:\nstdout:\n{proc.stdout}")
Expand Down
Loading