Permission error installing Plone 6 with cookieplone

This is on RHEL

(venv) myuser@vscode-btp-dev-01:~/volto> pipx run cookieplone volto-project
╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
│ /usr/lib64/python3.11/shutil.py:665 in _rmtree_safe_fd                                           │
│                                                                                                  │
│    662 │   │   │   │   │   continue                                                              │
│    663 │   │   if is_dir:                                                                        │
│    664 │   │   │   try:                                                                          │
│ ❱  665 │   │   │   │   dirfd = os.open(entry.name, os.O_RDONLY | os.O_NONBLOCK, dir_fd=topfd)    │
│    666 │   │   │   │   dirfd_closed = False                                                      │
│    667 │   │   │   except OSError:                                                               │
│    668 │   │   │   │   onerror(os.open, fullname, sys.exc_info())                                │
│                                                                                                  │
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
│ │    entries = [<DirEntry 'pack'>, <DirEntry 'info'>]                                          │ │
│ │      entry = <DirEntry 'pack'>                                                               │ │
│ │   fullname = '/home/myuser/.cookiecutters/cookieplone-templates/.git/objects/pack'         │ │
│ │     is_dir = True                                                                            │ │
│ │    orig_st = os.stat_result(st_mode=16512, st_ino=11244477, st_dev=58, st_nlink=2,           │ │
│ │              st_uid=1574, st_gid=100, st_size=4096, st_atime=1738164697,                     │ │
│ │              st_mtime=1738164698, st_ctime=1738165356)                                       │ │
│ │       path = '/home/myuser/.cookiecutters/cookieplone-templates/.git/objects'              │ │
│ │ scandir_it = <posix.ScandirIterator object at 0x7f7a85f03600>                                │ │
│ │      topfd = 5                                                                               │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
PermissionError: [Errno 13] Permission denied: 'pack'

During handling of the above exception, another exception occurred:

