@mindfoldhq/trellis 0.6.0-beta.5 → 0.6.0-beta.6
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.
- package/dist/migrations/manifests/0.5.11.json +16 -0
- package/dist/migrations/manifests/0.6.0-beta.6.json +16 -0
- package/dist/templates/trellis/config.yaml +18 -0
- package/dist/templates/trellis/scripts/add_session.py +15 -9
- package/dist/templates/trellis/scripts/common/config.py +57 -1
- package/dist/templates/trellis/scripts/common/safe_commit.py +52 -26
- package/dist/templates/trellis/scripts/common/task_store.py +17 -10
- package/package.json +1 -1
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.5.11",
|
|
3
|
+
"description": "Patch: drop 0.5.10 `git add -f` retry + new `session_auto_commit` config + session-start update hint.",
|
|
4
|
+
"breaking": false,
|
|
5
|
+
"recommendMigrate": false,
|
|
6
|
+
"changelog": "**Bug Fixes:**\n- fix(scripts): drop `git add -f` auto-retry from 0.5.10. When `.gitignore` excludes `.trellis/`, `add_session.py` and `task.py archive` print a warning and skip auto-commit instead of force-staging.\n\n**Enhancements:**\n- feat(scripts): new `session_auto_commit: true | false` in `.trellis/config.yaml` (default `true`). Set `false` to skip auto stage + commit — journal / archive files still write to disk. Closes #245.\n- feat(scripts): `get_context.py` shows `Trellis update available: <current> -> <latest>` once per session when local install lags. 1-second timeout, failures silent. Closes #254.",
|
|
7
|
+
"migrations": [],
|
|
8
|
+
"configSectionsAdded": [
|
|
9
|
+
{
|
|
10
|
+
"file": ".trellis/config.yaml",
|
|
11
|
+
"sentinel": "session_auto_commit:",
|
|
12
|
+
"sectionHeading": "Session Auto-Commit"
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"notes": "Patch on top of 0.5.10. Run `trellis update` — your `.trellis/config.yaml` gets a commented-out `session_auto_commit: true` block appended automatically. Uncomment and flip to `false` if your `.gitignore` excludes `.trellis/` and you want auto-commit off entirely."
|
|
16
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": "0.6.0-beta.6",
|
|
3
|
+
"description": "Beta patch: brings v0.5.11 fixes into 0.6 beta line — drops `git add -f` auto-retry + new `session_auto_commit` config.",
|
|
4
|
+
"breaking": false,
|
|
5
|
+
"recommendMigrate": false,
|
|
6
|
+
"changelog": "**Bug Fixes:**\n- fix(scripts): drop `git add -f` auto-retry from 0.6.0-beta.5. When `.gitignore` excludes `.trellis/`, `add_session.py` and `task.py archive` print a warning and skip auto-commit instead of force-staging.\n\n**Enhancements:**\n- feat(scripts): new `session_auto_commit: true | false` in `.trellis/config.yaml` (default `true`). Set `false` to skip auto stage + commit — journal / archive files still write to disk. Closes #245.",
|
|
7
|
+
"migrations": [],
|
|
8
|
+
"configSectionsAdded": [
|
|
9
|
+
{
|
|
10
|
+
"file": ".trellis/config.yaml",
|
|
11
|
+
"sentinel": "session_auto_commit:",
|
|
12
|
+
"sectionHeading": "Session Auto-Commit"
|
|
13
|
+
}
|
|
14
|
+
],
|
|
15
|
+
"notes": "Beta patch on top of 0.6.0-beta.5. Run `trellis update` — your `.trellis/config.yaml` gets a commented-out `session_auto_commit: true` block appended automatically. Uncomment and flip to `false` if your `.gitignore` excludes `.trellis/` and you want auto-commit off entirely."
|
|
16
|
+
}
|
|
@@ -14,6 +14,24 @@ session_commit_message: "chore: record journal"
|
|
|
14
14
|
# Maximum lines per journal file before rotating to a new one
|
|
15
15
|
max_journal_lines: 2000
|
|
16
16
|
|
|
17
|
+
#-------------------------------------------------------------------------------
|
|
18
|
+
# Session Auto-Commit
|
|
19
|
+
#-------------------------------------------------------------------------------
|
|
20
|
+
|
|
21
|
+
# Auto-commit behavior for session journal + task archive operations.
|
|
22
|
+
# - true (default): scripts auto-stage and auto-commit journal / task changes
|
|
23
|
+
# after add_session.py / task.py archive runs.
|
|
24
|
+
# - false: scripts do not touch git. Files (journal-*.md, task archive moves)
|
|
25
|
+
# are still written to disk; you decide whether to git add / commit.
|
|
26
|
+
#
|
|
27
|
+
# Use `false` if your project's .gitignore intentionally excludes `.trellis/`
|
|
28
|
+
# and you want session data kept local-only, or if you prefer to review
|
|
29
|
+
# staged changes manually before each commit.
|
|
30
|
+
#
|
|
31
|
+
# Accepts: true / false / yes / no / 1 / 0 / on / off (case-insensitive).
|
|
32
|
+
#
|
|
33
|
+
# session_auto_commit: true
|
|
34
|
+
|
|
17
35
|
#-------------------------------------------------------------------------------
|
|
18
36
|
# Task Lifecycle Hooks
|
|
19
37
|
#-------------------------------------------------------------------------------
|
|
@@ -44,6 +44,7 @@ from common.safe_commit import (
|
|
|
44
44
|
from common.tasks import load_task
|
|
45
45
|
from common.config import (
|
|
46
46
|
get_packages,
|
|
47
|
+
get_session_auto_commit,
|
|
47
48
|
get_session_commit_message,
|
|
48
49
|
get_max_journal_lines,
|
|
49
50
|
is_monorepo,
|
|
@@ -322,16 +323,27 @@ def _auto_commit_workspace(repo_root: Path) -> None:
|
|
|
322
323
|
|
|
323
324
|
Path scope is restricted to specific products (journal files, index.md,
|
|
324
325
|
active task dirs, the archive subtree). We never `git add` the whole
|
|
325
|
-
`.trellis/` tree, and if `.gitignore` blocks the specific paths we
|
|
326
|
-
|
|
326
|
+
`.trellis/` tree, and if `.gitignore` blocks the specific paths we
|
|
327
|
+
warn + skip — never retry with ``-f``.
|
|
328
|
+
|
|
329
|
+
Honors ``session_auto_commit`` in ``.trellis/config.yaml``: when set to
|
|
330
|
+
``false``, this function returns immediately without touching git
|
|
331
|
+
(journal/index files are still written to disk by the caller).
|
|
327
332
|
"""
|
|
333
|
+
if not get_session_auto_commit(repo_root):
|
|
334
|
+
print(
|
|
335
|
+
"[OK] session_auto_commit: false — skipping git stage/commit.",
|
|
336
|
+
file=sys.stderr,
|
|
337
|
+
)
|
|
338
|
+
return
|
|
339
|
+
|
|
328
340
|
commit_msg = get_session_commit_message(repo_root)
|
|
329
341
|
paths = safe_trellis_paths_to_add(repo_root)
|
|
330
342
|
if not paths:
|
|
331
343
|
print("[OK] No workspace changes to commit.", file=sys.stderr)
|
|
332
344
|
return
|
|
333
345
|
|
|
334
|
-
success,
|
|
346
|
+
success, _, err = safe_git_add(paths, repo_root)
|
|
335
347
|
if not success:
|
|
336
348
|
if err and "ignored by" in err.lower():
|
|
337
349
|
print_gitignore_warning(paths)
|
|
@@ -342,12 +354,6 @@ def _auto_commit_workspace(repo_root: Path) -> None:
|
|
|
342
354
|
)
|
|
343
355
|
return
|
|
344
356
|
|
|
345
|
-
if used_force:
|
|
346
|
-
print(
|
|
347
|
-
"[OK] Staged Trellis-owned paths with -f (specific paths, not .trellis/).",
|
|
348
|
-
file=sys.stderr,
|
|
349
|
-
)
|
|
350
|
-
|
|
351
357
|
# Check if there are staged changes for the paths we just staged.
|
|
352
358
|
rc, _, _ = run_git(
|
|
353
359
|
["diff", "--cached", "--quiet", "--", *paths], cwd=repo_root
|
|
@@ -36,6 +36,29 @@ def _unquote(s: str) -> str:
|
|
|
36
36
|
return s
|
|
37
37
|
|
|
38
38
|
|
|
39
|
+
def _strip_inline_comment(value: str) -> str:
|
|
40
|
+
"""Strip ` # …` inline comments while preserving `#` inside quoted strings.
|
|
41
|
+
|
|
42
|
+
YAML treats ` #` (space-hash) as a comment opener; bare `#` inside a token
|
|
43
|
+
is part of the value. Quoted strings are immune.
|
|
44
|
+
|
|
45
|
+
Mirrors :func:`common.trellis_config._strip_inline_comment` so both
|
|
46
|
+
parsers handle ``key: value # comment`` identically.
|
|
47
|
+
"""
|
|
48
|
+
in_quote: str | None = None
|
|
49
|
+
for idx, ch in enumerate(value):
|
|
50
|
+
if in_quote:
|
|
51
|
+
if ch == in_quote:
|
|
52
|
+
in_quote = None
|
|
53
|
+
continue
|
|
54
|
+
if ch in ('"', "'"):
|
|
55
|
+
in_quote = ch
|
|
56
|
+
continue
|
|
57
|
+
if ch == "#" and (idx == 0 or value[idx - 1].isspace()):
|
|
58
|
+
return value[:idx]
|
|
59
|
+
return value
|
|
60
|
+
|
|
61
|
+
|
|
39
62
|
def parse_simple_yaml(content: str) -> dict:
|
|
40
63
|
"""Parse simple YAML with nested dict support (no dependencies).
|
|
41
64
|
|
|
@@ -93,7 +116,8 @@ def _parse_yaml_block(
|
|
|
93
116
|
elif ":" in stripped:
|
|
94
117
|
key, _, value = stripped.partition(":")
|
|
95
118
|
key = key.strip()
|
|
96
|
-
value =
|
|
119
|
+
value = _strip_inline_comment(value).strip()
|
|
120
|
+
value = _unquote(value)
|
|
97
121
|
current_list = None
|
|
98
122
|
|
|
99
123
|
if value:
|
|
@@ -142,6 +166,7 @@ def _next_content_line(lines: list[str], start: int) -> tuple[int, str]:
|
|
|
142
166
|
# Defaults
|
|
143
167
|
DEFAULT_SESSION_COMMIT_MESSAGE = "chore: record journal"
|
|
144
168
|
DEFAULT_MAX_JOURNAL_LINES = 2000
|
|
169
|
+
DEFAULT_SESSION_AUTO_COMMIT = True
|
|
145
170
|
|
|
146
171
|
CONFIG_FILE = "config.yaml"
|
|
147
172
|
|
|
@@ -187,6 +212,37 @@ def get_max_journal_lines(repo_root: Path | None = None) -> int:
|
|
|
187
212
|
return DEFAULT_MAX_JOURNAL_LINES
|
|
188
213
|
|
|
189
214
|
|
|
215
|
+
def get_session_auto_commit(repo_root: Path | None = None) -> bool:
|
|
216
|
+
"""Whether scripts should auto-stage + auto-commit session/task changes.
|
|
217
|
+
|
|
218
|
+
Governs both ``add_session.py:_auto_commit_workspace`` and
|
|
219
|
+
``task_store.py:_auto_commit_archive``.
|
|
220
|
+
|
|
221
|
+
Default: ``True`` (existing behavior — auto-stage + auto-commit).
|
|
222
|
+
Set ``session_auto_commit: false`` in ``.trellis/config.yaml`` to skip
|
|
223
|
+
auto-staging entirely; the journal/archive files are still written to
|
|
224
|
+
disk, but the user manages ``git add`` / ``git commit`` themselves.
|
|
225
|
+
|
|
226
|
+
Accepts native YAML booleans (``true`` / ``false``) and the string
|
|
227
|
+
aliases ``true / false / yes / no / 1 / 0 / on / off`` (case-insensitive).
|
|
228
|
+
Invalid values fall back to ``True`` with a stderr warning.
|
|
229
|
+
"""
|
|
230
|
+
config = _load_config(repo_root)
|
|
231
|
+
raw = config.get("session_auto_commit", DEFAULT_SESSION_AUTO_COMMIT)
|
|
232
|
+
if isinstance(raw, bool):
|
|
233
|
+
return raw
|
|
234
|
+
s = str(raw).strip().lower()
|
|
235
|
+
if s in ("true", "yes", "1", "on"):
|
|
236
|
+
return True
|
|
237
|
+
if s in ("false", "no", "0", "off"):
|
|
238
|
+
return False
|
|
239
|
+
print(
|
|
240
|
+
f"[WARN] invalid session_auto_commit value: {raw!r}; using true (default)",
|
|
241
|
+
file=sys.stderr,
|
|
242
|
+
)
|
|
243
|
+
return DEFAULT_SESSION_AUTO_COMMIT
|
|
244
|
+
|
|
245
|
+
|
|
190
246
|
def get_hooks(event: str, repo_root: Path | None = None) -> list[str]:
|
|
191
247
|
"""Get hook commands for a lifecycle event.
|
|
192
248
|
|
|
@@ -15,14 +15,19 @@ Design
|
|
|
15
15
|
------
|
|
16
16
|
- Scripts only stage SPECIFIC product paths (journal files, index.md, the
|
|
17
17
|
current task dir, the archive dir). Never the whole `.trellis/` tree.
|
|
18
|
-
- If plain `git add <specific>` fails with "ignored by", retry with
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
18
|
+
- If plain `git add <specific>` fails with "ignored by", DO NOT retry with
|
|
19
|
+
``-f``. The presence of `.trellis/` in `.gitignore` is treated as user
|
|
20
|
+
intent ("keep .trellis/ local-only"). The script warns and skips the
|
|
21
|
+
auto-commit; users who want auto-staging can either fix their `.gitignore`
|
|
22
|
+
or set ``session_auto_commit: false`` and manage git themselves.
|
|
23
|
+
- The warning includes a negative example: ``Do NOT use `git add -f .trellis/` ...``
|
|
24
|
+
so any AI rereading the log doesn't reinvent the bug.
|
|
25
|
+
|
|
26
|
+
History note: 0.5.10 introduced an automatic ``git add -f`` retry on the
|
|
27
|
+
specific paths. That was reverted in 0.5.11 — auto-forcing into a tree the
|
|
28
|
+
user had gitignored violates user intent even when the path list is narrow.
|
|
29
|
+
The wider-grain forbidden command stays forbidden, and the narrow-grain auto
|
|
30
|
+
``-f`` is gone too.
|
|
26
31
|
"""
|
|
27
32
|
|
|
28
33
|
from __future__ import annotations
|
|
@@ -145,20 +150,18 @@ def _stderr_indicates_ignored(stderr: str) -> bool:
|
|
|
145
150
|
def safe_git_add(
|
|
146
151
|
paths: list[str], repo_root: Path
|
|
147
152
|
) -> tuple[bool, bool, str]:
|
|
148
|
-
"""Run `git add` on specific paths
|
|
153
|
+
"""Run `git add` on specific paths; never retry with -f.
|
|
149
154
|
|
|
150
|
-
Returns (success, used_force, stderr)
|
|
151
|
-
|
|
155
|
+
Returns ``(success, used_force, stderr)``. The ``used_force`` field is
|
|
156
|
+
kept for signature compatibility with the 0.5.10 implementation but is
|
|
157
|
+
always ``False`` — we never auto-force.
|
|
152
158
|
|
|
153
159
|
Behavior:
|
|
154
160
|
- No paths passed → success, no force, empty stderr.
|
|
155
|
-
- Plain
|
|
156
|
-
- Plain fails
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
warning (see :func:`print_gitignore_warning`).
|
|
160
|
-
- Plain fails with a non-ignored error → return failure; do NOT retry
|
|
161
|
-
with -f (we only force when ignore is the cause).
|
|
161
|
+
- Plain ``git add -- <paths>`` succeeds → return success.
|
|
162
|
+
- Plain fails (any reason — ignored or otherwise) → return failure with
|
|
163
|
+
the stderr. Callers should inspect the stderr (see
|
|
164
|
+
:func:`print_gitignore_warning`) and skip the auto-commit.
|
|
162
165
|
"""
|
|
163
166
|
if not paths:
|
|
164
167
|
return True, False, ""
|
|
@@ -166,14 +169,7 @@ def safe_git_add(
|
|
|
166
169
|
rc, _, err = run_git(["add", "--", *paths], cwd=repo_root)
|
|
167
170
|
if rc == 0:
|
|
168
171
|
return True, False, ""
|
|
169
|
-
|
|
170
|
-
if not _stderr_indicates_ignored(err):
|
|
171
|
-
return False, False, err
|
|
172
|
-
|
|
173
|
-
rc2, _, err2 = run_git(["add", "-f", "--", *paths], cwd=repo_root)
|
|
174
|
-
if rc2 == 0:
|
|
175
|
-
return True, True, err2 or err
|
|
176
|
-
return False, True, err2 or err
|
|
172
|
+
return False, False, err
|
|
177
173
|
|
|
178
174
|
|
|
179
175
|
def print_gitignore_warning(paths: list[str]) -> None:
|
|
@@ -187,6 +183,15 @@ def print_gitignore_warning(paths: list[str]) -> None:
|
|
|
187
183
|
"[WARN] git add failed because .trellis/ paths are ignored by your .gitignore.",
|
|
188
184
|
file=sys.stderr,
|
|
189
185
|
)
|
|
186
|
+
print(
|
|
187
|
+
"[WARN] Skipping auto-commit. The journal/task files were still written to disk;",
|
|
188
|
+
file=sys.stderr,
|
|
189
|
+
)
|
|
190
|
+
print(
|
|
191
|
+
"[WARN] git was not touched.",
|
|
192
|
+
file=sys.stderr,
|
|
193
|
+
)
|
|
194
|
+
print("[WARN]", file=sys.stderr)
|
|
190
195
|
print(
|
|
191
196
|
"[WARN] Trellis manages these specific paths and they should be tracked:",
|
|
192
197
|
file=sys.stderr,
|
|
@@ -219,6 +224,27 @@ def print_gitignore_warning(paths: list[str]) -> None:
|
|
|
219
224
|
for sub in TRELLIS_IGNORED_SUBPATHS:
|
|
220
225
|
print(f"[WARN] {sub}", file=sys.stderr)
|
|
221
226
|
print("[WARN]", file=sys.stderr)
|
|
227
|
+
print(
|
|
228
|
+
"[WARN] Or, if you intentionally keep .trellis/ local-only, set in",
|
|
229
|
+
file=sys.stderr,
|
|
230
|
+
)
|
|
231
|
+
print(
|
|
232
|
+
"[WARN] .trellis/config.yaml:",
|
|
233
|
+
file=sys.stderr,
|
|
234
|
+
)
|
|
235
|
+
print(
|
|
236
|
+
"[WARN] session_auto_commit: false",
|
|
237
|
+
file=sys.stderr,
|
|
238
|
+
)
|
|
239
|
+
print(
|
|
240
|
+
"[WARN] so the scripts skip git entirely and you can review / commit",
|
|
241
|
+
file=sys.stderr,
|
|
242
|
+
)
|
|
243
|
+
print(
|
|
244
|
+
"[WARN] manually with `git status` / `git add` / `git commit`.",
|
|
245
|
+
file=sys.stderr,
|
|
246
|
+
)
|
|
247
|
+
print("[WARN]", file=sys.stderr)
|
|
222
248
|
print(
|
|
223
249
|
"[WARN] Do NOT use `git add -f .trellis/` — it pulls in backups, worktrees,",
|
|
224
250
|
file=sys.stderr,
|
|
@@ -24,6 +24,7 @@ from pathlib import Path
|
|
|
24
24
|
|
|
25
25
|
from .config import (
|
|
26
26
|
get_packages,
|
|
27
|
+
get_session_auto_commit,
|
|
27
28
|
is_monorepo,
|
|
28
29
|
resolve_package,
|
|
29
30
|
validate_package,
|
|
@@ -391,16 +392,28 @@ def _auto_commit_archive(task_name: str, repo_root: Path) -> None:
|
|
|
391
392
|
"""Stage Trellis-owned task paths and commit after archive.
|
|
392
393
|
|
|
393
394
|
Only stages specific subpaths (the archive subtree and active task dirs),
|
|
394
|
-
never the whole
|
|
395
|
-
|
|
396
|
-
forbids
|
|
395
|
+
never the whole ``.trellis/`` tree. If ``.gitignore`` blocks the paths,
|
|
396
|
+
we warn + skip — we do NOT retry with ``git add -f``. The warning
|
|
397
|
+
explicitly forbids ``git add -f .trellis/`` (which would fan out to
|
|
398
|
+
caches/backups) and points users at ``session_auto_commit: false``.
|
|
399
|
+
|
|
400
|
+
Honors ``session_auto_commit`` in ``.trellis/config.yaml``: when set to
|
|
401
|
+
``false``, this function returns immediately without touching git
|
|
402
|
+
(the archive directory move on disk is unaffected).
|
|
397
403
|
"""
|
|
404
|
+
if not get_session_auto_commit(repo_root):
|
|
405
|
+
print(
|
|
406
|
+
"[OK] session_auto_commit: false — skipping git stage/commit.",
|
|
407
|
+
file=sys.stderr,
|
|
408
|
+
)
|
|
409
|
+
return
|
|
410
|
+
|
|
398
411
|
paths = safe_archive_paths_to_add(repo_root)
|
|
399
412
|
if not paths:
|
|
400
413
|
print("[OK] No task changes to commit.", file=sys.stderr)
|
|
401
414
|
return
|
|
402
415
|
|
|
403
|
-
success,
|
|
416
|
+
success, _, err = safe_git_add(paths, repo_root)
|
|
404
417
|
if not success:
|
|
405
418
|
if err and "ignored by" in err.lower():
|
|
406
419
|
print_gitignore_warning(paths)
|
|
@@ -411,12 +424,6 @@ def _auto_commit_archive(task_name: str, repo_root: Path) -> None:
|
|
|
411
424
|
)
|
|
412
425
|
return
|
|
413
426
|
|
|
414
|
-
if used_force:
|
|
415
|
-
print(
|
|
416
|
-
"[OK] Staged Trellis-owned paths with -f (specific paths, not .trellis/).",
|
|
417
|
-
file=sys.stderr,
|
|
418
|
-
)
|
|
419
|
-
|
|
420
427
|
rc, _, _ = run_git(
|
|
421
428
|
["diff", "--cached", "--quiet", "--", *paths], cwd=repo_root
|
|
422
429
|
)
|
package/package.json
CHANGED