moai-adk 0.8.2__py3-none-any.whl → 0.9.0__py3-none-any.whl
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.
Potentially problematic release.
This version of moai-adk might be problematic. Click here for more details.
- moai_adk/core/config/migration.py +1 -1
- moai_adk/core/issue_creator.py +7 -3
- moai_adk/core/tags/__init__.py +23 -24
- moai_adk/core/tags/ci_validator.py +3 -5
- moai_adk/core/tags/cli.py +2 -2
- moai_adk/core/tags/pre_commit_validator.py +5 -5
- moai_adk/core/tags/reporter.py +3 -5
- moai_adk/core/tags/validator.py +3 -3
- moai_adk/templates/.claude/commands/alfred/1-plan.md +65 -15
- moai_adk/templates/.claude/commands/alfred/2-run.md +65 -15
- moai_adk/templates/.claude/commands/alfred/3-sync.md +68 -14
- moai_adk/templates/.claude/hooks/alfred/core/project.py +750 -0
- moai_adk/templates/.claude/hooks/alfred/core/version_cache.py +1 -1
- moai_adk/templates/.claude/hooks/alfred/shared/core/project.py +111 -33
- moai_adk/templates/.claude/hooks/alfred/shared/core/version_cache.py +3 -3
- moai_adk/templates/.moai/memory/gitflow-protection-policy.md +330 -0
- moai_adk/templates/README.md +256 -0
- {moai_adk-0.8.2.dist-info → moai_adk-0.9.0.dist-info}/METADATA +347 -48
- {moai_adk-0.8.2.dist-info → moai_adk-0.9.0.dist-info}/RECORD +22 -36
- moai_adk/templates/.claude/hooks/alfred/.moai/cache/version-check.json +0 -9
- moai_adk/templates/.claude/hooks/alfred/README.md +0 -343
- moai_adk/templates/.claude/hooks/alfred/TROUBLESHOOTING.md +0 -471
- moai_adk/templates/.github/workflows/tag-report.yml +0 -261
- moai_adk/templates/.github/workflows/tag-validation.yml +0 -176
- moai_adk/templates/.moai/docs/quick-issue-creation-guide.md +0 -219
- moai_adk/templates/.moai/hooks/install.sh +0 -79
- moai_adk/templates/.moai/hooks/pre-commit.sh +0 -66
- moai_adk/templates/.moai/memory/CONFIG-SCHEMA.md +0 -444
- moai_adk/templates/.moai/memory/GITFLOW-PROTECTION-POLICY.md +0 -220
- moai_adk/templates/src/moai_adk/core/__init__.py +0 -5
- moai_adk/templates/src/moai_adk/core/tags/__init__.py +0 -87
- moai_adk/templates/src/moai_adk/core/tags/ci_validator.py +0 -435
- moai_adk/templates/src/moai_adk/core/tags/cli.py +0 -283
- moai_adk/templates/src/moai_adk/core/tags/pre_commit_validator.py +0 -355
- moai_adk/templates/src/moai_adk/core/tags/reporter.py +0 -959
- moai_adk/templates/src/moai_adk/core/tags/validator.py +0 -897
- {moai_adk-0.8.2.dist-info → moai_adk-0.9.0.dist-info}/WHEEL +0 -0
- {moai_adk-0.8.2.dist-info → moai_adk-0.9.0.dist-info}/entry_points.txt +0 -0
- {moai_adk-0.8.2.dist-info → moai_adk-0.9.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -16,6 +16,58 @@ from typing import Any
|
|
|
16
16
|
CACHE_DIR_NAME = ".moai/cache"
|
|
17
17
|
|
|
18
18
|
|
|
19
|
+
def find_project_root(start_path: str | Path = ".") -> Path:
|
|
20
|
+
"""Find MoAI-ADK project root by searching upward for .moai/config.json
|
|
21
|
+
|
|
22
|
+
Traverses up the directory tree until it finds .moai/config.json or CLAUDE.md,
|
|
23
|
+
which indicates the project root. This ensures cache and other files are
|
|
24
|
+
always created in the correct location, regardless of where hooks execute.
|
|
25
|
+
|
|
26
|
+
Args:
|
|
27
|
+
start_path: Starting directory (default: current directory)
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
Project root Path. If not found, returns start_path as absolute path.
|
|
31
|
+
|
|
32
|
+
Examples:
|
|
33
|
+
>>> find_project_root(".")
|
|
34
|
+
Path("/Users/user/my-project")
|
|
35
|
+
>>> find_project_root(".claude/hooks/alfred")
|
|
36
|
+
Path("/Users/user/my-project") # Found root 3 levels up
|
|
37
|
+
|
|
38
|
+
Notes:
|
|
39
|
+
- Searches for .moai/config.json first (most reliable)
|
|
40
|
+
- Falls back to CLAUDE.md if config.json not found
|
|
41
|
+
- Max depth: 10 levels up (prevent infinite loop)
|
|
42
|
+
- Returns absolute path for consistency
|
|
43
|
+
|
|
44
|
+
TDD History:
|
|
45
|
+
- RED: 4 test scenarios (root, nested, not found, symlinks)
|
|
46
|
+
- GREEN: Minimal upward search with .moai/config.json detection
|
|
47
|
+
- REFACTOR: Add CLAUDE.md fallback, max depth limit, absolute path return
|
|
48
|
+
"""
|
|
49
|
+
current = Path(start_path).resolve()
|
|
50
|
+
max_depth = 10 # Prevent infinite loop
|
|
51
|
+
|
|
52
|
+
for _ in range(max_depth):
|
|
53
|
+
# Check for .moai/config.json (primary indicator)
|
|
54
|
+
if (current / ".moai" / "config.json").exists():
|
|
55
|
+
return current
|
|
56
|
+
|
|
57
|
+
# Check for CLAUDE.md (secondary indicator)
|
|
58
|
+
if (current / "CLAUDE.md").exists():
|
|
59
|
+
return current
|
|
60
|
+
|
|
61
|
+
# Move up one level
|
|
62
|
+
parent = current.parent
|
|
63
|
+
if parent == current: # Reached filesystem root
|
|
64
|
+
break
|
|
65
|
+
current = parent
|
|
66
|
+
|
|
67
|
+
# Not found - return start_path as absolute
|
|
68
|
+
return Path(start_path).resolve()
|
|
69
|
+
|
|
70
|
+
|
|
19
71
|
class TimeoutError(Exception):
|
|
20
72
|
"""Signal-based timeout exception"""
|
|
21
73
|
pass
|
|
@@ -246,7 +298,7 @@ def count_specs(cwd: str) -> dict[str, int]:
|
|
|
246
298
|
Counts the number of SPECs with status: completed.
|
|
247
299
|
|
|
248
300
|
Args:
|
|
249
|
-
cwd: Project root directory path
|
|
301
|
+
cwd: Project root directory path (or any subdirectory, will search upward)
|
|
250
302
|
|
|
251
303
|
Returns:
|
|
252
304
|
SPEC progress dictionary. Includes the following keys:
|
|
@@ -264,15 +316,19 @@ def count_specs(cwd: str) -> dict[str, int]:
|
|
|
264
316
|
|
|
265
317
|
Notes:
|
|
266
318
|
- SPEC File Location: .moai/specs/SPEC-{ID}/spec.md
|
|
267
|
-
- Completion condition: Include
|
|
319
|
+
- Completion condition: Include "status: completed" in YAML front matter
|
|
268
320
|
- If parsing fails, the SPEC is considered incomplete.
|
|
321
|
+
- Automatically finds project root to locate .moai/specs/
|
|
269
322
|
|
|
270
323
|
TDD History:
|
|
271
324
|
- RED: 5 items scenario test (0/0, 2/5, 5/5, no directory, parsing error)
|
|
272
325
|
- GREEN: SPEC search with Path.iterdir(), YAML parsing implementation
|
|
273
326
|
- REFACTOR: Strengthened exception handling, improved percentage calculation safety
|
|
327
|
+
- UPDATE: Add project root detection for consistent path resolution
|
|
274
328
|
"""
|
|
275
|
-
|
|
329
|
+
# Find project root to ensure we read specs from correct location
|
|
330
|
+
project_root = find_project_root(cwd)
|
|
331
|
+
specs_dir = project_root / ".moai" / "specs"
|
|
276
332
|
|
|
277
333
|
if not specs_dir.exists():
|
|
278
334
|
return {"completed": 0, "total": 0, "percentage": 0}
|
|
@@ -316,7 +372,7 @@ def get_project_language(cwd: str) -> str:
|
|
|
316
372
|
"""Determine the primary project language (prefers config.json).
|
|
317
373
|
|
|
318
374
|
Args:
|
|
319
|
-
cwd: Project root directory.
|
|
375
|
+
cwd: Project root directory (or any subdirectory, will search upward).
|
|
320
376
|
|
|
321
377
|
Returns:
|
|
322
378
|
Language string in lower-case.
|
|
@@ -324,8 +380,11 @@ def get_project_language(cwd: str) -> str:
|
|
|
324
380
|
Notes:
|
|
325
381
|
- Reads ``.moai/config.json`` first for a quick answer.
|
|
326
382
|
- Falls back to ``detect_language`` if configuration is missing.
|
|
383
|
+
- Automatically finds project root to locate .moai/config.json
|
|
327
384
|
"""
|
|
328
|
-
|
|
385
|
+
# Find project root to ensure we read config from correct location
|
|
386
|
+
project_root = find_project_root(cwd)
|
|
387
|
+
config_path = project_root / ".moai" / "config.json"
|
|
329
388
|
if config_path.exists():
|
|
330
389
|
try:
|
|
331
390
|
config = json.loads(config_path.read_text())
|
|
@@ -336,8 +395,8 @@ def get_project_language(cwd: str) -> str:
|
|
|
336
395
|
# Fall back to detection on parse errors
|
|
337
396
|
pass
|
|
338
397
|
|
|
339
|
-
# Fall back to the original language detection routine
|
|
340
|
-
return detect_language(
|
|
398
|
+
# Fall back to the original language detection routine (use project root)
|
|
399
|
+
return detect_language(str(project_root))
|
|
341
400
|
|
|
342
401
|
|
|
343
402
|
# @CODE:CONFIG-INTEGRATION-001
|
|
@@ -382,7 +441,9 @@ def get_version_check_config(cwd: str) -> dict[str, Any]:
|
|
|
382
441
|
"cache_ttl_hours": 24
|
|
383
442
|
}
|
|
384
443
|
|
|
385
|
-
|
|
444
|
+
# Find project root to ensure we read config from correct location
|
|
445
|
+
project_root = find_project_root(cwd)
|
|
446
|
+
config_path = project_root / ".moai" / "config.json"
|
|
386
447
|
if not config_path.exists():
|
|
387
448
|
return defaults
|
|
388
449
|
|
|
@@ -458,7 +519,7 @@ def is_network_available(timeout_seconds: float = 0.1) -> bool:
|
|
|
458
519
|
return False
|
|
459
520
|
|
|
460
521
|
|
|
461
|
-
# @CODE:
|
|
522
|
+
# @CODE:VERSION-DETECT-MAJOR-001
|
|
462
523
|
def is_major_version_change(current: str, latest: str) -> bool:
|
|
463
524
|
"""Detect if version change is a major version bump.
|
|
464
525
|
|
|
@@ -538,7 +599,7 @@ def get_package_version_info(cwd: str = ".") -> dict[str, Any]:
|
|
|
538
599
|
- RED: 5 test scenarios (network detection, cache integration, offline mode)
|
|
539
600
|
- GREEN: Integrate VersionCache with network detection
|
|
540
601
|
- REFACTOR: Extract cache directory constant, improve error handling
|
|
541
|
-
- Phase 3: Add release_notes_url and is_major_update fields (@CODE:
|
|
602
|
+
- Phase 3: Add release_notes_url and is_major_update fields (@CODE:VERSION-INTEGRATE-FIELDS-001)
|
|
542
603
|
"""
|
|
543
604
|
import importlib.util
|
|
544
605
|
import urllib.error
|
|
@@ -556,52 +617,68 @@ def get_package_version_info(cwd: str = ".") -> dict[str, Any]:
|
|
|
556
617
|
else:
|
|
557
618
|
# Skip caching if module can't be loaded
|
|
558
619
|
VersionCache = None
|
|
559
|
-
except (ImportError, OSError)
|
|
620
|
+
except (ImportError, OSError):
|
|
560
621
|
# Graceful degradation: skip caching on import errors
|
|
561
622
|
VersionCache = None
|
|
562
623
|
|
|
563
|
-
# 1.
|
|
564
|
-
|
|
624
|
+
# 1. Find project root (ensure cache is always in correct location)
|
|
625
|
+
# This prevents creating .moai/cache in wrong locations when hooks run
|
|
626
|
+
# from subdirectories like .claude/hooks/alfred/
|
|
627
|
+
project_root = find_project_root(cwd)
|
|
628
|
+
|
|
629
|
+
# 2. Initialize cache (skip if VersionCache couldn't be imported)
|
|
630
|
+
cache_dir = project_root / CACHE_DIR_NAME
|
|
565
631
|
version_cache = VersionCache(cache_dir) if VersionCache else None
|
|
566
632
|
|
|
567
|
-
# 2.
|
|
633
|
+
# 2. Get current installed version first (needed for cache validation)
|
|
634
|
+
current_version = "unknown"
|
|
635
|
+
try:
|
|
636
|
+
current_version = version("moai-adk")
|
|
637
|
+
except PackageNotFoundError:
|
|
638
|
+
current_version = "dev"
|
|
639
|
+
# Dev mode - skip cache and return immediately
|
|
640
|
+
return {
|
|
641
|
+
"current": "dev",
|
|
642
|
+
"latest": "unknown",
|
|
643
|
+
"update_available": False,
|
|
644
|
+
"upgrade_command": ""
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
# 3. Try to load from cache (fast path with version validation)
|
|
568
648
|
if version_cache and version_cache.is_valid():
|
|
569
649
|
cached_info = version_cache.load()
|
|
570
650
|
if cached_info:
|
|
571
|
-
#
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
651
|
+
# Only use cache if the cached version matches current installed version
|
|
652
|
+
# This prevents stale cache when package is upgraded locally
|
|
653
|
+
if cached_info.get("current") == current_version:
|
|
654
|
+
# Ensure new fields exist for backward compatibility
|
|
655
|
+
if "release_notes_url" not in cached_info:
|
|
656
|
+
# Add missing fields to old cached data
|
|
657
|
+
cached_info.setdefault("release_notes_url", None)
|
|
658
|
+
cached_info.setdefault("is_major_update", False)
|
|
659
|
+
return cached_info
|
|
660
|
+
# else: cache is stale (version changed), fall through to re-check
|
|
661
|
+
|
|
662
|
+
# 4. Cache miss or stale - need to query PyPI
|
|
579
663
|
result = {
|
|
580
|
-
"current":
|
|
664
|
+
"current": current_version,
|
|
581
665
|
"latest": "unknown",
|
|
582
666
|
"update_available": False,
|
|
583
667
|
"upgrade_command": ""
|
|
584
668
|
}
|
|
585
669
|
|
|
586
|
-
#
|
|
587
|
-
try:
|
|
588
|
-
result["current"] = version("moai-adk")
|
|
589
|
-
except PackageNotFoundError:
|
|
590
|
-
result["current"] = "dev"
|
|
591
|
-
return result
|
|
592
|
-
|
|
593
|
-
# 4. Check if version check is enabled in config (Phase 4)
|
|
670
|
+
# 5. Check if version check is enabled in config
|
|
594
671
|
config = get_version_check_config(cwd)
|
|
595
672
|
if not config["enabled"]:
|
|
596
673
|
# Version check disabled - return only current version
|
|
597
674
|
return result
|
|
598
675
|
|
|
599
|
-
#
|
|
676
|
+
# 6. Check network before PyPI query
|
|
600
677
|
if not is_network_available():
|
|
601
678
|
# Offline mode - return current version only
|
|
602
679
|
return result
|
|
603
680
|
|
|
604
|
-
#
|
|
681
|
+
# 7. Network available - query PyPI
|
|
605
682
|
pypi_data = None
|
|
606
683
|
try:
|
|
607
684
|
with timeout_handler(1):
|
|
@@ -661,6 +738,7 @@ def get_package_version_info(cwd: str = ".") -> dict[str, Any]:
|
|
|
661
738
|
|
|
662
739
|
|
|
663
740
|
__all__ = [
|
|
741
|
+
"find_project_root",
|
|
664
742
|
"detect_language",
|
|
665
743
|
"get_git_info",
|
|
666
744
|
"count_specs",
|
|
@@ -37,12 +37,12 @@ class VersionCache:
|
|
|
37
37
|
'0.8.1'
|
|
38
38
|
"""
|
|
39
39
|
|
|
40
|
-
def __init__(self, cache_dir: Path, ttl_hours: int =
|
|
40
|
+
def __init__(self, cache_dir: Path, ttl_hours: int = 4):
|
|
41
41
|
"""Initialize cache with TTL in hours
|
|
42
42
|
|
|
43
43
|
Args:
|
|
44
44
|
cache_dir: Directory where cache file will be stored
|
|
45
|
-
ttl_hours: Time-to-live in hours (default
|
|
45
|
+
ttl_hours: Time-to-live in hours (default 4)
|
|
46
46
|
"""
|
|
47
47
|
self.cache_dir = Path(cache_dir)
|
|
48
48
|
self.ttl_hours = ttl_hours
|
|
@@ -149,7 +149,7 @@ class VersionCache:
|
|
|
149
149
|
|
|
150
150
|
return True
|
|
151
151
|
|
|
152
|
-
except (OSError, TypeError)
|
|
152
|
+
except (OSError, TypeError):
|
|
153
153
|
# Graceful degradation on write errors
|
|
154
154
|
return False
|
|
155
155
|
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
# GitFlow Protection Policy
|
|
2
|
+
|
|
3
|
+
**Document ID**: @DOC:GITFLOW-POLICY-ALIAS
|
|
4
|
+
**Published**: 2025-10-17
|
|
5
|
+
**Updated**: 2025-10-29
|
|
6
|
+
**Status**: **Enforced via GitHub Branch Protection** (v0.8.3+)
|
|
7
|
+
**Scope**: Personal and Team modes
|
|
8
|
+
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
## Overview
|
|
12
|
+
|
|
13
|
+
MoAI-ADK **enforces** a GitFlow-inspired workflow through GitHub Branch Protection. As of v0.8.3, the `main` branch is protected and requires Pull Requests for all changes, including from administrators.
|
|
14
|
+
|
|
15
|
+
**What Changed**: Previously (v0.3.5-v0.8.2), we used an advisory approach with warnings. Now we enforce proper GitFlow to ensure code quality and prevent accidental direct pushes to main.
|
|
16
|
+
|
|
17
|
+
## Key Requirements (Enforced)
|
|
18
|
+
|
|
19
|
+
### 1. Main Branch Access (Enforced)
|
|
20
|
+
|
|
21
|
+
| Requirement | Summary | Enforcement |
|
|
22
|
+
|-------------|---------|-------------|
|
|
23
|
+
| **Merge via develop** | MUST merge `develop` into `main` | ✅ Enforced |
|
|
24
|
+
| **Feature branches off develop** | MUST branch from `develop` and raise PRs back to `develop` | ✅ Enforced |
|
|
25
|
+
| **Release process** | Release flow: `develop` → `main` (PR required) | ✅ Enforced |
|
|
26
|
+
| **Force push** | Blocked on `main` | ✅ Blocked |
|
|
27
|
+
| **Direct push** | Blocked on `main` (PR required) | ✅ Blocked |
|
|
28
|
+
|
|
29
|
+
### 2. Git Workflow (Required)
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
┌─────────────────────────────────────────────────────────┐
|
|
33
|
+
│ ENFORCED GITFLOW │
|
|
34
|
+
│ (GitHub Branch Protection Active) │
|
|
35
|
+
└─────────────────────────────────────────────────────────┘
|
|
36
|
+
|
|
37
|
+
develop (required base branch)
|
|
38
|
+
↑ ↓
|
|
39
|
+
┌─────────────────┐
|
|
40
|
+
│ │
|
|
41
|
+
│ developer work │
|
|
42
|
+
│ │
|
|
43
|
+
↓ ↑
|
|
44
|
+
feature/SPEC-{ID} [PR: feature -> develop]
|
|
45
|
+
[code review + approval]
|
|
46
|
+
[Merge to develop]
|
|
47
|
+
|
|
48
|
+
develop (stable)
|
|
49
|
+
↓
|
|
50
|
+
│ (release manager prepares)
|
|
51
|
+
↓
|
|
52
|
+
[PR: develop -> main]
|
|
53
|
+
[Code review + approval REQUIRED]
|
|
54
|
+
[All discussions resolved]
|
|
55
|
+
[CI/CD validation]
|
|
56
|
+
[tag creation]
|
|
57
|
+
↓
|
|
58
|
+
main (protected release)
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**Enforcement**: Direct pushes to `main` are **blocked** via GitHub Branch Protection. All changes must go through Pull Requests.
|
|
62
|
+
|
|
63
|
+
## Technical Implementation
|
|
64
|
+
|
|
65
|
+
### Pre-push Hook (Advisory Mode)
|
|
66
|
+
|
|
67
|
+
**Location**: `.git/hooks/pre-push`
|
|
68
|
+
**Purpose**: Warn on `main` branch pushes without blocking them
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
# When attempting to push to main:
|
|
72
|
+
⚠️ ADVISORY: Non-standard GitFlow detected
|
|
73
|
+
|
|
74
|
+
Current branch: feature/SPEC-123
|
|
75
|
+
Target branch: main
|
|
76
|
+
|
|
77
|
+
Recommended GitFlow workflow:
|
|
78
|
+
1. Work on feature/SPEC-{ID} branch (created from develop)
|
|
79
|
+
2. Push to feature/SPEC-{ID} and create PR to develop
|
|
80
|
+
3. Merge into develop after code review
|
|
81
|
+
4. When develop is stable, create PR from develop to main
|
|
82
|
+
5. Release manager merges develop -> main with tag
|
|
83
|
+
|
|
84
|
+
✓ Push will proceed (flexibility mode enabled)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Force Push Advisory
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
⚠️ ADVISORY: Force-push to main branch detected
|
|
91
|
+
|
|
92
|
+
Recommended approach:
|
|
93
|
+
- Use GitHub PR with proper code review
|
|
94
|
+
- Ensure changes are merged via fast-forward
|
|
95
|
+
|
|
96
|
+
✓ Push will proceed (flexibility mode enabled)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
---
|
|
100
|
+
|
|
101
|
+
## Workflow Examples
|
|
102
|
+
|
|
103
|
+
### Scenario 1: Standard Feature Development (Recommended)
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
# 1. Sync latest code from develop
|
|
107
|
+
git checkout develop
|
|
108
|
+
git pull origin develop
|
|
109
|
+
|
|
110
|
+
# 2. Create a feature branch (from develop)
|
|
111
|
+
git checkout -b feature/SPEC-001-new-feature
|
|
112
|
+
|
|
113
|
+
# 3. Implement the change
|
|
114
|
+
# ... write code and tests ...
|
|
115
|
+
|
|
116
|
+
# 4. Commit
|
|
117
|
+
git add .
|
|
118
|
+
git commit -m "..."
|
|
119
|
+
|
|
120
|
+
# 5. Push
|
|
121
|
+
git push origin feature/SPEC-001-new-feature
|
|
122
|
+
|
|
123
|
+
# 6. Open a PR: feature/SPEC-001-new-feature -> develop
|
|
124
|
+
|
|
125
|
+
# 7. Merge into develop after review and approval
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
### Scenario 2: Fast Hotfix (Flexible)
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
# When an urgent fix is required:
|
|
132
|
+
|
|
133
|
+
# Option 1: Recommended (via develop)
|
|
134
|
+
git checkout develop
|
|
135
|
+
git checkout -b hotfix/critical-bug
|
|
136
|
+
# ... apply fix ...
|
|
137
|
+
git push origin hotfix/critical-bug
|
|
138
|
+
# Open PRs: hotfix -> develop -> main
|
|
139
|
+
|
|
140
|
+
# Option 2: Direct fix on main (allowed, not recommended)
|
|
141
|
+
git checkout main
|
|
142
|
+
# ... apply fix ...
|
|
143
|
+
git commit -m "Fix critical bug"
|
|
144
|
+
git push origin main # ⚠️ Advisory warning appears but push continues
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
### Scenario 3: Release (Standard or Flexible)
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
# Standard approach (recommended):
|
|
151
|
+
git checkout develop
|
|
152
|
+
gh pr create --base main --head develop --title "Release v1.0.0"
|
|
153
|
+
|
|
154
|
+
# Direct push (allowed):
|
|
155
|
+
git checkout develop
|
|
156
|
+
git push origin main # ⚠️ Advisory warning appears but push continues
|
|
157
|
+
git tag -a v1.0.0 -m "Release v1.0.0"
|
|
158
|
+
git push origin v1.0.0
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## Policy Modes
|
|
164
|
+
|
|
165
|
+
### Strict Mode (Active, v0.8.3+) ✅ ENFORCED
|
|
166
|
+
|
|
167
|
+
**GitHub Branch Protection Enabled**:
|
|
168
|
+
- ✅ **enforce_admins: true** - Administrators must follow all rules
|
|
169
|
+
- ✅ **required_pull_request_reviews** - 1 approval required
|
|
170
|
+
- ✅ **required_conversation_resolution** - All discussions must be resolved
|
|
171
|
+
- ✅ **Block direct pushes to `main`** - PR required for all users
|
|
172
|
+
- ✅ **Block force pushes** - Prevents history rewriting
|
|
173
|
+
- ✅ **Block branch deletion** - Protects main from accidental deletion
|
|
174
|
+
|
|
175
|
+
**What This Means**:
|
|
176
|
+
- ❌ No one (including admins) can push directly to `main`
|
|
177
|
+
- ✅ All changes must go through Pull Requests
|
|
178
|
+
- ✅ PRs require code review approval
|
|
179
|
+
- ✅ All code discussions must be resolved before merge
|
|
180
|
+
- ✅ Enforces proper GitFlow: feature → develop → main
|
|
181
|
+
|
|
182
|
+
### Advisory Mode (Legacy, v0.3.5 - v0.8.2)
|
|
183
|
+
|
|
184
|
+
- ⚠️ Warned but allowed direct pushes to `main`
|
|
185
|
+
- ⚠️ Warned but allowed force pushes
|
|
186
|
+
- ⚠️ Recommended best practices while preserving flexibility
|
|
187
|
+
- ❌ **Deprecated** - Replaced by Strict Mode for better quality control
|
|
188
|
+
|
|
189
|
+
---
|
|
190
|
+
|
|
191
|
+
## Recommended Checklist
|
|
192
|
+
|
|
193
|
+
Every contributor should ensure:
|
|
194
|
+
|
|
195
|
+
- [ ] `.git/hooks/pre-push` exists and is executable (755)
|
|
196
|
+
- [ ] Feature branches fork from `develop`
|
|
197
|
+
- [ ] Pull requests target `develop`
|
|
198
|
+
- [ ] Releases merge `develop` → `main`
|
|
199
|
+
|
|
200
|
+
**Verification Commands**:
|
|
201
|
+
```bash
|
|
202
|
+
ls -la .git/hooks/pre-push
|
|
203
|
+
git branch -vv
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## FAQ
|
|
209
|
+
|
|
210
|
+
**Q: Can we merge into `main` from branches other than `develop`?**
|
|
211
|
+
A: Yes. You will see an advisory warning, but the merge proceeds. The recommended path remains `develop` → `main`.
|
|
212
|
+
|
|
213
|
+
**Q: Are force pushes allowed?**
|
|
214
|
+
A: Yes. You receive a warning, but the push succeeds. Use with caution.
|
|
215
|
+
|
|
216
|
+
**Q: Can we commit/push directly to `main`?**
|
|
217
|
+
A: Yes. Expect an advisory warning, yet the push continues.
|
|
218
|
+
|
|
219
|
+
**Q: Can I disable the hook entirely?**
|
|
220
|
+
A: Yes. Remove `.git/hooks/pre-push` or strip its execute permission.
|
|
221
|
+
|
|
222
|
+
**Q: Why switch to Advisory Mode?**
|
|
223
|
+
A: Advisory Mode was used in v0.3.5-v0.8.2. As of v0.8.3, we've switched to Strict Mode with GitHub Branch Protection for better quality control.
|
|
224
|
+
|
|
225
|
+
**Q: What if develop falls behind main?**
|
|
226
|
+
A: This can happen when hotfixes or releases go directly to main. Regularly sync main → develop to prevent divergence. See "Maintaining develop-main Sync" section below.
|
|
227
|
+
|
|
228
|
+
**Q: Can I bypass branch protection in emergencies?**
|
|
229
|
+
A: No. Even administrators must follow the PR process. For true emergencies, temporarily disable protection via GitHub Settings (requires admin access), but re-enable immediately after.
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Maintaining develop-main Sync
|
|
234
|
+
|
|
235
|
+
### ⚠️ Critical Rule: develop Must Stay Current
|
|
236
|
+
|
|
237
|
+
**Problem**: When main receives direct commits (hotfixes, emergency releases) without syncing back to develop, GitFlow breaks:
|
|
238
|
+
|
|
239
|
+
```
|
|
240
|
+
❌ BAD STATE:
|
|
241
|
+
develop: 3 commits ahead, 29 commits behind main
|
|
242
|
+
- develop has outdated dependencies
|
|
243
|
+
- New features branch from old code
|
|
244
|
+
- Merge conflicts multiply over time
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
### Signs of Drift
|
|
248
|
+
|
|
249
|
+
Monitor for these warnings:
|
|
250
|
+
- `git status` shows "Your branch is X commits behind main"
|
|
251
|
+
- Feature branches conflict with main during PR
|
|
252
|
+
- CI/CD failures due to dependency mismatches
|
|
253
|
+
- Version numbers in develop don't match main
|
|
254
|
+
|
|
255
|
+
### Recovery Procedure
|
|
256
|
+
|
|
257
|
+
When develop falls behind main:
|
|
258
|
+
|
|
259
|
+
1. **Assess the Gap**
|
|
260
|
+
```bash
|
|
261
|
+
git log --oneline develop..main # Commits in main but not develop
|
|
262
|
+
git log --oneline main..develop # Commits in develop but not main
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
2. **Sync Strategy: Merge main into develop (Recommended)**
|
|
266
|
+
```bash
|
|
267
|
+
git checkout develop
|
|
268
|
+
git pull origin develop # Get latest develop
|
|
269
|
+
git merge main # Merge main into develop
|
|
270
|
+
# Resolve conflicts if any (prefer main for version/config files)
|
|
271
|
+
git push origin develop
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
3. **Emergency Only: Reset develop to main (Destructive)**
|
|
275
|
+
```bash
|
|
276
|
+
# ⚠️ ONLY if develop's unique commits are unwanted
|
|
277
|
+
git checkout develop
|
|
278
|
+
git reset --hard main
|
|
279
|
+
git push origin develop --force
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
### Prevention: Regular Sync Schedule
|
|
283
|
+
|
|
284
|
+
**After every main release** (REQUIRED):
|
|
285
|
+
```bash
|
|
286
|
+
# Immediately after merging develop → main:
|
|
287
|
+
git checkout develop
|
|
288
|
+
git merge main
|
|
289
|
+
git push origin develop
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
**Weekly maintenance** (for active projects):
|
|
293
|
+
```bash
|
|
294
|
+
# Every Monday morning:
|
|
295
|
+
git checkout develop
|
|
296
|
+
git pull origin main
|
|
297
|
+
git push origin develop
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
### Real-World Case Study (2025-10-29)
|
|
301
|
+
|
|
302
|
+
**Situation**: develop was 29 commits behind main due to:
|
|
303
|
+
- v0.8.2, v0.8.3 released directly to main
|
|
304
|
+
- No reverse sync to develop
|
|
305
|
+
- Feature branches contained outdated code
|
|
306
|
+
|
|
307
|
+
**Resolution**:
|
|
308
|
+
- Merged main → develop (14 file conflicts)
|
|
309
|
+
- Resolved conflicts prioritizing main's versions
|
|
310
|
+
- TAG validation bypassed for merge commit
|
|
311
|
+
- Enabled Strict Mode to prevent future direct pushes
|
|
312
|
+
|
|
313
|
+
**Lesson**: With Strict Mode active, this won't happen again. All releases must go through develop → main PR flow.
|
|
314
|
+
|
|
315
|
+
---
|
|
316
|
+
|
|
317
|
+
## Policy Change Log
|
|
318
|
+
|
|
319
|
+
| Date | Change | Owner |
|
|
320
|
+
|------|------|--------|
|
|
321
|
+
| 2025-10-17 | Initial policy drafted (Strict Mode) | git-manager |
|
|
322
|
+
| 2025-10-17 | Switched to Advisory Mode (warnings only) | git-manager |
|
|
323
|
+
| 2025-10-29 | **Enabled GitHub Branch Protection (Strict Mode)** | Alfred |
|
|
324
|
+
| 2025-10-29 | Added develop-main sync guidelines and real-world case study | Alfred |
|
|
325
|
+
| 2025-10-29 | Enforced `enforce_admins`, `required_conversation_resolution` | Alfred |
|
|
326
|
+
|
|
327
|
+
---
|
|
328
|
+
|
|
329
|
+
**This policy is advisory—adapt it to fit your project needs.**
|
|
330
|
+
**Reach out to the team lead or release engineer for questions or suggestions.**
|