╭─────────────────────────────── Traceback (most recent call last) ────────────────────────────────╮
│ /home/myuser/.cache/pipx/ff7797bb943bb34/lib64/python3.11/site-packages/cookieplone/cli.py:140 │
│ in cli                                                                                           │
│                                                                                                  │
│   137 │   │   console.info_screen(repository=repository, passwd=passwd, tag=tag)                 │
│   138 │   │   raise typer.Exit()                                                                 │
│   139 │                                                                                          │
│ ❱ 140 │   repo_path = get_base_repository(repository)                                            │
│   141 │   if not template:                                                                       │
│   142 │   │   # Display template options                                                         │
│   143 │   │   template = prompt_for_template(repo_path)                                          │
│                                                                                                  │
│ ╭────────────────────────── locals ──────────────────────────╮                                   │
│ │             config_file = None                             │                                   │
│ │              debug_file = None                             │                                   │
│ │          default_config = False                            │                                   │
│ │           extra_context = None                             │                                   │
│ │                    info = False                            │                                   │
│ │ keep_project_on_failure = False                            │                                   │
│ │                no_input = False                            │                                   │
│ │              output_dir = None                             │                                   │
│ │     overwrite_if_exists = False                            │                                   │
│ │                  passwd = None                             │                                   │
│ │                  replay = False                            │                                   │
│ │             replay_file = None                             │                                   │
│ │              repository = 'gh:plone/cookieplone-templates' │                                   │
│ │     skip_if_file_exists = False                            │                                   │
│ │                     tag = 'main'                           │                                   │
│ │                template = 'volto-project'                  │                                   │
│ │                 verbose = False                            │                                   │
│ │                 version = False                            │                                   │
│ ╰────────────────────────────────────────────────────────────╯                                   │
│                                                                                                  │
│ /home/myuser/.cache/pipx/ff7797bb943bb34/lib64/python3.11/site-packages/cookieplone/repository │
│ .py:24 in get_base_repository                                                                    │
│                                                                                                  │
│   21 │   │   config_file=config_file,                                                            │
│   22 │   │   default_config=default_config,                                                      │
│   23 │   )                                                                                       │
│ ❱ 24 │   base_repo_dir, _ = determine_repo_dir(                                                  │
│   25 │   │   template=repository,                                                                │
│   26 │   │   abbreviations=config_dict["abbreviations"],                                         │
│   27 │   │   clone_to_dir=config_dict["cookiecutters_dir"],                                      │
│                                                                                                  │
│ ╭────────────────────────────────── locals ───────────────────────────────────╮                  │
│ │    config_dict = {                                                          │                  │
│ │                  │   'cookiecutters_dir': '/home/myuser/.cookiecutters/', │                  │
│ │                  │   'replay_dir': '/home/myuser/.cookiecutter_replay/',  │                  │
│ │                  │   'default_context': OrderedDict(),                      │                  │
│ │                  │   'abbreviations': {                                     │                  │
│ │                  │   │   'gh': 'https://github.com/{0}.git',                │                  │
│ │                  │   │   'gl': 'https://gitlab.com/{0}.git',                │                  │
│ │                  │   │   'bb': 'https://bitbucket.org/{0}'                  │                  │
│ │                  │   }                                                      │                  │
│ │                  }                                                          │                  │
│ │    config_file = None                                                       │                  │
│ │ default_config = False                                                      │                  │
│ │       password = ''                                                         │                  │
│ │     repository = 'gh:plone/cookieplone-templates'                           │                  │
│ │            tag = None                                                       │                  │
│ ╰─────────────────────────────────────────────────────────────────────────────╯                  │
│                                                                                                  │
│ /home/myuser/.cache/pipx/ff7797bb943bb34/lib64/python3.11/site-packages/cookiecutter/repositor │
│ y.py:108 in determine_repo_dir                                                                   │
│                                                                                                  │
│   105 │   │   repository_candidates = [unzipped_dir]                                             │
│   106 │   │   cleanup = True                                                                     │
│   107 │   elif is_repo_url(template):                                                            │
│ ❱ 108 │   │   cloned_repo = clone(                                                               │
│   109 │   │   │   repo_url=template,                                                             │
│   110 │   │   │   checkout=checkout,                                                             │
│   111 │   │   │   clone_to_dir=clone_to_dir,                                                     │
│                                                                                                  │
│ ╭─────────────────────────────── locals ───────────────────────────────╮                         │
│ │ abbreviations = {                                                    │                         │
│ │                 │   'gh': 'https://github.com/{0}.git',              │                         │
│ │                 │   'gl': 'https://gitlab.com/{0}.git',              │                         │
│ │                 │   'bb': 'https://bitbucket.org/{0}'                │                         │
│ │                 }                                                    │                         │
│ │      checkout = None                                                 │                         │
│ │  clone_to_dir = '/home/myuser/.cookiecutters/'                     │                         │
│ │     directory = ''                                                   │                         │
│ │      no_input = True                                                 │                         │
│ │      password = ''                                                   │                         │
│ │      template = 'https://github.com/plone/cookieplone-templates.git' │                         │
│ ╰──────────────────────────────────────────────────────────────────────╯                         │
│                                                                                                  │
│ /home/myuser/.cache/pipx/ff7797bb943bb34/lib64/python3.11/site-packages/cookiecutter/vcs.py:99 │
│ in clone                                                                                         │
│                                                                                                  │
│    96 │   logger.debug(f'repo_dir is {repo_dir}')                                                │
│    97 │                                                                                          │
│    98 │   if os.path.isdir(repo_dir):                                                            │
│ ❱  99 │   │   clone = prompt_and_delete(repo_dir, no_input=no_input)                             │
│   100 │   else:                                                                                  │
│   101 │   │   clone = True                                                                       │
│   102                                                                                            │
│                                                                                                  │
│ ╭─────────────────────────────── locals ───────────────────────────────╮                         │
│ │     checkout = None                                                  │                         │
│ │ clone_to_dir = PosixPath('/home/myuser/.cookiecutters')            │                         │
│ │     no_input = True                                                  │                         │
│ │     repo_dir = '/home/myuser/.cookiecutters/cookieplone-templates' │                         │
│ │    repo_name = 'cookieplone-templates'                               │                         │
│ │    repo_type = 'git'                                                 │                         │
│ │     repo_url = 'https://github.com/plone/cookieplone-templates.git'  │                         │
│ ╰──────────────────────────────────────────────────────────────────────╯                         │
│                                                                                                  │
│ /home/myuser/.cache/pipx/ff7797bb943bb34/lib64/python3.11/site-packages/cookiecutter/prompt.py │
│ :401 in prompt_and_delete                                                                        │
│                                                                                                  │
│   398 │                                                                                          │
│   399 │   if ok_to_delete:                                                                       │
│   400 │   │   if os.path.isdir(path):                                                            │
│ ❱ 401 │   │   │   rmtree(path)                                                                   │
│   402 │   │   else:                                                                              │
│   403 │   │   │   os.remove(path)                                                                │
│   404 │   │   return True                                                                        │
│                                                                                                  │
│ ╭─────────────────────────────── locals ───────────────────────────────╮                         │
│ │     no_input = True                                                  │                         │
│ │ ok_to_delete = True                                                  │                         │
│ │         path = '/home/myuser/.cookiecutters/cookieplone-templates' │                         │
│ ╰──────────────────────────────────────────────────────────────────────╯                         │
│                                                                                                  │
│ /home/myuser/.cache/pipx/ff7797bb943bb34/lib64/python3.11/site-packages/cookiecutter/utils.py: │
│ 34 in rmtree                                                                                     │
│                                                                                                  │
│    31 │                                                                                          │
│    32 │   :param path: A directory path.                                                         │
│    33 │   """                                                                                    │
│ ❱  34 │   shutil.rmtree(path, onerror=force_delete)                                              │
│    35                                                                                            │
│    36                                                                                            │
│    37 def make_sure_path_exists(path: "os.PathLike[str]") -> None:                               │
│                                                                                                  │
│ ╭─────────────────────────── locals ───────────────────────────╮                                 │
│ │ path = '/home/myuser/.cookiecutters/cookieplone-templates' │                                 │
│ ╰──────────────────────────────────────────────────────────────╯                                 │
│                                                                                                  │
│ /usr/lib64/python3.11/shutil.py:752 in rmtree                                                    │
│                                                                                                  │
│    749 │   │   │   return                                                                        │
│    750 │   │   try:                                                                              │
│    751 │   │   │   if os.path.samestat(orig_st, os.fstat(fd)):                                   │
│ ❱  752 │   │   │   │   _rmtree_safe_fd(fd, path, onerror)                                        │
│    753 │   │   │   │   try:                                                                      │
│    754 │   │   │   │   │   os.close(fd)                                                          │
│    755 │   │   │   │   except OSError:                                                           │
│                                                                                                  │
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
│ │        dir_fd = None                                                                         │ │
│ │            fd = 3                                                                            │ │
│ │     fd_closed = False                                                                        │ │
│ │ ignore_errors = False                                                                        │ │
│ │       orig_st = os.stat_result(st_mode=16877, st_ino=11243965, st_dev=58, st_nlink=12,       │ │
│ │                 st_uid=1574, st_gid=100, st_size=4096, st_atime=1738164698,                  │ │
│ │                 st_mtime=1738164697, st_ctime=1738164697)                                    │ │
│ │          path = '/home/myuser/.cookiecutters/cookieplone-templates'                        │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                                  │
│ /usr/lib64/python3.11/shutil.py:672 in _rmtree_safe_fd                                           │
│                                                                                                  │
│    669 │   │   │   else:                                                                         │
│    670 │   │   │   │   try:                                                                      │
│    671 │   │   │   │   │   if os.path.samestat(orig_st, os.fstat(dirfd)):                        │
│ ❱  672 │   │   │   │   │   │   _rmtree_safe_fd(dirfd, fullname, onerror)                         │
│    673 │   │   │   │   │   │   try:                                                              │
│    674 │   │   │   │   │   │   │   os.close(dirfd)                                               │
│    675 │   │   │   │   │   │   except OSError:                                                   │
│                                                                                                  │
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
│ │        dirfd = 4                                                                             │ │
│ │ dirfd_closed = False                                                                         │ │
│ │      entries = [                                                                             │ │
│ │                │   <DirEntry '.git'>,                                                        │ │
│ │                │   <DirEntry '.editorconfig'>,                                               │ │
│ │                │   <DirEntry '.github'>,                                                     │ │
│ │                │   <DirEntry '.gitignore'>,                                                  │ │
│ │                │   <DirEntry '.reports'>,                                                    │ │
│ │                │   <DirEntry '.scripts'>,                                                    │ │
│ │                │   <DirEntry 'LICENSE'>,                                                     │ │
│ │                │   <DirEntry 'Makefile'>,                                                    │ │
│ │                │   <DirEntry 'README.md'>,                                                   │ │
│ │                │   <DirEntry 'backend_addon'>,                                               │ │
│ │                │   ... +8                                                                    │ │
│ │                ]                                                                             │ │
│ │        entry = <DirEntry '.git'>                                                             │ │
│ │     fullname = '/home/myuser/.cookiecutters/cookieplone-templates/.git'                    │ │
│ │       is_dir = True                                                                          │ │
│ │      orig_st = os.stat_result(st_mode=16877, st_ino=11244042, st_dev=58, st_nlink=4,         │ │
│ │                st_uid=1574, st_gid=100, st_size=4096, st_atime=1738164915,                   │ │
│ │                st_mtime=1738164698, st_ctime=1738164698)                                     │ │
│ │         path = '/home/myuser/.cookiecutters/cookieplone-templates'                         │ │
│ │   scandir_it = <posix.ScandirIterator object at 0x7f7a85f03440>                              │ │
│ │        topfd = 3                                                                             │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                                  │
│ /usr/lib64/python3.11/shutil.py:672 in _rmtree_safe_fd                                           │
│                                                                                                  │
│    669 │   │   │   else:                                                                         │
│    670 │   │   │   │   try:                                                                      │
│    671 │   │   │   │   │   if os.path.samestat(orig_st, os.fstat(dirfd)):                        │
│ ❱  672 │   │   │   │   │   │   _rmtree_safe_fd(dirfd, fullname, onerror)                         │
│    673 │   │   │   │   │   │   try:                                                              │
│    674 │   │   │   │   │   │   │   os.close(dirfd)                                               │
│    675 │   │   │   │   │   │   except OSError:                                                   │
│                                                                                                  │
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
│ │        dirfd = 5                                                                             │ │
│ │ dirfd_closed = False                                                                         │ │
│ │      entries = [                                                                             │ │
│ │                │   <DirEntry 'objects'>,                                                     │ │
│ │                │   <DirEntry 'HEAD'>,                                                        │ │
│ │                │   <DirEntry 'config'>,                                                      │ │
│ │                │   <DirEntry 'logs'>,                                                        │ │
│ │                │   <DirEntry 'packed-refs'>,                                                 │ │
│ │                │   <DirEntry 'index'>                                                        │ │
│ │                ]                                                                             │ │
│ │        entry = <DirEntry 'objects'>                                                          │ │
│ │     fullname = '/home/myuser/.cookiecutters/cookieplone-templates/.git/objects'            │ │
│ │       is_dir = True                                                                          │ │
│ │      orig_st = os.stat_result(st_mode=16877, st_ino=11244471, st_dev=58, st_nlink=4,         │ │
│ │                st_uid=1574, st_gid=100, st_size=4096, st_atime=1738164698,                   │ │
│ │                st_mtime=1738164696, st_ctime=1738164696)                                     │ │
│ │         path = '/home/myuser/.cookiecutters/cookieplone-templates/.git'                    │ │
│ │   scandir_it = <posix.ScandirIterator object at 0x7f7a85f03590>                              │ │
│ │        topfd = 4                                                                             │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                                  │
│ /usr/lib64/python3.11/shutil.py:668 in _rmtree_safe_fd                                           │
│                                                                                                  │
│    665 │   │   │   │   dirfd = os.open(entry.name, os.O_RDONLY | os.O_NONBLOCK, dir_fd=topfd)    │
│    666 │   │   │   │   dirfd_closed = False                                                      │
│    667 │   │   │   except OSError:                                                               │
│ ❱  668 │   │   │   │   onerror(os.open, fullname, sys.exc_info())                                │
│    669 │   │   │   else:                                                                         │
│    670 │   │   │   │   try:                                                                      │
│    671 │   │   │   │   │   if os.path.samestat(orig_st, os.fstat(dirfd)):                        │
│                                                                                                  │
│ ╭─────────────────────────────────────────── locals ───────────────────────────────────────────╮ │
│ │    entries = [<DirEntry 'pack'>, <DirEntry 'info'>]                                          │ │
│ │      entry = <DirEntry 'pack'>                                                               │ │
│ │   fullname = '/home/myuser/.cookiecutters/cookieplone-templates/.git/objects/pack'         │ │
│ │     is_dir = True                                                                            │ │
│ │    orig_st = os.stat_result(st_mode=16512, st_ino=11244477, st_dev=58, st_nlink=2,           │ │
│ │              st_uid=1574, st_gid=100, st_size=4096, st_atime=1738164697,                     │ │
│ │              st_mtime=1738164698, st_ctime=1738165356)                                       │ │
│ │       path = '/home/myuser/.cookiecutters/cookieplone-templates/.git/objects'              │ │
│ │ scandir_it = <posix.ScandirIterator object at 0x7f7a85f03600>                                │ │
│ │      topfd = 5                                                                               │ │
│ ╰──────────────────────────────────────────────────────────────────────────────────────────────╯ │
│                                                                                                  │
│ /home/myuser/.cache/pipx/ff7797bb943bb34/lib64/python3.11/site-packages/cookiecutter/utils.py: │
│ 26 in force_delete                                                                               │
│                                                                                                  │
│    23 │   From https://docs.python.org/3/library/shutil.html#rmtree-example                      │
│    24 │   """                                                                                    │
│    25 │   os.chmod(path, stat.S_IWRITE)                                                          │
│ ❱  26 │   func(path)                                                                             │
│    27                                                                                            │
│    28                                                                                            │
│    29 def rmtree(path):                                                                          │
│                                                                                                  │
│ ╭────────────────────────────────────── locals ──────────────────────────────────────╮           │
│ │ exc_info = (                                                                       │           │
│ │            │   <class 'PermissionError'>,                                          │           │
│ │            │   PermissionError(13, 'Permission denied'),                           │           │
│ │            │   <traceback object at 0x7f7a85d82840>                                │           │
│ │            )                                                                       │           │
│ │     func = <built-in function open>                                                │           │
│ │     path = '/home/myuser/.cookiecutters/cookieplone-templates/.git/objects/pack' │           │
│ ╰────────────────────────────────────────────────────────────────────────────────────╯           │
╰──────────────────────────────────────────────────────────────────────────────────────────────────╯
TypeError: open() missing required argument 'flags' (pos 2)

I tried deleting ~/.cookiecutters to force it to rebuild, but I get the same error.

I see some NFS files in ~/.cookiecutters/cookieplone-templates/.git/objects/pack/ but lsof finds no processes. So I deleted these files and confirmed they weren't rebuilt with new names (i.e. a process is still trying to use it). However I get this error when running the pipx command again

You've downloaded /home/myuser/.cookiecutters/cookieplone-templates before. Is
it okay to delete and re-download it? [y/n] (y): y
[Errno 39] Directory not empty:
'/home/myuser/.cookiecutters/cookieplone-templates/.git/objects/pack'

https://git-scm.com/book/en/v2/Git-Internals-Packfiles

"To see what happens, you can manually ask Git to pack up the objects by calling the git gc command"

"When Git packs objects, it looks for files that are named and sized similarly, and stores just the deltas from one version of the file to the next. You can look into the packfile and see what Git did to save space."

So there are files in pack but the cookiecutter "delete" didn't expect those. I would put a breakpoint here:

and look at what is happening.

I'm not sure what exactly you are suggesting I look for. The path is "/home/myuser/.cookiecutters/cookieplone-templates".

(Pdb) shutil.rmtree(path)
*** PermissionError: [Errno 13] Permission denied: 'pack'

I can't run any git commands there because it doesn't find a git repo, I guess because it's partially deleted it already at this point

~/.cookiecutters/cookieplone-templates> git gc
fatal: not a git repository (or any parent up to mount point /home)
1 Like

Well, you confirmed that when Cookieplone runs, it lacks permission to rmtree(path). Just thinking out loud:

  • Can you inspect the permissions of the path?
  • Is there some way to determine under which user Cookieplone runs?

DuckDuckGoing for the error message provided a couple of suggestions to try.

If either of those suggestions work for you, then would you please file a bug report in the upstream cookiecutter issue tracker and a pull request?

1 Like

Yes, the .git/objects/pack folder was read only, I needed to +x to list the contents and see the two NFS files inside. I'm not a network guy but my understanding is these are file locks created by some process, however I couldn't find a process that was using them.

I ssh'd into the server with my personal account so cookieplone should be running on that. Does it need to run as root?

Python may not have closed the file or directory after reading it and exiting it. See the two SO links I previously posted for a possible solution to test by modifying Cookiecutter code.

There's also the file ownership and group membership to check. I think Cookieplone runs as the user that invokes it, so the file membership should align with your user.

I do not recommend running Cookieplone as root.

I don't follow how to adapt the SO comments to my situation here. I'm getting the error when it tries to delete the directory. Are you suggesting that the root of the problem could be coming from cookiecutter trying to improperly open something in a previous step? Any suggestions where that code in cookiecutter might be?

Yes, in cookiecutter. However, I think if you used pipx to install it, it was put into a temporary Python virtual environment, so it might get removed so you can't find it. I'm not clear about how it works.

Anyway, instead you could manually install cookiecutter, edit the file as @yurj and the SO posts indicated, then run it without pipx in the command line. HTH.

The error is on rmtree cookiecutter/cookiecutter/utils.py at b4451231809fb9e4fc2a1e95d433cb030e4b9e06 · cookiecutter/cookiecutter · GitHub and it seems that
the force_delete command does not sufficiently clean up. I need to do chmod +xr on .git/objects/pack and thenchmod +w .git/objects/pack/.nfs*. As to why those nfs files are there, I don't know, there does not seem to be a process trying to use them by the time I inspect. Maybe a race condition with git? Just a guess. I modified force_delete a bit as a test

def force_delete(func, path, exc_info):
    """Error handler for `shutil.rmtree()` equivalent to `rm -rf`.

    Usage: `shutil.rmtree(path, onerror=force_delete)`
    From https://docs.python.org/3/library/shutil.html#rmtree-example
    """
    os.chmod(path, stat.S_IRWXU)
    import pdb; pdb.set_trace()
    shutil.rmtree(path)

The odd thing there is that without the trace point I get an error that there are still .nfs files in there. If I keep the debug trace and then immediately continue, it works (or rather, fails at an unrelated later steps*). The value of path here is /home/myuser/.cookiecutters/cookieplone=-templates/.git/objects/pack

Another strange behavior is that cookiecutter/cookiecutter/vcs.py at b4451231809fb9e4fc2a1e95d433cb030e4b9e06 · cookiecutter/cookiecutter · GitHub seems to be called twice. The first time is called with no_input=True (no prompt) and then with no_input=False and the prompt. Even if I delete the .cookiecutters directory first, I will get the prompt that the template has been downloaded before. So I can actually just say no, and "reuse" the template I just downloaded.

*This is a tangent from the original post, but the subsequent error is that it fails to check the docker version because there is no docker executable. At my employer we use podman, not straight up docker, but I thought docker was supposed to be optional for the install?

FWIW I submitted a bug for the docker issue Fixes #62 by ewohnlich · Pull Request #63 · plone/cookieplone · GitHub I think it was just not catching the correct exception type.

1 Like

I get a similar problem running make install now

Write [r]: requirements-mxdev.txt
🎂 You are now ready for: pip install -r requirements-mxdev.txt
   (path to pip may vary dependent on your installation method)
+ /home/myuser/volto/voltofeb/backend/.venv/bin/uv pip install -r requirements-mxdev.txt
Resolved 319 packages in 250ms
  × Failed to build `voltofeb @ file:///home/myuser/volto/voltofeb/backend`
  ├─▶ The build backend returned an error
  ╰─▶ Call to `setuptools.build_meta:__legacy__.build_editable` failed (exit status: 1)

      [stdout]
      running editable_wheel
      creating /home/myuser/.cache/uv/builds-v0/.tmptazZk1/.tmp-233sr7dd/voltofeb.egg-info
      writing /home/myuser/.cache/uv/builds-v0/.tmptazZk1/.tmp-233sr7dd/voltofeb.egg-info/PKG-INFO
      writing dependency_links to /home/myuser/.cache/uv/builds-v0/.tmptazZk1/.tmp-233sr7dd/voltofeb.egg-info/dependency_links.txt
      writing entry points to /home/myuser/.cache/uv/builds-v0/.tmptazZk1/.tmp-233sr7dd/voltofeb.egg-info/entry_points.txt
      writing namespace_packages to /home/myuser/.cache/uv/builds-v0/.tmptazZk1/.tmp-233sr7dd/voltofeb.egg-info/namespace_packages.txt
      writing requirements to /home/myuser/.cache/uv/builds-v0/.tmptazZk1/.tmp-233sr7dd/voltofeb.egg-info/requires.txt
      writing top-level names to /home/myuser/.cache/uv/builds-v0/.tmptazZk1/.tmp-233sr7dd/voltofeb.egg-info/top_level.txt
      writing manifest file '/home/myuser/.cache/uv/builds-v0/.tmptazZk1/.tmp-233sr7dd/voltofeb.egg-info/SOURCES.txt'
      reading manifest file '/home/myuser/.cache/uv/builds-v0/.tmptazZk1/.tmp-233sr7dd/voltofeb.egg-info/SOURCES.txt'
      reading manifest template 'MANIFEST.in'
      adding license file 'LICENSE.GPL'
      adding license file 'LICENSE.md'
      writing manifest file '/home/myuser/.cache/uv/builds-v0/.tmptazZk1/.tmp-233sr7dd/voltofeb.egg-info/SOURCES.txt'
      creating '/home/myuser/.cache/uv/builds-v0/.tmptazZk1/.tmp-233sr7dd/voltofeb-1.0.0a0.dist-info'

      [stderr]
      warning: no files found matching '.coveragerc'
      warning: no files found matching '*.yml'
      warning: no previously-included files found matching '.mrbob.ini'
      warning: no previously-included files found matching 'bobtemplate.cfg'
      warning: no previously-included files found matching 'Dockerfile'
      warning: no previously-included files found matching 'mx.ini'
      warning: no previously-included files found matching 'Makefile'
      warning: no previously-included files matching '*' found under directory 'frontend'
      warning: no previously-included files found matching 'instance.yaml'
      warning: no previously-included files matching '*.pyc' found anywhere in distribution
      warning: no previously-included files matching '.DS_Store' found anywhere in distribution
      Traceback (most recent call last):
        File "/home/myuser/.cache/uv/builds-v0/.tmpF5JEmq/lib64/python3.11/site-packages/setuptools/command/editable_wheel.py", line 132, in run
          self._ensure_dist_info()
        File "/home/myuser/.cache/uv/builds-v0/.tmpF5JEmq/lib64/python3.11/site-packages/setuptools/command/editable_wheel.py", line 151, in
      _ensure_dist_info
          dist_info.run()
        File "/home/myuser/.cache/uv/builds-v0/.tmpF5JEmq/lib64/python3.11/site-packages/setuptools/command/dist_info.py", line 103, in run
          bdist_wheel.egg2dist(egg_info_dir, self.dist_info_dir)
        File "/home/myuser/.cache/uv/builds-v0/.tmpF5JEmq/lib64/python3.11/site-packages/setuptools/command/bdist_wheel.py", line 597, in egg2dist
          adios(egginfo_path)
        File "/home/myuser/.cache/uv/builds-v0/.tmpF5JEmq/lib64/python3.11/site-packages/setuptools/command/bdist_wheel.py", line 546, in adios
          _shutil.rmtree(p)
        File "/home/myuser/.cache/uv/builds-v0/.tmpF5JEmq/lib64/python3.11/site-packages/setuptools/_shutil.py", line 48, in rmtree
          return py311.shutil_rmtree(path, ignore_errors, onexc)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/home/myuser/.cache/uv/builds-v0/.tmpF5JEmq/lib64/python3.11/site-packages/setuptools/compat/py311.py", line 27, in shutil_rmtree
          return shutil.rmtree(path, ignore_errors, onerror=_handler)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/usr/lib64/python3.11/shutil.py", line 763, in rmtree
          onerror(os.rmdir, path, sys.exc_info())
        File "/home/myuser/.cache/uv/builds-v0/.tmpF5JEmq/lib64/python3.11/site-packages/setuptools/compat/py311.py", line 25, in _handler
          onexc(fn, path, excinfo[1])
        File "/home/myuser/.cache/uv/builds-v0/.tmpF5JEmq/lib64/python3.11/site-packages/setuptools/_shutil.py", line 40, in _auto_chmod
          raise exc
        File "/usr/lib64/python3.11/shutil.py", line 761, in rmtree
          os.rmdir(path, dir_fd=dir_fd)
      OSError: [Errno 39] Directory not empty: '/home/myuser/.cache/uv/builds-v0/.tmptazZk1/.tmp-233sr7dd/voltofeb.egg-info'
      /home/myuser/.cache/uv/builds-v0/.tmpF5JEmq/lib64/python3.11/site-packages/setuptools/_distutils/dist.py:1002: _DebuggingTips: Problem in editable
      installation.
      !!

              ********************************************************************************
              An error happened while installing `voltofeb` in editable mode.

              The following steps are recommended to help debug this problem:

              - Try to install the project normally, without using the editable mode.
                Does the error still persist?
                (If it does, try fixing the problem before attempting the editable mode).
              - If you are using binary extensions, make sure you have all OS-level
                dependencies installed (e.g. compilers, toolchains, binary libraries, ...).
              - Try the latest version of setuptools (maybe the error was already fixed).
              - If you (or your project dependencies) are using any setuptools extension
                or customization, make sure they support the editable mode.

              After following the steps above, if the problem still persists and
              you think this is related to how setuptools handles editable installations,
              please submit a reproducible example
              (see https://stackoverflow.com/help/minimal-reproducible-example) to:

                  https://github.com/pypa/setuptools/issues

              See https://setuptools.pypa.io/en/latest/userguide/development_mode.html for details.
              ********************************************************************************

      !!
        cmd_obj.run()
      Traceback (most recent call last):
        File "/home/myuser/.cache/uv/builds-v0/.tmpF5JEmq/lib64/python3.11/site-packages/setuptools/_distutils/core.py", line 202, in run_commands
          dist.run_commands()
        File "/home/myuser/.cache/uv/builds-v0/.tmpF5JEmq/lib64/python3.11/site-packages/setuptools/_distutils/dist.py", line 983, in run_commands
          self.run_command(cmd)
        File "/home/myuser/.cache/uv/builds-v0/.tmpF5JEmq/lib64/python3.11/site-packages/setuptools/dist.py", line 999, in run_command
          super().run_command(command)
        File "/home/myuser/.cache/uv/builds-v0/.tmpF5JEmq/lib64/python3.11/site-packages/setuptools/_distutils/dist.py", line 1002, in run_command
          cmd_obj.run()
        File "/home/myuser/.cache/uv/builds-v0/.tmpF5JEmq/lib64/python3.11/site-packages/setuptools/command/editable_wheel.py", line 132, in run
          self._ensure_dist_info()
        File "/home/myuser/.cache/uv/builds-v0/.tmpF5JEmq/lib64/python3.11/site-packages/setuptools/command/editable_wheel.py", line 151, in
      _ensure_dist_info
          dist_info.run()
        File "/home/myuser/.cache/uv/builds-v0/.tmpF5JEmq/lib64/python3.11/site-packages/setuptools/command/dist_info.py", line 103, in run
          bdist_wheel.egg2dist(egg_info_dir, self.dist_info_dir)
        File "/home/myuser/.cache/uv/builds-v0/.tmpF5JEmq/lib64/python3.11/site-packages/setuptools/command/bdist_wheel.py", line 597, in egg2dist
          adios(egginfo_path)
        File "/home/myuser/.cache/uv/builds-v0/.tmpF5JEmq/lib64/python3.11/site-packages/setuptools/command/bdist_wheel.py", line 546, in adios
          _shutil.rmtree(p)
        File "/home/myuser/.cache/uv/builds-v0/.tmpF5JEmq/lib64/python3.11/site-packages/setuptools/_shutil.py", line 48, in rmtree
          return py311.shutil_rmtree(path, ignore_errors, onexc)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/home/myuser/.cache/uv/builds-v0/.tmpF5JEmq/lib64/python3.11/site-packages/setuptools/compat/py311.py", line 27, in shutil_rmtree
          return shutil.rmtree(path, ignore_errors, onerror=_handler)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/usr/lib64/python3.11/shutil.py", line 763, in rmtree
          onerror(os.rmdir, path, sys.exc_info())
        File "/home/myuser/.cache/uv/builds-v0/.tmpF5JEmq/lib64/python3.11/site-packages/setuptools/compat/py311.py", line 25, in _handler
          onexc(fn, path, excinfo[1])
        File "/home/myuser/.cache/uv/builds-v0/.tmpF5JEmq/lib64/python3.11/site-packages/setuptools/_shutil.py", line 40, in _auto_chmod
          raise exc
        File "/usr/lib64/python3.11/shutil.py", line 761, in rmtree
          os.rmdir(path, dir_fd=dir_fd)
      OSError: [Errno 39] Directory not empty: '/home/myuser/.cache/uv/builds-v0/.tmptazZk1/.tmp-233sr7dd/voltofeb.egg-info'

      During handling of the above exception, another exception occurred:

      Traceback (most recent call last):
        File "/home/myuser/.cache/uv/builds-v0/.tmpF5JEmq/lib64/python3.11/site-packages/setuptools/build_meta.py", line 407, in _build_with_temp_dir
          self.run_setup()
        File "/home/myuser/.cache/uv/builds-v0/.tmpF5JEmq/lib64/python3.11/site-packages/setuptools/build_meta.py", line 522, in run_setup
          super().run_setup(setup_script=setup_script)
        File "/home/myuser/.cache/uv/builds-v0/.tmpF5JEmq/lib64/python3.11/site-packages/setuptools/build_meta.py", line 320, in run_setup
          exec(code, locals())
        File "<string>", line 15, in <module>
        File "/home/myuser/.cache/uv/builds-v0/.tmpF5JEmq/lib64/python3.11/site-packages/setuptools/__init__.py", line 117, in setup
          return distutils.core.setup(**attrs)
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/home/myuser/.cache/uv/builds-v0/.tmpF5JEmq/lib64/python3.11/site-packages/setuptools/_distutils/core.py", line 186, in setup
          return run_commands(dist)
                 ^^^^^^^^^^^^^^^^^^
        File "/home/myuser/.cache/uv/builds-v0/.tmpF5JEmq/lib64/python3.11/site-packages/setuptools/_distutils/core.py", line 210, in run_commands
          raise SystemExit(f"error: {exc}")
      SystemExit: error: [Errno 39] Directory not empty: '/home/myuser/.cache/uv/builds-v0/.tmptazZk1/.tmp-233sr7dd/voltofeb.egg-info'

      During handling of the above exception, another exception occurred:

      Traceback (most recent call last):
        File "<string>", line 11, in <module>
        File "/home/myuser/.cache/uv/builds-v0/.tmpF5JEmq/lib64/python3.11/site-packages/setuptools/build_meta.py", line 476, in build_editable
          return self._build_with_temp_dir(
                 ^^^^^^^^^^^^^^^^^^^^^^^^^^
        File "/home/myuser/.cache/uv/builds-v0/.tmpF5JEmq/lib64/python3.11/site-packages/setuptools/build_meta.py", line 395, in _build_with_temp_dir
          with tempfile.TemporaryDirectory(
        File "/usr/lib64/python3.11/tempfile.py", line 943, in __exit__
          self.cleanup()
        File "/usr/lib64/python3.11/tempfile.py", line 947, in cleanup
          self._rmtree(self.name, ignore_errors=self._ignore_cleanup_errors)
        File "/usr/lib64/python3.11/tempfile.py", line 929, in _rmtree
          _shutil.rmtree(name, onerror=onerror)
        File "/usr/lib64/python3.11/shutil.py", line 752, in rmtree
          _rmtree_safe_fd(fd, path, onerror)
        File "/usr/lib64/python3.11/shutil.py", line 672, in _rmtree_safe_fd
          _rmtree_safe_fd(dirfd, fullname, onerror)
        File "/usr/lib64/python3.11/shutil.py", line 703, in _rmtree_safe_fd
          onerror(os.unlink, fullname, sys.exc_info())
        File "/usr/lib64/python3.11/shutil.py", line 701, in _rmtree_safe_fd
          os.unlink(entry.name, dir_fd=topfd)
      OSError: [Errno 16] Device or resource busy: '.nfs000000000082008a000003da'

      hint: This usually indicates a problem with the package or the build environment.
make[2]: *** [Makefile:69: build-dev] Error 1
make[1]: *** [Makefile:63: backend-install] Error 2
make: *** [Makefile:89: install] Error 2

~/.cache/uv was previously empty. And after this errors out I am able to clear it again - there are no longer any nfs files.

That means that the process that opened the files did not close them. NFS thinks that some process is using them, so if you rm them, NFS just rename it so the process still have access to it using the open fd.

In this case, there's a lock on it.

I would force NFS v3 and mount the nfs without locking, if possible.

if you inspect the file .nfs000000000082008a000003da it should be possible to guess what process is using it, also using lsof on it.

Any chance to use a local file system via link for this directories? This would avoid these kind of problems.

I used lsof on the initial issue and did not find a process running it at the time. For the latest one, the voltofeb.egg-info is actually empty by the time I look at it. So my understanding is whatever is doing the locking is part of cookieplone or the make file.

Unfortunately I do not have the option to mount the volume without locking, and I am explicitly not using a local filesystem because my local filesystem is Windows.