pixi-ros 0.1.2__tar.gz → 0.3.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (52) hide show
  1. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/.github/workflows/ci.yml +2 -0
  2. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/PKG-INFO +123 -7
  3. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/README.md +122 -6
  4. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/pixi.lock +1 -1
  5. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/pyproject.toml +1 -1
  6. pixi_ros-0.3.0/src/pixi_ros/cli.py +134 -0
  7. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/src/pixi_ros/data/conda-forge.yaml +2 -0
  8. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/src/pixi_ros/init.py +337 -67
  9. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/src/pixi_ros/mappings.py +39 -0
  10. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/src/pixi_ros/package_xml.py +69 -8
  11. pixi_ros-0.3.0/tests/examples/ws1/README_PIXI.md +125 -0
  12. pixi_ros-0.3.0/tests/examples/ws1/pixi.toml +38 -0
  13. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/tests/examples/ws1/src/package-a/package.xml +2 -2
  14. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/tests/examples/ws1/src/package-b/package.xml +3 -0
  15. pixi_ros-0.3.0/tests/test_init.py +558 -0
  16. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/tests/test_package_xml.py +92 -0
  17. pixi_ros-0.1.2/src/pixi_ros/cli.py +0 -77
  18. pixi_ros-0.1.2/tests/examples/ws1/pixi.toml +0 -39
  19. pixi_ros-0.1.2/tests/test_init.py +0 -144
  20. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/.gitattributes +0 -0
  21. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/.github/workflows/publish-pypi.yml +0 -0
  22. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/.gitignore +0 -0
  23. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/LICENSE +0 -0
  24. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/pixi.toml +0 -0
  25. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/src/pixi_ros/__init__.py +0 -0
  26. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/src/pixi_ros/config.py +0 -0
  27. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/src/pixi_ros/data/README.md +0 -0
  28. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/src/pixi_ros/data/README_PIXI.md.template +0 -0
  29. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/src/pixi_ros/utils.py +0 -0
  30. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/src/pixi_ros/workspace.py +0 -0
  31. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/tests/examples/ws1/pixi.lock +0 -0
  32. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/tests/examples/ws1/src/package-a/CMakeLists.txt +0 -0
  33. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/tests/examples/ws1/src/package-a/LICENSE +0 -0
  34. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/tests/examples/ws1/src/package-b/package-b/__init__.py +0 -0
  35. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/tests/examples/ws1/src/package-b/setup.cfg +0 -0
  36. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/tests/examples/ws1/src/package-b/setup.py +0 -0
  37. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/tests/examples/ws1/src/package-b/test/test_copyright.py +0 -0
  38. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/tests/examples/ws1/src/package-b/test/test_flake8.py +0 -0
  39. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/tests/examples/ws1/src/package-b/test/test_pep257.py +0 -0
  40. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/tests/fixtures/mock_workspace/README.md +0 -0
  41. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/tests/fixtures/mock_workspace/src/legacy_pkg/package.xml +0 -0
  42. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/tests/fixtures/mock_workspace/src/my_cpp_pkg/CMakeLists.txt +0 -0
  43. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/tests/fixtures/mock_workspace/src/my_cpp_pkg/package.xml +0 -0
  44. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/tests/fixtures/mock_workspace/src/my_mixed_pkg/package.xml +0 -0
  45. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/tests/fixtures/mock_workspace/src/my_python_pkg/package.xml +0 -0
  46. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/tests/fixtures/mock_workspace/src/my_python_pkg/setup.py +0 -0
  47. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/tests/test_cli.py +0 -0
  48. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/tests/test_config.py +0 -0
  49. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/tests/test_gateway_availability.py +0 -0
  50. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/tests/test_mappings.py +0 -0
  51. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/tests/test_utils.py +0 -0
  52. {pixi_ros-0.1.2 → pixi_ros-0.3.0}/tests/test_workspace.py +0 -0
@@ -1,3 +1,5 @@
1
+ name: CI
2
+
1
3
  on:
2
4
  push:
3
5
  pull_request:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pixi-ros
3
- Version: 0.1.2
3
+ Version: 0.3.0
4
4
  Summary: Pixi extension for ROS package management
5
5
  Project-URL: Homepage, https://github.com/ruben-arts/pixi-ros
6
6
  Project-URL: Repository, https://github.com/ruben-arts/pixi-ros
@@ -90,13 +90,16 @@ pixi shell
90
90
 
