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
This commit is contained in:
ohemorange 2026-05-19 12:27:11 -07:00 committed by GitHub
parent 7440ff40dd
commit ab34e5dcf7
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -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 == "<!DOCTYPE html>":
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.