Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions .claude/settings.local.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
{
"permissions": {
"allow": [
"Bash(source .venv/bin/activate)",
"Bash(pytest:*)",
"Bash(gh pr checks:*)",
"Bash(gh issue:*)",
"Bash(git checkout:*)",
"Bash(git push:*)",
"Bash(uvx:*)",
"Bash(gh run view:*)",
"Bash(gh pr ready:*)"
],
"deny": [],
"ask": []
}
}
3 changes: 3 additions & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## 4.1.2 (unreleased)

- Fix #34: The `offline` configuration setting and `--offline` CLI flag are now properly respected to prevent VCS fetch/update operations. Previously, setting `offline = true` in mx.ini or using the `--offline` CLI flag was ignored, and VCS operations still occurred.
[jensens]

- Fix #46: Git tags in branch option are now correctly detected and handled during updates. Previously, updating from one tag to another failed because tags were incorrectly treated as branches.
[jensens]

Expand Down
64 changes: 64 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -519,6 +519,70 @@ Quick summary:

**CRITICAL: Always follow these steps before pushing code:**

### Bug Fix Workflow (Test-Driven Development - MANDATORY)

**IMPORTANT**: All bug fixes MUST follow the TDD (Test-Driven Development) approach:

1. **Analysis**
- Investigate and understand the root cause
- Identify the exact location and nature of the bug
- Document your findings

2. **Comment on Issue with Analysis Only**
```bash
gh issue comment <ISSUE_NUMBER> --body "Root cause analysis..."
```
- Post detailed root cause analysis to the GitHub issue
- Do NOT include the solution or plan in the comment
- Include code references, line numbers, and explanation

3. **Create Failing Test**
- Create branch: `git checkout -b fix/<issue-number>-description`
- Write a test that reproduces the bug
- Verify the test fails with the current code
- Commit: `git commit -m "Add failing test for issue #XX"`

4. **Push and Create Draft PR**
```bash
git push -u origin fix/<issue-number>-description
gh pr create --draft --title "..." --body "..."
```
- PR body should explain the bug, show the failing test, and describe next steps

5. **Implement the Fix**
- Write the minimal code needed to make the test pass
- Verify the test now passes
- Run all related tests to ensure no regressions

6. **Commit and Push Fix**
```bash
git add <files>
git commit -m "Fix issue #XX: description"
git push
```
- Include issue reference in commit message
- Update CHANGES.md in the same commit or separately

7. **Verify CI is Green**
```bash
gh pr checks <PR_NUMBER>
```
- Wait for all CI checks to pass
- Address any failures

8. **Mark PR Ready for Review**
```bash
gh pr ready <PR_NUMBER>
```
- Only mark ready when all checks are green
- Update PR description if needed

**Why TDD for Bug Fixes?**
- Ensures the bug is actually fixed
- Prevents regressions in the future
- Documents the expected behavior
- Provides confidence in the solution

### Pre-Push Checklist

1. **Always run linting before push**
Expand Down
5 changes: 4 additions & 1 deletion src/mxdev/main.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from .config import Configuration
from .config import to_bool
from .hooks import load_hooks
from .hooks import read_hooks
from .hooks import write_hooks
Expand Down Expand Up @@ -89,7 +90,9 @@ def main() -> None:
read(state)
if not args.fetch_only:
read_hooks(state, hooks)
if not args.no_fetch:
# Skip fetch if --no-fetch flag is set OR if offline mode is enabled
offline = to_bool(state.configuration.settings.get("offline", False))
if not args.no_fetch and not offline:
fetch(state)
if args.fetch_only:
return
Expand Down
6 changes: 5 additions & 1 deletion src/mxdev/processing.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,8 @@ def read(state: State) -> None:

def fetch(state: State) -> None:
"""Fetch all configured sources from a VCS."""
from .config import to_bool

packages = state.configuration.packages
logger.info("#" * 79)
if not packages:
Expand All @@ -192,13 +194,15 @@ def fetch(state: State) -> None:
workingcopies = WorkingCopies(
packages, threads=int(state.configuration.settings["threads"])
)
# Pass offline setting from configuration instead of hardcoding False
offline = to_bool(state.configuration.settings.get("offline", False))
workingcopies.checkout(
sorted(packages),
verbose=False,
update=True,
submodules="always",
always_accept_server_certificate=True,
offline=False,
offline=offline,
)


Expand Down
5 changes: 5 additions & 0 deletions src/mxdev/vcs/common.py
Original file line number Diff line number Diff line change
Expand Up @@ -288,6 +288,11 @@ def status(

def update(self, packages: typing.Iterable[str], **kwargs) -> None:
the_queue: queue.Queue = queue.Queue()
# Check for offline mode early - skip all updates if offline
offline = kwargs.get("offline", False)
if offline:
logger.info("Skipped updates (offline mode)")
return
for name in packages:
kw = kwargs.copy()
if name not in self.sources:
Expand Down
49 changes: 49 additions & 0 deletions tests/test_git.py
Original file line number Diff line number Diff line change
Expand Up @@ -259,3 +259,52 @@ def test_update_git_tag_to_new_tag(mkgitrepo, src):
result = repository.process.check_call(f"git -C {path} describe --tags", echo=False)
current_tag = result[0].decode("utf8").strip()
assert current_tag == "2.0.0"


def test_offline_prevents_vcs_operations(mkgitrepo, src):
"""Test that offline mode prevents VCS fetch/update operations.

This test reproduces issue #34: offline setting should prevent VCS operations
but is currently being ignored.

When offline=True is set (either in config or via CLI --offline flag),
mxdev should NOT perform any VCS operations (no fetch, no update).
"""
repository = mkgitrepo("repository")
path = src / "egg"

# Create initial content
repository.add_file("foo", msg="Initial")

sources = {
"egg": dict(
vcs="git",
name="egg",
url=str(repository.base),
path=str(path),
)
}
packages = ["egg"]
verbose = False

# Initial checkout (not offline)
vcs_checkout(sources, packages, verbose, offline=False)
assert {x for x in path.iterdir()} == {path / ".git", path / "foo"}

# Add new content to remote repository
repository.add_file("bar", msg="Second")

# Try to update with offline=True
# BUG: This should NOT fetch/update anything, but currently it does
# because offline parameter is ignored
vcs_update(sources, packages, verbose, offline=True)

# After offline update, should still have only initial content (foo)
# The "bar" file should NOT be present because offline prevented the update
assert {x for x in path.iterdir()} == {path / ".git", path / "foo"}

# Now update with offline=False to verify update works when not offline
vcs_update(sources, packages, verbose, offline=False)

# After normal update, should have both files
assert {x for x in path.iterdir()} == {path / ".git", path / "foo", path / "bar"}