def get_release_history(snap_name): try: release_history = publisher_api.snap_release_history( flask.session, snap_name) except StoreApiResponseErrorList as api_response_error_list: return _handle_error_list(api_response_error_list.errors) except (StoreApiError, ApiError) as api_error: return _handle_error(api_error) try: channel_map = publisher_api.snap_channel_map(flask.session, snap_name) except StoreApiResponseErrorList as api_response_error_list: return _handle_error_list(api_response_error_list.errors) except (StoreApiError, ApiError) as api_error: return _handle_error(api_error) snap = channel_map.get("snap", {}) context = { "snap_name": snap_name, "snap_title": snap.get("title"), "publisher_name": snap.get("publisher", {}).get("display-name", {}), "release_history": release_history, "private": snap.get("private"), "default_track": snap.get("default-track"), "channel_map": channel_map.get("channel-map"), "tracks": snap.get("tracks"), } return flask.render_template("publisher/release-history.html", **context)
def get_update_gh_webhooks(snap_name): try: details = publisher_api.get_snap_info(snap_name, flask.session) except StoreApiResponseErrorList as api_response_error_list: if api_response_error_list.status_code == 404: return flask.abort(404, "No snap named {}".format(snap_name)) else: return _handle_error_list(api_response_error_list.errors) except (StoreApiError, ApiError) as api_error: return _handle_error(api_error) lp_snap = launchpad.get_snap_by_store_name(details["snap_name"]) if not lp_snap: flask.flash( "This snap is not linked with a GitHub repository", "negative" ) return flask.redirect( flask.url_for(".get_settings", snap_name=snap_name) ) github = GitHub(flask.session.get("github_auth_secret")) try: github.get_user() except Unauthorized: return flask.redirect(f"/github/auth?back={flask.request.path}") gh_link = lp_snap["git_repository_url"][19:] gh_owner, gh_repo = gh_link.split("/") # Remove old BSI webhook if present old_url = f"https://build.snapcraft.io/{gh_owner}/{gh_repo}/webhook/notify" old_hook = github.get_hook_by_url(gh_owner, gh_repo, old_url) if old_hook: github.remove_hook( gh_owner, gh_repo, old_hook["id"], ) # Remove current hook github_hook_url = f"{GITHUB_WEBHOOK_HOST_URL}{snap_name}/webhook/notify" snapcraft_hook = github.get_hook_by_url(gh_owner, gh_repo, github_hook_url) if snapcraft_hook: github.remove_hook( gh_owner, gh_repo, snapcraft_hook["id"], ) # Create webhook in the repo github.create_hook(gh_owner, gh_repo, github_hook_url) flask.flash("The webhook has been created successfully", "positive") return flask.redirect(flask.url_for(".get_settings", snap_name=snap_name))
def post_update_gh_webhooks(snap_name): try: details = api.get_snap_info(snap_name, flask.session) except ApiResponseErrorList as api_response_error_list: if api_response_error_list.status_code == 404: return flask.abort(404, "No snap named {}".format(snap_name)) else: return _handle_error_list(api_response_error_list.errors) except ApiError as api_error: return _handle_error(api_error) lp_snap = launchpad.get_snap_by_store_name(details["snap_name"]) gh_link = lp_snap["git_repository_url"][19:] gh_owner, gh_repo = gh_link.split("/") github = GitHub(flask.session.get("github_auth_secret")) old_url = f"https://build.snapcraft.io/{gh_owner}/{gh_repo}/webhook/notify" old_hook = github.get_hook_by_url(gh_owner, gh_repo, old_url) if old_hook: github.update_hook_url( gh_owner, gh_repo, old_hook["id"], f"https://snapcraft.io/{snap_name}/webhook/notify", ) return flask.redirect( flask.url_for(".get_snap_builds", snap_name=snap_name))
def login_callback(): code = flask.request.args["code"] state = flask.request.args["state"] # Avoid CSRF attacks validate_csrf(state) discharged_token = candid.discharge_token(code) candid_macaroon = candid.discharge_macaroon( flask.session["publisher-macaroon"], discharged_token) # Store bakery authentication flask.session["macaroons"] = candid.get_serialized_bakery_macaroon( flask.session["publisher-macaroon"], candid_macaroon) try: publisher = publisher_api.whoami(flask.session) except StoreApiResponseErrorList as api_response_error_list: return _handle_error_list(api_response_error_list.errors) except (StoreApiError, ApiError) as api_error: return _handle_error(api_error) # whoami is the only Dashboard API endpoint # that support Candid. return publisher
def get_publicise_badges(snap_name): try: snap_details = publisher_api.get_snap_info(snap_name, flask.session) except StoreApiResponseErrorList as api_response_error_list: if api_response_error_list.status_code == 404: return flask.abort(404, "No snap named {}".format(snap_name)) else: return _handle_error_list(api_response_error_list.errors) except (StoreApiError, ApiError) as api_error: return _handle_error(api_error) if snap_details["private"]: return flask.abort(404, "No snap named {}".format(snap_name)) try: snap_public_details = store_api.get_item_details(snap_name, api_version=2) except (StoreApiError, ApiError) as api_error: return _handle_error(api_error) context = { "snap_name": snap_details["snap_name"], "snap_title": snap_details["title"], "publisher_name": snap_details["publisher"]["display-name"], "snap_id": snap_details["snap_id"], "trending": snap_public_details["snap"]["trending"], } return flask.render_template("publisher/publicise/github_badges.html", **context)
def get_publicise_cards(snap_name): try: snap_details = publisher_api.get_snap_info(snap_name, flask.session) except StoreApiResponseErrorList as api_response_error_list: if api_response_error_list.status_code == 404: return flask.abort(404, "No snap named {}".format(snap_name)) else: return _handle_error_list(api_response_error_list.errors) except (StoreApiError, ApiError) as api_error: return _handle_error(api_error) if snap_details["private"]: return flask.abort(404, "No snap named {}".format(snap_name)) screenshots = filter_screenshots(snap_details["media"]) has_screenshot = True if screenshots else False context = { "has_screenshot": has_screenshot, "snap_name": snap_details["snap_name"], "snap_title": snap_details["title"], "publisher_name": snap_details["publisher"]["display-name"], "snap_id": snap_details["snap_id"], } return flask.render_template("publisher/publicise/embedded_cards.html", **context)
def post_build(snap_name): try: details = api.get_snap_info(snap_name, flask.session) except ApiResponseErrorList as api_response_error_list: if api_response_error_list.status_code == 404: return flask.abort(404, "No snap named {}".format(snap_name)) else: return _handle_error_list(api_response_error_list.errors) except ApiError as api_error: return _handle_error(api_error) try: if launchpad.is_snap_building(details["snap_name"]): launchpad.cancel_snap_builds(details["snap_name"]) launchpad.build_snap(details["snap_name"]) except HTTPError as e: # Timeout or not found from Launchpad if e.response.status_code in [408, 404]: flask.flash("An error occurred, please try again.", "negative") return flask.redirect( flask.url_for(".get_snap_builds", snap_name=snap_name)) raise e flask.flash("Build triggered", "positive") return flask.redirect( flask.url_for(".get_snap_builds", snap_name=snap_name))
def get_store_snaps(store_id): try: snaps = admin_api.get_store_snaps(flask.session, store_id) except StoreApiResponseErrorList as api_response_error_list: return _handle_error_list(api_response_error_list.errors) except (StoreApiError, ApiError) as api_error: return _handle_error(api_error) return jsonify(snaps)
def get_settings(snap_name): try: snap_details = publisher_api.get_snap_info(snap_name, flask.session) except StoreApiResponseErrorList as api_response_error_list: if api_response_error_list.status_code == 404: return flask.abort(404, "No snap named {}".format(snap_name)) else: return _handle_error_list(api_response_error_list.errors) except (StoreApiError, ApiError) as api_error: return _handle_error(api_error) if "whitelist_country_codes" in snap_details: whitelist_country_codes = ( snap_details["whitelist_country_codes"] if len(snap_details["whitelist_country_codes"]) > 0 else [] ) else: whitelist_country_codes = [] if "blacklist_country_codes" in snap_details: blacklist_country_codes = ( snap_details["blacklist_country_codes"] if len(snap_details["blacklist_country_codes"]) > 0 else [] ) else: blacklist_country_codes = [] countries = [] for country in pycountry.countries: countries.append({"key": country.alpha_2, "name": country.name}) is_on_lp = False lp_snap = launchpad.get_snap_by_store_name(snap_name) if lp_snap: is_on_lp = True context = { "snap_name": snap_details["snap_name"], "snap_title": snap_details["title"], "snap_id": snap_details["snap_id"], "publisher_name": snap_details["publisher"]["display-name"], "license": license, "private": snap_details["private"], "unlisted": snap_details["unlisted"], "countries": countries, "whitelist_country_codes": whitelist_country_codes, "blacklist_country_codes": blacklist_country_codes, "price": snap_details["price"], "store": snap_details["store"], "keywords": snap_details["keywords"], "status": snap_details["status"], "is_on_lp": is_on_lp, } return flask.render_template("publisher/settings.html", **context)
def login_callback(): code = flask.request.args["code"] state = flask.request.args["state"] flask.session.pop("macaroon_root", None) flask.session.pop("macaroon_discharge", None) # Avoid CSRF attacks validate_csrf(state) discharged_token = candid.discharge_token(code) candid_macaroon = candid.discharge_macaroon( flask.session["publisher-macaroon"], discharged_token) # Store bakery authentication flask.session["macaroons"] = candid.get_serialized_bakery_macaroon( flask.session["publisher-macaroon"], candid_macaroon) try: publisher = publisher_api.whoami(flask.session) account = publisher_api.get_account(flask.session) except StoreApiResponseErrorList as api_response_error_list: return _handle_error_list(api_response_error_list.errors) except (StoreApiError, ApiError) as api_error: return _handle_error(api_error) flask.session["publisher"] = { "account_id": publisher["account"]["id"], "nickname": publisher["account"]["username"], "fullname": publisher["account"]["name"], "image": None, "email": publisher["account"]["email"], } flask.session["publisher"]["stores"] = logic.get_stores( account["stores"], roles=["admin", "review", "view"]) response = flask.make_response( flask.redirect( flask.session.pop( "next_url", flask.url_for("publisher_snaps.get_account_snaps"), ), 302, ), ) # Set cookie to know where to redirect users for re-auth response.set_cookie( "last_login_method", "candid", expires=datetime.datetime.now() + datetime.timedelta(days=365), ) return response
def post_preview(snap_name): try: snap_details = publisher_api.get_snap_info(snap_name, flask.session) except StoreApiResponseErrorList as api_response_error_list: if api_response_error_list.status_code == 404: return flask.abort(404, "No snap named {}".format(snap_name)) else: return _handle_error_list(api_response_error_list.errors) except (StoreApiError, ApiError) as api_error: return _handle_error(api_error) context = { "publisher": snap_details["publisher"]["display-name"], "username": snap_details["publisher"]["username"], "developer_validation": snap_details["publisher"]["validation"], } state = loads(flask.request.form["state"]) for item in state: if item == "description": context[item] = parse_markdown_description( bleach.clean(state[item], tags=[])) else: context[item] = state[item] context["is_preview"] = True context["package_name"] = context["snap_name"] context["snap_title"] = context["title"] context["appliances"] = [] # Images icons = get_icon(context["images"]) context["screenshots"] = filter_screenshots(context["images"]) context["icon_url"] = icons[0] if icons else None context["video"] = get_video(context["images"]) # Channel map context["channel_map"] = [] context["default_track"] = "latest" context["lowest_risk_available"] = "stable" context["version"] = "test" context["has_stable"] = True # metadata context["last_updated"] = "Preview" context["filesize"] = "1mb" # maps context["countries"] = preview_data.get_countries() context["normalized_os"] = preview_data.get_normalised_oses() return flask.render_template("store/snap-details.html", **context)
def get_stores(): """ In this view we get all the stores the user is an admin or we show a 403 """ try: stores = admin_api.get_stores(flask.session) except StoreApiResponseErrorList as api_response_error_list: return _handle_error_list(api_response_error_list.errors) except (StoreApiError, ApiError) as api_error: return _handle_error(api_error) return jsonify(stores)
def get_settings(store_id): try: stores = admin_api.get_stores(flask.session) store = admin_api.get_store(flask.session, store_id) except StoreApiResponseErrorList as api_response_error_list: return _handle_error_list(api_response_error_list.errors) except (StoreApiError, ApiError) as api_error: return _handle_error(api_error) return flask.render_template("admin/settings.html", stores=stores, store=store)
def get_snap_builds(snap_name): try: details = api.get_snap_info(snap_name, flask.session) # API call to make users without needed permissions refresh the session # Users needs package_upload_request permission to use this feature api.get_package_upload_macaroon(session=flask.session, snap_name=snap_name, channels=["edge"]) except ApiResponseErrorList as api_response_error_list: if api_response_error_list.status_code == 404: return flask.abort(404, "No snap named {}".format(snap_name)) else: return _handle_error_list(api_response_error_list.errors) except ApiError as api_error: return _handle_error(api_error) context = { "snap_id": details["snap_id"], "snap_name": details["snap_name"], "snap_title": details["title"], "snap_builds_enabled": False, "snap_builds": [], "total_builds": 0, } # Get built snap in launchpad with this store name github = GitHub(flask.session.get("github_auth_secret")) lp_snap = launchpad.get_snap_by_store_name(details["snap_name"]) if lp_snap: # Git repository without GitHub hostname context["github_repository"] = lp_snap["git_repository_url"][19:] github_owner, github_repo = context["github_repository"].split("/") context["yaml_file_exists"] = github.get_snapcraft_yaml_location( github_owner, github_repo) if not context["yaml_file_exists"]: flask.flash("This repository doesn't contain a snapcraft.yaml", "negative") context = get_builds(lp_snap, context, slice(0, BUILDS_PER_PAGE)) else: try: context["github_user"] = github.get_user() except Unauthorized: context["github_user"] = None if context["github_user"]: context["github_orgs"] = github.get_orgs() return flask.render_template("publisher/builds.html", **context)
def snap_count(): try: account_info = publisher_api.get_account(flask.session) except StoreApiResponseErrorList as api_response_error_list: return _handle_error_list(api_response_error_list.errors) except (StoreApiError, ApiError) as api_error: return _handle_error(api_error) user_snaps, registered_snaps = logic.get_snaps_account_info(account_info) context = {"count": len(user_snaps), "snaps": list(user_snaps.keys())} return flask.jsonify(context)
def get_manage_members(store_id): try: members = admin_api.get_store_members(flask.session, store_id) for item in members: if item["email"] == flask.session["publisher"]["email"]: item["current_user"] = True except StoreApiResponseErrorList as api_response_error_list: return _handle_error_list(api_response_error_list.errors) except (StoreApiError, ApiError) as api_error: return _handle_error(api_error) return jsonify(members)
def get_snaps_search(store_id): try: snaps = admin_api.get_store_snaps( flask.session, store_id, flask.request.args.get("q"), flask.request.args.get("allowed_for_inclusion"), ) except StoreApiResponseErrorList as api_response_error_list: return _handle_error_list(api_response_error_list.errors) except (StoreApiError, ApiError) as api_error: return _handle_error(api_error) return jsonify(snaps)
def get_stores(): """ In this view we get all the stores the user is an admin or we show a 403 """ try: stores = admin_api.get_stores(flask.session) except StoreApiResponseErrorList as api_response_error_list: return _handle_error_list(api_response_error_list.errors) except (StoreApiError, ApiError) as api_error: return _handle_error(api_error) if not stores: flask.abort(403) # We redirect to the first store snap list return flask.redirect( flask.url_for(".get_settings", store_id=stores[0]["id"]))
def get_manage_members(store_id): try: stores = admin_api.get_stores(flask.session) store = admin_api.get_store(flask.session, store_id) members = admin_api.get_store_members(flask.session, store_id) invites = admin_api.get_store_invites(flask.session, store_id) except StoreApiResponseErrorList as api_response_error_list: return _handle_error_list(api_response_error_list.errors) except (StoreApiError, ApiError) as api_error: return _handle_error(api_error) return flask.render_template( "admin/manage_members.html", stores=stores, store=store, members=members, invites=invites, )
def get_invites(store_id): try: stores = admin_api.get_stores(flask.session) store = admin_api.get_store(flask.session, store_id) invites = admin_api.get_store_invites(flask.session, store_id) except StoreApiResponseErrorList as api_response_error_list: return _handle_error_list(api_response_error_list.errors) except (StoreApiError, ApiError) as api_error: return _handle_error(api_error) pending_invites = [] expired_invites = [] revoked_invites = [] for invite in invites: if invite["status"] == "Pending": pending_invites.append(invite) if invite["status"] == "Expired": expired_invites.append(invite) if invite["status"] == "Revoked": revoked_invites.append(invite) sorted_pending_invites = sorted( pending_invites, key=lambda item: item["expiration-date"] ) sorted_expired_invites = sorted( expired_invites, key=lambda item: item["expiration-date"] ) sorted_revoked_invites = sorted( revoked_invites, key=lambda item: item["expiration-date"] ) return flask.render_template( "admin/invites.html", stores=stores, store=store, pending_invites=sorted_pending_invites, expired_invites=sorted_expired_invites, revoked_invites=sorted_revoked_invites, )
def get_account_snaps(): try: account_info = publisher_api.get_account(flask.session) except StoreApiResponseErrorList as api_response_error_list: return _handle_error_list(api_response_error_list.errors) except (StoreApiError, ApiError) as api_error: return _handle_error(api_error) user_snaps, registered_snaps = logic.get_snaps_account_info(account_info) flask_user = flask.session["publisher"] context = { "snaps": user_snaps, "current_user": flask_user["nickname"], "registered_snaps": registered_snaps, } return flask.render_template("publisher/account-snaps.html", **context)
def get_snap_build(snap_name, build_id): try: details = publisher_api.get_snap_info(snap_name, flask.session) except StoreApiResponseErrorList as api_response_error_list: if api_response_error_list.status_code == 404: return flask.abort(404, "No snap named {}".format(snap_name)) else: return _handle_error_list(api_response_error_list.errors) except (StoreApiError, ApiError) as api_error: return _handle_error(api_error) context = { "snap_id": details["snap_id"], "snap_name": details["snap_name"], "snap_title": details["title"], "snap_build": {}, } # Get build by snap name and build_id lp_build = launchpad.get_snap_build(details["snap_name"], build_id) if lp_build: status = map_build_and_upload_states( lp_build["buildstate"], lp_build["store_upload_status"] ) context["snap_build"] = { "id": lp_build["self_link"].split("/")[-1], "arch_tag": lp_build["arch_tag"], "datebuilt": lp_build["datebuilt"], "duration": lp_build["duration"], "logs": lp_build["build_log_url"], "revision_id": lp_build["revision_id"], "status": status, "title": lp_build["title"], } if context["snap_build"]["logs"]: context["raw_logs"] = launchpad.get_snap_build_log( details["snap_name"], build_id ) return flask.render_template("publisher/build.html", **context)
def post_build(snap_name): try: details = api.get_snap_info(snap_name, flask.session) except ApiResponseErrorList as api_response_error_list: if api_response_error_list.status_code == 404: return flask.abort(404, "No snap named {}".format(snap_name)) else: return _handle_error_list(api_response_error_list.errors) except ApiError as api_error: return _handle_error(api_error) if launchpad.is_snap_building(details["snap_name"]): launchpad.cancel_snap_builds(details["snap_name"]) launchpad.build_snap(details["snap_name"]) flask.flash("Build triggered", "positive") return flask.redirect( flask.url_for(".get_snap_builds", snap_name=snap_name))
def get_validate_repo(snap_name): try: details = api.get_snap_info(snap_name, flask.session) except ApiResponseErrorList as api_response_error_list: if api_response_error_list.status_code == 404: return flask.abort(404, "No snap named {}".format(snap_name)) else: return _handle_error_list(api_response_error_list.errors) except ApiError as api_error: return _handle_error(api_error) owner, repo = flask.request.args.get("repo").split("/") return flask.jsonify( validate_repo( flask.session.get("github_auth_secret"), details["snap_name"], owner, repo, ))
def post_register_name_dispute(): try: snap_name = flask.request.form.get("snap-name", "") claim_comment = flask.request.form.get("claim-comment", "") api.post_register_name_dispute(flask.session, bleach.clean(snap_name), bleach.clean(claim_comment)) except ApiResponseErrorList as api_response_error_list: if api_response_error_list.status_code in [400, 409]: return flask.render_template( "publisher/register-name-dispute.html", snap_name=snap_name, errors=api_response_error_list.errors, ) else: return _handle_error_list(api_response_error_list.errors) except ApiError as api_error: return _handle_error(api_error) return flask.render_template( "publisher/register-name-dispute-success.html", snap_name=snap_name)
def login_callback(): code = flask.request.args["code"] state = flask.request.args["state"] flask.session.pop("macaroon_root", None) flask.session.pop("macaroon_discharge", None) # Avoid CSRF attacks validate_csrf(state) discharged_token = candid.discharge_token(code) candid_macaroon = candid.discharge_macaroon( flask.session["publisher-macaroon"], discharged_token) # Store bakery authentication flask.session["macaroons"] = candid.get_serialized_bakery_macaroon( flask.session["publisher-macaroon"], candid_macaroon) try: publisher = publisher_api.whoami(flask.session) except StoreApiResponseErrorList as api_response_error_list: return _handle_error_list(api_response_error_list.errors) except (StoreApiError, ApiError) as api_error: return _handle_error(api_error) flask.session["publisher"] = { "account_id": publisher["account"]["id"], "nickname": publisher["account"]["username"], "fullname": publisher["account"]["name"], "image": None, "email": publisher["account"]["email"], } return flask.redirect( flask.session.pop( "next_url", flask.url_for("publisher_snaps.get_account_snaps"), ), 302, )
def post_disconnect_repo(snap_name): try: details = publisher_api.get_snap_info(snap_name, flask.session) except StoreApiResponseErrorList as api_response_error_list: if api_response_error_list.status_code == 404: return flask.abort(404, "No snap named {}".format(snap_name)) else: return _handle_error_list(api_response_error_list.errors) except StoreApiError as api_error: return _handle_error(api_error) lp_snap = launchpad.get_snap_by_store_name(snap_name) launchpad.delete_snap(details["snap_name"]) # Try to remove the GitHub webhook if possible if flask.session.get("github_auth_secret"): github = GitHub(flask.session.get("github_auth_secret")) try: gh_owner, gh_repo = lp_snap["git_repository_url"][19:].split("/") old_hook = github.get_hook_by_url( gh_owner, gh_repo, f"{GITHUB_WEBHOOK_HOST_URL}{snap_name}/webhook/notify", ) if old_hook: github.remove_hook( gh_owner, gh_repo, old_hook["id"], ) except HTTPError: pass return flask.redirect( flask.url_for(".get_snap_builds", snap_name=snap_name) )
def get_snap_builds_json(snap_name): try: details = api.get_snap_info(snap_name, flask.session) except ApiResponseErrorList as api_response_error_list: if api_response_error_list.status_code == 404: return flask.abort(404, "No snap named {}".format(snap_name)) else: return _handle_error_list(api_response_error_list.errors) except ApiError as api_error: return _handle_error(api_error) context = {"snap_builds": []} start = flask.request.args.get("start", 0, type=int) size = flask.request.args.get("size", 15, type=int) build_slice = slice(start, start + size) # Get built snap in launchpad with this store name lp_snap = launchpad.get_snap_by_store_name(details["snap_name"]) if lp_snap: context.update(get_builds(lp_snap, build_slice)) return flask.jsonify(context)
def get_store_snaps(store_id): try: stores = admin_api.get_stores(flask.session) store = admin_api.get_store(flask.session, store_id) snaps = admin_api.get_store_snaps(flask.session, store_id) # list of all deduped store IDs that are not current store other_store_ids = list(dict.fromkeys([d["store"] for d in snaps])) other_stores = list( filter(lambda id: id != store["id"], other_store_ids)) # store data for each store ID other_stores_data = [] for other_store_id in other_stores: if other_store_id == "ubuntu": other_stores_data.append({ "id": "ubuntu", "name": "Global store" }) else: store_data = admin_api.get_store(flask.session, other_store_id) other_stores_data.append(store_data) except StoreApiResponseErrorList as api_response_error_list: return _handle_error_list(api_response_error_list.errors) except (StoreApiError, ApiError) as api_error: return _handle_error(api_error) return flask.render_template( "admin/snaps.html", stores=stores, store=store, store_json=json.dumps(store), snaps=json.dumps(snaps), other_stores_data=json.dumps(other_stores_data), )
def post_snap_builds(snap_name): try: details = api.get_snap_info(snap_name, flask.session) except ApiResponseErrorList as api_response_error_list: if api_response_error_list.status_code == 404: return flask.abort(404, "No snap named {}".format(snap_name)) else: return _handle_error_list(api_response_error_list.errors) except ApiError as api_error: return _handle_error(api_error) # Don't allow changes from Admins that are no contributors account_snaps = api.get_account_snaps(flask.session) if snap_name not in account_snaps: flask.flash("You do not have permissions to modify this Snap", "negative") return flask.redirect( flask.url_for(".get_snap_builds", snap_name=snap_name)) redirect_url = flask.url_for(".get_snap_builds", snap_name=snap_name) # Get built snap in launchpad with this store name github = GitHub(flask.session.get("github_auth_secret")) owner, repo = flask.request.form.get("github_repository").split("/") if not github.check_permissions_over_repo(owner, repo): flask.flash( "Your GitHub account doesn't have permissions in the repository", "negative", ) return flask.redirect(redirect_url) repo_validation = validate_repo(flask.session.get("github_auth_secret"), snap_name, owner, repo) if not repo_validation["success"]: flask.flash(repo_validation["error"]["message"], "negative") return flask.redirect(redirect_url) lp_snap = launchpad.get_snap_by_store_name(details["snap_name"]) git_url = f"https://github.com/{owner}/{repo}" if not lp_snap: lp_snap_name = md5(git_url.encode("UTF-8")).hexdigest() try: repo_exist = launchpad.get_snap(lp_snap_name) except HTTPError as e: if e.response.status_code == 404: repo_exist = False else: raise e if repo_exist: # The user registered the repo in BSI but didn't register a name # We can remove it and continue with the normal process if not repo_exist["store_name"]: # This conditional should be removed when issue 2657 is solved launchpad._request(path=repo_exist["self_link"][32:], method="DELETE") else: flask.flash( "The specified repository is being used by another snap:" f" {repo_exist['store_name']}", "negative", ) return flask.redirect(redirect_url) macaroon = api.get_package_upload_macaroon(session=flask.session, snap_name=snap_name, channels=["edge" ])["macaroon"] launchpad.create_snap(snap_name, git_url, macaroon) flask.flash("The GitHub repository was linked correctly.", "positive") # Create webhook in the repo, it should also trigger the first build github_hook_url = f"https://snapcraft.io/{snap_name}/webhook/notify" try: hook = github.get_hook_by_url(owner, repo, github_hook_url) # We create the webhook if doesn't exist already in this repo if not hook: github.create_hook(owner, repo, github_hook_url) except HTTPError: flask.flash( "The GitHub Webhook could not be created. " "Please trigger a new build manually.", "caution", ) elif lp_snap["git_repository_url"] != git_url: # In the future, create a new record, delete the old one raise AttributeError( f"Snap {snap_name} already has a build repository associated") return flask.redirect(redirect_url)