Coverage for webapp/endpoints/publisher/builds.py: 49%
61 statements
« prev ^ index » next coverage.py v7.10.5, created at 2025-08-26 22:06 +0000
« prev ^ index » next coverage.py v7.10.5, created at 2025-08-26 22:06 +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}
32 yaml_location = github.get_snapcraft_yaml_location(gh_owner, gh_repo)
34 # The snapcraft.yaml is not present
35 if not yaml_location:
36 result["success"] = False
37 result["error"] = {
38 "type": "MISSING_YAML_FILE",
39 "message": (
40 "Missing snapcraft.yaml: this repo needs a snapcraft.yaml "
41 "file, so that Snapcraft can make it buildable, installable "
42 "and runnable."
43 ),
44 }
45 else:
46 try:
47 gh_snap_name = github.get_snapcraft_yaml_data(
48 gh_owner, gh_repo
49 ).get("name")
51 # The property name inside the yaml file doesn't match the snap
52 if gh_snap_name is not None and gh_snap_name != snap_name:
53 result["success"] = False
54 result["error"] = {
55 "type": "SNAP_NAME_DOES_NOT_MATCH",
56 "message": (
57 "Name mismatch: the snapcraft.yaml uses the snap "
58 f'name "{gh_snap_name}", but you\'ve registered'
59 f' the name "{snap_name}". Update your '
60 "snapcraft.yaml to continue."
61 ),
62 "yaml_location": yaml_location,
63 "gh_snap_name": gh_snap_name,
64 }
65 except InvalidYAML:
66 result["success"] = False
67 result["error"] = {
68 "type": "INVALID_YAML_FILE",
69 "message": (
70 "Invalid snapcraft.yaml: there was an issue parsing the "
71 f"snapcraft.yaml for {snap_name}."
72 ),
73 }
75 return result
78@login_required
79def get_validate_repo(snap_name):
80 details = dashboard.get_snap_info(flask.session, snap_name)
82 owner, repo = flask.request.args.get("repo").split("/")
84 return flask.jsonify(
85 validate_repo(
86 flask.session.get("github_auth_secret"),
87 details["snap_name"],
88 owner,
89 repo,
90 )
91 )
94@login_required
95def post_build(snap_name):
96 # Don't allow builds from no contributors
97 account_snaps = dashboard.get_account_snaps(flask.session)
99 if snap_name not in account_snaps:
100 return flask.jsonify(
101 {
102 "success": False,
103 "error": {
104 "type": "FORBIDDEN",
105 "message": "You are not allowed to request "
106 "builds for this snap",
107 },
108 }
109 )
111 try:
112 if launchpad.is_snap_building(snap_name):
113 launchpad.cancel_snap_builds(snap_name)
115 build_id = launchpad.build_snap(snap_name)
117 except HTTPError as e:
118 return flask.jsonify(
119 {
120 "success": False,
121 "error": {
122 "message": "An error happened building "
123 "this snap, please try again."
124 },
125 "details": e.response.text,
126 "status_code": e.response.status_code,
127 }
128 )
130 return flask.jsonify({"success": True, "build_id": build_id})
133@login_required
134def post_disconnect_repo(snap_name):
135 details = dashboard.get_snap_info(flask.session, snap_name)
137 lp_snap = launchpad.get_snap_by_store_name(snap_name)
138 launchpad.delete_snap(details["snap_name"])
140 # Try to remove the GitHub webhook if possible
141 if flask.session.get("github_auth_secret"):
142 github = GitHub(flask.session.get("github_auth_secret"))
144 try:
145 gh_owner, gh_repo = lp_snap["git_repository_url"][19:].split("/")
147 old_hook = github.get_hook_by_url(
148 gh_owner,
149 gh_repo,
150 f"{GITHUB_WEBHOOK_HOST_URL}api/{snap_name}/webhook/notify",
151 )
153 if old_hook:
154 github.remove_hook(
155 gh_owner,
156 gh_repo,
157 old_hook["id"],
158 )
159 except HTTPError:
160 pass
162 return flask.redirect(
163 flask.url_for(".get_snap_builds", snap_name=snap_name)
164 )