91
91
  ### Dependency Mapping
92
92
 
93
- `pixi-ros` reads all dependency types from `package.xml` files.
93
+ `pixi-ros` reads all dependency types from `package.xml` files.
94
94
  It then does a best effort mapping of ROS package names to conda packages.
95
95
 
96
96
  - **ROS packages**: `ros-{distro}-{package}` from robostack channels (e.g., `ros-humble-rclcpp`)
97
97
  - **System packages**: Mapped to conda-forge equivalents (e.g., `cmake`, `eigen`)
98
+ - **Platform-specific packages**: Different mappings per platform (e.g., OpenGL → `libgl-devel` on Linux, X11 packages on macOS)
98
99
 
99
- After the mapping, it validates package availability in the configured channels. This starts a connection with `https://prefix.dev` to check if packages exist.
100
+ The mapping rules are defined in YAML files (see `src/pixi_ros/data/conda-forge.yaml`) and can be customized by placing your own mapping files in `pixi-ros/*.yaml` or `~/.pixi-ros/*.yaml`.
101
+
102
+ After the mapping, it validates package availability in the configured channels for each target platform. This starts a connection with `https://prefix.dev` to check if packages exist.
100
103
 
101
104
  ### Example
102
105
 
@@ -117,6 +120,41 @@ ros-humble-rclcpp = "*"
117
120
  ros-humble-std-msgs = "*"
118
121
  ```
119
122
 
123
+ ### Version Constraints
124
+
125
+ `pixi-ros` supports version constraints from `package.xml` files and automatically applies them to the generated `pixi.toml`.
126
+
127
+ #### Supported Version Attributes
128
+
129
+ You can specify version requirements in your `package.xml` using standard ROS version attributes:
130
+
131
+ | package.xml attribute | pixi.toml constraint | Description |
132
+ |----------------------|----------------------|-------------|
133
+ | `version_eq="X.Y.Z"` | `==X.Y.Z` | Exactly version X.Y.Z |
134
+ | `version_gte="X.Y.Z"` | `>=X.Y.Z` | Version X.Y.Z or newer |
135
+ | `version_gt="X.Y.Z"` | `>X.Y.Z` | Newer than version X.Y.Z |
136
+ | `version_lte="X.Y.Z"` | `<=X.Y.Z` | Version X.Y.Z or older |
137
+ | `version_lt="X.Y.Z"` | `<X.Y.Z` | Older than version X.Y.Z |
138
+
139
+ Multiple constraints can be combined on the same dependency and will be joined with commas in the output.
140
+
141
+ Given a `package.xml` with version constraints:
142
+
143
+ ```xml
144
+ <depend version_gte="3.12.4">cmake</depend>
145
+ <build_depend version_gte="3.3.0" version_lt="4.0.0">eigen</build_depend>
146
+ <exec_depend version_eq="1.2.3">boost</exec_depend>
147
+ ```
148
+
149
+ `pixi-ros init` generates:
150
+
151
+ ```toml
152
+ [dependencies]
153
+ cmake = ">=3.12.4"
154
+ eigen = ">=3.3.0,<4.0.0"
155
+ boost = "==1.2.3"
156
+ ```
157
+
120
158
  ## Supported ROS Distributions
121
159
 
122
160
  - ROS 2 Humble: https://prefix.dev/robostack-humble
@@ -132,24 +170,102 @@ Initialize or update a ROS workspace's `pixi.toml`.
132
170
 
133
171
  ```bash
134
172
  pixi-ros init --distro <ros_distro>
173
+ pixi-ros init --distro humble --platform linux-64 --platform osx-arm64
135
174
  pixi-ros init
