What happens
packOptions.filesIncluded accepts a relative path so a file in a subdirectory can be added to the package. Any path under a dot-prefixed directory is dropped instead, and nothing reports it. The same applies to a dotfile named explicitly.
The spec describes filesIncluded as "Specific files to always include" (packOptions), and uipath pack exits successfully, so there is no signal that part of the configuration was ignored.
Reproduction
import os, tempfile
from uipath._cli._utils._project_files import files_to_include
from uipath._cli.models.uipath_json_schema import PackOptions
root = tempfile.mkdtemp()
os.makedirs(f"{root}/.config/assets/demo")
os.makedirs(f"{root}/plain")
open(f"{root}/.config/assets/demo/ASSET.md", "w").write("x")
open(f"{root}/plain/ASSET.md", "w").write("x") # control, same shape without the dot
open(f"{root}/.python-version", "w").write("3.11")
open(f"{root}/pyproject.toml", "w").write("[project]")
opts = PackOptions(**{"filesIncluded": [
".config/assets/demo/ASSET.md",
".python-version",
"plain/ASSET.md",
]})
included, skipped = files_to_include(opts, root, include_uv_lock=False)
print("included:", sorted(f.relative_path for f in included))
print("skipped :", skipped)
Output:
included: ['plain/ASSET.md', 'pyproject.toml']
skipped : []
plain/ASSET.md is honoured. .config/assets/demo/ASSET.md and .python-version are not, and skipped is empty.
Cause
Two unconditional filters run before the inclusion rules are evaluated.
_project_files.py:471 drops dot-prefixed directories from dirs[:], so os.walk never descends into them. Line 497 skips dot-prefixed files with a bare continue.
The per-file inclusion check that would honour the configuration is lines 514 to 520, and it already supports a path match for subdirectories. It is simply unreachable for these paths.
Because both filters continue rather than appending to skipped_files, the omission does not surface anywhere, including in the caller that reports skipped files.
Why it matters
Tooling configuration and assets conventionally live in a dot-prefixed directory. A project that needs such a directory inside its package has no way to express that: directoriesExcluded has no directoriesIncluded counterpart, and the documented per-file escape hatch does not work there.
The available workaround is to keep the content under a non-dot directory and copy it into place at runtime, which means the package layout no longer matches the source layout and the copy step has to be reimplemented per project.
Suggested fix
Any of these would resolve it, in rough order of smallest change:
- Honour an explicit
filesIncluded entry regardless of a dot prefix. Descend into a dot-prefixed directory only when some filesIncluded entry has it as a path prefix, which keeps the current pruning fast for .git, .venv and similar. Apply the same to the dotfile filter at line 497. This matches the word "always" already in the spec.
- Add
directoriesIncluded to packOptions as the counterpart to directoriesExcluded.
Independent of which is chosen, appending to skipped_files at both filters would turn a silent omission into something a caller can report.
Environment
uipath 2.14.2, resolved from PyPI
- Python 3.14, macOS
- Line numbers above are against
main
What happens
packOptions.filesIncludedaccepts a relative path so a file in a subdirectory can be added to the package. Any path under a dot-prefixed directory is dropped instead, and nothing reports it. The same applies to a dotfile named explicitly.The spec describes
filesIncludedas "Specific files to always include" (packOptions), anduipath packexits successfully, so there is no signal that part of the configuration was ignored.Reproduction
Output:
plain/ASSET.mdis honoured..config/assets/demo/ASSET.mdand.python-versionare not, andskippedis empty.Cause
Two unconditional filters run before the inclusion rules are evaluated.
_project_files.py:471drops dot-prefixed directories fromdirs[:], soos.walknever descends into them. Line 497 skips dot-prefixed files with a barecontinue.The per-file inclusion check that would honour the configuration is lines 514 to 520, and it already supports a path match for subdirectories. It is simply unreachable for these paths.
Because both filters
continuerather than appending toskipped_files, the omission does not surface anywhere, including in the caller that reports skipped files.Why it matters
Tooling configuration and assets conventionally live in a dot-prefixed directory. A project that needs such a directory inside its package has no way to express that:
directoriesExcludedhas nodirectoriesIncludedcounterpart, and the documented per-file escape hatch does not work there.The available workaround is to keep the content under a non-dot directory and copy it into place at runtime, which means the package layout no longer matches the source layout and the copy step has to be reimplemented per project.
Suggested fix
Any of these would resolve it, in rough order of smallest change:
filesIncludedentry regardless of a dot prefix. Descend into a dot-prefixed directory only when somefilesIncludedentry has it as a path prefix, which keeps the current pruning fast for.git,.venvand similar. Apply the same to the dotfile filter at line 497. This matches the word "always" already in the spec.directoriesIncludedtopackOptionsas the counterpart todirectoriesExcluded.Independent of which is chosen, appending to
skipped_filesat both filters would turn a silent omission into something a caller can report.Environment
uipath2.14.2, resolved from PyPImain