flutter-dev 0.1.5__tar.gz → 0.1.6__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.
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/CHANGELOG.md +15 -0
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/PKG-INFO +24 -5
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/README.md +23 -4
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/fdev.py +14 -0
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/flutter_dev.egg-info/SOURCES.txt +3 -1
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/managers/gui.py +8 -3
- flutter_dev-0.1.6/managers/package.py +148 -0
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/pyproject.toml +1 -1
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/tests/test_fdev_cli.py +22 -0
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/tests/test_gui.py +38 -0
- flutter_dev-0.1.6/tests/test_package_commands.py +129 -0
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/.env.example +0 -0
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/LICENSE +0 -0
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/MANIFEST.in +0 -0
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/common_utils.py +0 -0
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/core/__init__.py +0 -0
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/core/constants.py +0 -0
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/core/state.py +0 -0
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/create_page.py +0 -0
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/gemini_api.py +0 -0
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/git_diff_output_editor.py +0 -0
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/install_legacy.py +0 -0
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/managers/__init__.py +0 -0
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/managers/ai.py +0 -0
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/managers/app.py +0 -0
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/managers/brew.py +0 -0
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/managers/build.py +0 -0
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/managers/datetime.py +0 -0
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/managers/device.py +0 -0
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/managers/doctor.py +0 -0
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/managers/git.py +0 -0
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/managers/git_account.py +0 -0
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/managers/localization.py +0 -0
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/managers/merge.py +0 -0
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/managers/mirror.py +0 -0
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/managers/project.py +0 -0
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/managers/web_deploy.py +0 -0
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/requirements.txt +0 -0
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/setup.cfg +0 -0
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/setup.py +0 -0
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/switch_ai.py +0 -0
- {flutter_dev-0.1.5 → flutter_dev-0.1.6}/tests/test_localization.py +0 -0
|
@@ -2,6 +2,21 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project are documented in this file.
|
|
4
4
|
|
|
5
|
+
## 0.1.6 - 2026-06-25
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- Added `fdev upgrade` to update the local `flutter-dev` package from PyPI after detecting the current OS.
|
|
10
|
+
- Added `fdev delete` to uninstall the local `flutter-dev` package after detecting the current OS and confirming the destructive action.
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
- `fdev upgrade` and `fdev delete` now print the exact Python executable they target before running `sys.executable -m pip`.
|
|
15
|
+
|
|
16
|
+
### Fixed
|
|
17
|
+
|
|
18
|
+
- Reworked the Tkinter dashboard layout to avoid platform-sensitive paned-window sizing that could open a blank `fdev` popup after installing from PyPI.
|
|
19
|
+
|
|
5
20
|
## 0.1.5 - 2026-06-24
|
|
6
21
|
|
|
7
22
|
### Added
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: flutter-dev
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.6
|
|
4
4
|
Summary: A cross-platform CLI toolkit for Flutter development workflows.
|
|
5
5
|
Author: Royal Court BD
|
|
6
6
|
License-Expression: MIT
|
|
@@ -39,10 +39,10 @@ Dynamic: license-file
|
|
|
39
39
|
The package is published on PyPI as `flutter-dev` and installs console commands for day-to-day Flutter work:
|
|
40
40
|
|
|
41
41
|
```bash
|
|
42
|
-
pip install flutter-dev
|
|
42
|
+
python -m pip install flutter-dev
|
|
43
43
|
```
|
|
44
44
|
|
|
45
|
-
Current package metadata declares version `0.1.
|
|
45
|
+
Current package metadata declares version `0.1.6`, Python `>=3.8`, MIT license, and support for macOS, Linux, and Windows.
|
|
46
46
|
|
|
47
47
|
## Key Features
|
|
48
48
|
|
|
@@ -55,7 +55,7 @@ Current package metadata declares version `0.1.5`, Python `>=3.8`, MIT license,
|
|
|
55
55
|
- Feature/page generator that creates a `lib/features/<name>` structure using the project's presenter/base-state pattern.
|
|
56
56
|
- Small Tkinter desktop dashboard opened by running `fdev` with no command, with live colored command logs.
|
|
57
57
|
- Environment diagnostics through `fdev doctor`.
|
|
58
|
-
- Small utilities for Homebrew cleanup, file merging from `paths.txt`, AI provider switching, and git diff prompt export.
|
|
58
|
+
- Small utilities for Homebrew cleanup, file merging from `paths.txt`, AI provider switching, PyPI self-upgrade/delete, and git diff prompt export.
|
|
59
59
|
|
|
60
60
|
## Installation
|
|
61
61
|
|
|
@@ -71,7 +71,7 @@ Verify the CLI is available:
|
|
|
71
71
|
fdev doctor
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
-
`pip install flutter-dev` installs the Python package, Python dependencies, and console scripts
|
|
74
|
+
`python -m pip install flutter-dev` installs the Python package, Python dependencies, and console scripts into the Python environment selected by `python`. It does not install Flutter, Dart, Git, Android SDK tools, CocoaPods, Firebase CLI, GitHub CLI, `scrcpy`, Java/JDK, or VS Code CLI.
|
|
75
75
|
|
|
76
76
|
The desktop dashboard uses Python Tkinter. Some Python distributions install this separately; for Homebrew Python, install the matching package such as `brew install python-tk@3.14`.
|
|
77
77
|
|
|
@@ -135,6 +135,9 @@ fdev
|
|
|
135
135
|
# Show command help
|
|
136
136
|
fdev help
|
|
137
137
|
|
|
138
|
+
# Upgrade the fdev CLI from PyPI
|
|
139
|
+
fdev upgrade
|
|
140
|
+
|
|
138
141
|
# Check environment and optional tools
|
|
139
142
|
fdev doctor
|
|
140
143
|
|
|
@@ -239,6 +242,8 @@ Switching GitHub accounts runs `gh auth switch` and `gh auth setup-git`, but it
|
|
|
239
242
|
| `fdev` | Opens a small Tkinter desktop dashboard for running `fdev` commands with live colored logs and command input. The terminal process stays running while the window is open. |
|
|
240
243
|
| `fdev help` | Shows command help without opening the dashboard. |
|
|
241
244
|
| `fdev doctor` | Checks platform, Python, Flutter/Dart/Git, Python packages, Android/iOS tools, optional CLIs, `.env`, Flutter project detection, and global `fdev` availability. |
|
|
245
|
+
| `fdev upgrade` | Detects the OS, then upgrades the active Python environment's `flutter-dev` package from PyPI with `python -m pip install --upgrade flutter-dev`. |
|
|
246
|
+
| `fdev delete` | Detects the OS, asks for confirmation, then uninstalls the active Python environment's `flutter-dev` package. Use `fdev delete --yes` to skip confirmation. |
|
|
242
247
|
| `fdev ai` | Shows the current AI provider and opens an interactive provider switch prompt. |
|
|
243
248
|
| `fdev ai <provider>` | Switches `DEFAULT_AI_SERVICE` to `groq`, `mistral`, `sambanova`, or `openrouter`. |
|
|
244
249
|
| `fdev brew` | Interactive Homebrew package manager for viewing packages, uninstalling with cleanup, clearing cache, and global cleanup. |
|
|
@@ -368,6 +373,7 @@ managers/mirror.py scrcpy install, mirror, wireless ADB setup
|
|
|
368
373
|
managers/git.py commit, tag, discard, branch sync, deployment merge
|
|
369
374
|
managers/git_account.py GitHub account manager around gh CLI
|
|
370
375
|
managers/doctor.py Environment diagnostics
|
|
376
|
+
managers/package.py PyPI self-upgrade and local uninstall commands
|
|
371
377
|
managers/ai.py AI provider status and switching
|
|
372
378
|
managers/brew.py Interactive Homebrew manager
|
|
373
379
|
managers/merge.py paths.txt to path_merge_files.txt merger
|
|
@@ -386,6 +392,8 @@ The installed Python layout is top-level modules plus the `core` and `managers`
|
|
|
386
392
|
- `fdev cleanup` applies `dart fix`, formats the project, and runs a major-version dependency upgrade.
|
|
387
393
|
- `fdev clear-data` clears app data on Android after confirmation.
|
|
388
394
|
- `fdev uninstall` may fall back to foreground app detection outside a Flutter project root.
|
|
395
|
+
- `fdev upgrade` and `fdev delete` manage the local `flutter-dev` Python package, not the Flutter app on a device.
|
|
396
|
+
- `fdev delete` removes the `fdev` command from the current Python environment after confirmation; reinstall with `python -m pip install flutter-dev`.
|
|
389
397
|
- `fdev` with no command opens the desktop dashboard. Use `fdev help` to print command usage.
|
|
390
398
|
- Most command subcommands create build output directories at startup in the current working directory.
|
|
391
399
|
- Some subcommands return failure internally without always mapping that failure to a non-zero process exit code, so automation should inspect command output carefully.
|
|
@@ -414,6 +422,17 @@ On Windows, use:
|
|
|
414
422
|
where fdev
|
|
415
423
|
```
|
|
416
424
|
|
|
425
|
+
### Upgrade Or Remove fdev
|
|
426
|
+
|
|
427
|
+
Use the built-in package management commands from the Python environment where `fdev` is installed:
|
|
428
|
+
|
|
429
|
+
```bash
|
|
430
|
+
fdev upgrade
|
|
431
|
+
fdev delete
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
Both commands print the detected OS and exact Python executable before running. They use that executable with `-m pip`, not a hardcoded `python`, `python3`, `pip`, or `pip3`, so the active venv/global Python that launched `fdev` is the environment that gets upgraded or deleted. On Windows, `fdev delete` and `fdev upgrade` schedule the same `sys.executable -m pip ...` operation in a delayed command process so the currently running console launcher can exit first.
|
|
435
|
+
|
|
417
436
|
### Flutter Project Not Detected
|
|
418
437
|
|
|
419
438
|
Run project-specific commands from the directory that contains `pubspec.yaml`.
|
|
@@ -5,10 +5,10 @@
|
|
|
5
5
|
The package is published on PyPI as `flutter-dev` and installs console commands for day-to-day Flutter work:
|
|
6
6
|
|
|
7
7
|
```bash
|
|
8
|
-
pip install flutter-dev
|
|
8
|
+
python -m pip install flutter-dev
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
Current package metadata declares version `0.1.
|
|
11
|
+
Current package metadata declares version `0.1.6`, Python `>=3.8`, MIT license, and support for macOS, Linux, and Windows.
|
|
12
12
|
|
|
13
13
|
## Key Features
|
|
14
14
|
|
|
@@ -21,7 +21,7 @@ Current package metadata declares version `0.1.5`, Python `>=3.8`, MIT license,
|
|
|
21
21
|
- Feature/page generator that creates a `lib/features/<name>` structure using the project's presenter/base-state pattern.
|
|
22
22
|
- Small Tkinter desktop dashboard opened by running `fdev` with no command, with live colored command logs.
|
|
23
23
|
- Environment diagnostics through `fdev doctor`.
|
|
24
|
-
- Small utilities for Homebrew cleanup, file merging from `paths.txt`, AI provider switching, and git diff prompt export.
|
|
24
|
+
- Small utilities for Homebrew cleanup, file merging from `paths.txt`, AI provider switching, PyPI self-upgrade/delete, and git diff prompt export.
|
|
25
25
|
|
|
26
26
|
## Installation
|
|
27
27
|
|
|
@@ -37,7 +37,7 @@ Verify the CLI is available:
|
|
|
37
37
|
fdev doctor
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
`pip install flutter-dev` installs the Python package, Python dependencies, and console scripts
|
|
40
|
+
`python -m pip install flutter-dev` installs the Python package, Python dependencies, and console scripts into the Python environment selected by `python`. It does not install Flutter, Dart, Git, Android SDK tools, CocoaPods, Firebase CLI, GitHub CLI, `scrcpy`, Java/JDK, or VS Code CLI.
|
|
41
41
|
|
|
42
42
|
The desktop dashboard uses Python Tkinter. Some Python distributions install this separately; for Homebrew Python, install the matching package such as `brew install python-tk@3.14`.
|
|
43
43
|
|
|
@@ -101,6 +101,9 @@ fdev
|
|
|
101
101
|
# Show command help
|
|
102
102
|
fdev help
|
|
103
103
|
|
|
104
|
+
# Upgrade the fdev CLI from PyPI
|
|
105
|
+
fdev upgrade
|
|
106
|
+
|
|
104
107
|
# Check environment and optional tools
|
|
105
108
|
fdev doctor
|
|
106
109
|
|
|
@@ -205,6 +208,8 @@ Switching GitHub accounts runs `gh auth switch` and `gh auth setup-git`, but it
|
|
|
205
208
|
| `fdev` | Opens a small Tkinter desktop dashboard for running `fdev` commands with live colored logs and command input. The terminal process stays running while the window is open. |
|
|
206
209
|
| `fdev help` | Shows command help without opening the dashboard. |
|
|
207
210
|
| `fdev doctor` | Checks platform, Python, Flutter/Dart/Git, Python packages, Android/iOS tools, optional CLIs, `.env`, Flutter project detection, and global `fdev` availability. |
|
|
211
|
+
| `fdev upgrade` | Detects the OS, then upgrades the active Python environment's `flutter-dev` package from PyPI with `python -m pip install --upgrade flutter-dev`. |
|
|
212
|
+
| `fdev delete` | Detects the OS, asks for confirmation, then uninstalls the active Python environment's `flutter-dev` package. Use `fdev delete --yes` to skip confirmation. |
|
|
208
213
|
| `fdev ai` | Shows the current AI provider and opens an interactive provider switch prompt. |
|
|
209
214
|
| `fdev ai <provider>` | Switches `DEFAULT_AI_SERVICE` to `groq`, `mistral`, `sambanova`, or `openrouter`. |
|
|
210
215
|
| `fdev brew` | Interactive Homebrew package manager for viewing packages, uninstalling with cleanup, clearing cache, and global cleanup. |
|
|
@@ -334,6 +339,7 @@ managers/mirror.py scrcpy install, mirror, wireless ADB setup
|
|
|
334
339
|
managers/git.py commit, tag, discard, branch sync, deployment merge
|
|
335
340
|
managers/git_account.py GitHub account manager around gh CLI
|
|
336
341
|
managers/doctor.py Environment diagnostics
|
|
342
|
+
managers/package.py PyPI self-upgrade and local uninstall commands
|
|
337
343
|
managers/ai.py AI provider status and switching
|
|
338
344
|
managers/brew.py Interactive Homebrew manager
|
|
339
345
|
managers/merge.py paths.txt to path_merge_files.txt merger
|
|
@@ -352,6 +358,8 @@ The installed Python layout is top-level modules plus the `core` and `managers`
|
|
|
352
358
|
- `fdev cleanup` applies `dart fix`, formats the project, and runs a major-version dependency upgrade.
|
|
353
359
|
- `fdev clear-data` clears app data on Android after confirmation.
|
|
354
360
|
- `fdev uninstall` may fall back to foreground app detection outside a Flutter project root.
|
|
361
|
+
- `fdev upgrade` and `fdev delete` manage the local `flutter-dev` Python package, not the Flutter app on a device.
|
|
362
|
+
- `fdev delete` removes the `fdev` command from the current Python environment after confirmation; reinstall with `python -m pip install flutter-dev`.
|
|
355
363
|
- `fdev` with no command opens the desktop dashboard. Use `fdev help` to print command usage.
|
|
356
364
|
- Most command subcommands create build output directories at startup in the current working directory.
|
|
357
365
|
- Some subcommands return failure internally without always mapping that failure to a non-zero process exit code, so automation should inspect command output carefully.
|
|
@@ -380,6 +388,17 @@ On Windows, use:
|
|
|
380
388
|
where fdev
|
|
381
389
|
```
|
|
382
390
|
|
|
391
|
+
### Upgrade Or Remove fdev
|
|
392
|
+
|
|
393
|
+
Use the built-in package management commands from the Python environment where `fdev` is installed:
|
|
394
|
+
|
|
395
|
+
```bash
|
|
396
|
+
fdev upgrade
|
|
397
|
+
fdev delete
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
Both commands print the detected OS and exact Python executable before running. They use that executable with `-m pip`, not a hardcoded `python`, `python3`, `pip`, or `pip3`, so the active venv/global Python that launched `fdev` is the environment that gets upgraded or deleted. On Windows, `fdev delete` and `fdev upgrade` schedule the same `sys.executable -m pip ...` operation in a delayed command process so the currently running console launcher can exit first.
|
|
401
|
+
|
|
383
402
|
### Flutter Project Not Detected
|
|
384
403
|
|
|
385
404
|
Run project-specific commands from the directory that contains `pubspec.yaml`.
|
|
@@ -73,6 +73,10 @@ from managers.git_account import (
|
|
|
73
73
|
from managers.gui import (
|
|
74
74
|
launch_gui,
|
|
75
75
|
)
|
|
76
|
+
from managers.package import (
|
|
77
|
+
delete_fdev_command,
|
|
78
|
+
upgrade_fdev_command,
|
|
79
|
+
)
|
|
76
80
|
|
|
77
81
|
|
|
78
82
|
def show_usage(exit_code=1):
|
|
@@ -118,6 +122,8 @@ def show_usage(exit_code=1):
|
|
|
118
122
|
print(f"\n{BLUE}Utility Commands:{NC}")
|
|
119
123
|
print(" doctor Check environment health (tools, deps, config)")
|
|
120
124
|
print(" brew Interactive Homebrew package manager")
|
|
125
|
+
print(" upgrade Upgrade fdev from PyPI")
|
|
126
|
+
print(" delete Uninstall fdev from this computer")
|
|
121
127
|
print(" help Show this help")
|
|
122
128
|
print(" merge Merge files from paths.txt into single output file")
|
|
123
129
|
print(" ai Show current AI service or switch to another")
|
|
@@ -142,6 +148,8 @@ def show_usage(exit_code=1):
|
|
|
142
148
|
print(f" {GREEN}fdev git config{NC} # Update git user.name / user.email")
|
|
143
149
|
print(f" {GREEN}fdev arb-to-csv{NC} # Export lib/l10n ARB files to translations.csv")
|
|
144
150
|
print(f" {GREEN}fdev csv-to-arb{NC} # Generate lib/l10n ARB files from translations.csv")
|
|
151
|
+
print(f" {GREEN}fdev upgrade{NC} # Upgrade flutter-dev from PyPI")
|
|
152
|
+
print(f" {GREEN}fdev delete{NC} # Remove flutter-dev/fdev from this computer")
|
|
145
153
|
print(f" {GREEN}fdev{NC} # Open small desktop dashboard")
|
|
146
154
|
|
|
147
155
|
print(f"\n{BLUE}Note:{NC}")
|
|
@@ -259,6 +267,12 @@ def main():
|
|
|
259
267
|
elif command == "brew":
|
|
260
268
|
brew_manager()
|
|
261
269
|
|
|
270
|
+
# Package self-management commands
|
|
271
|
+
elif command == "upgrade":
|
|
272
|
+
sys.exit(upgrade_fdev_command(sys.argv[2:]))
|
|
273
|
+
elif command == "delete":
|
|
274
|
+
sys.exit(delete_fdev_command(sys.argv[2:]))
|
|
275
|
+
|
|
262
276
|
# AI command
|
|
263
277
|
elif command == "ai":
|
|
264
278
|
if len(sys.argv) < 3:
|
|
@@ -30,8 +30,10 @@ managers/gui.py
|
|
|
30
30
|
managers/localization.py
|
|
31
31
|
managers/merge.py
|
|
32
32
|
managers/mirror.py
|
|
33
|
+
managers/package.py
|
|
33
34
|
managers/project.py
|
|
34
35
|
managers/web_deploy.py
|
|
35
36
|
tests/test_fdev_cli.py
|
|
36
37
|
tests/test_gui.py
|
|
37
|
-
tests/test_localization.py
|
|
38
|
+
tests/test_localization.py
|
|
39
|
+
tests/test_package_commands.py
|
|
@@ -194,6 +194,8 @@ def get_gui_sections():
|
|
|
194
194
|
"Tools",
|
|
195
195
|
[
|
|
196
196
|
GuiCommand("Brew", ["brew"], "Open Homebrew package manager."),
|
|
197
|
+
GuiCommand("Upgrade fdev", ["upgrade"], "Upgrade flutter-dev from PyPI."),
|
|
198
|
+
GuiCommand("Delete fdev", ["delete"], "Uninstall flutter-dev from this computer.", confirm=True),
|
|
197
199
|
GuiCommand("Merge Files", ["merge"], "Merge paths listed in paths.txt."),
|
|
198
200
|
GuiCommand(
|
|
199
201
|
"AI Switch",
|
|
@@ -335,18 +337,21 @@ class FdevGui:
|
|
|
335
337
|
self.status_label = ttk.Label(header, text="Ready")
|
|
336
338
|
self.status_label.grid(row=0, column=1, sticky="e")
|
|
337
339
|
|
|
338
|
-
body = ttk.
|
|
340
|
+
body = ttk.Frame(content)
|
|
339
341
|
body.grid(row=1, column=0, sticky="nsew")
|
|
342
|
+
body.columnconfigure(0, weight=1)
|
|
343
|
+
body.rowconfigure(0, weight=0)
|
|
344
|
+
body.rowconfigure(1, weight=1)
|
|
340
345
|
|
|
341
346
|
self.command_frame = ttk.Frame(body, padding=(0, 0, 0, 6))
|
|
347
|
+
self.command_frame.grid(row=0, column=0, sticky="ew")
|
|
342
348
|
self.command_frame.columnconfigure(0, weight=1)
|
|
343
349
|
self.command_frame.columnconfigure(1, weight=1)
|
|
344
|
-
body.add(self.command_frame, weight=2)
|
|
345
350
|
|
|
346
351
|
log_frame = ttk.Frame(body)
|
|
352
|
+
log_frame.grid(row=1, column=0, sticky="nsew", pady=(6, 0))
|
|
347
353
|
log_frame.columnconfigure(0, weight=1)
|
|
348
354
|
log_frame.rowconfigure(0, weight=1)
|
|
349
|
-
body.add(log_frame, weight=3)
|
|
350
355
|
|
|
351
356
|
self.log_text = tk.Text(
|
|
352
357
|
log_frame,
|
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Package Manager - self-management commands for the installed fdev package.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import argparse
|
|
7
|
+
import platform
|
|
8
|
+
import subprocess
|
|
9
|
+
import sys
|
|
10
|
+
|
|
11
|
+
from common_utils import BLUE, GREEN, RED, YELLOW, NC
|
|
12
|
+
|
|
13
|
+
PACKAGE_NAME = "flutter-dev"
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def get_os_info():
|
|
17
|
+
"""
|
|
18
|
+
Detect the operating system before package self-management actions.
|
|
19
|
+
Returns a tuple of (key, display_name).
|
|
20
|
+
"""
|
|
21
|
+
system = platform.system()
|
|
22
|
+
|
|
23
|
+
if system == "Darwin":
|
|
24
|
+
return "macos", "macOS"
|
|
25
|
+
if system == "Linux":
|
|
26
|
+
return "linux", "Linux"
|
|
27
|
+
if system == "Windows":
|
|
28
|
+
return "windows", "Windows"
|
|
29
|
+
|
|
30
|
+
return "unknown", system or "Unknown"
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _print_os_info(os_name):
|
|
34
|
+
print(f"{BLUE}Detected OS: {GREEN}{os_name}{NC}")
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _print_python_environment():
|
|
38
|
+
print(f"{BLUE}Python environment: {GREEN}{sys.executable}{NC}")
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _build_pip_command(pip_args):
|
|
42
|
+
return [sys.executable, "-m", "pip", *pip_args]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _run_pip_direct(pip_args):
|
|
46
|
+
result = subprocess.run(_build_pip_command(pip_args))
|
|
47
|
+
return result.returncode
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _build_windows_detached_command(pip_args):
|
|
51
|
+
pip_command = subprocess.list2cmdline(_build_pip_command(pip_args))
|
|
52
|
+
delayed_command = f"timeout /t 2 /nobreak > nul & {pip_command}"
|
|
53
|
+
return [
|
|
54
|
+
"cmd",
|
|
55
|
+
"/c",
|
|
56
|
+
"start",
|
|
57
|
+
"fdev package command",
|
|
58
|
+
"cmd",
|
|
59
|
+
"/c",
|
|
60
|
+
delayed_command,
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _run_windows_detached(pip_args, action_label):
|
|
65
|
+
command = _build_windows_detached_command(pip_args)
|
|
66
|
+
creationflags = (
|
|
67
|
+
getattr(subprocess, "CREATE_NEW_PROCESS_GROUP", 0)
|
|
68
|
+
| getattr(subprocess, "DETACHED_PROCESS", 0)
|
|
69
|
+
)
|
|
70
|
+
subprocess.Popen(command, creationflags=creationflags)
|
|
71
|
+
print(f"{YELLOW}{action_label} has been scheduled in a new Windows command process.{NC}")
|
|
72
|
+
print(f"{YELLOW}Close this terminal after the command starts, then open a new one.{NC}")
|
|
73
|
+
return 0
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _confirm_delete():
|
|
77
|
+
print(f"{RED}This will uninstall the local {PACKAGE_NAME} package and remove the fdev command.{NC}")
|
|
78
|
+
answer = input(f"Delete {GREEN}{PACKAGE_NAME}{NC} from this Python environment? (y/N): ")
|
|
79
|
+
return answer.strip().lower() == "y"
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _delete_parser():
|
|
83
|
+
parser = argparse.ArgumentParser(
|
|
84
|
+
prog="fdev delete",
|
|
85
|
+
description="Uninstall the local flutter-dev package that provides fdev.",
|
|
86
|
+
)
|
|
87
|
+
parser.add_argument(
|
|
88
|
+
"-y",
|
|
89
|
+
"--yes",
|
|
90
|
+
action="store_true",
|
|
91
|
+
help="Skip confirmation and uninstall flutter-dev.",
|
|
92
|
+
)
|
|
93
|
+
return parser
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _upgrade_parser():
|
|
97
|
+
return argparse.ArgumentParser(
|
|
98
|
+
prog="fdev upgrade",
|
|
99
|
+
description="Upgrade the local flutter-dev package from PyPI.",
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def delete_fdev_command(argv=None):
|
|
104
|
+
"""Uninstall the local flutter-dev package from the active Python environment."""
|
|
105
|
+
args = _delete_parser().parse_args(argv)
|
|
106
|
+
os_key, os_name = get_os_info()
|
|
107
|
+
_print_os_info(os_name)
|
|
108
|
+
_print_python_environment()
|
|
109
|
+
|
|
110
|
+
if not args.yes and not _confirm_delete():
|
|
111
|
+
print(f"{YELLOW}Operation cancelled.{NC}")
|
|
112
|
+
return 1
|
|
113
|
+
|
|
114
|
+
pip_args = ["uninstall", "-y", PACKAGE_NAME]
|
|
115
|
+
print(f"{YELLOW}Uninstalling {PACKAGE_NAME} from the current Python environment...{NC}")
|
|
116
|
+
|
|
117
|
+
if os_key == "windows":
|
|
118
|
+
return _run_windows_detached(pip_args, "Uninstall")
|
|
119
|
+
|
|
120
|
+
result = _run_pip_direct(pip_args)
|
|
121
|
+
if result == 0:
|
|
122
|
+
print(f"{GREEN}✓ {PACKAGE_NAME} uninstalled successfully.{NC}")
|
|
123
|
+
print(f"{BLUE}Install again with: {GREEN}python -m pip install {PACKAGE_NAME}{NC}")
|
|
124
|
+
else:
|
|
125
|
+
print(f"{RED}✗ Failed to uninstall {PACKAGE_NAME}.{NC}")
|
|
126
|
+
return result
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def upgrade_fdev_command(argv=None):
|
|
130
|
+
"""Upgrade the local flutter-dev package from PyPI in the active environment."""
|
|
131
|
+
_upgrade_parser().parse_args(argv)
|
|
132
|
+
os_key, os_name = get_os_info()
|
|
133
|
+
_print_os_info(os_name)
|
|
134
|
+
_print_python_environment()
|
|
135
|
+
|
|
136
|
+
pip_args = ["install", "--upgrade", PACKAGE_NAME]
|
|
137
|
+
print(f"{YELLOW}Upgrading {PACKAGE_NAME} from PyPI...{NC}")
|
|
138
|
+
|
|
139
|
+
if os_key == "windows":
|
|
140
|
+
return _run_windows_detached(pip_args, "Upgrade")
|
|
141
|
+
|
|
142
|
+
result = _run_pip_direct(pip_args)
|
|
143
|
+
if result == 0:
|
|
144
|
+
print(f"{GREEN}✓ {PACKAGE_NAME} upgraded successfully.{NC}")
|
|
145
|
+
print(f"{BLUE}Restart your terminal if the fdev command still uses the old version.{NC}")
|
|
146
|
+
else:
|
|
147
|
+
print(f"{RED}✗ Failed to upgrade {PACKAGE_NAME}.{NC}")
|
|
148
|
+
return result
|
|
@@ -38,6 +38,28 @@ class FdevCliTests(unittest.TestCase):
|
|
|
38
38
|
show_usage.assert_called_once_with()
|
|
39
39
|
launch_gui.assert_not_called()
|
|
40
40
|
|
|
41
|
+
def test_delete_command_routes_to_package_delete(self):
|
|
42
|
+
with (
|
|
43
|
+
patch.object(sys, "argv", ["fdev", "delete", "--yes"]),
|
|
44
|
+
patch.object(fdev, "delete_fdev_command", return_value=0) as delete_command,
|
|
45
|
+
):
|
|
46
|
+
with self.assertRaises(SystemExit) as raised:
|
|
47
|
+
fdev.main()
|
|
48
|
+
|
|
49
|
+
self.assertEqual(raised.exception.code, 0)
|
|
50
|
+
delete_command.assert_called_once_with(["--yes"])
|
|
51
|
+
|
|
52
|
+
def test_upgrade_command_routes_to_package_upgrade(self):
|
|
53
|
+
with (
|
|
54
|
+
patch.object(sys, "argv", ["fdev", "upgrade"]),
|
|
55
|
+
patch.object(fdev, "upgrade_fdev_command", return_value=0) as upgrade_command,
|
|
56
|
+
):
|
|
57
|
+
with self.assertRaises(SystemExit) as raised:
|
|
58
|
+
fdev.main()
|
|
59
|
+
|
|
60
|
+
self.assertEqual(raised.exception.code, 0)
|
|
61
|
+
upgrade_command.assert_called_once_with([])
|
|
62
|
+
|
|
41
63
|
|
|
42
64
|
if __name__ == "__main__":
|
|
43
65
|
unittest.main()
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import sys
|
|
2
2
|
import unittest
|
|
3
3
|
from pathlib import Path
|
|
4
|
+
from tkinter import TclError
|
|
4
5
|
|
|
5
6
|
from managers.gui import (
|
|
7
|
+
FdevGui,
|
|
6
8
|
GuiCommand,
|
|
7
9
|
parse_ansi_segments,
|
|
8
10
|
build_fdev_command,
|
|
@@ -86,6 +88,8 @@ class GuiCatalogTests(unittest.TestCase):
|
|
|
86
88
|
("deploy",),
|
|
87
89
|
("doctor",),
|
|
88
90
|
("brew",),
|
|
91
|
+
("upgrade",),
|
|
92
|
+
("delete",),
|
|
89
93
|
("merge",),
|
|
90
94
|
("ai",),
|
|
91
95
|
("ai", "groq"),
|
|
@@ -140,6 +144,40 @@ class GuiCatalogTests(unittest.TestCase):
|
|
|
140
144
|
["sync", "dev", "main"],
|
|
141
145
|
)
|
|
142
146
|
|
|
147
|
+
def test_dashboard_uses_stable_grid_layout_without_paned_window(self):
|
|
148
|
+
try:
|
|
149
|
+
import tkinter as tk
|
|
150
|
+
except ImportError:
|
|
151
|
+
self.skipTest("Tkinter is not available")
|
|
152
|
+
|
|
153
|
+
try:
|
|
154
|
+
root = tk.Tk()
|
|
155
|
+
except TclError as error:
|
|
156
|
+
self.skipTest(f"Tk display is not available: {error}")
|
|
157
|
+
|
|
158
|
+
try:
|
|
159
|
+
app = FdevGui(root)
|
|
160
|
+
root.update_idletasks()
|
|
161
|
+
|
|
162
|
+
widget_classes = {
|
|
163
|
+
widget.winfo_class()
|
|
164
|
+
for widget in _walk_widgets(root)
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
self.assertNotIn("TPanedwindow", widget_classes)
|
|
168
|
+
self.assertEqual(app.section_label.cget("text"), "Dashboard")
|
|
169
|
+
self.assertGreaterEqual(len(app.command_frame.winfo_children()), 3)
|
|
170
|
+
self.assertGreater(app.command_frame.winfo_width(), 100)
|
|
171
|
+
self.assertGreater(app.log_text.winfo_width(), 100)
|
|
172
|
+
finally:
|
|
173
|
+
root.destroy()
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _walk_widgets(widget):
|
|
177
|
+
yield widget
|
|
178
|
+
for child in widget.winfo_children():
|
|
179
|
+
yield from _walk_widgets(child)
|
|
180
|
+
|
|
143
181
|
|
|
144
182
|
if __name__ == "__main__":
|
|
145
183
|
unittest.main()
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import io
|
|
2
|
+
import subprocess
|
|
3
|
+
import unittest
|
|
4
|
+
from contextlib import redirect_stdout
|
|
5
|
+
from unittest.mock import patch
|
|
6
|
+
|
|
7
|
+
from managers import package
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class PackageCommandTests(unittest.TestCase):
|
|
11
|
+
def test_delete_yes_runs_pip_uninstall_in_current_python_environment(self):
|
|
12
|
+
completed = subprocess.CompletedProcess(
|
|
13
|
+
["/venv/bin/python", "-m", "pip", "uninstall", "-y", "flutter-dev"],
|
|
14
|
+
0,
|
|
15
|
+
)
|
|
16
|
+
|
|
17
|
+
with (
|
|
18
|
+
patch.object(package.platform, "system", return_value="Darwin"),
|
|
19
|
+
patch.object(package.sys, "executable", "/venv/bin/python"),
|
|
20
|
+
patch.object(package.subprocess, "run", return_value=completed) as run,
|
|
21
|
+
):
|
|
22
|
+
result = package.delete_fdev_command(["--yes"])
|
|
23
|
+
|
|
24
|
+
self.assertEqual(result, 0)
|
|
25
|
+
run.assert_called_once_with(
|
|
26
|
+
["/venv/bin/python", "-m", "pip", "uninstall", "-y", "flutter-dev"]
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
def test_delete_cancelled_by_user_does_not_run_pip(self):
|
|
30
|
+
with (
|
|
31
|
+
patch.object(package.platform, "system", return_value="Linux"),
|
|
32
|
+
patch("builtins.input", return_value="n"),
|
|
33
|
+
patch.object(package.subprocess, "run") as run,
|
|
34
|
+
):
|
|
35
|
+
result = package.delete_fdev_command([])
|
|
36
|
+
|
|
37
|
+
self.assertEqual(result, 1)
|
|
38
|
+
run.assert_not_called()
|
|
39
|
+
|
|
40
|
+
def test_upgrade_runs_pip_upgrade_in_current_python_environment(self):
|
|
41
|
+
completed = subprocess.CompletedProcess(
|
|
42
|
+
["/venv/bin/python", "-m", "pip", "install", "--upgrade", "flutter-dev"],
|
|
43
|
+
0,
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
with (
|
|
47
|
+
patch.object(package.platform, "system", return_value="Linux"),
|
|
48
|
+
patch.object(package.sys, "executable", "/venv/bin/python"),
|
|
49
|
+
patch.object(package.subprocess, "run", return_value=completed) as run,
|
|
50
|
+
):
|
|
51
|
+
result = package.upgrade_fdev_command([])
|
|
52
|
+
|
|
53
|
+
self.assertEqual(result, 0)
|
|
54
|
+
run.assert_called_once_with(
|
|
55
|
+
["/venv/bin/python", "-m", "pip", "install", "--upgrade", "flutter-dev"]
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
def test_windows_delete_schedules_detached_uninstall(self):
|
|
59
|
+
with (
|
|
60
|
+
patch.object(package.platform, "system", return_value="Windows"),
|
|
61
|
+
patch.object(package.sys, "executable", r"C:\Python\python.exe"),
|
|
62
|
+
patch.object(package.subprocess, "Popen") as popen,
|
|
63
|
+
):
|
|
64
|
+
result = package.delete_fdev_command(["--yes"])
|
|
65
|
+
|
|
66
|
+
self.assertEqual(result, 0)
|
|
67
|
+
popen.assert_called_once()
|
|
68
|
+
command = popen.call_args.args[0]
|
|
69
|
+
self.assertEqual(command[0], "cmd")
|
|
70
|
+
joined_command = " ".join(command)
|
|
71
|
+
self.assertIn("uninstall", joined_command)
|
|
72
|
+
self.assertIn("flutter-dev", joined_command)
|
|
73
|
+
|
|
74
|
+
def test_windows_detached_command_quotes_python_paths_with_spaces(self):
|
|
75
|
+
with patch.object(package.sys, "executable", r"C:\Program Files\Python312\python.exe"):
|
|
76
|
+
command = package._build_windows_detached_command(["install", "--upgrade", "flutter-dev"])
|
|
77
|
+
|
|
78
|
+
self.assertEqual(command[:3], ["cmd", "/c", "start"])
|
|
79
|
+
self.assertEqual(command[3], "fdev package command")
|
|
80
|
+
self.assertEqual(command[4:6], ["cmd", "/c"])
|
|
81
|
+
self.assertIn('"C:\\Program Files\\Python312\\python.exe"', command[6])
|
|
82
|
+
self.assertIn("-m pip install --upgrade flutter-dev", command[6])
|
|
83
|
+
|
|
84
|
+
def test_linux_delete_uses_running_python_not_python3_or_pip3(self):
|
|
85
|
+
completed = subprocess.CompletedProcess(
|
|
86
|
+
["/home/user/project/.venv/bin/python", "-m", "pip", "uninstall", "-y", "flutter-dev"],
|
|
87
|
+
0,
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
with (
|
|
91
|
+
patch.object(package.platform, "system", return_value="Linux"),
|
|
92
|
+
patch.object(package.sys, "executable", "/home/user/project/.venv/bin/python"),
|
|
93
|
+
patch.object(package.subprocess, "run", return_value=completed) as run,
|
|
94
|
+
):
|
|
95
|
+
result = package.delete_fdev_command(["--yes"])
|
|
96
|
+
|
|
97
|
+
self.assertEqual(result, 0)
|
|
98
|
+
run.assert_called_once_with(
|
|
99
|
+
["/home/user/project/.venv/bin/python", "-m", "pip", "uninstall", "-y", "flutter-dev"]
|
|
100
|
+
)
|
|
101
|
+
|
|
102
|
+
def test_upgrade_prints_target_python_environment(self):
|
|
103
|
+
completed = subprocess.CompletedProcess(
|
|
104
|
+
["/home/user/project/.venv/bin/python", "-m", "pip", "install", "--upgrade", "flutter-dev"],
|
|
105
|
+
0,
|
|
106
|
+
)
|
|
107
|
+
output = io.StringIO()
|
|
108
|
+
|
|
109
|
+
with (
|
|
110
|
+
patch.object(package.platform, "system", return_value="Linux"),
|
|
111
|
+
patch.object(package.sys, "executable", "/home/user/project/.venv/bin/python"),
|
|
112
|
+
patch.object(package.subprocess, "run", return_value=completed),
|
|
113
|
+
redirect_stdout(output),
|
|
114
|
+
):
|
|
115
|
+
result = package.upgrade_fdev_command([])
|
|
116
|
+
|
|
117
|
+
self.assertEqual(result, 0)
|
|
118
|
+
self.assertIn("Python environment:", output.getvalue())
|
|
119
|
+
self.assertIn("/home/user/project/.venv/bin/python", output.getvalue())
|
|
120
|
+
|
|
121
|
+
def test_command_help_returns_zero(self):
|
|
122
|
+
with self.assertRaises(SystemExit) as raised:
|
|
123
|
+
package.delete_fdev_command(["--help"])
|
|
124
|
+
|
|
125
|
+
self.assertEqual(raised.exception.code, 0)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
if __name__ == "__main__":
|
|
129
|
+
unittest.main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|