136
175
  ```
137
176
 
138
177
  **Options:**
139
- - `--distro`, `-d`: ROS distribution (optional)
178
+ - `--distro`, `-d`: ROS distribution (optional, will prompt if not provided)
179
+ - `--platform`, `-p`: Target platforms (optional, can be specified multiple times, will prompt if not provided)
180
+ - Available: `linux-64`, `osx-64`, `osx-arm64`, `win-64`
181
+ - Platforms come from the mapping files and determine which dependencies are available
140
182
 
141
183
  **What it does:**
142
184
  - Scans workspace for `package.xml` files
143
- - Reads all dependency types (build, exec, test)
144
- - Maps ROS dependencies to conda packages
185
+ - Reads all dependency types (build, exec, test) and version constraints
186
+ - Maps ROS dependencies to conda packages for each platform
187
+ - Applies version constraints from package.xml to pixi.toml dependencies
145
188
  - Configures robostack channels
146
- - Checks package availability
189
+ - Checks package availability per platform
147
190
  - Creates build tasks using colcon
148
191
  - Generates helpful `README_PIXI.md`
192
+ - Sets up platform-specific dependencies in `pixi.toml`
149
193
 
150
194
  **Running multiple times:**
151
195
  The command is idempotent - you can run it multiple times to update dependencies as your workspace changes.
152
196
 
197
+ ## Multi-Platform Support
198
+
199
+ `pixi-ros` supports generating cross-platform configurations. When you specify multiple platforms, it:
200
+
201
+ 1. **Analyzes dependencies per platform**: Some packages have platform-specific mappings (e.g., OpenGL requirements differ between Linux and macOS)
202
+
203
+ 2. **Organizes dependencies intelligently**:
204
+ - **Common dependencies** (available on all platforms) → `[dependencies]`
205
+ - **Unix dependencies** (available on Linux and macOS, but not Windows) → `[target.unix.dependencies]`
206
+ - **Platform-specific dependencies** → `[target.linux.dependencies]`, `[target.osx.dependencies]`, etc.
207
+
208
+ 3. **Sets up correct platform list**: The `[workspace]` section gets the appropriate pixi platform names
209
+
210
+ ### Platform Naming
211
+
212
+ pixi-ros uses standard pixi platform names:
213
+ - `linux-64` - Linux x86_64
214
+ - `osx-64` - macOS Intel
215
+ - `osx-arm64` - macOS Apple Silicon (M1/M2/M3)
216
+ - `win-64` - Windows x86_64
217
+
218
+ Internally, mapping files use a simplified format (`linux`, `osx`, `win64`), but this is transparent to users. When you specify `osx-64` and `osx-arm64`, they both use the same `osx` mapping rules since package availability is typically the same for both architectures.
219
+
220
+ ### Example: Multi-Platform Setup
221
+
222
+ ```bash
223
+ pixi-ros init --distro humble --platform linux-64 --platform osx-arm64
224
+ ```
225
+
226
+ Generates:
227
+
228
+ ```toml
229
+ [workspace]
230
+ name = "my_workspace"
231
+ channels = [
232
+ "https://prefix.dev/robostack-humble",
233
+ "https://prefix.dev/conda-forge",
234
+ ]
235
+ platforms = ["linux-64", "osx-arm64"]
236
+
237
+ [dependencies]
238
+ # Common dependencies (available on all platforms)
239
+ ros-humble-rclcpp = "*"
240
+ ros-humble-std-msgs = "*"
241
+
242
+ [target.unix.dependencies]
243
+ # Unix-specific dependencies (Linux and macOS)
244
+ xorg-libx11 = "*"
245
+ xorg-libxext = "*"
246
+
247
+ [target.linux.dependencies]
248
+ # Linux-specific dependencies
249
+ libgl-devel = "*"
250
+ libopengl-devel = "*"
251
+ ```
252
+
253
+ ### Interactive Platform Selection
254
+
255
+ If you don't specify platforms, you'll be prompted:
256
+
257
+ ```bash
258
+ $ pixi-ros init --distro humble
259
+
260
+ Available target platforms:
261
+ 1. linux-64
262
+ 2. osx-64
263
+ 3. osx-arm64
264
+ 4. win-64
265
+
266
+ Select platforms (enter numbers or names, comma or space separated): 1 3
267
+ ```
268
+
153
269
  ## Philosophy
154
270
 
155
271
  `pixi-ros` aims to be a quick **gateway drug**. It:
@@ -71,13 +71,16 @@ pixi shell
71
71
 
72
72
  ### Dependency Mapping
73
73
 
74
- `pixi-ros` reads all dependency types from `package.xml` files.
74
+ `pixi-ros` reads all dependency types from `package.xml` files.
75
75
  It then does a best effort mapping of ROS package names to conda packages.
76
76
 
77
77
  - **ROS packages**: `ros-{distro}-{package}` from robostack channels (e.g., `ros-humble-rclcpp`)
78
78
  - **System packages**: Mapped to conda-forge equivalents (e.g., `cmake`, `eigen`)
79
+ - **Platform-specific packages**: Different mappings per platform (e.g., OpenGL → `libgl-devel` on Linux, X11 packages on macOS)
79
80
 
80
- After the mapping, it validates package availability in the configured channels. This starts a connection with `https://prefix.dev` to check if packages exist.
81
+ The mapping rules are defined in YAML files (see `src/pixi_ros/data/conda-forge.yaml`) and can be customized by placing your own mapping files in `pixi-ros/*.yaml` or `~/.pixi-ros/*.yaml`.
82
+
83
+ After the mapping, it validates package availability in the configured channels for each target platform. This starts a connection with `https://prefix.dev` to check if packages exist.
81
84
 
