From ab34e5dcf713a2c3d3510500011c02efbb4e03f2 Mon Sep 17 00:00:00 2001 From: ohemorange Date: Tue, 19 May 2026 12:27:11 -0700 Subject: [PATCH] Check to see if launchpad snap build put html in the .snap file (#10646) Fixes #10617 I restructured the conditionals to avoid too much nesting. This should have the same effect, just with the additional check conditioned on all the target snap files being available. Here is the logic I used for the restructuring: start ```python dump_output = exit_code != 0 or failed_archs if exit_code == 0 and not failed_archs: # We expect to have all target snaps available, or something bad happened. snaps_list = glob.glob(join(workspace, '*.snap')) if not len(snaps_list) == len(archs): print('Some of the expected snaps for a successful build are missing ' f'(current list: {snaps_list}).') dump_output = True else: build_success = True break ``` note that `(exit_code == 0 and not failed_archs) == not (exit_code != 0 or failed_archs) == not dump_output` ```python dump_output = exit_code != 0 or failed_archs if not dump_output: # We expect to have all target snaps available, or something bad happened. snaps_list = glob.glob(join(workspace, '*.snap')) if not len(snaps_list) == len(archs): print('Some of the expected snaps for a successful build are missing ' f'(current list: {snaps_list}).') dump_output = True else: build_success = True break ``` distribute the if ```python dump_output = exit_code != 0 or failed_archs snaps_list = glob.glob(join(workspace, '*.snap')) if not dump_output and (not len(snaps_list) == len(archs)): # We expect to have all target snaps available, or something bad happened. print('Some of the expected snaps for a successful build are missing ' f'(current list: {snaps_list}).') dump_output = True if not dump_output and (len(snaps_list) == len(archs)): # redundant; if it were false, we would have changed dump_output right above this build_success = True break ``` remove redundant check ```python dump_output = exit_code != 0 or failed_archs snaps_list = glob.glob(join(workspace, '*.snap')) if not dump_output and (not len(snaps_list) == len(archs)): # We expect to have all target snaps available, or something bad happened. print('Some of the expected snaps for a successful build are missing ' f'(current list: {snaps_list}).') dump_output = True if not dump_output: build_success = True break ``` As this shows, we can now add additional checks that only happen if we think we're in danger of succeeding based on checks thus far, and simply change `dump_output` to `True` if the additional check fails. You can see the build step failing when the [file contains html](https://github.com/certbot/certbot/compare/html-problem...refs/heads/test-html-problem-2) at https://github.com/certbot/certbot/actions/runs/26071811878/job/76654623490#step:5:42 --- tools/snap/build_remote.py | 41 ++++++++++++++++++++++++++++---------- 1 file changed, 30 insertions(+), 11 deletions(-) diff --git a/tools/snap/build_remote.py b/tools/snap/build_remote.py index c818a9857..324caf5f9 100755 --- a/tools/snap/build_remote.py +++ b/tools/snap/build_remote.py @@ -123,25 +123,44 @@ def _build_snap( # This output may change, and is set by # https://github.com/canonical/snapcraft/blob/8ab7fd0c8a1d3f13045bec41a6e0158c063faa9b/snapcraft/commands/remote.py#L278 - failed_archs = [arch for arch in archs if status[target][arch] != 'Succeeded'] + failed_archs: set = set([arch for arch in archs if status[target][arch] != 'Succeeded']) + # If the command failed or any architecture wasn't built # successfully, let's try to print all the output about the problem # that we can. - dump_output = exit_code != 0 or failed_archs - if exit_code == 0 and not failed_archs: - # We expect to have all target snaps available, or something bad happened. + dump_output: bool = exit_code != 0 or bool(failed_archs) + + # Check snap list length + # We expect to have all target snaps available, or something bad happened. + if not dump_output: snaps_list = glob.glob(join(workspace, '*.snap')) if not len(snaps_list) == len(archs): print('Some of the expected snaps for a successful build are missing ' f'(current list: {snaps_list}).') dump_output = True - else: - build_success = True - break - if dump_output: - print(f'Dumping snapcraft remote-build output build for {target}:') - print('\n'.join(process_output)) - _dump_failed_build_logs(target, archs, status, workspace) + + # Check if the snap file just contains html + if not dump_output: + for arch in archs: + snap_path_list = glob.glob(join(workspace, f'{target}_*_{arch}.snap')) + assert len(snap_path_list) == 1 + with open(snap_path_list[0], 'r') as f: + try: + first_line = f.readline().rstrip() + except UnicodeDecodeError: + first_line = '' + if first_line == "": + failed_archs.add(arch) + print(f'The {target} {arch} snap file contains html instead of a snap') + dump_output = bool(failed_archs) + + if not dump_output: + build_success = True + break + + print(f'Dumping snapcraft remote-build output build for {target}:') + print('\n'.join(process_output)) + _dump_failed_build_logs(target, archs, status, workspace) # Retry the remote build if it has been interrupted (non zero status code) # or if some builds have failed.