Coverage for webapp/publisher/snaps/builds.py: 75%

77 statements  

« prev     ^ index     » next       coverage.py v7.15.2, created at 2026-07-31 22:23 +0000

1from enum import Enum 

2 

3 

4class StoreFrontBuildState(Enum): 

5 NEVER_BUILT = "never_built" 

6 BUILDING_SOON = "building_soon" 

7 WONT_RELEASE = "wont_release" 

8 RELEASED = "released" 

9 RELEASE_FAILED = "release_failed" 

10 RELEASING_SOON = "releasing_soon" 

11 IN_PROGRESS = "in_progress" 

12 FAILED_TO_BUILD = "failed_to_build" 

13 CANCELLED = "cancelled" 

14 UNKNOWN = "unknown" 

15 

16 

17class LaunchpadBuildState(Enum): 

18 NEEDS_BUILD = "Needs building" 

19 FULLY_BUILT = "Successfully built" 

20 FAILED_BUILD = "Failed to build" 

21 MANUALDEPWAIT = "Dependency wait" 

22 CHROOTWAIT = "Chroot problem" 

23 SUPERSEDED = "Build for superseded Source" 

24 BUILDING = "Currently building" 

25 GATHERING_OUTPUT = "Gathering build output" 

26 FAILED_UPLOAD = "Failed to upload" 

27 UPLOADING = "Uploading build" 

28 CANCELLING = "Cancelling build" 

29 CANCELLED = "Cancelled build" 

30 

31 

32class LaunchpadStoreUploadState(Enum): 

33 UNSCHEDULED = "Unscheduled" 

34 PENDING = "Pending" 

35 FAILED_UPLOAD = "Failed to upload" 

36 FAILED_RELEASE = "Failed to release to channels" 

37 UPLOADED = "Uploaded" 

38 

39 

40def build_link(bsi_url, snap, build): 

41 """Builds the link to the build page""" 

42 build_id = build["self_link"].split("/")[-1] 

43 

44 # Remove GitHub hostname & split owner/repo 

45 owner, repo = snap["git_repository_url"][19:].split("/") 

46 

47 return f"{bsi_url}/user/{owner}/{repo}/{build_id}" 

48 

49 

50def _map_upload_state(upload_state): 

51 """Returns a user facing status based on 

52 the status of the snap's upload to the store. 

53 """ 

54 upload_state = LaunchpadStoreUploadState(upload_state) 

55 if upload_state == LaunchpadStoreUploadState.UNSCHEDULED: 

56 return StoreFrontBuildState.WONT_RELEASE.value 

57 

58 elif upload_state == LaunchpadStoreUploadState.PENDING: 

59 return StoreFrontBuildState.RELEASING_SOON.value 

60 

61 elif ( 

62 upload_state == LaunchpadStoreUploadState.FAILED_UPLOAD 

63 or upload_state == LaunchpadStoreUploadState.FAILED_RELEASE 

64 ): 

65 return StoreFrontBuildState.RELEASE_FAILED.value 

66 

67 elif upload_state == LaunchpadStoreUploadState.UPLOADED: 

68 return StoreFrontBuildState.RELEASED.value 

69 

70 return StoreFrontBuildState.UNKNOWN.value 

71 

72 

73def map_build_and_upload_states(build_state, upload_state): 

74 """Returns a user facing status based on the LP 

75 build state and the status of the snap's 

76 upload to the store. 

77 """ 

78 build_state = LaunchpadBuildState(build_state) 

79 

80 if build_state == LaunchpadBuildState.NEEDS_BUILD: 

81 return StoreFrontBuildState.BUILDING_SOON.value 

82 

83 elif build_state == LaunchpadBuildState.FULLY_BUILT: 

84 return _map_upload_state(upload_state) 

85 

86 elif build_state == LaunchpadBuildState.BUILDING: 

87 return StoreFrontBuildState.IN_PROGRESS.value 

88 

89 elif build_state == LaunchpadBuildState.GATHERING_OUTPUT: 

90 return StoreFrontBuildState.IN_PROGRESS.value 

91 

92 elif build_state == LaunchpadBuildState.UPLOADING: 

93 return StoreFrontBuildState.IN_PROGRESS.value 

94 

95 elif build_state in [ 

96 LaunchpadBuildState.CANCELLING, 

97 LaunchpadBuildState.CANCELLED, 

98 ]: 

99 return StoreFrontBuildState.CANCELLED.value 

100 

101 elif build_state in [ 

102 LaunchpadBuildState.FAILED_BUILD, 

103 LaunchpadBuildState.MANUALDEPWAIT, 

104 LaunchpadBuildState.CHROOTWAIT, 

105 LaunchpadBuildState.SUPERSEDED, 

106 LaunchpadBuildState.FAILED_UPLOAD, 

107 ]: 

108 return StoreFrontBuildState.FAILED_TO_BUILD.value 

109 

110 return StoreFrontBuildState.UNKNOWN.value 

111 

112 

113def map_snap_build_status(snap_build_statuses): 

114 """Returns a user facing status based on the LP 

115 build state and the status of the snap's 

116 upload to the store. 

117 """ 

118 mapped_arch_statuses = set() 

119 

120 for arch_statuses in snap_build_statuses.values(): 

121 mapped_status = map_build_and_upload_states( 

122 arch_statuses["buildstate"], arch_statuses["store_upload_status"] 

123 ) 

124 

125 # Return instantly a failure status if one arch is failing 

126 if mapped_status in [ 

127 StoreFrontBuildState.NEVER_BUILT.value, 

128 StoreFrontBuildState.WONT_RELEASE.value, 

129 StoreFrontBuildState.RELEASE_FAILED.value, 

130 StoreFrontBuildState.FAILED_TO_BUILD.value, 

131 StoreFrontBuildState.CANCELLED.value, 

132 ]: 

133 return mapped_status 

134 

135 mapped_arch_statuses.add(mapped_status) 

136 

137 # All the archs have the same status 

138 if len(mapped_arch_statuses) == 1: 

139 return mapped_arch_statuses.pop() 

140 

141 # List of the non-failure status in the preferred order to return 

142 positive_statuses = [ 

143 StoreFrontBuildState.BUILDING_SOON.value, 

144 StoreFrontBuildState.IN_PROGRESS.value, 

145 StoreFrontBuildState.RELEASING_SOON.value, 

146 StoreFrontBuildState.RELEASED.value, 

147 ] 

148 

149 for status in positive_statuses: 

150 if status in mapped_arch_statuses: 

151 return status 

152 

153 return StoreFrontBuildState.UNKNOWN.value