mega_snake 0.1.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.
- mega_snake-0.1.0/.devcontainer/devcontainer.json +71 -0
- mega_snake-0.1.0/.github/copilot-instructions.md +432 -0
- mega_snake-0.1.0/.github/workflows/copilot-setup-steps.yml +44 -0
- mega_snake-0.1.0/.github/workflows/pr-validation.yml +37 -0
- mega_snake-0.1.0/.gitignore +18 -0
- mega_snake-0.1.0/LICENSE +201 -0
- mega_snake-0.1.0/PKG-INFO +258 -0
- mega_snake-0.1.0/README.md +227 -0
- mega_snake-0.1.0/build.gradle +0 -0
- mega_snake-0.1.0/offproject/mega_snake/gcloud/__init__.py +1 -0
- mega_snake-0.1.0/offproject/mega_snake/gcloud/module.py +33 -0
- mega_snake-0.1.0/offproject/mega_snake/gcloud/parse_instances_deployment_id.py +42 -0
- mega_snake-0.1.0/offproject/mega_snake/gcloud/parse_json_logs.py +55 -0
- mega_snake-0.1.0/offproject/mega_snake/gcloud/sign.py +130 -0
- mega_snake-0.1.0/offproject/tests/gcloud/test_gcloud_module.py +46 -0
- mega_snake-0.1.0/offproject/tests/gcloud/test_parse_instances_deployment_id.py +139 -0
- mega_snake-0.1.0/offproject/tests/gcloud/test_sign.py +387 -0
- mega_snake-0.1.0/out/jq.pyi +58 -0
- mega_snake-0.1.0/pyproject.toml +106 -0
- mega_snake-0.1.0/src/mega_snake/__init__.py +1 -0
- mega_snake-0.1.0/src/mega_snake/__main__.py +107 -0
- mega_snake-0.1.0/src/mega_snake/config.properties +11 -0
- mega_snake-0.1.0/src/mega_snake/config_environment/__init__.py +1 -0
- mega_snake-0.1.0/src/mega_snake/config_environment/create_working_env.py +437 -0
- mega_snake-0.1.0/src/mega_snake/config_environment/gradle_set.py +209 -0
- mega_snake-0.1.0/src/mega_snake/config_environment/graphql_schema.py +79 -0
- mega_snake-0.1.0/src/mega_snake/config_environment/java_set.py +292 -0
- mega_snake-0.1.0/src/mega_snake/config_environment/local_config.py +72 -0
- mega_snake-0.1.0/src/mega_snake/config_environment/maven_set.py +330 -0
- mega_snake-0.1.0/src/mega_snake/config_environment/models/github_queries.py +78 -0
- mega_snake-0.1.0/src/mega_snake/config_environment/models/log_viewer_watcher.py +70 -0
- mega_snake-0.1.0/src/mega_snake/config_environment/models/tools_version.py +204 -0
- mega_snake-0.1.0/src/mega_snake/config_environment/models/vscode_input.py +117 -0
- mega_snake-0.1.0/src/mega_snake/config_environment/models/vscode_launch.py +140 -0
- mega_snake-0.1.0/src/mega_snake/config_environment/models/vscode_task.py +266 -0
- mega_snake-0.1.0/src/mega_snake/config_environment/module.py +33 -0
- mega_snake-0.1.0/src/mega_snake/config_environment/util.py +61 -0
- mega_snake-0.1.0/src/mega_snake/config_setup.ps1 +44 -0
- mega_snake-0.1.0/src/mega_snake/config_setup.sh +48 -0
- mega_snake-0.1.0/src/mega_snake/constants.py +60 -0
- mega_snake-0.1.0/src/mega_snake/diff_tree/__init__.py +1 -0
- mega_snake-0.1.0/src/mega_snake/diff_tree/file_type.py +81 -0
- mega_snake-0.1.0/src/mega_snake/diff_tree/module.py +154 -0
- mega_snake-0.1.0/src/mega_snake/light_weight/__init__.py +1 -0
- mega_snake-0.1.0/src/mega_snake/light_weight/create_release.py +105 -0
- mega_snake-0.1.0/src/mega_snake/light_weight/echo.py +81 -0
- mega_snake-0.1.0/src/mega_snake/light_weight/jks_expired_certs.py +105 -0
- mega_snake-0.1.0/src/mega_snake/light_weight/module.py +30 -0
- mega_snake-0.1.0/src/mega_snake/light_weight/release.py +132 -0
- mega_snake-0.1.0/src/mega_snake/light_weight/release_handler.py +76 -0
- mega_snake-0.1.0/src/mega_snake/light_weight/shell_init.py +76 -0
- mega_snake-0.1.0/src/mega_snake/remote_branches/__init__.py +1 -0
- mega_snake-0.1.0/src/mega_snake/remote_branches/cleanup_remote_branches.py +63 -0
- mega_snake-0.1.0/src/mega_snake/remote_branches/details_remote_branches.py +101 -0
- mega_snake-0.1.0/src/mega_snake/remote_branches/module.py +24 -0
- mega_snake-0.1.0/src/mega_snake/remote_branches/parse_remote_branches.py +73 -0
- mega_snake-0.1.0/src/mega_snake/remote_branches/remote_branch.py +161 -0
- mega_snake-0.1.0/src/mega_snake/resources/java-formatter.xml +337 -0
- mega_snake-0.1.0/src/mega_snake/util/__init__.py +1 -0
- mega_snake-0.1.0/src/mega_snake/util/cli_group.py +71 -0
- mega_snake-0.1.0/src/mega_snake/util/formatting.py +234 -0
- mega_snake-0.1.0/src/mega_snake/util/props.py +384 -0
- mega_snake-0.1.0/src/mega_snake/util/util.py +247 -0
- mega_snake-0.1.0/src/tests/config.properties +9 -0
- mega_snake-0.1.0/src/tests/config_environment/models/test_github_queries.py +109 -0
- mega_snake-0.1.0/src/tests/config_environment/models/test_log_viewer_watcher.py +119 -0
- mega_snake-0.1.0/src/tests/config_environment/models/test_tools_version.py +353 -0
- mega_snake-0.1.0/src/tests/config_environment/models/test_vscode_input.py +129 -0
- mega_snake-0.1.0/src/tests/config_environment/models/test_vscode_launch.py +147 -0
- mega_snake-0.1.0/src/tests/config_environment/models/test_vscode_task.py +128 -0
- mega_snake-0.1.0/src/tests/config_environment/test_ce_util.py +89 -0
- mega_snake-0.1.0/src/tests/config_environment/test_config_environment_module.py +20 -0
- mega_snake-0.1.0/src/tests/config_environment/test_create_working_env.py +886 -0
- mega_snake-0.1.0/src/tests/config_environment/test_gradle_set.py +449 -0
- mega_snake-0.1.0/src/tests/config_environment/test_graphql_schema.py +249 -0
- mega_snake-0.1.0/src/tests/config_environment/test_java_set.py +790 -0
- mega_snake-0.1.0/src/tests/config_environment/test_local_config.py +169 -0
- mega_snake-0.1.0/src/tests/config_environment/test_maven_set.py +844 -0
- mega_snake-0.1.0/src/tests/gradle/bash_local_file.sh +14 -0
- mega_snake-0.1.0/src/tests/gradle/darwin.code-workspace +457 -0
- mega_snake-0.1.0/src/tests/gradle/empty.code-workspace +15 -0
- mega_snake-0.1.0/src/tests/gradle/empty_local_file.sh +14 -0
- mega_snake-0.1.0/src/tests/light_weight/test_light_weight_helpers.py +114 -0
- mega_snake-0.1.0/src/tests/light_weight/test_light_weight_module.py +11 -0
- mega_snake-0.1.0/src/tests/light_weight/test_release_handler.py +61 -0
- mega_snake-0.1.0/src/tests/light_weight/test_shell_init.py +54 -0
- mega_snake-0.1.0/src/tests/maven/bash_local_file.sh +14 -0
- mega_snake-0.1.0/src/tests/maven/darwin.code-workspace +150 -0
- mega_snake-0.1.0/src/tests/maven/empty.code-workspace +15 -0
- mega_snake-0.1.0/src/tests/maven/empty_local_file.sh +14 -0
- mega_snake-0.1.0/src/tests/maven/pom.xml +26 -0
- mega_snake-0.1.0/src/tests/remote_branches/test_remote_branch_helpers.py +129 -0
- mega_snake-0.1.0/src/tests/remote_branches/test_remote_branches_module.py +11 -0
- mega_snake-0.1.0/src/tests/resources/graphql/extended-types.graphql +75 -0
- mega_snake-0.1.0/src/tests/resources/graphql/mutations.graphql +42 -0
- mega_snake-0.1.0/src/tests/resources/graphql/queries.graphql +37 -0
- mega_snake-0.1.0/src/tests/resources/graphql/specialized-types.graphql +57 -0
- mega_snake-0.1.0/src/tests/resources/graphql/types.graphql +31 -0
- mega_snake-0.1.0/src/tests/test.code-workspace +9 -0
- mega_snake-0.1.0/src/tests/test_cli_commands_aliases.py +31 -0
- mega_snake-0.1.0/src/tests/test_cli_entrypoint.py +97 -0
- mega_snake-0.1.0/src/tests/test_main_and_diff_tree.py +93 -0
- mega_snake-0.1.0/src/tests/test_resources/test_resources +1 -0
- mega_snake-0.1.0/src/tests/test_util/side_effect_wrapper.py +30 -0
- mega_snake-0.1.0/src/tests/test_util/util_test.py +52 -0
- mega_snake-0.1.0/src/tests/util/test_cli_group.py +140 -0
- mega_snake-0.1.0/src/tests/util/test_formatting.py +271 -0
- mega_snake-0.1.0/src/tests/util/test_props_helpers.py +93 -0
- mega_snake-0.1.0/src/tests/util/test_util.py +366 -0
- mega_snake-0.1.0/stuff.code-workspace +656 -0
- mega_snake-0.1.0/uv.lock +614 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "MegaSnake CLI - Java 21 + Python 3.13",
|
|
3
|
+
"image": "mcr.microsoft.com/devcontainers/java:21-bullseye",
|
|
4
|
+
"features": {
|
|
5
|
+
"ghcr.io/devcontainers/features/java:1": {
|
|
6
|
+
"version": "21",
|
|
7
|
+
"installMaven": false,
|
|
8
|
+
"installGradle": true
|
|
9
|
+
},
|
|
10
|
+
"ghcr.io/devcontainers/features/python:1": {
|
|
11
|
+
"version": "3.13"
|
|
12
|
+
},
|
|
13
|
+
"ghcr.io/devcontainers/features/git:1": {}
|
|
14
|
+
},
|
|
15
|
+
"containerEnv": {
|
|
16
|
+
"PYTHONPATH": "${containerWorkspaceFolder}",
|
|
17
|
+
"JAVA_HOME": "/usr/local/sdkman/candidates/java/current",
|
|
18
|
+
"GRADLE_HOME": "/opt/gradle/gradle-8.11"
|
|
19
|
+
},
|
|
20
|
+
"remoteEnv": {
|
|
21
|
+
"PYTHONPATH": "${containerWorkspaceFolder}"
|
|
22
|
+
},
|
|
23
|
+
"postCreateCommand": "pip install --upgrade pip && pip install poetry",
|
|
24
|
+
"customizations": {
|
|
25
|
+
"vscode": {
|
|
26
|
+
"extensions": [
|
|
27
|
+
"ms-python.python",
|
|
28
|
+
"ms-python.vscode-pylance",
|
|
29
|
+
"ms-python.debugpy",
|
|
30
|
+
"vscjava.vscode-java-pack",
|
|
31
|
+
"vscjava.vscode-gradle",
|
|
32
|
+
"vscjava.vscode-java-debug",
|
|
33
|
+
"redhat.java",
|
|
34
|
+
"github.vscode-github-actions",
|
|
35
|
+
"github.vscode-pull-request-github",
|
|
36
|
+
"graphql.vscode-graphql-syntax",
|
|
37
|
+
"graphql.vscode-graphql"
|
|
38
|
+
],
|
|
39
|
+
"settings": {
|
|
40
|
+
"python.defaultInterpreterPath": "/usr/local/bin/python3.13",
|
|
41
|
+
"python.analysis.extraPaths": [
|
|
42
|
+
"${containerWorkspaceFolder}"
|
|
43
|
+
],
|
|
44
|
+
"python.linting.enabled": true,
|
|
45
|
+
"python.linting.pylintEnabled": true,
|
|
46
|
+
"python.linting.pylintPath": "/usr/local/bin/pylint",
|
|
47
|
+
"[python]": {
|
|
48
|
+
"editor.defaultFormatter": "ms-python.python",
|
|
49
|
+
"editor.formatOnSave": true
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
"mounts": [
|
|
55
|
+
"source=${localEnv:HOME}${localEnv:USERPROFILE}/.ssh,target=/home/vscode/.ssh,type=bind,consistency=cached"
|
|
56
|
+
],
|
|
57
|
+
"forwardPorts": [
|
|
58
|
+
5005,
|
|
59
|
+
8080
|
|
60
|
+
],
|
|
61
|
+
"portsAttributes": {
|
|
62
|
+
"5005": {
|
|
63
|
+
"label": "Java Debug",
|
|
64
|
+
"onAutoForward": "notify"
|
|
65
|
+
},
|
|
66
|
+
"8080": {
|
|
67
|
+
"label": "Application",
|
|
68
|
+
"onAutoForward": "notify"
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -0,0 +1,432 @@
|
|
|
1
|
+
# GitHub Copilot Instructions for `unix-scripts` (Mega Snake)
|
|
2
|
+
|
|
3
|
+
## 1. Project Overview & Philosophy
|
|
4
|
+
|
|
5
|
+
`mega_snake` is a robust Python 3.13+ CLI tool designed to standardize the local development lifecycle. It acts as a "Swiss Army Knife" for developers, primarily automating the complex configuration of VS Code environments for Java/Gradle, but extending into Git management, Release orchestration context, and Google Cloud observability.
|
|
6
|
+
|
|
7
|
+
**Core Philosophy:**
|
|
8
|
+
- **Zero Config Start**: A developer should be able to run `mgsnake working-env` and have a fully functional IDE state immediately.
|
|
9
|
+
- **Idempotency**: Commands should be safe to run multiple times without destructive side effects unless explicitly requested.
|
|
10
|
+
- **System Integration**: The tool deeply integrates with the OS shell (Bash/Zsh/PowerShell) and external tools (Git, Java, Gradle).
|
|
11
|
+
|
|
12
|
+
**Tech Stack:**
|
|
13
|
+
- **Runtime**: Python 3.13+
|
|
14
|
+
- **CLI Framework**: `click` (Command composition), `rich-click` (Beautiful help text/formatting)
|
|
15
|
+
- **UI/Output**: `colorama` (Terminal colors), `rich` (Tables/Trees)
|
|
16
|
+
- **Dependency Management**: `uv`
|
|
17
|
+
- **Shell Interop**: Custom shell scripts (`config_setup.sh/ps1`) that wrap the python execution.
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## 2. Architecture & Patterns
|
|
22
|
+
|
|
23
|
+
### 2.1 Entry Point & CLI Orchestration (`src/mega_snake/__main__.py`)
|
|
24
|
+
|
|
25
|
+
The application uses a `click.Group` with a custom `CliGroup` class to support command aliases. The entry point `cli()` function initializes global application properties before any command runs.
|
|
26
|
+
|
|
27
|
+
**Critical Pattern: Initialization & Light-weight Mode**
|
|
28
|
+
The CLI checks for a `skip` flag in command metadata. If present, it bypasses heavy environment checks (like requiring a valid workspace folder), allowing "light-weight" commands (e.g., `create-release`) to run anywhere.
|
|
29
|
+
|
|
30
|
+
```python
|
|
31
|
+
# src/mega_snake/__main__.py
|
|
32
|
+
@click.pass_context
|
|
33
|
+
def cli(ctx: click.Context, log_level: str, shell: str) -> None:
|
|
34
|
+
# ...
|
|
35
|
+
# Access metadata to check for 'skip' flag
|
|
36
|
+
metadata = getattr(cmd.callback, "flags", {})
|
|
37
|
+
if flags and "skip" in flags:
|
|
38
|
+
# Light-weight mode: minimal initialization
|
|
39
|
+
init_app_properties(log_level, shell, light_weight=True)
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 2.2 Command Registration & Aliases (`src/mega_snake/util/cli_group.py`)
|
|
43
|
+
|
|
44
|
+
We do not standard `click` alias implementation. We use `CliGroup` to register commands with multiple names.
|
|
45
|
+
|
|
46
|
+
**Usage:**
|
|
47
|
+
```python
|
|
48
|
+
# Registration in __main__.py
|
|
49
|
+
from .diff_tree.module import main as diff_tree
|
|
50
|
+
# Registers 'diff-tree' command accessible via 'dt' and 'tree' aliases
|
|
51
|
+
cli.add_command_with_alias(diff_tree, ["dt", "tree"])
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 2.3 The Wrapper Pattern (`module.py` files)
|
|
55
|
+
|
|
56
|
+
Each functional module (e.g., `config_environment`) exposes an `add_wrapper` decorator. This allows module-specific checks to run before the command execution, keeping the core logic clean.
|
|
57
|
+
|
|
58
|
+
**Example from `src/mega_snake/config_environment/module.py`:**
|
|
59
|
+
```python
|
|
60
|
+
def wrapper(_ctx: click.Context, *_args, **_kwargs) -> None:
|
|
61
|
+
# Pre-flight check: verify we're in a valid workspace
|
|
62
|
+
if not get_workspace_folder():
|
|
63
|
+
raise RuntimeError("Not in a valid workspace.")
|
|
64
|
+
|
|
65
|
+
add_wrapper = wrapper_decorator(wrapper)
|
|
66
|
+
|
|
67
|
+
# Usage in __main__.py
|
|
68
|
+
for command in config_environment.commands.values():
|
|
69
|
+
# Wraps every command in the module with the pre-flight check
|
|
70
|
+
cli.add_command(config_environment_result_callback(command))
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
**Educational Logic:**
|
|
74
|
+
This implements the **Decorator Pattern**. Instead of repeating validation logic in every command, we define it once in the wrapper. The `__main__.py` entry point applies this wrapper dynamically when registering commands, ensuring checks only run when a relevant command is invoked.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 3. Core Functional Modules
|
|
79
|
+
|
|
80
|
+
### 3.1 Environment Configuration (`src/mega_snake/config_environment/`)
|
|
81
|
+
|
|
82
|
+
This is the most complex module, responsible for generating `.code-workspace` files. It configures the "settings" section of the workspace file directly, avoiding reliance on `.vscode/settings.json`.
|
|
83
|
+
|
|
84
|
+
#### `working-env`
|
|
85
|
+
- **Logic**:
|
|
86
|
+
1. Validates Git repository status.
|
|
87
|
+
2. Loads local developer overrides (`initial_load`).
|
|
88
|
+
3. Configures Java (`set-java`) and Gradle (`set-gradle`).
|
|
89
|
+
4. Generates VS Code tasks, launch configurations, and recommendations.
|
|
90
|
+
|
|
91
|
+
#### `set-java` (`java_set.py`)
|
|
92
|
+
Manages the `java.configuration.runtimes` and `terminal.integrated.env` settings in VS Code.
|
|
93
|
+
|
|
94
|
+
**Key Logic:**
|
|
95
|
+
- It parses the `.code-workspace` file (as JSON with comments).
|
|
96
|
+
- It queries the OS for installed JDKs.
|
|
97
|
+
- It updates the `settings` structure inside the workspace file to point `JAVA_HOME` to the selected version.
|
|
98
|
+
|
|
99
|
+
```python
|
|
100
|
+
# src/mega_snake/config_environment/java_set.py
|
|
101
|
+
ENV_VARIABLE = f"terminal.integrated.env.{OS_MAP[OS]}"
|
|
102
|
+
JAVA_JQ_QUERY = f'.settings["{ENV_VARIABLE}"].JAVA_HOME'
|
|
103
|
+
|
|
104
|
+
# It uses python logic to traverse the JSON structure similar to JQ
|
|
105
|
+
# to find and replace the Java path.
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
#### `set-gradle` (`gradle_set.py`)
|
|
109
|
+
Like `set-java`, this configures the Gradle version for the workspace.
|
|
110
|
+
|
|
111
|
+
**Key Logic:**
|
|
112
|
+
- It identifies installed Gradle versions via `ToolVersion` abstraction.
|
|
113
|
+
- It updates `java.import.gradle.home` and `terminal.integrated.env` settings.
|
|
114
|
+
- It ensures consistency between the terminal environment and the IDE's internal Gradle wrapper.
|
|
115
|
+
|
|
116
|
+
**Technical Note: JSON with Comments**
|
|
117
|
+
Both `set-java` and `set-gradle` modify the `.code-workspace` file which contains comments. The standard python `json` library fails on comments. We use a custom `load_json_with_comments` utility to handle VS Code's configuration format safely without stripping comments, which are vital for developers understanding the config.
|
|
118
|
+
|
|
119
|
+
#### `init-local-config` (`local_config.py`)
|
|
120
|
+
Creates a local developer-specific configuration file that is **ignored by Git**.
|
|
121
|
+
|
|
122
|
+
**Why?**
|
|
123
|
+
Developers often have machine-specific tokens, paths, or aliases that shouldn't be committed to the repo. `init-local-config` generates a shell-specific file (`.sh` or `.ps1` logic embedded) that is sourced by the main environment. This pattern allows the tool to support "Convention over Configuration" while still allowing for "Configuration" when necessary.
|
|
124
|
+
|
|
125
|
+
### 3.2 Git Utilities (`src/mega_snake/diff_tree/`)
|
|
126
|
+
|
|
127
|
+
#### `diff-tree` (`dt`)
|
|
128
|
+
Generates a visual tree representation of changed files.
|
|
129
|
+
|
|
130
|
+
**Implementation Details:**
|
|
131
|
+
- Uses `git diff-tree -r {main_branch} {current_branch}` to get raw file lists.
|
|
132
|
+
- categorizes files using `FileType.from_symbol(symbol)`.
|
|
133
|
+
- Reconstructs a dummy directory structure in `workspace_temp/diff_tree_dummy_repo`.
|
|
134
|
+
- Uses `directory_tree` library to generating the visual text tree.
|
|
135
|
+
|
|
136
|
+
```python
|
|
137
|
+
# src/mega_snake/diff_tree/module.py
|
|
138
|
+
for diff in diff_str.split("\n"):
|
|
139
|
+
columns: list[str] = diff.split("\t")
|
|
140
|
+
symbol = columns[0].split(" ")[4] # M, A, D, etc.
|
|
141
|
+
path: str = columns[1]
|
|
142
|
+
# builds tree structure...
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### 3.3 Remote Branch Management (`src/mega_snake/remote_branches/`)
|
|
146
|
+
|
|
147
|
+
#### `remote-branches-details`
|
|
148
|
+
Analyzes remote branches to suggest cleanup candidates.
|
|
149
|
+
|
|
150
|
+
**Filtering Logic (`-f` flag):**
|
|
151
|
+
- **'M' (Merged)**: Branches that have been merged into `master`.
|
|
152
|
+
- **'U' (Unmerged)**: Branches with unique commits not in `master`.
|
|
153
|
+
- **'A' (All)**: Both.
|
|
154
|
+
|
|
155
|
+
It creates `workspace_temp/remote_branches.txt` containing detailed metadata (author, last commit date, ahead/behind count) for every branch.
|
|
156
|
+
|
|
157
|
+
#### `remote-branches-cleanup`
|
|
158
|
+
An interactive tool that consumes the output of `remote-branches-details`.
|
|
159
|
+
|
|
160
|
+
**Logic:**
|
|
161
|
+
1. Allows re-running `remote-branches-details` to refresh data.
|
|
162
|
+
2. Reads `workspace_temp/remote_branches.txt`.
|
|
163
|
+
3. Presents an interactive list to the user to select branches for deletion.
|
|
164
|
+
4. Performs `git push origin --delete <branch>` and prunes local references.
|
|
165
|
+
|
|
166
|
+
**Design Pattern: Pipeline via Files**
|
|
167
|
+
Instead of passing complex objects between commands in memory, we use the filesystem (`remote_branches.txt`) as an intermediate buffer. This allows the user to inspect (and potentially edit) the list of candidates before running the destructive cleanup command.
|
|
168
|
+
|
|
169
|
+
### 3.4 Release Management (`src/mega_snake/light_weight/create_release.py`)
|
|
170
|
+
|
|
171
|
+
Automates GitHub releases, creating tags and proper GitHub Release entries.
|
|
172
|
+
|
|
173
|
+
**Arguments:**
|
|
174
|
+
- `tag_suffix`: e.g., `v1.0.0-{suffix}`
|
|
175
|
+
- `release_type`:
|
|
176
|
+
- `p`: Prerelease (`--prerelease`)
|
|
177
|
+
- `l`: Latest (`--latest`)
|
|
178
|
+
- `r`: Replace Latest (Updates the `latest` tag to point to a new commit)
|
|
179
|
+
|
|
180
|
+
**Logic:**
|
|
181
|
+
It fetches the current tags, calculates the new tag based on the suffix, and relies on the `gh` CLI to publish the release.
|
|
182
|
+
|
|
183
|
+
**Technical Note: Why use the `gh` wrapper?**
|
|
184
|
+
We use the `gh` (GitHub CLI) tool because it leverages the user's existing authentication state, avoiding the need to manage complex API tokens within the Python code.
|
|
185
|
+
|
|
186
|
+
```python
|
|
187
|
+
# src/mega_snake/light_weight/release_handler.py
|
|
188
|
+
cwd: str = (
|
|
189
|
+
f'gh release create {tag_name} {release_type} --target "{release_branch}" '
|
|
190
|
+
f'--title "{tag_name}" {release_notes} --generate-notes'
|
|
191
|
+
)
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
**Educational Logic:**
|
|
195
|
+
Instead of re-implementing the GitHub API client (which requires managing OAuth tokens, permissions, etc.), we delegate the heavy lifting to the `gh` binary. This is a common "shell wrapper" pattern where Python manages the *control flow* and *validation*, but the shell executes the *remote action*.
|
|
196
|
+
|
|
197
|
+
### 3.5 Other Utilities
|
|
198
|
+
|
|
199
|
+
#### `graphql-schema` (`graphql_schema.py`)
|
|
200
|
+
Compiles multiple `.graphql` files into a single schema and generates introspection JSON.
|
|
201
|
+
|
|
202
|
+
**Why introspection?**
|
|
203
|
+
Frontend tools (like Apollo) and IDE plugins often require a full introspection result (`schema.json`) to provide autocompletion and type checking, not just the raw SDL string.
|
|
204
|
+
|
|
205
|
+
#### `expired-certs-jks` (`jks_expired_certs.py`)
|
|
206
|
+
Audits Java KeyStore (JKS) files for expired certificates using `keytool`.
|
|
207
|
+
|
|
208
|
+
**Technical Challenge:**
|
|
209
|
+
Parsing the output of `keytool -v -list` is non-trivial because the date format depends on the system locale and standard Java output formats. The tool attempts to parse these formats to warn developers before their local dev environments break due to expired SSL certs.
|
|
210
|
+
|
|
211
|
+
#### `msg` (`echo.py`)
|
|
212
|
+
Exposes the internal logging mechanism to the shell. Used by `config_setup.sh` to print consistent success/error messages from shell scripts.
|
|
213
|
+
|
|
214
|
+
---
|
|
215
|
+
|
|
216
|
+
## 4. Utilities & Helpers
|
|
217
|
+
|
|
218
|
+
### 4.1 Output Formatting (`src/mega_snake/util/formatting.py`)
|
|
219
|
+
|
|
220
|
+
** STRICT RULE**: NEVER use `print()`. Always use valid logging/formatting functions.
|
|
221
|
+
|
|
222
|
+
- `ws_info(msg)`: ℹ️ Blue info message.
|
|
223
|
+
- `ws_success(msg)`: ✅ Green success message.
|
|
224
|
+
- `ws_warning(msg)`: ⚠️ Yellow warning.
|
|
225
|
+
- `ws_error(msg)`: ❌ Red error.
|
|
226
|
+
- `ws_advice(msg)`: 💡 Helpful tip/advice.
|
|
227
|
+
|
|
228
|
+
### 4.2 Property Management (`src/mega_snake/util/props.py`)
|
|
229
|
+
|
|
230
|
+
Configuration is layered:
|
|
231
|
+
1. **Hardcoded Defaults**
|
|
232
|
+
2. **`src/config.properties`**: Static project config (versions, default paths).
|
|
233
|
+
3. **Local Overrides**: A local file (usually ignored by git) that overrides specific keys for a specific developer machine.
|
|
234
|
+
|
|
235
|
+
Access properties via `get_property(key)`.
|
|
236
|
+
|
|
237
|
+
### 4.3 Shell Execution (`src/mega_snake/util/util.py`)
|
|
238
|
+
|
|
239
|
+
Use `run_operation` for ALL shell commands. It handles logging, error capturing, and return codes.
|
|
240
|
+
|
|
241
|
+
```python
|
|
242
|
+
result = run_operation(
|
|
243
|
+
command="git fetch --all",
|
|
244
|
+
message="Fetching remotes" # This is logged to console/file
|
|
245
|
+
)
|
|
246
|
+
if result.returncode != 0:
|
|
247
|
+
# handle error...
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## 5. Shell Integration & Deployment
|
|
253
|
+
|
|
254
|
+
### User Installation Flow
|
|
255
|
+
|
|
256
|
+
When end-users install `mega_snake` via `uv tool install` or `pipx install`:
|
|
257
|
+
|
|
258
|
+
1. The package is installed in an isolated virtual environment.
|
|
259
|
+
2. The `mgsnake` command becomes available globally.
|
|
260
|
+
3. Users add initialization code to their shell profile:
|
|
261
|
+
- **Bash/Zsh**: `. "$(mgsnake shell-path bash)"` → outputs the path to `config_setup.sh`
|
|
262
|
+
- **PowerShell**: `. (mgsnake shell-path pwsh)` → outputs the path to `config_setup.ps1`
|
|
263
|
+
4. The initialization script (`config_setup.sh` or `config_setup.ps1`) is sourced, which:
|
|
264
|
+
- Sets `MEGA_SNAKE_SHELL` environment variable
|
|
265
|
+
- Defines `mgsnake_reload` to (re)load the local config file (if present)
|
|
266
|
+
- Calls `mgsnake_reload` once so the local config is applied immediately
|
|
267
|
+
**Why this approach?**
|
|
268
|
+
- Allows the tool to run anywhere without polluting the user's active Python environment
|
|
269
|
+
- Users don't need to manually activate/deactivate virtual environments
|
|
270
|
+
- The `mgsnake` console script runs from its isolated `uv tool`/`pipx` environment
|
|
271
|
+
- Sourcing the shell setup only configures shell integration (`MEGA_SNAKE_SHELL` and `mgsnake_reload`) per session
|
|
272
|
+
|
|
273
|
+
### Local Development Setup
|
|
274
|
+
|
|
275
|
+
**Prerequisites:**
|
|
276
|
+
- Python 3.13+
|
|
277
|
+
- `uv` package manager
|
|
278
|
+
|
|
279
|
+
**Setup Steps:**
|
|
280
|
+
|
|
281
|
+
1. Clone the repository and navigate to the root:
|
|
282
|
+
```bash
|
|
283
|
+
git clone <repo-url>
|
|
284
|
+
cd unix-scripts
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
2. Install dependencies (including dev dependencies):
|
|
288
|
+
```bash
|
|
289
|
+
uv sync --all-extras
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
3. Build the wheel:
|
|
293
|
+
```bash
|
|
294
|
+
uv build
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
4. Install locally for testing:
|
|
298
|
+
```bash
|
|
299
|
+
uv tool install dist/*.whl --force-reinstall
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
5. Add the initialization script to your shell profile (same as end-users do):
|
|
303
|
+
- **Bash/Zsh**: Add `. "$(mgsnake shell-path bash)"` to `~/.bashrc` or `~/.zshrc`
|
|
304
|
+
- **PowerShell**: Add `. (mgsnake shell-path pwsh)` to your PowerShell profile
|
|
305
|
+
|
|
306
|
+
6. Restart your terminal and verify:
|
|
307
|
+
```bash
|
|
308
|
+
mgsnake --help
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
---
|
|
312
|
+
|
|
313
|
+
## 6. Development Rules
|
|
314
|
+
|
|
315
|
+
### 6.1 Code Quality Standards
|
|
316
|
+
|
|
317
|
+
**ALL code must follow these standards without exception:**
|
|
318
|
+
|
|
319
|
+
1. **Type Hinting - MANDATORY for all functions and parameters:**
|
|
320
|
+
- **All function parameters** must have explicit type annotations (e.g., `name: str`, `count: int`)
|
|
321
|
+
- **All function return types** must be explicitly declared (e.g., `-> str`, `-> None`, `-> list[str]`)
|
|
322
|
+
- **Use `Optional[T]`** for optional types instead of `T | None` (e.g., `Optional[str]` not `str | None`)
|
|
323
|
+
- Example:
|
|
324
|
+
```python
|
|
325
|
+
def process_data(items: list[str], timeout: Optional[int] = None) -> dict[str, int]:
|
|
326
|
+
"""Process items with optional timeout."""
|
|
327
|
+
pass
|
|
328
|
+
```
|
|
329
|
+
|
|
330
|
+
2. **Docstrings - MANDATORY for all modules, classes, and functions:**
|
|
331
|
+
- **Module-level docstring**: Must be at the top of every `.py` file
|
|
332
|
+
- **Class docstring**: Required for all class definitions
|
|
333
|
+
- **Function/method docstring**: Required for every function and method (including `__init__`, `__str__`, etc.)
|
|
334
|
+
- **Format**: Use the following structure for methods:
|
|
335
|
+
```python
|
|
336
|
+
"""Brief description of what the method does.
|
|
337
|
+
|
|
338
|
+
Parameters:
|
|
339
|
+
param_name: Description of parameter.
|
|
340
|
+
another_param: Description of another parameter.
|
|
341
|
+
|
|
342
|
+
Raises:
|
|
343
|
+
ValueError: Description of when this exception is raised.
|
|
344
|
+
RuntimeError: Description of when this exception is raised.
|
|
345
|
+
|
|
346
|
+
Returns:
|
|
347
|
+
str: Description of the return value.
|
|
348
|
+
"""
|
|
349
|
+
```
|
|
350
|
+
- **Note**: If there are no parameters, raises, or returns, explicitly state `None` in those sections.
|
|
351
|
+
- Example:
|
|
352
|
+
```python
|
|
353
|
+
def validate_path(path: str) -> bool:
|
|
354
|
+
"""Check if the given path is valid and accessible.
|
|
355
|
+
|
|
356
|
+
Parameters:
|
|
357
|
+
path: The file system path to validate.
|
|
358
|
+
|
|
359
|
+
Raises:
|
|
360
|
+
ValueError: If path is None or empty string.
|
|
361
|
+
|
|
362
|
+
Returns:
|
|
363
|
+
bool: True if the path is valid, False otherwise.
|
|
364
|
+
"""
|
|
365
|
+
pass
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
3. **Imports**: Group imports in this order:
|
|
369
|
+
- Standard Library
|
|
370
|
+
- Third Party
|
|
371
|
+
- Local Application
|
|
372
|
+
- Each group separated by a blank line
|
|
373
|
+
|
|
374
|
+
4. **Error Handling**:
|
|
375
|
+
- Raise `ValueError` for invalid user input
|
|
376
|
+
- Raise `click.ClickException` for expected CLI errors
|
|
377
|
+
- Let unexpected errors bubble up to `__main__.py` to be caught by the global handler
|
|
378
|
+
|
|
379
|
+
5. **Paths**: Always use `pathlib.Path` or `os.path` joins. Never use string concatenation for paths.
|
|
380
|
+
|
|
381
|
+
### 6.2 Testing & Coverage Requirements (CRITICAL)
|
|
382
|
+
|
|
383
|
+
**MANDATORY: Any file created, modified, or deleted must have corresponding tests created, updated, or removed respectively.**
|
|
384
|
+
|
|
385
|
+
#### Core Principles
|
|
386
|
+
|
|
387
|
+
- **Do not simplify, remove, weaken, or rewrite existing passing tests** unless strictly necessary to fix a defect.
|
|
388
|
+
- **Do not exclude files, modules, classes, or functions from the pytest test suite** to artificially increase coverage.
|
|
389
|
+
- **Do not modify source code solely to make testing easier** or to reduce the number of required tests.
|
|
390
|
+
- **New tests must validate real application behavior**, not be written solely to inflate coverage metrics.
|
|
391
|
+
- **Reuse existing fixtures, helpers, and testing patterns** whenever possible to maintain consistency.
|
|
392
|
+
- **Preserve the current project structure and testing conventions** throughout all changes.
|
|
393
|
+
|
|
394
|
+
#### Coverage Requirements
|
|
395
|
+
|
|
396
|
+
- **Overall project coverage**: Minimum 95%
|
|
397
|
+
- **All new or modified source code**: Minimum 98% coverage
|
|
398
|
+
- **All tests must pass** before any PR is submitted
|
|
399
|
+
|
|
400
|
+
#### Testing Workflow
|
|
401
|
+
|
|
402
|
+
1. **New Source Code**: Create comprehensive tests in `src/tests/{module}/` directory. Ensure 98% coverage.
|
|
403
|
+
2. **Modified Source Code**: Update existing tests to reflect changes. Add new tests for new behavior. Maintain 98% coverage.
|
|
404
|
+
3. **Deleted Source Code**: Remove or update corresponding tests in `src/tests/{module}/` directory.
|
|
405
|
+
4. **Run Tests**: Execute `pytest` to verify all tests pass and coverage goals are met.
|
|
406
|
+
|
|
407
|
+
```bash
|
|
408
|
+
# Run full test suite with coverage reporting
|
|
409
|
+
pytest
|
|
410
|
+
|
|
411
|
+
# This generates:
|
|
412
|
+
# - report.html: HTML report of test results
|
|
413
|
+
# - coverage_html/index.html: Detailed coverage breakdown by file
|
|
414
|
+
# - Fails if coverage < 95% overall or < 98% for new code
|
|
415
|
+
```
|
|
416
|
+
|
|
417
|
+
#### Example: What This Means
|
|
418
|
+
|
|
419
|
+
**If you modify `config_environment/java_set.py`:**
|
|
420
|
+
- Update tests in `src/tests/config_environment/test_java_set.py`
|
|
421
|
+
- Add tests for any new functions or branches
|
|
422
|
+
- Ensure the modified functions have 98% coverage
|
|
423
|
+
- Verify overall project coverage remains ≥ 95%
|
|
424
|
+
|
|
425
|
+
**If you create `util/new_helper.py`:**
|
|
426
|
+
- Create `src/tests/util/test_new_helper.py` with comprehensive tests
|
|
427
|
+
- All functions must have 98% coverage
|
|
428
|
+
- Tests must validate real behavior, not just code paths
|
|
429
|
+
|
|
430
|
+
**If you delete a module:**
|
|
431
|
+
- Remove its corresponding test file or test class
|
|
432
|
+
- Verify overall coverage still meets 95% threshold
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
name: Copilot Setup Steps
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
workflow_dispatch:
|
|
5
|
+
push:
|
|
6
|
+
paths:
|
|
7
|
+
- .github/workflows/copilot-setup-steps.yml
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
copilot-setup-steps: # This name is mandatory
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
permissions:
|
|
13
|
+
contents: read # This is mandatory to allow the action to read the repository contents
|
|
14
|
+
steps:
|
|
15
|
+
- name: Checkout code
|
|
16
|
+
uses: actions/checkout@v4
|
|
17
|
+
|
|
18
|
+
- name: Set up Python 3.13
|
|
19
|
+
uses: actions/setup-python@v5
|
|
20
|
+
with:
|
|
21
|
+
python-version: '3.13'
|
|
22
|
+
|
|
23
|
+
- name: Upgrade pip
|
|
24
|
+
run: |
|
|
25
|
+
python -m pip install --upgrade pip setuptools wheel
|
|
26
|
+
|
|
27
|
+
- name: Install uv
|
|
28
|
+
uses: astral-sh/setup-uv@v6
|
|
29
|
+
|
|
30
|
+
- name: Install Rust toolchain
|
|
31
|
+
uses: dtolnay/rust-toolchain@stable
|
|
32
|
+
|
|
33
|
+
- name: Verify toolchain
|
|
34
|
+
run: |
|
|
35
|
+
python --version
|
|
36
|
+
pip --version
|
|
37
|
+
uv --version
|
|
38
|
+
rustc --version
|
|
39
|
+
cargo --version
|
|
40
|
+
|
|
41
|
+
# Opcional: crea un .venv para que Copilot lo tenga listo
|
|
42
|
+
- name: Create virtual environment
|
|
43
|
+
run: |
|
|
44
|
+
uv venv
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
name: PR Validation
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
types: [ opened, ready_for_review, reopened, synchronize ]
|
|
6
|
+
|
|
7
|
+
jobs:
|
|
8
|
+
test:
|
|
9
|
+
runs-on: ubuntu-latest
|
|
10
|
+
|
|
11
|
+
steps:
|
|
12
|
+
- uses: actions/checkout@v4
|
|
13
|
+
|
|
14
|
+
- name: Set up Python
|
|
15
|
+
uses: actions/setup-python@v4
|
|
16
|
+
with:
|
|
17
|
+
python-version: '3.13'
|
|
18
|
+
|
|
19
|
+
- name: Install uv
|
|
20
|
+
run: |
|
|
21
|
+
pip install uv
|
|
22
|
+
|
|
23
|
+
- name: Install dependencies
|
|
24
|
+
run: |
|
|
25
|
+
uv sync --all-extras
|
|
26
|
+
|
|
27
|
+
- name: Run pytest
|
|
28
|
+
run: |
|
|
29
|
+
uv run pytest
|
|
30
|
+
|
|
31
|
+
- name: Run ruff check
|
|
32
|
+
run: |
|
|
33
|
+
uv run ruff check src/mega_snake --output-format=concise
|
|
34
|
+
|
|
35
|
+
- name: Run mypy
|
|
36
|
+
run: |
|
|
37
|
+
uv run mypy --show-column-numbers --no-error-summary src/mega_snake
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
.DS_Store
|
|
2
|
+
mega_snake/**/__pycache__
|
|
3
|
+
tests/**/__pycache__
|
|
4
|
+
mega_snake/lib
|
|
5
|
+
mega_snake/bin
|
|
6
|
+
mega_snake/include
|
|
7
|
+
pyvenv.cfg
|
|
8
|
+
poetry.lock
|
|
9
|
+
.gradle
|
|
10
|
+
build/
|
|
11
|
+
.vscode
|
|
12
|
+
coverage_html
|
|
13
|
+
.coverage
|
|
14
|
+
report.html
|
|
15
|
+
assets/
|
|
16
|
+
**/__pycache__
|
|
17
|
+
src/tests/util/test_props.py
|
|
18
|
+
workspace_temp
|