bp = Blueprint("github", __name__)
-from flask import redirect, url_for, request, flash, abort, render_template, jsonify
+from flask import redirect, url_for, request, flash, abort, render_template, jsonify, current_app
from flask_user import current_user, login_required
from sqlalchemy import func
from flask_github import GitHub
from app import github, csrf
from app.models import db, User, APIToken, Package, Permission
-from app.utils import loginUser, randomString
+from app.utils import loginUser, randomString, abs_url_for
from app.blueprints.api.support import error, handleCreateRelease
import hmac, requests, json
@bp.route("/github/start/")
def start():
- return github.authorize("", redirect_uri=url_for("github.callback"))
+ return github.authorize("", redirect_uri=abs_url_for("github.callback"))
+
+@bp.route("/github/view/")
+def view_permissions():
+ url = "https://github.com/settings/connections/applications/" + \
+ current_app.config["GITHUB_CLIENT_ID"]
+ return redirect(url)
@bp.route("/github/callback/")
@github.authorized_handler
event = request.headers.get("X-GitHub-Event")
if event == "push":
- title = json["head_commit"]["message"].partition("\n")[0]
ref = json["after"]
+ title = json["head_commit"]["message"].partition("\n")[0]
+ elif event == "create" and json["ref_type"] == "tag":
+ ref = json["ref"]
+ title = ref
elif event == "ping":
return jsonify({ "success": True, "message": "Ping successful" })
else:
- return error(400, "Unsupported event. Only 'push' and 'ping' are supported.")
+ return error(400, "Unsupported event. Only 'push', `create:tag`, and 'ping' are supported.")
#
# Perform release
class SetupWebhookForm(FlaskForm):
- event = SelectField("Event Type", choices=[('push', 'Push'), ('tag', 'New tag')])
+ event = SelectField("Event Type", choices=[('create', 'New tag or GitHub release'), ('push', 'Push')])
submit = SubmitField("Save")
if current_user.github_access_token is None:
return github.authorize("write:repo_hook", \
- redirect_uri=url_for("github.callback_webhook", pid=pid, _external=True))
+ redirect_uri=abs_url_for("github.callback_webhook", pid=pid))
form = SetupWebhookForm(formdata=request.form)
if request.method == "POST" and form.validate():
token = APIToken()
- token.name = "Github Webhook for " + package.title
+ token.name = "GitHub Webhook for " + package.title
token.owner = current_user
token.access_token = randomString(32)
token.package = package
event = form.event.data
- if event != "push" and event != "tag":
+ if event != "push" and event != "create":
abort(500)
- # Create webhook
- url = "https://api.github.com/repos/{}/{}/hooks".format(gh_user, gh_repo)
- data = {
- "name": "web",
- "active": True,
- "events": [event],
- "config": {
- "url": url_for("github.webhook", _external=True),
- "content_type": "json",
- "secret": token.access_token
- },
- }
-
- headers = {
- "Authorization": "token " + current_user.github_access_token
- }
-
- r = requests.post(url, headers=headers, data=json.dumps(data))
- if r.status_code == 201:
- db.session.add(token)
- db.session.commit()
-
+ if handleMakeWebhook(gh_user, gh_repo, package, \
+ current_user.github_access_token, event, token):
+ flash("Successfully created webhook", "success")
return redirect(package.getDetailsURL())
- elif r.status_code == 401 or r.status_code == 403:
- current_user.github_access_token = None
- db.session.commit()
-
- return github.authorize("write:repo_hook", \
- redirect_uri=url_for("github.callback_webhook", pid=pid, _external=True))
else:
- flash("Failed to create webhook, received response from Github " +
- str(r.status_code) + ": " +
- str(r.json().get("message")), "danger")
+ return redirect(url_for("github.setup_webhook", pid=package.id))
return render_template("github/setup_webhook.html", \
form=form, package=package)
+
+
+def handleMakeWebhook(gh_user, gh_repo, package, oauth, event, token):
+ url = "https://api.github.com/repos/{}/{}/hooks".format(gh_user, gh_repo)
+ headers = {
+ "Authorization": "token " + oauth
+ }
+ data = {
+ "name": "web",
+ "active": True,
+ "events": [event],
+ "config": {
+ "url": abs_url_for("github.webhook"),
+ "content_type": "json",
+ "secret": token.access_token
+ },
+ }
+
+ # First check that the webhook doesn't already exist
+ r = requests.get(url, headers=headers)
+
+ if r.status_code == 401 or r.status_code == 403:
+ current_user.github_access_token = None
+ db.session.commit()
+ return False
+
+ if r.status_code != 200:
+ flash("Failed to create webhook, received response from Github " +
+ str(r.status_code) + ": " +
+ str(r.json().get("message")), "danger")
+ return False
+
+ for hook in r.json():
+ if hook.get("config") and hook["config"].get("url") and \
+ hook["config"]["url"] == data["config"]["url"]:
+ flash("Failed to create webhook, as it already exists", "danger")
+ return False
+
+
+ # Create it
+ r = requests.post(url, headers=headers, data=json.dumps(data))
+
+ if r.status_code == 201:
+ db.session.add(token)
+ db.session.commit()
+
+ return True
+
+ elif r.status_code == 401 or r.status_code == 403:
+ current_user.github_access_token = None
+ db.session.commit()
+
+ return False
+
+ else:
+ flash("Failed to create webhook, received response from Github " +
+ str(r.status_code) + ": " +
+ str(r.json().get("message")), "danger")
+ return False