82
85
  ### Example
83
86
 
@@ -98,6 +101,41 @@ ros-humble-rclcpp = "*"
98
101
  ros-humble-std-msgs = "*"
99
102
  ```
100
103
 
104
+ ### Version Constraints
105
+
106
+ `pixi-ros` supports version constraints from `package.xml` files and automatically applies them to the generated `pixi.toml`.
107
+
108
+ #### Supported Version Attributes
109
+
110
+ You can specify version requirements in your `package.xml` using standard ROS version attributes:
111
+
112
+ | package.xml attribute | pixi.toml constraint | Description |
113
+ |----------------------|----------------------|-------------|
114
+ | `version_eq="X.Y.Z"` | `==X.Y.Z` | Exactly version X.Y.Z |
115
+ | `version_gte="X.Y.Z"` | `>=X.Y.Z` | Version X.Y.Z or newer |
116
+ | `version_gt="X.Y.Z"` | `>X.Y.Z` | Newer than version X.Y.Z |
117
+ | `version_lte="X.Y.Z"` | `<=X.Y.Z` | Version X.Y.Z or older |
118
+ | `version_lt="X.Y.Z"` | `<X.Y.Z` | Older than version X.Y.Z |
119
+
120
+ Multiple constraints can be combined on the same dependency and will be joined with commas in the output.
121
+
122
+ Given a `package.xml` with version constraints:
123
+
124
+ ```xml
125
+ <depend version_gte="3.12.4">cmake</depend>
126
+ <build_depend version_gte="3.3.0" version_lt="4.0.0">eigen</build_depend>
127
+ <exec_depend version_eq="1.2.3">boost</exec_depend>
128
+ ```
129
+
130
+ `pixi-ros init` generates:
131
+
132
+ ```toml
133
+ [dependencies]
134
+ cmake = ">=3.12.4"
135
+ eigen = ">=3.3.0,<4.0.0"
136
+ boost = "==1.2.3"
137
+ ```
138
+
101
139
  ## Supported ROS Distributions
102
140
 
103
141
  - ROS 2 Humble: https://prefix.dev/robostack-humble
@@ -113,24 +151,102 @@ Initialize or update a ROS workspace's `pixi.toml`.
113
151
 
114
152
  ```bash
115
153
  pixi-ros init --distro <ros_distro>
154
+ pixi-ros init --distro humble --platform linux-64 --platform osx-arm64
116
155
  pixi-ros init
