(
target: str, archs: Set[str], status: Dict[str, Dict[str, str]],
workspace: str, output_lock: Lock)
| 41 | |
| 42 | |
| 43 | def _execute_build( |
| 44 | target: str, archs: Set[str], status: Dict[str, Dict[str, str]], |
| 45 | workspace: str, output_lock: Lock) -> Tuple[int, List[str]]: |
| 46 | # The implementation of remote-build recovery has changed over time. |
| 47 | # Currently, you cannot set a build-id, and the build-id is instead derived |
| 48 | # from a hash of the contents of the files in the directory: |
| 49 | # https://github.com/canonical/craft-application/blob/5b09ab3d9152a2b61ffcdf57691289023ed6ba26/craft_application/remote/utils.py#L64 |
| 50 | # |
| 51 | # We want a unique build ID so a fresh build is started for each run instead |
| 52 | # of potentially reusing an old build. See https://github.com/certbot/certbot/pull/8719 |
| 53 | # and https://github.com/snapcore/snapcraft/pull/3554 for more info. |
| 54 | # |
| 55 | # In the hope that one day you can again set a build ID, we will modify |
| 56 | # the directory by creating a file containing a build ID that conforms |
| 57 | # to the shape of snapcraft's build ID: using a MD5 hash represented as a |
| 58 | # 32 character hex string (we use a larger character set). |
| 59 | |
| 60 | random_string = ''.join(random.choice(string.ascii_lowercase + string.digits) |
| 61 | for _ in range(32)) |
| 62 | # place random string in build_id file inside `workspace` directory |
| 63 | with open(join(workspace, 'build_id'), 'w') as build_id_file: |
| 64 | build_id_file.write(random_string) |
| 65 | |
| 66 | with tempfile.TemporaryDirectory() as tempdir: |
| 67 | environ = os.environ.copy() |
| 68 | environ['XDG_CACHE_HOME'] = tempdir |
| 69 | process = subprocess.Popen([ |
| 70 | 'snapcraft', 'remote-build', '--launchpad-accept-public-upload', |
| 71 | '--build-for', ','.join(archs)], |
| 72 | stdout=subprocess.PIPE, stderr=subprocess.STDOUT, |
| 73 | universal_newlines=True, env=environ, cwd=workspace) |
| 74 | |
| 75 | killed = False |
| 76 | process_output: List[str] = [] |
| 77 | for line in process.stdout: |
| 78 | process_output.append(line) |
| 79 | _extract_state(target, line, status) |
| 80 | |
| 81 | if not killed and any(state for state in status[target].values() if state == 'Chroot problem'): |
| 82 | # On this error the snapcraft process hangs. Let's finish it. |
| 83 | # |
| 84 | # killed is used to stop us from executing this code path |
| 85 | # multiple times per build that encounters "Chroot problem". |
| 86 | with output_lock: |
| 87 | print('Chroot problem encountered for build ' |
| 88 | f'{target} for {",".join(archs)}.\n' |
| 89 | 'Launchpad seems to be unable to recover from this ' |
| 90 | 'state so we are terminating the build.') |
| 91 | process.kill() |
| 92 | killed = True |
| 93 | |
| 94 | process_state = process.wait() |
| 95 | |
| 96 | return process_state, process_output |
| 97 | |
| 98 | |
| 99 | def _build_snap( |
no test coverage detected