Coverage for webapp/endpoints/publisher/builds.py: 48%
63 statements
« prev ^ index » next coverage.py v7.10.7, created at 2025-10-14 22:07 +0000
« prev ^ index » next coverage.py v7.10.7, created at 2025-10-14 22:07 +0000
1# Standard library
2import os
4# Packages
5import flask
6from canonicalwebteam.store_api.dashboard import Dashboard
8from requests.exceptions import HTTPError
10# Local
11from webapp.helpers import api_publisher_session, launchpad
12from webapp.api.github import GitHub, InvalidYAML
13from webapp.decorators import login_required
15GITHUB_WEBHOOK_HOST_URL = os.getenv("GITHUB_WEBHOOK_HOST_URL")
17dashboard = Dashboard(api_publisher_session)
20@login_required
21def get_snap_build_page(snap_name, build_id):
22 # If this fails, the page will 404
23 dashboard.get_snap_info(flask.session, snap_name)
24 return flask.render_template(
25 "store/publisher.html", snap_name=snap_name, build_id=build_id
26 )
29def validate_repo(github_token, snap_name, gh_owner, gh_repo):
30 github = GitHub(github_token)
31 result = {"success": True, "data": None, "error": None}
32 yaml_location = github.get_snapcraft_yaml_location(gh_owner, gh_repo)
33 default_branch = github.get_default_branch(gh_owner, gh_repo)
35 # The snapcraft.yaml is not present
36 if not yaml_location:
37 result["data"] = {"default_branch": default_branch}
38 result["success"] = False
39 result["error"] = {
40 "type": "MISSING_YAML_FILE",
41 "message": (
42 "Missing snapcraft.yaml: this repo needs a snapcraft.yaml "
43 "file, so that Snapcraft can make it buildable, installable "
44 "and runnable."
45 ),
46 }
47 else:
48 try:
49 gh_snap_name = github.get_snapcraft_yaml_data(
50 gh_owner, gh_repo
51 ).get("name")
53 # The property name inside the yaml file doesn't match the snap
54 if gh_snap_name is not None and gh_snap_name != snap_name:
55 result["success"] = False
56 result["error"] = {
57 "type": "SNAP_NAME_DOES_NOT_MATCH",
58 "message": (
59 "Name mismatch: the snapcraft.yaml uses the snap "
60 f'name "{gh_snap_name}", but you\'ve registered'
61 f' the name "{snap_name}". Update your '
62 "snapcraft.yaml to continue."
63 ),
64 "yaml_location": yaml_location,
65 "gh_snap_name": gh_snap_name,
66 }
67 except InvalidYAML:
68 result["success"] = False
69 result["error"] = {
70 "type": "INVALID_YAML_FILE",
71 "message": (
72 "Invalid snapcraft.yaml: there was an issue parsing the "
73 f"snapcraft.yaml for {snap_name}."
74 ),
75 }
77 return result
80@login_required
81def get_validate_repo(snap_name):
82 details = dashboard.get_snap_info(flask.session, snap_name)
84 owner, repo = flask.request.args.get("repo").split("/")
86 return flask.jsonify(
87 validate_repo(
88 flask.session.get("github_auth_secret"),
89 details["snap_name"],
90 owner,
91 repo,
92 )
93 )
96@login_required
97def post_build(snap_name):
98 # Don't allow builds from no contributors
99 account_snaps = dashboard.get_account_snaps(flask.session)
101 if snap_name not in account_snaps:
102 return flask.jsonify(
103 {
104 "success": False,
105 "error": {
106 "type": "FORBIDDEN",
107 "message": "You are not allowed to request "
108 "builds for this snap",
109 },
110 }
111 )
113 try:
114 if launchpad.is_snap_building(snap_name):
115 launchpad.cancel_snap_builds(snap_name)
117 build_id = launchpad.build_snap(snap_name)
119 except HTTPError as e:
120 return flask.jsonify(
121 {
122 "success": False,
123 "error": {
124 "message": "An error happened building "
125 "this snap, please try again."
126 },
127 "details": e.response.text,
128 "status_code": e.response.status_code,
129 }
130 )
132 return flask.jsonify({"success": True, "build_id": build_id})
135@login_required
136def post_disconnect_repo(snap_name):
137 details = dashboard.get_snap_info(flask.session, snap_name)
139 lp_snap = launchpad.get_snap_by_store_name(snap_name)
140 launchpad.delete_snap(details["snap_name"])
142 # Try to remove the GitHub webhook if possible
143 if flask.session.get("github_auth_secret"):
144 github = GitHub(flask.session.get("github_auth_secret"))
146 try:
147 gh_owner, gh_repo = lp_snap["git_repository_url"][19:].split("/")
149 old_hook = github.get_hook_by_url(
150 gh_owner,
151 gh_repo,
152 f"{GITHUB_WEBHOOK_HOST_URL}api/{snap_name}/webhook/notify",
153 )
155 if old_hook:
156 github.remove_hook(
157 gh_owner,
158 gh_repo,
159 old_hook["id"],
160 )
161 except HTTPError:
162 pass
164 return flask.redirect(
165 flask.url_for(".get_snap_builds", snap_name=snap_name)
166 )