117
156
  ```
118
157
 
119
158
  **Options:**
120
- - `--distro`, `-d`: ROS distribution (optional)
159
+ - `--distro`, `-d`: ROS distribution (optional, will prompt if not provided)
160
+ - `--platform`, `-p`: Target platforms (optional, can be specified multiple times, will prompt if not provided)
161
+ - Available: `linux-64`, `osx-64`, `osx-arm64`, `win-64`
162
+ - Platforms come from the mapping files and determine which dependencies are available
121
163
 
122
164
  **What it does:**
123
165
  - Scans workspace for `package.xml` files
124
- - Reads all dependency types (build, exec, test)
125
- - Maps ROS dependencies to conda packages
166
+ - Reads all dependency types (build, exec, test) and version constraints
167
+ - Maps ROS dependencies to conda packages for each platform
168
+ - Applies version constraints from package.xml to pixi.toml dependencies
126
169
  - Configures robostack channels
127
- - Checks package availability
170
+ - Checks package availability per platform
128
171
  - Creates build tasks using colcon
129
172
  - Generates helpful `README_PIXI.md`
173
+ - Sets up platform-specific dependencies in `pixi.toml`
130
174
 
131
175
  **Running multiple times:**
132
176
  The command is idempotent - you can run it multiple times to update dependencies as your workspace changes.
133
177
 
178
+ ## Multi-Platform Support
179
+
180
+ `pixi-ros` supports generating cross-platform configurations. When you specify multiple platforms, it:
181
+
182
+ 1. **Analyzes dependencies per platform**: Some packages have platform-specific mappings (e.g., OpenGL requirements differ between Linux and macOS)
183
+
184
+ 2. **Organizes dependencies intelligently**:
185
+ - **Common dependencies** (available on all platforms) → `[dependencies]`
186
+ - **Unix dependencies** (available on Linux and macOS, but not Windows) → `[target.unix.dependencies]`
187
+ - **Platform-specific dependencies** → `[target.linux.dependencies]`, `[target.osx.dependencies]`, etc.
188
+
189
+ 3. **Sets up correct platform list**: The `[workspace]` section gets the appropriate pixi platform names
190
+
191
+ ### Platform Naming
192
+
193
+ pixi-ros uses standard pixi platform names:
194
+ - `linux-64` - Linux x86_64
195
+ - `osx-64` - macOS Intel
196
+ - `osx-arm64` - macOS Apple Silicon (M1/M2/M3)
197
+ - `win-64` - Windows x86_64
198
+
199
+ Internally, mapping files use a simplified format (`linux`, `osx`, `win64`), but this is transparent to users. When you specify `osx-64` and `osx-arm64`, they both use the same `osx` mapping rules since package availability is typically the same for both architectures.
200
+
201
+ ### Example: Multi-Platform Setup
202
+
203
+ ```bash
204
+ pixi-ros init --distro humble --platform linux-64 --platform osx-arm64
205
+ ```
206
+
207
+ Generates:
208
+
209
+ ```toml
210
+ [workspace]
211
+ name = "my_workspace"
212
+ channels = [
213
+ "https://prefix.dev/robostack-humble",
214
+ "https://prefix.dev/conda-forge",
215
+ ]
216
+ platforms = ["linux-64", "osx-arm64"]
217
+
218
+ [dependencies]
219
+ # Common dependencies (available on all platforms)
220
+ ros-humble-rclcpp = "*"
221
+ ros-humble-std-msgs = "*"
222
+
223
+ [target.unix.dependencies]
224
+ # Unix-specific dependencies (Linux and macOS)
225
+ xorg-libx11 = "*"
226
+ xorg-libxext = "*"
227
+
228
+ [target.linux.dependencies]
229
+ # Linux-specific dependencies
230
+ libgl-devel = "*"
231
+ libopengl-devel = "*"
232
+ ```
233
+
234
+ ### Interactive Platform Selection
235
+
236
+ If you don't specify platforms, you'll be prompted:
237
+
238
+ ```bash
239
+ $ pixi-ros init --distro humble
240
+
241
+ Available target platforms:
242
+ 1. linux-64
243
+ 2. osx-64
244
+ 3. osx-arm64
245
+ 4. win-64
246
+
247
+ Select platforms (enter numbers or names, comma or space separated): 1 3
248
+ ```
249
+
134
250
  ## Philosophy
135
251
 
136
252
  `pixi-ros` aims to be a quick **gateway drug**. It:
@@ -1073,7 +1073,7 @@ packages:
1073
1073
  timestamp: 1769677743677
1074
1074
  - conda: .
1075
1075
  name: pixi-ros
1076
- version: 0.1.2
1076
+ version: 0.3.0
1077
1077
  build: pyh4616a5c_0
1078
1078
  subdir: noarch
1079
1079
  variants:
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "pixi-ros"
7
- version = "0.1.2"
7
+ version = "0.3.0"
8
8
  description = "Pixi extension for ROS package management"
9
9
  authors = [
10
10
  { name = "Ruben Arts", email = "ruben@prefix.dev" }
@@ -0,0 +1,134 @@
1
+ """Main CLI entry point for pixi-ros."""
2
+
3
+ from typing import Annotated
4
+
5
+ import typer
6
+
7
+ from pixi_ros.init import init_workspace
8
+ from pixi_ros.mappings import get_platforms, get_ros_distros
9
+
10
+ app = typer.Typer(
11
+ name="pixi-ros",
12
+ help="Pixi extension for ROS package management",
13
+ no_args_is_help=True,
14
+ )
15
+
16
+ pkg_app = typer.Typer(help="Manage ROS packages")
17
+ app.add_typer(pkg_app, name="pkg")
18
+
19
+
20
+ @app.command()
21
+ def init(
22
+ distro: Annotated[
23
+ str | None,
24
+ typer.Option(
25
+ "--distro",
26
+ "-d",
27
+ help="ROS distribution (e.g., humble, iron, jazzy)",
28
+ ),
29
+ ] = None,
30
+ platforms: Annotated[
31
+ list[str] | None,
32
+ typer.Option(
33
+ "--platform",
34
+ "-p",
35
+ help="Target platforms (e.g., linux-64, osx-arm64, win-64)."
36
+ " Can be specified multiple times.",
37
+ ),
38
+ ] = None,
39
+ ):
40
+ """Initialize pixi.toml for a ROS workspace."""
41
+ # If distro not provided, prompt user to select one
42
+ if distro is None:
43
+ available_distros = get_ros_distros()
44
+ typer.echo("Available ROS distributions:")
45
+ for i, d in enumerate(available_distros, 1):
46
+ typer.echo(f" {i}. {d}")
47
+
48
+ # Prompt for selection
49
+ selection = typer.prompt(
50
+ "\nSelect a distribution (enter number or name)",
51
+ type=str,
52
+ )
53
+
54
+ # Parse selection (either number or name)
55
+ try:
56
+ selection_num = int(selection)
57
+ dist_count = len(available_distros)
58
+ if 1 <= selection_num <= dist_count:
59
+ distro = available_distros[selection_num - 1]
60
+ else:
61
+ typer.echo(
62
+ f"Error: Invalid selection. Please choose 1-{dist_count}",
63
+ err=True,
64
+ )
65
+ raise typer.Exit(code=1)
66
+ except ValueError as err:
67
+ # User entered a name instead of number
68
+ if selection in available_distros:
69
+ distro = selection
70
+ else:
71
+ typer.echo(
72
+ f"Error: '{selection}' is not a valid ROS distribution", err=True
73
+ )
74
+ typer.echo(f"Available: {', '.join(available_distros)}", err=True)
75
+ raise typer.Exit(code=1) from err
76
+
77
+ # If platforms not provided, prompt user to select
78
+ if platforms is None or len(platforms) == 0:
79
+ available_platforms = get_platforms()
80
+ typer.echo("\nAvailable target platforms:")
81
+ for i, p in enumerate(available_platforms, 1):
82
+ typer.echo(f" {i}. {p}")
83
+
84
+ # Prompt for selection (can be comma-separated or space-separated)
85
+ selection = typer.prompt(
86
+ "\nSelect platforms (enter numbers or names, comma or space separated)",
87
+ type=str,
88
+ )
89
+
90
+ # Parse selection (can be numbers or names, comma or space separated)
91
+ platforms = []
92
+ # Split by comma or space
93
+ selections = selection.replace(",", " ").split()
94
+
95
+ for sel in selections:
96
+ sel = sel.strip()
97
+ if not sel:
98
+ continue
99
+
100
+ try:
101
+ # Try parsing as number
102
+ sel_num = int(sel)
103
+ if 1 <= sel_num <= len(available_platforms):
104
+ platforms.append(available_platforms[sel_num - 1])
105
+ else:
106
+ typer.echo(
107
+ f"Error: Invalid selection {sel_num}."
108
+ + f"Please choose 1-{len(available_platforms)}",
109
+ err=True,
110
+ )
111
+ raise typer.Exit(code=1)
112
+ except ValueError as err:
113
+ # User entered a name instead of number
114
+ if sel in available_platforms:
115
+ platforms.append(sel)
116
+ else:
117
+ typer.echo(f"Error: '{sel}' is not a valid platform", err=True)
118
+ typer.echo(f"Available: {', '.join(available_platforms)}", err=True)
119
+ raise typer.Exit(code=1) from err
120
+
121
+ if not platforms:
122
+ typer.echo("Error: No platforms selected", err=True)
123
+ raise typer.Exit(code=1)
124
+
125
+ init_workspace(distro, platforms=platforms)
126
+
127
+
128
+ def main():
129
+ """Entry point for the CLI."""
130
+ app()
131
+
132
+
133
+ if __name__ == "__main__":
134
+ main()
@@ -792,6 +792,8 @@ python3-importlib-resources:
792
792
  pixi: [importlib_resources]
793
793
  python3-jinja2:
794
794
  pixi: [jinja2]
795
+ python3-jsonschema:
796
+ pixi: [jsonschema]
795
797
  python3-kitchen:
796
798
  pixi: [kitchen]
797
799
  python3-lark-parser: