Skip to content

fix: Allow west commands to be imported from a project subdirectory if manifest is located in subdirectory#920

Draft
nmunnich wants to merge 1 commit intozephyrproject-rtos:mainfrom
nmunnich:fix-subdirectory-westcommands
Draft

fix: Allow west commands to be imported from a project subdirectory if manifest is located in subdirectory#920
nmunnich wants to merge 1 commit intozephyrproject-rtos:mainfrom
nmunnich:fix-subdirectory-westcommands

Conversation

@nmunnich
Copy link

Fixes #725

Note that I relied on the test to tell me if the fix is working, I did not test it with a proper repository setup. Let me know if this is desired/this project is one where manual testing of such kind is necessary.

I'm unfamiliar with west release cycles - if this is accepted, could someone let me know when I could expect to see a new version of west released, so that we can start recommending setups relying on this to our users?

@nmunnich nmunnich force-pushed the fix-subdirectory-westcommands branch from c1641aa to d473971 Compare February 16, 2026 23:32
@nmunnich
Copy link
Author

Should pass the linter this time (sorry!)

Copy link
Collaborator

@marc-hb marc-hb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you so much for taking this! See comments below.

Sorry if some comments appear twice, I had to make them again because you force pushed in the mean time. Please keep force-pushing, it's just Github support for force-pushes is pretty bad.

@nmunnich nmunnich force-pushed the fix-subdirectory-westcommands branch from d473971 to 250fd5b Compare February 17, 2026 01:07
Copy link
Author

