← Back to issue list

Build step fails on special files

View original Github issue

Metadata

Project
craft-parts
Number
#1458
Type
issue
State
open
Author
smethnani
Labels
Created
2026-02-17 05:11:07+00:00
Updated
2026-03-30 19:17:10+00:00
Closed

Current evaluation

No evaluation has been recorded for this issue yet.

Issue body

### Bug Description The build step uses `shutil.copytree` which fails on special files. See [failing test](https://github.com/canonical/craft-parts/actions/runs/22070744732/job/63774393159?pr=1415#step:4:145) ### To Reproduce Run build step with special file in source directory ### Relevant log output ```shell /home/runner/work/craft-parts/craft-parts/tests/integration/executor/test_organize.py:60: _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ /home/runner/work/craft-parts/craft-parts/craft_parts/executor/executor.py:353: in execute self._executor.execute(actions, stdout=stdout, stderr=stderr) /home/runner/work/craft-parts/craft-parts/craft_parts/executor/executor.py:153: in execute self._run_action(act, stdout=stdout, stderr=stderr) /home/runner/work/craft-parts/craft-parts/craft_parts/executor/executor.py:246: in _run_action handler.run_action(action, stdout=stdout, stderr=stderr) /home/runner/work/craft-parts/craft-parts/craft_parts/executor/part_handler.py:313: in run_action state = handler(step_info, stdout=stdout, stderr=stderr) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ /home/runner/work/craft-parts/craft-parts/craft_parts/executor/part_handler.py:463: in _run_build shutil.copytree( /usr/lib/python3.12/shutil.py:600: in copytree return _copytree(entries=entries, src=src, dst=dst, symlinks=symlinks, _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ entries = [<DirEntry 'bar.fifo'>, <DirEntry 'dev'>, <DirEntry 'qux'>, <DirEntry 'foo.txt'>] src = PosixPath('/tmp/pytest-of-root/pytest-0/test_organize_special_files0/parts/p1/src') dst = PosixPath('/tmp/pytest-of-root/pytest-0/test_organize_special_files0/parts/p1/build') symlinks = True, ignore = None copy_function = <function copy2 at 0x7f8dc5222c00> ignore_dangling_symlinks = False, dirs_exist_ok = False def _copytree(entries, src, dst, symlinks, ignore, copy_function, ignore_dangling_symlinks, dirs_exist_ok=False): if ignore is not None: ignored_names = ignore(os.fspath(src), [x.name for x in entries]) else: ignored_names = () os.makedirs(dst, exist_ok=dirs_exist_ok) errors = [] use_srcentry = copy_function is copy2 or copy_function is copy for srcentry in entries: if srcentry.name in ignored_names: continue srcname = os.path.join(src, srcentry.name) dstname = os.path.join(dst, srcentry.name) srcobj = srcentry if use_srcentry else srcname try: is_symlink = srcentry.is_symlink() if is_symlink and os.name == 'nt': # Special check for directory junctions, which appear as # symlinks but we want to recurse. lstat = srcentry.stat(follow_symlinks=False) if lstat.st_reparse_tag == stat.IO_REPARSE_TAG_MOUNT_POINT: is_symlink = False if is_symlink: linkto = os.readlink(srcname) if symlinks: # We can't just leave it to `copy_function` because legacy # code with a custom `copy_function` may rely on copytree # doing the right thing. os.symlink(linkto, dstname) copystat(srcobj, dstname, follow_symlinks=not symlinks) else: # ignore dangling symlink if the flag is on if not os.path.exists(linkto) and ignore_dangling_symlinks: continue # otherwise let the copy occur. copy2 will raise an error if srcentry.is_dir(): copytree(srcobj, dstname, symlinks, ignore, copy_function, ignore_dangling_symlinks, dirs_exist_ok) else: copy_function(srcobj, dstname) elif srcentry.is_dir(): copytree(srcobj, dstname, symlinks, ignore, copy_function, ignore_dangling_symlinks, dirs_exist_ok) else: # Will raise a SpecialFileError for unsupported file types copy_function(srcobj, dstname) # catch the Error from the recursive copytree so that we can # continue with other files except Error as err: errors.extend(err.args[0]) except OSError as why: errors.append((srcname, dstname, str(why))) try: copystat(src, dst) except OSError as why: # Copying file access times may fail on Windows if getattr(why, 'winerror', None) is None: errors.append((src, dst, str(why))) if errors: > raise Error(errors) E shutil.Error: [('/tmp/pytest-of-root/pytest-0/test_organize_special_files0/parts/p1/src/bar.fifo', '/tmp/pytest-of-root/pytest-0/test_organize_special_files0/parts/p1/build/bar.fifo', '`/tmp/pytest-of-root/pytest-0/test_organize_special_files0/parts/p1/src/bar.fifo` is a named pipe')] ```

Evaluation history

No evaluation history available.