From 464483f25b3ce2993e15ab22299a1989d1b87d1a Mon Sep 17 00:00:00 2001 From: filipe oliveira Date: Tue, 21 Sep 2021 16:53:13 +0100 Subject: [PATCH] Moved from gh action API to gh webhook API (#49) * [add] Moved from gh action API to gh webhook API * [add] Further logging on API. Using PULL_REQUEST_TRIGGER_LABEL env var to decide uppon which PR label to use for trigger automation --- pyproject.toml | 2 +- .../__api__/Readme.md | 9 - .../__api__/__init__.py | 5 + redis_benchmarks_specification/__api__/api.py | 10 +- redis_benchmarks_specification/__api__/app.py | 153 ++++-- .../__api__/schema.py | 7 - .../__common__/builder_schema.py | 22 +- .../__common__/env.py | 3 + utils/tests/test_api.py | 6 +- utils/tests/test_app.py | 123 +++++ utils/tests/test_builder.py | 7 +- .../test_data/event_webhook_labelled_pr.json | 498 ++++++++++++++++++ .../test_data/event_webhook_pushed_repo.json | 187 +++++++ 13 files changed, 945 insertions(+), 87 deletions(-) delete mode 100644 redis_benchmarks_specification/__api__/schema.py create mode 100644 utils/tests/test_app.py create mode 100644 utils/tests/test_data/event_webhook_labelled_pr.json create mode 100644 utils/tests/test_data/event_webhook_pushed_repo.json diff --git a/pyproject.toml b/pyproject.toml index 1e6d6a6..21358d8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "redis-benchmarks-specification" -version = "0.1.11" +version = "0.1.12" description = "The Redis benchmarks specification describes the cross-language/tools requirements and expectations to foster performance and observability standards around redis related technologies. Members from both industry and academia, including organizations and individuals are encouraged to contribute." authors = ["filipecosta90 ","Redis Performance Group "] readme = "Readme.md" diff --git a/redis_benchmarks_specification/__api__/Readme.md b/redis_benchmarks_specification/__api__/Readme.md index d3f8f04..8ca3621 100644 --- a/redis_benchmarks_specification/__api__/Readme.md +++ b/redis_benchmarks_specification/__api__/Readme.md @@ -5,12 +5,3 @@ Within root project folder ``` poetry run redis-benchmarks-spec-api ``` - -## Testing triggering a new commit spec via local api - -``` -curl -u : \ - -X POST -H "Content-Type: application/json" \ - --data '{"git_hash":"0cf2df84d4b27af4bffd2bf3543838f09e10f874"}' \ - http://localhost:5000/api/gh/redis/redis/commits -``` \ No newline at end of file diff --git a/redis_benchmarks_specification/__api__/__init__.py b/redis_benchmarks_specification/__api__/__init__.py index e69de29..f49c81f 100644 --- a/redis_benchmarks_specification/__api__/__init__.py +++ b/redis_benchmarks_specification/__api__/__init__.py @@ -0,0 +1,5 @@ +# Apache License Version 2.0 +# +# Copyright (c) 2021., Redis Labs +# All rights reserved. +# diff --git a/redis_benchmarks_specification/__api__/api.py b/redis_benchmarks_specification/__api__/api.py index e599ebd..335262e 100644 --- a/redis_benchmarks_specification/__api__/api.py +++ b/redis_benchmarks_specification/__api__/api.py @@ -15,13 +15,12 @@ GH_REDIS_SERVER_HOST, GH_REDIS_SERVER_PORT, GH_REDIS_SERVER_AUTH, - REDIS_AUTH_SERVER_HOST, - REDIS_AUTH_SERVER_PORT, LOG_FORMAT, LOG_DATEFMT, LOG_LEVEL, REDIS_HEALTH_CHECK_INTERVAL, REDIS_SOCKET_TIMEOUT, + GH_REDIS_SERVER_USER, ) from redis_benchmarks_specification.__common__.package import ( populate_with_poetry_data, @@ -47,11 +46,6 @@ def main(): GH_REDIS_SERVER_HOST, GH_REDIS_SERVER_PORT ) ) - print( - "Using redis available at: {}:{} as auth server.".format( - REDIS_AUTH_SERVER_HOST, REDIS_AUTH_SERVER_PORT - ) - ) conn = redis.StrictRedis( host=GH_REDIS_SERVER_HOST, port=GH_REDIS_SERVER_PORT, @@ -61,7 +55,7 @@ def main(): socket_connect_timeout=REDIS_SOCKET_TIMEOUT, socket_keepalive=True, ) - app = create_app(conn) + app = create_app(conn, GH_REDIS_SERVER_USER) if args.logname is not None: print("Writting log to {}".format(args.logname)) handler = logging.handlers.RotatingFileHandler( diff --git a/redis_benchmarks_specification/__api__/app.py b/redis_benchmarks_specification/__api__/app.py index 8cf764b..4419aec 100644 --- a/redis_benchmarks_specification/__api__/app.py +++ b/redis_benchmarks_specification/__api__/app.py @@ -1,71 +1,126 @@ -from flask import Flask, jsonify, request -from marshmallow import ValidationError -from json import dumps +import json + +from flask import jsonify import redis -from flask_httpauth import HTTPBasicAuth +from flask import Flask, request +from hmac import HMAC, compare_digest +from hashlib import sha1 -from redis_benchmarks_specification.__api__.schema import ( - CommitSchema, -) from redis_benchmarks_specification.__common__.builder_schema import ( commit_schema_to_stream, ) -from redis_benchmarks_specification.__common__.env import ( - REDIS_AUTH_SERVER_HOST, - REDIS_AUTH_SERVER_PORT, -) +from redis_benchmarks_specification.__common__.env import PULL_REQUEST_TRIGGER_LABEL +SIG_HEADER = "X-Hub-Signature" -def create_app(conn, test_config=None): + +def create_app(conn, user, test_config=None): app = Flask(__name__) - auth = HTTPBasicAuth() + conn = conn - @auth.verify_password - def verify_password(username, password): + # GH Token Authentication + def verify_signature(req): result = False try: - auth_server_conn = redis.StrictRedis( - host=REDIS_AUTH_SERVER_HOST, - port=REDIS_AUTH_SERVER_PORT, - decode_responses=True, - username=username, - password=password, - ) - auth_server_conn.ping() - result = True + secret = conn.get("{}:auth_token".format(user)) + sig_header = req.headers.get(SIG_HEADER) + if secret is not None and sig_header is not None: + if type(secret) == str: + secret = secret.encode() + if "sha1=" in sig_header: + received_sign = sig_header.split("sha1=")[-1].strip() + expected_sign = HMAC( + key=secret, msg=req.data, digestmod=sha1 + ).hexdigest() + result = compare_digest(received_sign, expected_sign) except redis.exceptions.ResponseError: - result = False + pass except redis.exceptions.AuthenticationError: - result = False + pass return result @app.route("/api/gh/redis/redis/commits", methods=["POST"]) - @auth.login_required def base(): - # Get Request body from JSON - request_data = request.json - gh_org = "redis" - gh_repo = "redis" - schema = CommitSchema() - response_data = {} - err_message = "" - try: - # Validate request body against schema data types - result = schema.load(request_data) - except ValidationError as err: - err_message = err.messages - if result is True: - # Convert request body back to JSON str - data_now_json_str = dumps(result) + if verify_signature(request): + print(request) + # Get Request body from JSON + request_data = request.json + if type(request_data) is str: + request_data = json.loads(request_data) + if type(request_data) is bytes: + request_data = json.loads(request_data.decode()) + + gh_org = "redis" + gh_repo = "redis" + ref = None + ref_label = None + sha = None + + event_type = "Ignored event from webhook" + use_event = False + # Pull request labeled + trigger_label = PULL_REQUEST_TRIGGER_LABEL + if "pull_request" in request_data: + action = request_data["action"] + if "labeled" == action: + pull_request_dict = request_data["pull_request"] + head_dict = pull_request_dict["head"] + repo_dict = head_dict["repo"] + labels = [] + if "labels" in pull_request_dict: + labels = pull_request_dict["labels"] + ref = head_dict["ref"] + ref_label = head_dict["label"] + sha = head_dict["sha"] + html_url = repo_dict["html_url"].split("/") + gh_repo = html_url[-1] + gh_org = html_url[-2] + for label in labels: + label_name = label["name"] + if trigger_label == label_name: + use_event = True + event_type = "Pull request labeled with '{}'".format( + trigger_label + ) + + # Git pushes to repo + if "ref" in request_data: + repo_dict = request_data["repository"] + html_url = repo_dict["html_url"].split("/") + gh_repo = html_url[-1] + gh_org = html_url[-2] + ref = request_data["ref"].split("/")[-1] + ref_label = request_data["ref"] + sha = request_data["after"] + use_event = True + event_type = "Git pushes to repo" + + if use_event is True: + fields = {"git_hash": sha, "ref_label": ref_label, "ref": ref} + app.logger.info( + "Using event {} to trigger benchmark. final fields: {}".format( + event_type, fields + ) + ) + result, response_data, err_message = commit_schema_to_stream( + fields, conn, gh_org, gh_repo + ) + app.logger.info( + "Using event {} to trigger benchmark. final fields: {}".format( + event_type, response_data + ) + ) - result, response_data, err_message = commit_schema_to_stream( - data_now_json_str, conn, gh_org, gh_repo - ) - if result is False: - return jsonify(err_message), 400 + else: + app.logger.info( + "{}. input json was: {}".format(event_type, request_data) + ) + response_data = {"message": event_type} - # Send data back as JSON - return jsonify(response_data), 200 + # Send data back as JSON + return jsonify(response_data), 200 + else: + return "Forbidden", 403 return app diff --git a/redis_benchmarks_specification/__api__/schema.py b/redis_benchmarks_specification/__api__/schema.py deleted file mode 100644 index 84db45e..0000000 --- a/redis_benchmarks_specification/__api__/schema.py +++ /dev/null @@ -1,7 +0,0 @@ -from marshmallow import Schema, fields - - -class CommitSchema(Schema): - git_branch = fields.String(required=False) - git_tag = fields.String(required=False) - git_hash = fields.String(required=True) diff --git a/redis_benchmarks_specification/__common__/builder_schema.py b/redis_benchmarks_specification/__common__/builder_schema.py index 7c5178e..eefd937 100644 --- a/redis_benchmarks_specification/__common__/builder_schema.py +++ b/redis_benchmarks_specification/__common__/builder_schema.py @@ -4,7 +4,6 @@ # All rights reserved. # import logging -from json import loads from urllib.error import URLError from urllib.request import urlopen from github import Github @@ -17,15 +16,15 @@ def commit_schema_to_stream( - json_str: str, + fields: dict, conn: redis.StrictRedis, - gh_org="redis", - gh_repo="redis", + gh_org, + gh_repo, gh_token=None, ): """ uses to the provided JSON dict of fields and pushes that info to the corresponding stream """ - fields = loads(json_str) - reply_fields = loads(json_str) + fields = fields + reply_fields = dict(fields) result = False error_msg = None use_git_timestamp = False @@ -72,8 +71,8 @@ def get_archive_zip_from_hash(gh_org, gh_repo, git_hash, fields): def get_commit_dict_from_sha( git_hash, - gh_org="redis", - gh_repo="redis", + gh_org, + gh_repo, commit_dict={}, use_git_timestamp=False, gh_token=None, @@ -119,6 +118,13 @@ def request_build_from_commit_info(conn, fields, reply_fields): """ result = True error_msg = None + for k, v in fields.items(): + if type(v) not in [str, int, float, bytes]: + raise Exception( + "Type of field {} is not bytes, string, int or float. Type ({}). Value={}".format( + k, type(v), v + ) + ) id = conn.xadd(STREAM_KEYNAME_GH_EVENTS_COMMIT.encode(), fields) reply_fields["id"] = id return result, reply_fields, error_msg diff --git a/redis_benchmarks_specification/__common__/env.py b/redis_benchmarks_specification/__common__/env.py index ae4b485..e1f3e2f 100644 --- a/redis_benchmarks_specification/__common__/env.py +++ b/redis_benchmarks_specification/__common__/env.py @@ -50,6 +50,9 @@ REDIS_SOCKET_TIMEOUT = int(os.getenv("REDIS_SOCKET_TIMEOUT", "300")) # environment variables +PULL_REQUEST_TRIGGER_LABEL = os.getenv( + "PULL_REQUEST_TRIGGER_LABEL", "trigger-benchmark" +) DATASINK_RTS_PUSH = bool(os.getenv("DATASINK_PUSH_RTS", False)) DATASINK_RTS_AUTH = os.getenv("DATASINK_RTS_AUTH", None) DATASINK_RTS_USER = os.getenv("DATASINK_RTS_USER", None) diff --git a/utils/tests/test_api.py b/utils/tests/test_api.py index fe26ab5..ebfe868 100644 --- a/utils/tests/test_api.py +++ b/utils/tests/test_api.py @@ -3,8 +3,6 @@ # Copyright (c) 2021., Redis Labs # All rights reserved. # -import redis_benchmarks_specification -from redis_benchmarks_specification.__api__.app import create_app from redis_benchmarks_specification.__common__.builder_schema import ( commit_schema_to_stream, @@ -26,7 +24,7 @@ def test_commit_schema_to_stream(): result, reply_fields, error_msg = commit_schema_to_stream( - '{"git_hashss":"0cf2df84d4b27af4bffd2bf3543838f09e10f874"}', + {"git_hashss": "0cf2df84d4b27af4bffd2bf3543838f09e10f874"}, None, "redis", "redis", @@ -38,7 +36,7 @@ def test_commit_schema_to_stream(): conn.ping() conn.flushall() result, reply_fields, error_msg = commit_schema_to_stream( - '{"git_hash":"0cf2df84d4b27af4bffd2bf3543838f09e10f874"}', + {"git_hash": "0cf2df84d4b27af4bffd2bf3543838f09e10f874"}, conn, "redis", "redis", diff --git a/utils/tests/test_app.py b/utils/tests/test_app.py new file mode 100644 index 0000000..26ed297 --- /dev/null +++ b/utils/tests/test_app.py @@ -0,0 +1,123 @@ +# BSD 3-Clause License +# +# Copyright (c) 2021., Redis Labs Modules +# All rights reserved. +# +import json +from hashlib import sha1 +from hmac import HMAC + +import redis + +from redis_benchmarks_specification.__api__.app import create_app, SIG_HEADER +from redis_benchmarks_specification.__common__.env import ( + STREAM_KEYNAME_GH_EVENTS_COMMIT, +) + + +def test_create_app(): + try: + conn = redis.StrictRedis(port=16379, decode_responses=True) + conn.ping() + conn.flushall() + auth_token = conn.acl_genpass() + conn.set("default:auth_token", auth_token) + flask_app = create_app(conn, "default") + req_data = "{}".encode() + expected_sign = HMAC( + key=auth_token.encode(), msg=req_data, digestmod=sha1 + ).hexdigest() + + # Unathorized due to missing header + with flask_app.test_client() as test_client: + response = test_client.post( + "/api/gh/redis/redis/commits", + json={}, + headers={}, + content_type="application/json", + ) + assert response.status_code == 403 + + # Unathorized due to wrong header value + with flask_app.test_client() as test_client: + response = test_client.post( + "/api/gh/redis/redis/commits", + json={}, + headers={SIG_HEADER: "sha1=abc"}, + content_type="application/json", + ) + assert response.status_code == 403 + + # Authorized but ignored event + with flask_app.test_client() as test_client: + response = test_client.post( + "/api/gh/redis/redis/commits", + data=json.dumps(dict({})), + headers={SIG_HEADER: "sha1={}".format(expected_sign)}, + content_type="application/json", + ) + assert response.status_code == 200 + assert response.json == {"message": "Ignored event from webhook"} + + # Authorized and PR event + with open( + "./utils/tests/test_data/event_webhook_labelled_pr.json" + ) as json_file: + label_pr_json = json.load(json_file) + json_str = json.dumps(label_pr_json) + req_data = json_str.encode() + expected_sign = HMAC( + key=auth_token.encode(), msg=req_data, digestmod=sha1 + ).hexdigest() + + with flask_app.test_client() as test_client: + response = test_client.post( + "/api/gh/redis/redis/commits", + content_type="application/json", + data=req_data, + headers={ + "Content-type": "application/json", + SIG_HEADER: "sha1={}".format(expected_sign), + }, + ) + assert response.status_code == 200 + assert ( + response.json["git_hash"] + == "a3448f39efb8900f6f66778783461cf49de94b4f" + ) + assert response.json["ref_label"] == "filipecosta90:unstable.55555" + assert response.json["ref"] == "unstable.55555" + assert conn.exists(STREAM_KEYNAME_GH_EVENTS_COMMIT) + + # Authorized and git pushes to repo + with open( + "./utils/tests/test_data/event_webhook_pushed_repo.json" + ) as json_file: + label_pr_json = json.load(json_file) + json_str = json.dumps(label_pr_json) + req_data = json_str.encode() + expected_sign = HMAC( + key=auth_token.encode(), msg=req_data, digestmod=sha1 + ).hexdigest() + + with flask_app.test_client() as test_client: + response = test_client.post( + "/api/gh/redis/redis/commits", + content_type="application/json", + data=req_data, + headers={ + "Content-type": "application/json", + SIG_HEADER: "sha1={}".format(expected_sign), + }, + ) + assert response.status_code == 200 + assert ( + response.json["git_hash"] + == "921489d5392a13e10493c6578a27b4bd5324a929" + ) + assert response.json["ref_label"] == "refs/heads/unstable.55555" + assert response.json["ref"] == "unstable.55555" + assert conn.exists(STREAM_KEYNAME_GH_EVENTS_COMMIT) + + except redis.exceptions.ConnectionError: + pass diff --git a/utils/tests/test_builder.py b/utils/tests/test_builder.py index a9db011..c8bbbe4 100644 --- a/utils/tests/test_builder.py +++ b/utils/tests/test_builder.py @@ -50,8 +50,13 @@ def test_commit_schema_to_stream_then_build(): assert conn.xlen(STREAM_KEYNAME_GH_EVENTS_COMMIT) == 0 result, reply_fields, error_msg = commit_schema_to_stream( - '{"git_hash":"0cf2df84d4b27af4bffd2bf3543838f09e10f874", "git_branch":"unstable"}', + { + "git_hash": "0cf2df84d4b27af4bffd2bf3543838f09e10f874", + "git_branch": "unstable", + }, conn, + "redis", + "redis", ) assert result == True assert error_msg == None diff --git a/utils/tests/test_data/event_webhook_labelled_pr.json b/utils/tests/test_data/event_webhook_labelled_pr.json new file mode 100644 index 0000000..b57e82f --- /dev/null +++ b/utils/tests/test_data/event_webhook_labelled_pr.json @@ -0,0 +1,498 @@ +{ + "action": "labeled", + "number": 9, + "pull_request": { + "url": "https://api.github.com/repos/filipecosta90/redis/pulls/9", + "id": 738720420, + "node_id": "PR_kwDOCs9mRs4sB_qk", + "html_url": "https://github.com/filipecosta90/redis/pull/9", + "diff_url": "https://github.com/filipecosta90/redis/pull/9.diff", + "patch_url": "https://github.com/filipecosta90/redis/pull/9.patch", + "issue_url": "https://api.github.com/repos/filipecosta90/redis/issues/9", + "number": 9, + "state": "open", + "locked": false, + "title": "test pr", + "user": { + "login": "filipecosta90", + "id": 5832149, + "node_id": "MDQ6VXNlcjU4MzIxNDk=", + "avatar_url": "https://avatars.githubusercontent.com/u/5832149?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/filipecosta90", + "html_url": "https://github.com/filipecosta90", + "followers_url": "https://api.github.com/users/filipecosta90/followers", + "following_url": "https://api.github.com/users/filipecosta90/following{/other_user}", + "gists_url": "https://api.github.com/users/filipecosta90/gists{/gist_id}", + "starred_url": "https://api.github.com/users/filipecosta90/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/filipecosta90/subscriptions", + "organizations_url": "https://api.github.com/users/filipecosta90/orgs", + "repos_url": "https://api.github.com/users/filipecosta90/repos", + "events_url": "https://api.github.com/users/filipecosta90/events{/privacy}", + "received_events_url": "https://api.github.com/users/filipecosta90/received_events", + "type": "User", + "site_admin": false + }, + "body": null, + "created_at": "2021-09-21T11:50:13Z", + "updated_at": "2021-09-21T11:54:16Z", + "closed_at": null, + "merged_at": null, + "merge_commit_sha": "5f11c04d3fba181152322e6147f7a4ae9e01b14c", + "assignee": null, + "assignees": [], + "requested_reviewers": [], + "requested_teams": [], + "labels": [ + { + "id": 3374931112, + "node_id": "LA_kwDOCs9mRs7JKVyo", + "url": "https://api.github.com/repos/filipecosta90/redis/labels/run-benchmark", + "name": "trigger-benchmark", + "color": "65D94A", + "default": false, + "description": "" + } + ], + "milestone": null, + "draft": false, + "commits_url": "https://api.github.com/repos/filipecosta90/redis/pulls/9/commits", + "review_comments_url": "https://api.github.com/repos/filipecosta90/redis/pulls/9/comments", + "review_comment_url": "https://api.github.com/repos/filipecosta90/redis/pulls/comments{/number}", + "comments_url": "https://api.github.com/repos/filipecosta90/redis/issues/9/comments", + "statuses_url": "https://api.github.com/repos/filipecosta90/redis/statuses/a3448f39efb8900f6f66778783461cf49de94b4f", + "head": { + "label": "filipecosta90:unstable.55555", + "ref": "unstable.55555", + "sha": "a3448f39efb8900f6f66778783461cf49de94b4f", + "user": { + "login": "filipecosta90", + "id": 5832149, + "node_id": "MDQ6VXNlcjU4MzIxNDk=", + "avatar_url": "https://avatars.githubusercontent.com/u/5832149?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/filipecosta90", + "html_url": "https://github.com/filipecosta90", + "followers_url": "https://api.github.com/users/filipecosta90/followers", + "following_url": "https://api.github.com/users/filipecosta90/following{/other_user}", + "gists_url": "https://api.github.com/users/filipecosta90/gists{/gist_id}", + "starred_url": "https://api.github.com/users/filipecosta90/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/filipecosta90/subscriptions", + "organizations_url": "https://api.github.com/users/filipecosta90/orgs", + "repos_url": "https://api.github.com/users/filipecosta90/repos", + "events_url": "https://api.github.com/users/filipecosta90/events{/privacy}", + "received_events_url": "https://api.github.com/users/filipecosta90/received_events", + "type": "User", + "site_admin": false + }, + "repo": { + "id": 181364294, + "node_id": "MDEwOlJlcG9zaXRvcnkxODEzNjQyOTQ=", + "name": "redis", + "full_name": "filipecosta90/redis", + "private": false, + "owner": { + "login": "filipecosta90", + "id": 5832149, + "node_id": "MDQ6VXNlcjU4MzIxNDk=", + "avatar_url": "https://avatars.githubusercontent.com/u/5832149?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/filipecosta90", + "html_url": "https://github.com/filipecosta90", + "followers_url": "https://api.github.com/users/filipecosta90/followers", + "following_url": "https://api.github.com/users/filipecosta90/following{/other_user}", + "gists_url": "https://api.github.com/users/filipecosta90/gists{/gist_id}", + "starred_url": "https://api.github.com/users/filipecosta90/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/filipecosta90/subscriptions", + "organizations_url": "https://api.github.com/users/filipecosta90/orgs", + "repos_url": "https://api.github.com/users/filipecosta90/repos", + "events_url": "https://api.github.com/users/filipecosta90/events{/privacy}", + "received_events_url": "https://api.github.com/users/filipecosta90/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/filipecosta90/redis", + "description": "Redis is an in-memory database that persists on disk. The data model is key-value, but many different kind of values are supported: Strings, Lists, Sets, Sorted Sets, Hashes, HyperLogLogs, Bitmaps.", + "fork": true, + "url": "https://api.github.com/repos/filipecosta90/redis", + "forks_url": "https://api.github.com/repos/filipecosta90/redis/forks", + "keys_url": "https://api.github.com/repos/filipecosta90/redis/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/filipecosta90/redis/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/filipecosta90/redis/teams", + "hooks_url": "https://api.github.com/repos/filipecosta90/redis/hooks", + "issue_events_url": "https://api.github.com/repos/filipecosta90/redis/issues/events{/number}", + "events_url": "https://api.github.com/repos/filipecosta90/redis/events", + "assignees_url": "https://api.github.com/repos/filipecosta90/redis/assignees{/user}", + "branches_url": "https://api.github.com/repos/filipecosta90/redis/branches{/branch}", + "tags_url": "https://api.github.com/repos/filipecosta90/redis/tags", + "blobs_url": "https://api.github.com/repos/filipecosta90/redis/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/filipecosta90/redis/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/filipecosta90/redis/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/filipecosta90/redis/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/filipecosta90/redis/statuses/{sha}", + "languages_url": "https://api.github.com/repos/filipecosta90/redis/languages", + "stargazers_url": "https://api.github.com/repos/filipecosta90/redis/stargazers", + "contributors_url": "https://api.github.com/repos/filipecosta90/redis/contributors", + "subscribers_url": "https://api.github.com/repos/filipecosta90/redis/subscribers", + "subscription_url": "https://api.github.com/repos/filipecosta90/redis/subscription", + "commits_url": "https://api.github.com/repos/filipecosta90/redis/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/filipecosta90/redis/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/filipecosta90/redis/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/filipecosta90/redis/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/filipecosta90/redis/contents/{+path}", + "compare_url": "https://api.github.com/repos/filipecosta90/redis/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/filipecosta90/redis/merges", + "archive_url": "https://api.github.com/repos/filipecosta90/redis/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/filipecosta90/redis/downloads", + "issues_url": "https://api.github.com/repos/filipecosta90/redis/issues{/number}", + "pulls_url": "https://api.github.com/repos/filipecosta90/redis/pulls{/number}", + "milestones_url": "https://api.github.com/repos/filipecosta90/redis/milestones{/number}", + "notifications_url": "https://api.github.com/repos/filipecosta90/redis/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/filipecosta90/redis/labels{/name}", + "releases_url": "https://api.github.com/repos/filipecosta90/redis/releases{/id}", + "deployments_url": "https://api.github.com/repos/filipecosta90/redis/deployments", + "created_at": "2019-04-14T20:19:58Z", + "updated_at": "2021-09-21T10:49:39Z", + "pushed_at": "2021-09-21T11:53:39Z", + "git_url": "git://github.com/filipecosta90/redis.git", + "ssh_url": "git@github.com:filipecosta90/redis.git", + "clone_url": "https://github.com/filipecosta90/redis.git", + "svn_url": "https://github.com/filipecosta90/redis", + "homepage": "http://redis.io", + "size": 98041, + "stargazers_count": 1, + "watchers_count": 1, + "language": "C", + "has_issues": false, + "has_projects": true, + "has_downloads": true, + "has_wiki": false, + "has_pages": false, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 6, + "license": { + "key": "bsd-3-clause", + "name": "BSD 3-Clause \"New\" or \"Revised\" License", + "spdx_id": "BSD-3-Clause", + "url": "https://api.github.com/licenses/bsd-3-clause", + "node_id": "MDc6TGljZW5zZTU=" + }, + "allow_forking": true, + "forks": 0, + "open_issues": 6, + "watchers": 1, + "default_branch": "unstable", + "allow_squash_merge": true, + "allow_merge_commit": true, + "allow_rebase_merge": true, + "allow_auto_merge": false, + "delete_branch_on_merge": false + } + }, + "base": { + "label": "filipecosta90:unstable", + "ref": "unstable", + "sha": "f827bb773a22eaa265c96c8781f254bdf39cf218", + "user": { + "login": "filipecosta90", + "id": 5832149, + "node_id": "MDQ6VXNlcjU4MzIxNDk=", + "avatar_url": "https://avatars.githubusercontent.com/u/5832149?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/filipecosta90", + "html_url": "https://github.com/filipecosta90", + "followers_url": "https://api.github.com/users/filipecosta90/followers", + "following_url": "https://api.github.com/users/filipecosta90/following{/other_user}", + "gists_url": "https://api.github.com/users/filipecosta90/gists{/gist_id}", + "starred_url": "https://api.github.com/users/filipecosta90/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/filipecosta90/subscriptions", + "organizations_url": "https://api.github.com/users/filipecosta90/orgs", + "repos_url": "https://api.github.com/users/filipecosta90/repos", + "events_url": "https://api.github.com/users/filipecosta90/events{/privacy}", + "received_events_url": "https://api.github.com/users/filipecosta90/received_events", + "type": "User", + "site_admin": false + }, + "repo": { + "id": 181364294, + "node_id": "MDEwOlJlcG9zaXRvcnkxODEzNjQyOTQ=", + "name": "redis", + "full_name": "filipecosta90/redis", + "private": false, + "owner": { + "login": "filipecosta90", + "id": 5832149, + "node_id": "MDQ6VXNlcjU4MzIxNDk=", + "avatar_url": "https://avatars.githubusercontent.com/u/5832149?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/filipecosta90", + "html_url": "https://github.com/filipecosta90", + "followers_url": "https://api.github.com/users/filipecosta90/followers", + "following_url": "https://api.github.com/users/filipecosta90/following{/other_user}", + "gists_url": "https://api.github.com/users/filipecosta90/gists{/gist_id}", + "starred_url": "https://api.github.com/users/filipecosta90/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/filipecosta90/subscriptions", + "organizations_url": "https://api.github.com/users/filipecosta90/orgs", + "repos_url": "https://api.github.com/users/filipecosta90/repos", + "events_url": "https://api.github.com/users/filipecosta90/events{/privacy}", + "received_events_url": "https://api.github.com/users/filipecosta90/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/filipecosta90/redis", + "description": "Redis is an in-memory database that persists on disk. The data model is key-value, but many different kind of values are supported: Strings, Lists, Sets, Sorted Sets, Hashes, HyperLogLogs, Bitmaps.", + "fork": true, + "url": "https://api.github.com/repos/filipecosta90/redis", + "forks_url": "https://api.github.com/repos/filipecosta90/redis/forks", + "keys_url": "https://api.github.com/repos/filipecosta90/redis/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/filipecosta90/redis/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/filipecosta90/redis/teams", + "hooks_url": "https://api.github.com/repos/filipecosta90/redis/hooks", + "issue_events_url": "https://api.github.com/repos/filipecosta90/redis/issues/events{/number}", + "events_url": "https://api.github.com/repos/filipecosta90/redis/events", + "assignees_url": "https://api.github.com/repos/filipecosta90/redis/assignees{/user}", + "branches_url": "https://api.github.com/repos/filipecosta90/redis/branches{/branch}", + "tags_url": "https://api.github.com/repos/filipecosta90/redis/tags", + "blobs_url": "https://api.github.com/repos/filipecosta90/redis/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/filipecosta90/redis/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/filipecosta90/redis/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/filipecosta90/redis/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/filipecosta90/redis/statuses/{sha}", + "languages_url": "https://api.github.com/repos/filipecosta90/redis/languages", + "stargazers_url": "https://api.github.com/repos/filipecosta90/redis/stargazers", + "contributors_url": "https://api.github.com/repos/filipecosta90/redis/contributors", + "subscribers_url": "https://api.github.com/repos/filipecosta90/redis/subscribers", + "subscription_url": "https://api.github.com/repos/filipecosta90/redis/subscription", + "commits_url": "https://api.github.com/repos/filipecosta90/redis/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/filipecosta90/redis/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/filipecosta90/redis/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/filipecosta90/redis/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/filipecosta90/redis/contents/{+path}", + "compare_url": "https://api.github.com/repos/filipecosta90/redis/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/filipecosta90/redis/merges", + "archive_url": "https://api.github.com/repos/filipecosta90/redis/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/filipecosta90/redis/downloads", + "issues_url": "https://api.github.com/repos/filipecosta90/redis/issues{/number}", + "pulls_url": "https://api.github.com/repos/filipecosta90/redis/pulls{/number}", + "milestones_url": "https://api.github.com/repos/filipecosta90/redis/milestones{/number}", + "notifications_url": "https://api.github.com/repos/filipecosta90/redis/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/filipecosta90/redis/labels{/name}", + "releases_url": "https://api.github.com/repos/filipecosta90/redis/releases{/id}", + "deployments_url": "https://api.github.com/repos/filipecosta90/redis/deployments", + "created_at": "2019-04-14T20:19:58Z", + "updated_at": "2021-09-21T10:49:39Z", + "pushed_at": "2021-09-21T11:53:39Z", + "git_url": "git://github.com/filipecosta90/redis.git", + "ssh_url": "git@github.com:filipecosta90/redis.git", + "clone_url": "https://github.com/filipecosta90/redis.git", + "svn_url": "https://github.com/filipecosta90/redis", + "homepage": "http://redis.io", + "size": 98041, + "stargazers_count": 1, + "watchers_count": 1, + "language": "C", + "has_issues": false, + "has_projects": true, + "has_downloads": true, + "has_wiki": false, + "has_pages": false, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 6, + "license": { + "key": "bsd-3-clause", + "name": "BSD 3-Clause \"New\" or \"Revised\" License", + "spdx_id": "BSD-3-Clause", + "url": "https://api.github.com/licenses/bsd-3-clause", + "node_id": "MDc6TGljZW5zZTU=" + }, + "allow_forking": true, + "forks": 0, + "open_issues": 6, + "watchers": 1, + "default_branch": "unstable", + "allow_squash_merge": true, + "allow_merge_commit": true, + "allow_rebase_merge": true, + "allow_auto_merge": false, + "delete_branch_on_merge": false + } + }, + "_links": { + "self": { + "href": "https://api.github.com/repos/filipecosta90/redis/pulls/9" + }, + "html": { + "href": "https://github.com/filipecosta90/redis/pull/9" + }, + "issue": { + "href": "https://api.github.com/repos/filipecosta90/redis/issues/9" + }, + "comments": { + "href": "https://api.github.com/repos/filipecosta90/redis/issues/9/comments" + }, + "review_comments": { + "href": "https://api.github.com/repos/filipecosta90/redis/pulls/9/comments" + }, + "review_comment": { + "href": "https://api.github.com/repos/filipecosta90/redis/pulls/comments{/number}" + }, + "commits": { + "href": "https://api.github.com/repos/filipecosta90/redis/pulls/9/commits" + }, + "statuses": { + "href": "https://api.github.com/repos/filipecosta90/redis/statuses/a3448f39efb8900f6f66778783461cf49de94b4f" + } + }, + "author_association": "OWNER", + "auto_merge": null, + "active_lock_reason": null, + "merged": false, + "mergeable": true, + "rebaseable": true, + "mergeable_state": "unstable", + "merged_by": null, + "comments": 0, + "review_comments": 0, + "maintainer_can_modify": false, + "commits": 2, + "additions": 1, + "deletions": 0, + "changed_files": 2 + }, + "label": { + "id": 3374931112, + "node_id": "LA_kwDOCs9mRs7JKVyo", + "url": "https://api.github.com/repos/filipecosta90/redis/labels/run-benchmark", + "name": "run-benchmark", + "color": "65D94A", + "default": false, + "description": "" + }, + "repository": { + "id": 181364294, + "node_id": "MDEwOlJlcG9zaXRvcnkxODEzNjQyOTQ=", + "name": "redis", + "full_name": "filipecosta90/redis", + "private": false, + "owner": { + "login": "filipecosta90", + "id": 5832149, + "node_id": "MDQ6VXNlcjU4MzIxNDk=", + "avatar_url": "https://avatars.githubusercontent.com/u/5832149?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/filipecosta90", + "html_url": "https://github.com/filipecosta90", + "followers_url": "https://api.github.com/users/filipecosta90/followers", + "following_url": "https://api.github.com/users/filipecosta90/following{/other_user}", + "gists_url": "https://api.github.com/users/filipecosta90/gists{/gist_id}", + "starred_url": "https://api.github.com/users/filipecosta90/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/filipecosta90/subscriptions", + "organizations_url": "https://api.github.com/users/filipecosta90/orgs", + "repos_url": "https://api.github.com/users/filipecosta90/repos", + "events_url": "https://api.github.com/users/filipecosta90/events{/privacy}", + "received_events_url": "https://api.github.com/users/filipecosta90/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/filipecosta90/redis", + "description": "Redis is an in-memory database that persists on disk. The data model is key-value, but many different kind of values are supported: Strings, Lists, Sets, Sorted Sets, Hashes, HyperLogLogs, Bitmaps.", + "fork": true, + "url": "https://api.github.com/repos/filipecosta90/redis", + "forks_url": "https://api.github.com/repos/filipecosta90/redis/forks", + "keys_url": "https://api.github.com/repos/filipecosta90/redis/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/filipecosta90/redis/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/filipecosta90/redis/teams", + "hooks_url": "https://api.github.com/repos/filipecosta90/redis/hooks", + "issue_events_url": "https://api.github.com/repos/filipecosta90/redis/issues/events{/number}", + "events_url": "https://api.github.com/repos/filipecosta90/redis/events", + "assignees_url": "https://api.github.com/repos/filipecosta90/redis/assignees{/user}", + "branches_url": "https://api.github.com/repos/filipecosta90/redis/branches{/branch}", + "tags_url": "https://api.github.com/repos/filipecosta90/redis/tags", + "blobs_url": "https://api.github.com/repos/filipecosta90/redis/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/filipecosta90/redis/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/filipecosta90/redis/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/filipecosta90/redis/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/filipecosta90/redis/statuses/{sha}", + "languages_url": "https://api.github.com/repos/filipecosta90/redis/languages", + "stargazers_url": "https://api.github.com/repos/filipecosta90/redis/stargazers", + "contributors_url": "https://api.github.com/repos/filipecosta90/redis/contributors", + "subscribers_url": "https://api.github.com/repos/filipecosta90/redis/subscribers", + "subscription_url": "https://api.github.com/repos/filipecosta90/redis/subscription", + "commits_url": "https://api.github.com/repos/filipecosta90/redis/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/filipecosta90/redis/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/filipecosta90/redis/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/filipecosta90/redis/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/filipecosta90/redis/contents/{+path}", + "compare_url": "https://api.github.com/repos/filipecosta90/redis/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/filipecosta90/redis/merges", + "archive_url": "https://api.github.com/repos/filipecosta90/redis/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/filipecosta90/redis/downloads", + "issues_url": "https://api.github.com/repos/filipecosta90/redis/issues{/number}", + "pulls_url": "https://api.github.com/repos/filipecosta90/redis/pulls{/number}", + "milestones_url": "https://api.github.com/repos/filipecosta90/redis/milestones{/number}", + "notifications_url": "https://api.github.com/repos/filipecosta90/redis/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/filipecosta90/redis/labels{/name}", + "releases_url": "https://api.github.com/repos/filipecosta90/redis/releases{/id}", + "deployments_url": "https://api.github.com/repos/filipecosta90/redis/deployments", + "created_at": "2019-04-14T20:19:58Z", + "updated_at": "2021-09-21T10:49:39Z", + "pushed_at": "2021-09-21T11:53:39Z", + "git_url": "git://github.com/filipecosta90/redis.git", + "ssh_url": "git@github.com:filipecosta90/redis.git", + "clone_url": "https://github.com/filipecosta90/redis.git", + "svn_url": "https://github.com/filipecosta90/redis", + "homepage": "http://redis.io", + "size": 98041, + "stargazers_count": 1, + "watchers_count": 1, + "language": "C", + "has_issues": false, + "has_projects": true, + "has_downloads": true, + "has_wiki": false, + "has_pages": false, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 6, + "license": { + "key": "bsd-3-clause", + "name": "BSD 3-Clause \"New\" or \"Revised\" License", + "spdx_id": "BSD-3-Clause", + "url": "https://api.github.com/licenses/bsd-3-clause", + "node_id": "MDc6TGljZW5zZTU=" + }, + "allow_forking": true, + "forks": 0, + "open_issues": 6, + "watchers": 1, + "default_branch": "unstable" + }, + "sender": { + "login": "filipecosta90", + "id": 5832149, + "node_id": "MDQ6VXNlcjU4MzIxNDk=", + "avatar_url": "https://avatars.githubusercontent.com/u/5832149?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/filipecosta90", + "html_url": "https://github.com/filipecosta90", + "followers_url": "https://api.github.com/users/filipecosta90/followers", + "following_url": "https://api.github.com/users/filipecosta90/following{/other_user}", + "gists_url": "https://api.github.com/users/filipecosta90/gists{/gist_id}", + "starred_url": "https://api.github.com/users/filipecosta90/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/filipecosta90/subscriptions", + "organizations_url": "https://api.github.com/users/filipecosta90/orgs", + "repos_url": "https://api.github.com/users/filipecosta90/repos", + "events_url": "https://api.github.com/users/filipecosta90/events{/privacy}", + "received_events_url": "https://api.github.com/users/filipecosta90/received_events", + "type": "User", + "site_admin": false + } +} \ No newline at end of file diff --git a/utils/tests/test_data/event_webhook_pushed_repo.json b/utils/tests/test_data/event_webhook_pushed_repo.json new file mode 100644 index 0000000..d4170f7 --- /dev/null +++ b/utils/tests/test_data/event_webhook_pushed_repo.json @@ -0,0 +1,187 @@ +{ + "ref": "refs/heads/unstable.55555", + "before": "572abee6f17753fe2af59aeb7c3dd42baee66e1b", + "after": "921489d5392a13e10493c6578a27b4bd5324a929", + "repository": { + "id": 181364294, + "node_id": "MDEwOlJlcG9zaXRvcnkxODEzNjQyOTQ=", + "name": "redis", + "full_name": "filipecosta90/redis", + "private": false, + "owner": { + "name": "filipecosta90", + "email": "filipecosta.90@gmail.com", + "login": "filipecosta90", + "id": 5832149, + "node_id": "MDQ6VXNlcjU4MzIxNDk=", + "avatar_url": "https://avatars.githubusercontent.com/u/5832149?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/filipecosta90", + "html_url": "https://github.com/filipecosta90", + "followers_url": "https://api.github.com/users/filipecosta90/followers", + "following_url": "https://api.github.com/users/filipecosta90/following{/other_user}", + "gists_url": "https://api.github.com/users/filipecosta90/gists{/gist_id}", + "starred_url": "https://api.github.com/users/filipecosta90/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/filipecosta90/subscriptions", + "organizations_url": "https://api.github.com/users/filipecosta90/orgs", + "repos_url": "https://api.github.com/users/filipecosta90/repos", + "events_url": "https://api.github.com/users/filipecosta90/events{/privacy}", + "received_events_url": "https://api.github.com/users/filipecosta90/received_events", + "type": "User", + "site_admin": false + }, + "html_url": "https://github.com/filipecosta90/redis", + "description": "Redis is an in-memory database that persists on disk. The data model is key-value, but many different kind of values are supported: Strings, Lists, Sets, Sorted Sets, Hashes, HyperLogLogs, Bitmaps.", + "fork": true, + "url": "https://github.com/filipecosta90/redis", + "forks_url": "https://api.github.com/repos/filipecosta90/redis/forks", + "keys_url": "https://api.github.com/repos/filipecosta90/redis/keys{/key_id}", + "collaborators_url": "https://api.github.com/repos/filipecosta90/redis/collaborators{/collaborator}", + "teams_url": "https://api.github.com/repos/filipecosta90/redis/teams", + "hooks_url": "https://api.github.com/repos/filipecosta90/redis/hooks", + "issue_events_url": "https://api.github.com/repos/filipecosta90/redis/issues/events{/number}", + "events_url": "https://api.github.com/repos/filipecosta90/redis/events", + "assignees_url": "https://api.github.com/repos/filipecosta90/redis/assignees{/user}", + "branches_url": "https://api.github.com/repos/filipecosta90/redis/branches{/branch}", + "tags_url": "https://api.github.com/repos/filipecosta90/redis/tags", + "blobs_url": "https://api.github.com/repos/filipecosta90/redis/git/blobs{/sha}", + "git_tags_url": "https://api.github.com/repos/filipecosta90/redis/git/tags{/sha}", + "git_refs_url": "https://api.github.com/repos/filipecosta90/redis/git/refs{/sha}", + "trees_url": "https://api.github.com/repos/filipecosta90/redis/git/trees{/sha}", + "statuses_url": "https://api.github.com/repos/filipecosta90/redis/statuses/{sha}", + "languages_url": "https://api.github.com/repos/filipecosta90/redis/languages", + "stargazers_url": "https://api.github.com/repos/filipecosta90/redis/stargazers", + "contributors_url": "https://api.github.com/repos/filipecosta90/redis/contributors", + "subscribers_url": "https://api.github.com/repos/filipecosta90/redis/subscribers", + "subscription_url": "https://api.github.com/repos/filipecosta90/redis/subscription", + "commits_url": "https://api.github.com/repos/filipecosta90/redis/commits{/sha}", + "git_commits_url": "https://api.github.com/repos/filipecosta90/redis/git/commits{/sha}", + "comments_url": "https://api.github.com/repos/filipecosta90/redis/comments{/number}", + "issue_comment_url": "https://api.github.com/repos/filipecosta90/redis/issues/comments{/number}", + "contents_url": "https://api.github.com/repos/filipecosta90/redis/contents/{+path}", + "compare_url": "https://api.github.com/repos/filipecosta90/redis/compare/{base}...{head}", + "merges_url": "https://api.github.com/repos/filipecosta90/redis/merges", + "archive_url": "https://api.github.com/repos/filipecosta90/redis/{archive_format}{/ref}", + "downloads_url": "https://api.github.com/repos/filipecosta90/redis/downloads", + "issues_url": "https://api.github.com/repos/filipecosta90/redis/issues{/number}", + "pulls_url": "https://api.github.com/repos/filipecosta90/redis/pulls{/number}", + "milestones_url": "https://api.github.com/repos/filipecosta90/redis/milestones{/number}", + "notifications_url": "https://api.github.com/repos/filipecosta90/redis/notifications{?since,all,participating}", + "labels_url": "https://api.github.com/repos/filipecosta90/redis/labels{/name}", + "releases_url": "https://api.github.com/repos/filipecosta90/redis/releases{/id}", + "deployments_url": "https://api.github.com/repos/filipecosta90/redis/deployments", + "created_at": 1555273198, + "updated_at": "2021-09-21T10:49:39Z", + "pushed_at": 1632224558, + "git_url": "git://github.com/filipecosta90/redis.git", + "ssh_url": "git@github.com:filipecosta90/redis.git", + "clone_url": "https://github.com/filipecosta90/redis.git", + "svn_url": "https://github.com/filipecosta90/redis", + "homepage": "http://redis.io", + "size": 97836, + "stargazers_count": 1, + "watchers_count": 1, + "language": "C", + "has_issues": false, + "has_projects": true, + "has_downloads": true, + "has_wiki": false, + "has_pages": false, + "forks_count": 0, + "mirror_url": null, + "archived": false, + "disabled": false, + "open_issues_count": 5, + "license": { + "key": "bsd-3-clause", + "name": "BSD 3-Clause \"New\" or \"Revised\" License", + "spdx_id": "BSD-3-Clause", + "url": "https://api.github.com/licenses/bsd-3-clause", + "node_id": "MDc6TGljZW5zZTU=" + }, + "allow_forking": true, + "forks": 0, + "open_issues": 5, + "watchers": 1, + "default_branch": "unstable", + "stargazers": 1, + "master_branch": "unstable" + }, + "pusher": { + "name": "filipecosta90", + "email": "filipecosta.90@gmail.com" + }, + "sender": { + "login": "filipecosta90", + "id": 5832149, + "node_id": "MDQ6VXNlcjU4MzIxNDk=", + "avatar_url": "https://avatars.githubusercontent.com/u/5832149?v=4", + "gravatar_id": "", + "url": "https://api.github.com/users/filipecosta90", + "html_url": "https://github.com/filipecosta90", + "followers_url": "https://api.github.com/users/filipecosta90/followers", + "following_url": "https://api.github.com/users/filipecosta90/following{/other_user}", + "gists_url": "https://api.github.com/users/filipecosta90/gists{/gist_id}", + "starred_url": "https://api.github.com/users/filipecosta90/starred{/owner}{/repo}", + "subscriptions_url": "https://api.github.com/users/filipecosta90/subscriptions", + "organizations_url": "https://api.github.com/users/filipecosta90/orgs", + "repos_url": "https://api.github.com/users/filipecosta90/repos", + "events_url": "https://api.github.com/users/filipecosta90/events{/privacy}", + "received_events_url": "https://api.github.com/users/filipecosta90/received_events", + "type": "User", + "site_admin": false + }, + "created": false, + "deleted": false, + "forced": false, + "base_ref": null, + "compare": "https://github.com/filipecosta90/redis/compare/572abee6f177...921489d5392a", + "commits": [ + { + "id": "921489d5392a13e10493c6578a27b4bd5324a929", + "tree_id": "b285b225ee8c5d0b32b63e9a6e02a745da1aaae5", + "distinct": true, + "message": "test commit", + "timestamp": "2021-09-21T12:42:15+01:00", + "url": "https://github.com/filipecosta90/redis/commit/921489d5392a13e10493c6578a27b4bd5324a929", + "author": { + "name": "filipecosta90", + "email": "filipecosta.90@gmail.com", + "username": "filipecosta90" + }, + "committer": { + "name": "filipecosta90", + "email": "filipecosta.90@gmail.com", + "username": "filipecosta90" + }, + "added": [ + "1.txt" + ], + "removed": [], + "modified": [] + } + ], + "head_commit": { + "id": "921489d5392a13e10493c6578a27b4bd5324a929", + "tree_id": "b285b225ee8c5d0b32b63e9a6e02a745da1aaae5", + "distinct": true, + "message": "test commit", + "timestamp": "2021-09-21T12:42:15+01:00", + "url": "https://github.com/filipecosta90/redis/commit/921489d5392a13e10493c6578a27b4bd5324a929", + "author": { + "name": "filipecosta90", + "email": "filipecosta.90@gmail.com", + "username": "filipecosta90" + }, + "committer": { + "name": "filipecosta90", + "email": "filipecosta.90@gmail.com", + "username": "filipecosta90" + }, + "added": [ + "1.txt" + ], + "removed": [], + "modified": [] + } +} \ No newline at end of file