@nmunnich nmunnich left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the quick review! (I hope the linter is happy with me this time, uv run poe all doesn't seem to check whatever that last test is)

@marc-hb

This comment was marked as resolved.

@nmunnich nmunnich force-pushed the fix-subdirectory-westcommands branch from 250fd5b to 86e60fa Compare February 17, 2026 07:41
Copy link
Author

@nmunnich nmunnich left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, the formatter does run, but for whatever reason it thinks it needs to format 22 files (with no difference made to them) on my machine. I've kept that formatting on manifest.py, maybe now it'll be happy? Just another quirk of Windows I suppose.

@codecov
Copy link

codecov bot commented Feb 17, 2026

Codecov Report

❌ Patch coverage is 81.25000% with 3 lines in your changes missing coverage. Please review.
✅ Project coverage is 85.92%. Comparing base (548b9ee) to head (51c2f44).
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
src/west/manifest.py 81.25% 3 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main     #920      +/-   ##
==========================================
- Coverage   85.95%   85.92%   -0.03%     
==========================================
  Files          11       11              
  Lines        3453     3461       +8     
==========================================
+ Hits         2968     2974       +6     
- Misses        485      487       +2     
Files with missing lines Coverage Δ
src/west/manifest.py 95.36% <81.25%> (-0.16%) ⬇️

@nmunnich
Copy link
Author

Hmmm. I'm not sold on adding tests to test that the exceptions are indeed raised. Don't think the AssertionError can ever be raised anyway, it's just good practice to have it there.

Copy link
Collaborator

@marc-hb marc-hb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmmm. I'm not sold on adding tests to test that the exceptions are indeed raised. Don't think the AssertionError can ever be raised anyway, it's just good practice to have it there.

I agree. Error-handling is notoriously lacking coverage across the entire industry (one of the reasons software is so insecure), but I don't think we need test coverage for raise InternalBug("this should never happen").

@pdgendt is big on test coverage and rightly so, let's wait until next week when he is back. Every PR needs 2 approvals anyway.

marc-hb added a commit to marc-hb/west that referenced this pull request Feb 17, 2026
Use backwards slashes on Windows to catch any future hardcoding mistake.

Minor oversight found while discussing zephyrproject-rtos#920
marc-hb added a commit to marc-hb/west that referenced this pull request Feb 17, 2026
Use backwards slashes on Windows to catch any future hardcoding mistake.

Minor oversight found while discussing zephyrproject-rtos#920
marc-hb added a commit to marc-hb/west that referenced this pull request Feb 17, 2026
Use backwards slashes on Windows to catch any future hardcoding mistake.

Minor oversight found while discussing zephyrproject-rtos#920
marc-hb added a commit to marc-hb/west that referenced this pull request Feb 17, 2026
Use backwards slashes on Windows to catch any future hardcoding mistake.

Minor oversight found while discussing zephyrproject-rtos#920
@nmunnich nmunnich force-pushed the fix-subdirectory-westcommands branch from 86e60fa to 51c2f44 Compare February 18, 2026 18:50
marc-hb
marc-hb previously approved these changes Feb 19, 2026
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request fixes issue #725 where imported west-commands from project subdirectories were not resolving correctly. When a manifest file is located in a subdirectory of a project (e.g., app/west.yml), and that manifest defines custom west-commands, the paths to those commands need to be resolved relative to the manifest's location, not the project root.

Changes:

  • Modified _import_path_from_project to pass the manifest path instead of None to _import_data_from_project
  • Updated _import_data_from_project to accept both _import_map and str types using match/case syntax, extracting the manifest directory and prepending it to west_commands paths
  • Added a test case to verify that west-commands are correctly resolved when imported from project subdirectories

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.

File Description
src/west/manifest.py Updated import logic to correctly resolve west-commands paths relative to manifest subdirectory location
tests/test_manifest.py Added test case to verify west-commands resolution from project subdirectories

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

imap_path_prefix = '.'
mfst_path = imap_or_mfpath
case _:
raise AssertionError(f'imap_or_path has unexpected type {type(imap_or_mfpath)}')
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message refers to 'imap_or_path' but the parameter name is 'imap_or_mfpath'. The error message should be updated to match the parameter name for consistency.

Suggested change
raise AssertionError(f'imap_or_path has unexpected type {type(imap_or_mfpath)}')
raise AssertionError(f'imap_or_mfpath has unexpected type {type(imap_or_mfpath)}')

Copilot uses AI. Check for mistakes.
Comment on lines 2141 to 2143
def test_import_map_error_handling():
# Make sure we handle expected errors when loading import:
# values that are maps.
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While the new test covers string path imports (e.g., import: "subdir/west.yml"), it would be beneficial to also add a test case for import maps with subdirectories (e.g., import: {file: "subdir/west.yml"}). Although both code paths converge at the same logic in _import_data_from_project, having explicit test coverage for both import styles would ensure the fix works correctly in all scenarios and prevent potential regressions.

Suggested change
def test_import_map_error_handling():
# Make sure we handle expected errors when loading import:
# values that are maps.
def test_import_project_submanifest_commands_from_project_subdirectory_import_map(
manifest_repo,
):
# Similar to test_import_project_submanifest_commands_from_project_subdirectory,
# but this tests using an import map with a 'file' key instead of a string path.
# When a manifest is imported from a project subdirectory (e.g., mf_subdir/west.yml),
# and that manifest defines west-commands, the paths should be resolved relative to
# the manifest subdirectory. This tests _import_path_from_project with an import map.
with open(manifest_repo / 'west.yml', 'w') as f:
f.write('''\
manifest:
projects:
- name: p1
url: url-placeholder
import:
file: mf_subdir/west.yml
''')
p1 = manifest_repo / '..' / 'p1'
create_repo(p1)
create_branch(p1, 'manifest-rev', checkout=True)
add_commit(
p1,
'add mf_subdir/west.yml with west-commands (import map)',
files={
'mf_subdir/west.yml': '''\
manifest:
projects:
- name: p2
url: url-placeholder2
self:
west-commands: p2subdir/west-commands.yml
''',
},
)
checkout_branch(p1, 'master')
p1_proj = MF().get_projects(['p1'])[0]
# The west_commands path should be 'mf_subdir/p2subdir/west-commands.yml',
# not 'west-commands.yml', to be resolved correctly relative to the project root.
expected = ['mf_subdir/p2subdir/west-commands.yml']
for a, e in zip(p1_proj.west_commands, expected, strict=True):
assert PurePath(a) == PurePath(e)
def test_import_map_error_handling():
# Make sure we handle expected errors when loading import:
# values that are maps.

Copilot uses AI. Check for mistakes.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's an interesting suggestion. But that's also a MASSIVE duplication of the other test code for very little additional coverage :-( @nmunnich maybe you could easily combine both tests (yours and the copilot ~duplicate) into a single test like this:

        manifest:
          projects:
           
          - name: pA
            url: url-placeholderA
            import: mf_subdirA/west.yml

          - name: pB
            url: url-placeholderB
            import:
               file: mf_subdirB/west.yml

Then you can re-use the identical add_commit() twice in both A and B.

# (manifest_path is a relative path within the project),
# we need to adjust the west_commands paths to be relative
# to the project root, not to the manifest subdirectory.
mfst_dir = Path(mfst_path).parent
Copy link

Copilot AI Feb 19, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Path should be PurePosixPath to ensure cross-platform compatibility. Git stores paths internally using POSIX-style forward slashes, even on Windows. Using Path on Windows will create paths with backslashes, which may not work correctly. The codebase already uses PurePosixPath in similar contexts (see line 312).

Suggested change
mfst_dir = Path(mfst_path).parent
mfst_dir = PurePosixPath(mfst_path).parent

Copilot uses AI. Check for mistakes.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@marc-hb maybe related to #921 ?

Copy link
Collaborator

@marc-hb marc-hb Feb 20, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, related to #921 (which I just demoted to draft).

On one hand we should stick to Pathlib abstractions, avoid hardcoding forward slashes and not use Unix paths objects internally when running on Windows. This is not just about backslashes, there are other differences.

On the other hand we don't want west manifest --resolve to convert forward slashes to backslashes on Windows.

I need again more time to think about it and do more testing, sorry :-(

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a much bigger can of worms than I expected. I wish I could "unsee" it but I can't :-(

For instance, on Windows:

west list -f '{path}'      # forward slashes
west list -f '{abspath}'   # backslashes 
west list -f '{posixpath}' # forward slash
west list topdir           # absolute and forward since #375

https://docs.zephyrproject.org/latest/develop/west/west-apis.html#west.manifest.Project

Also relevant:

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Personally, I would argue that resolving that inconsistency is out of scope here, and I would prefer not to block waiting for a fix/resolution. Would you be alright with taking the PurePosixPath suggestion and going forward with that here?

Copy link
Collaborator

@marc-hb marc-hb Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure it's a pre-existing inconsistency. This could be all by design - which this PR may or may not be consistent with.

Copy link
Collaborator

@marc-hb marc-hb Feb 24, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So, there is some pre-existing inconsistency. But some of it is right in the middle of this PR. Example:

Input:

    - name: hal_espressif
      path: modules///hal\\\espressif
      west-commands: west\\\west-commands.yml

west manifest --resolve output:

    - name: hal_espressif
      path: modules/hal/espressif              # normalized to POSIX
      west-commands: west\\\west-commands.yml  # unchanged

We need to decide at the very least whether this pre-existing behavior is correct or not before merging this PR. I mean we can't modify code without even knowing whether the modified code is buggy or not.

Note this is only the output of west manifest --resolve. How does the actual import behave? On Windows and Linux?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've gone through the pain of doing a bunch of testing of actual imports in PowerShell. This may be simpler than we think:

  • No matter what path is set to, it always resolves in west manifest --resolve with /. So path: modules/\//\/\/\\\\/\//\./\zmk\/\/\/\.\\\/\test turned into modules/zmk/test.
  • No matter what west-commands is set to, it remained the same in the west manifest. In addition, regardless of how many extra \ and / combinations I put, my dummy west command was being picked up and executed correctly.

I've now modified the code somewhat, so that the west-commands will always resolve with posix paths, for consistency with path. I noticed a comment on L2468 that seems relevant to the conversation, and took the approach used there.

I did notice another bug while doing my testing:

manifest:
  projects:
    - name: p1
      url: placeholder
      revision: west-test
      import: app/west.yml
      west-commands: app/scripts/west-commands.yml
west-commands:
  - file: test/west-commands/west.py
    commands:
      - name: west
        class: West
        help: Print hello world to the console

When resolving this, while the west-commands.yml file is located correctly and the commands are visible in west help, trying to actually execute the command doesn't work because west is trying to resolve it as <module-path>/test/west-commands/west.py rather than <module-path>/app/test/west-commands/west.py. My current thinking is that we need to add manifestdir as a property to Project, so that we can refer to it on L691 of commands.py. That feels like a rather extensive change though, would you have any alternative idea?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've been working on adding some 2 "backslash" tests related to this. I have this one ready but I want to finish a similar one for west-commands:
https://github.com/marc-hb/west/commits/backslash-tests/

I should be able to get the last one ready on Friday. I would like these "pure" tests to be merged before any change (feature or bug fix) in west itself.

Just a heads-up, sorry for not answering your comment yet - will do that on Friday too.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would like these "pure" tests to be merged before any change (feature or bug fix) in west itself.

@marc-hb marc-hb dismissed their stale review February 20, 2026 05:14

backslashes :-(

@marc-hb marc-hb marked this pull request as draft February 20, 2026 05:14
@nmunnich nmunnich force-pushed the fix-subdirectory-westcommands branch 2 times, most recently from 6ad3a15 to dbf1a5d Compare February 25, 2026 16:42
…ories

Allow west commands to be imported from a project subdirectory if the
manifest is located in a subdirectory, e.g. `import: mf_subdir/west.yml`.

Fixes issue zephyrproject-rtos#725
@nmunnich nmunnich force-pushed the fix-subdirectory-westcommands branch from dbf1a5d to 30f879b Compare February 25, 2026 17:12
marc-hb added a commit to marc-hb/west that referenced this pull request Feb 26, 2026
Detect any unexpected changes in the way we've been handling
backslashes and multiple slashes in paths. Changes in how we handle
such edge cases may or may not be desired (and this test may be
updated accordingly), but we never want these changes to come as a
surprise and we want to keep control over them.

This came up as part of the review for
zephyrproject-rtos#920 which fixes
zephyrproject-rtos#725
marc-hb added a commit to marc-hb/west that referenced this pull request Feb 26, 2026
Detect any unexpected changes in the way we've been handling
backslashes and multiple slashes in paths. Changes in how we handle
such edge cases may or may not be desired (and this test may be
updated accordingly), but we never want these changes to come as a
surprise and we want to keep control over them.

This came up as part of the review for
zephyrproject-rtos#920 which fixes
zephyrproject-rtos#725
marc-hb added a commit to marc-hb/west that referenced this pull request Mar 3, 2026
Detect any unexpected changes in the way we've been handling
backslashes and multiple slashes in paths. Changes in how we handle
such edge cases may or may not be desired (and this test may be
updated accordingly), but we never want these changes to come as a
surprise and we want to keep control over them.

This came up as part of the review for
zephyrproject-rtos#920 which fixes
zephyrproject-rtos#725
marc-hb added a commit to marc-hb/west that referenced this pull request Mar 3, 2026
Add some test coverage for forward and backslashes + whitespace in
filenames for west extensions

Spurred by a discussion in the review of
zephyrproject-rtos#920 which fixes
zephyrproject-rtos#725
submodules=self._load_submodules(pd.get('submodules'), f'project {name}'),
clone_depth=pd.get('clone-depth'),
west_commands=pd.get('west-commands'),
west_commands=Path(pd.get('west-commands')).as_posix() if pd.get('west-commands') else None,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This normalizes west-commands: values for projects but not for self which seems to come from somewhere else :-(

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we go for west-commands normalization, then maybe just change the type to a Path across the board which can help catch inconsistencies.

marc-hb added a commit to marc-hb/west that referenced this pull request Mar 3, 2026
Add some test coverage for forward and backslashes + whitespace in
filenames for west extensions

Spurred by a discussion in the review of
zephyrproject-rtos#920 which fixes
zephyrproject-rtos#725
marc-hb added a commit to marc-hb/west that referenced this pull request Mar 3, 2026
Add some test coverage for forward and backslashes + whitespace in
filenames for west extensions

Spurred by a discussion in the review of
zephyrproject-rtos#920 which fixes
zephyrproject-rtos#725
@marc-hb
Copy link
Collaborator

marc-hb commented Mar 3, 2026

As discussed previously, this PR breaks my new tests submitted in #928. It's probably OK and these tests in #928 can likely be adjusted but it needs to be discussed because it's still a technically backwards-incompatible change. The easiest way to discuss and to record that behavior change is to merge #928 to fill the test coverage gap :-)

@marc-hb
Copy link
Collaborator

marc-hb commented Mar 3, 2026

One more thought: this needs to be split into at least 2 commits, maybe 2 PRs:

  1. the west-commands Path normalization
  2. the prefix bug fix

marc-hb added a commit to marc-hb/west that referenced this pull request Mar 4, 2026
Detect any unexpected changes in the way we've been handling
backslashes and multiple slashes in paths. Changes in how we handle
such edge cases may or may not be desired (and this test may be
updated accordingly), but we never want these changes to come as a
surprise and we want to keep control over them.

This came up as part of the review for
zephyrproject-rtos#920 which fixes
zephyrproject-rtos#725

Signed-off-by: Marc Herbert <[email protected]>
marc-hb added a commit to marc-hb/west that referenced this pull request Mar 4, 2026
Add some test coverage for forward and backslashes + whitespace in
filenames for west extensions

Spurred by a discussion in the review of
zephyrproject-rtos#920 which fixes
zephyrproject-rtos#725

Signed-off-by: Marc Herbert <[email protected]>
marc-hb added a commit to marc-hb/west that referenced this pull request Mar 4, 2026
Detect any unexpected changes in the way we've been handling
backslashes and multiple slashes in paths. Changes in how we handle
such edge cases may or may not be desired (and this test may be
updated accordingly), but we never want these changes to come as a
surprise and we want to keep control over them.

This came up as part of the review for
zephyrproject-rtos#920 which fixes
zephyrproject-rtos#725

Signed-off-by: Marc Herbert <[email protected]>
marc-hb added a commit to marc-hb/west that referenced this pull request Mar 4, 2026
Add some test coverage for forward and backslashes + whitespace in
filenames for west extensions

Spurred by a discussion in the review of
zephyrproject-rtos#920 which fixes
zephyrproject-rtos#725

Signed-off-by: Marc Herbert <[email protected]>
marc-hb added a commit to marc-hb/west that referenced this pull request Mar 4, 2026
Detect any unexpected changes in the way we've been handling
backslashes and multiple slashes in paths. Changes in how we handle
such edge cases may or may not be desired (and this test may be
updated accordingly), but we never want these changes to come as a
surprise and we want to keep control over them.

This came up as part of the review for
zephyrproject-rtos#920 which fixes
zephyrproject-rtos#725

Signed-off-by: Marc Herbert <[email protected]>
marc-hb added a commit to marc-hb/west that referenced this pull request Mar 4, 2026
Add some test coverage for forward and backslashes + whitespace in
filenames for west extensions

Spurred by a discussion in the review of
zephyrproject-rtos#920 which fixes
zephyrproject-rtos#725

Signed-off-by: Marc Herbert <[email protected]>
@nmunnich
Copy link
Author

nmunnich commented Mar 4, 2026

Just an fyi, I think it is unlikely that I'll find the time to continue on this until next month. If it's still open in April I'll have another go, but I'm encouraging anyone who has the time to continue this sooner.

marc-hb added a commit to marc-hb/west that referenced this pull request Mar 5, 2026
Detect any unexpected changes in the way we've been handling
backslashes and multiple slashes in paths. Changes in how we handle
such edge cases may or may not be desired (and this test may be
updated accordingly), but we never want these changes to come as a
surprise and we want to keep control over them.

This came up as part of the review for
zephyrproject-rtos#920 which fixes
zephyrproject-rtos#725

Signed-off-by: Marc Herbert <[email protected]>
marc-hb added a commit to marc-hb/west that referenced this pull request Mar 5, 2026
Add some test coverage for forward and backslashes + whitespace in
filenames for west extensions

Spurred by a discussion in the review of
zephyrproject-rtos#920 which fixes
zephyrproject-rtos#725

Signed-off-by: Marc Herbert <[email protected]>
marc-hb added a commit to marc-hb/west that referenced this pull request Mar 6, 2026
Detect any unexpected changes in the way we've been handling
backslashes and multiple slashes in paths. Changes in how we handle
such edge cases may or may not be desired (and this test may be
updated accordingly), but we never want these changes to come as a
surprise and we want to keep control over them.

This came up as part of the review for
zephyrproject-rtos#920 which fixes
zephyrproject-rtos#725

Signed-off-by: Marc Herbert <[email protected]>
marc-hb added a commit to marc-hb/west that referenced this pull request Mar 6, 2026
Add some test coverage for forward and backslashes + whitespace in
filenames for west extensions

Spurred by a discussion in the review of
zephyrproject-rtos#920 which fixes
zephyrproject-rtos#725

Signed-off-by: Marc Herbert <[email protected]>
@marc-hb
Copy link
Collaborator

marc-hb commented Mar 19, 2026

One more thought: this needs to be split into at least 2 commits, maybe 2 PRs:

  1. the west-commands Path normalization
  2. the prefix bug fix

I can do 1. after #928 is merged. I can probably rebase 2. as well, should be trivial enough (famous last words)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Imported west-commands don't resolve when module manifest isn't in module root

4 participants