github-sync-agent 0.1.1__tar.gz → 0.1.2__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.
- {github_sync_agent-0.1.1 → github_sync_agent-0.1.2}/PKG-INFO +1 -1
- github_sync_agent-0.1.2/github_sync/__init__.py +1 -0
- {github_sync_agent-0.1.1 → github_sync_agent-0.1.2}/github_sync/agent.py +26 -20
- {github_sync_agent-0.1.1 → github_sync_agent-0.1.2}/github_sync/cli.py +9 -3
- {github_sync_agent-0.1.1 → github_sync_agent-0.1.2}/github_sync/config.py +10 -6
- {github_sync_agent-0.1.1 → github_sync_agent-0.1.2}/github_sync/setup.py +14 -2
- {github_sync_agent-0.1.1 → github_sync_agent-0.1.2}/github_sync_agent.egg-info/PKG-INFO +1 -1
- {github_sync_agent-0.1.1 → github_sync_agent-0.1.2}/pyproject.toml +1 -1
- github_sync_agent-0.1.1/github_sync/__init__.py +0 -1
- {github_sync_agent-0.1.1 → github_sync_agent-0.1.2}/README.md +0 -0
- {github_sync_agent-0.1.1 → github_sync_agent-0.1.2}/github_sync_agent.egg-info/SOURCES.txt +0 -0
- {github_sync_agent-0.1.1 → github_sync_agent-0.1.2}/github_sync_agent.egg-info/dependency_links.txt +0 -0
- {github_sync_agent-0.1.1 → github_sync_agent-0.1.2}/github_sync_agent.egg-info/entry_points.txt +0 -0
- {github_sync_agent-0.1.1 → github_sync_agent-0.1.2}/github_sync_agent.egg-info/requires.txt +0 -0
- {github_sync_agent-0.1.1 → github_sync_agent-0.1.2}/github_sync_agent.egg-info/top_level.txt +0 -0
- {github_sync_agent-0.1.1 → github_sync_agent-0.1.2}/setup.cfg +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.1.2"
|
|
@@ -153,19 +153,23 @@ def sync_project(path: Path, cfg: dict, dry_run: bool) -> bool:
|
|
|
153
153
|
# ---------------------------------------------------------------------------
|
|
154
154
|
|
|
155
155
|
def discover_projects(cfg: dict, project_name: str | None = None) -> list[Path]:
|
|
156
|
-
|
|
156
|
+
roots = [Path(r) for r in cfg["projects_root"]]
|
|
157
157
|
exclude = set(cfg.get("exclude", []))
|
|
158
158
|
|
|
159
159
|
if project_name:
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
160
|
+
for root in roots:
|
|
161
|
+
path = root / project_name
|
|
162
|
+
if path.is_dir():
|
|
163
|
+
return [path]
|
|
164
|
+
searched = ", ".join(str(r) for r in roots)
|
|
165
|
+
raise ValueError(f"Project '{project_name}' not found in: {searched}")
|
|
164
166
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
167
|
+
projects = []
|
|
168
|
+
for root in roots:
|
|
169
|
+
for p in sorted(root.iterdir()):
|
|
170
|
+
if p.is_dir() and p.name not in exclude and not p.name.startswith("."):
|
|
171
|
+
projects.append(p)
|
|
172
|
+
return projects
|
|
169
173
|
|
|
170
174
|
|
|
171
175
|
# ---------------------------------------------------------------------------
|
|
@@ -173,20 +177,22 @@ def discover_projects(cfg: dict, project_name: str | None = None) -> list[Path]:
|
|
|
173
177
|
# ---------------------------------------------------------------------------
|
|
174
178
|
|
|
175
179
|
def get_status(cfg: dict) -> list[dict]:
|
|
176
|
-
|
|
180
|
+
roots = [Path(r) for r in cfg["projects_root"]]
|
|
177
181
|
exclude = set(cfg.get("exclude", []))
|
|
178
182
|
github_user = cfg["github_user"]
|
|
179
183
|
rows = []
|
|
180
184
|
|
|
181
|
-
for
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
185
|
+
for root in roots:
|
|
186
|
+
for path in sorted(root.iterdir()):
|
|
187
|
+
if not path.is_dir() or path.name in exclude or path.name.startswith("."):
|
|
188
|
+
continue
|
|
189
|
+
rows.append({
|
|
190
|
+
"project": path.name,
|
|
191
|
+
"root": str(root),
|
|
192
|
+
"git": is_git_repo(path),
|
|
193
|
+
"remote": is_git_repo(path) and has_remote(path),
|
|
194
|
+
"changes": has_uncommitted_changes(path) if is_git_repo(path) else False,
|
|
195
|
+
"on_github": github_repo_exists(github_user, path.name),
|
|
196
|
+
})
|
|
191
197
|
|
|
192
198
|
return rows
|
|
@@ -66,7 +66,9 @@ def sync(ctx: click.Context, project: str | None, dry_run: bool) -> None:
|
|
|
66
66
|
click.echo(f"Error: {exc}", err=True)
|
|
67
67
|
sys.exit(1)
|
|
68
68
|
|
|
69
|
-
|
|
69
|
+
roots = cfg['projects_root']
|
|
70
|
+
roots_str = roots[0] if len(roots) == 1 else f"{len(roots)} roots"
|
|
71
|
+
logging.info(f"Syncing {len(projects)} project(s) from {roots_str} → github.com/{cfg['github_user']}")
|
|
70
72
|
|
|
71
73
|
results = []
|
|
72
74
|
for path in projects:
|
|
@@ -95,16 +97,20 @@ def status(ctx: click.Context) -> None:
|
|
|
95
97
|
cfg = ctx.obj["cfg"]
|
|
96
98
|
rows = agent.get_status(cfg)
|
|
97
99
|
|
|
98
|
-
|
|
100
|
+
roots = cfg['projects_root']
|
|
101
|
+
click.echo(f"\nProjects root : {roots[0] if len(roots) == 1 else str(roots)}")
|
|
99
102
|
click.echo(f"GitHub user : {cfg['github_user']}\n")
|
|
100
103
|
|
|
101
|
-
header = f"{'Project':<25} {'Git':<6} {'Remote':<8} {'Changes':<10} {'On GitHub'}"
|
|
104
|
+
header = f"{'Project':<25} {'Root':<30} {'Git':<6} {'Remote':<8} {'Changes':<10} {'On GitHub'}"
|
|
102
105
|
click.echo(header)
|
|
103
106
|
click.echo("─" * len(header))
|
|
104
107
|
|
|
105
108
|
for row in rows:
|
|
109
|
+
# Show only the last part of root to keep table readable
|
|
110
|
+
root_short = Path(row['root']).name
|
|
106
111
|
click.echo(
|
|
107
112
|
f"{row['project']:<25} "
|
|
113
|
+
f"{root_short:<30} "
|
|
108
114
|
f"{'✓' if row['git'] else '✗':<6} "
|
|
109
115
|
f"{'✓' if row['remote'] else '✗':<8} "
|
|
110
116
|
f"{'dirty' if row['changes'] else 'clean':<10} "
|
|
@@ -26,10 +26,14 @@ def _apply_defaults(cfg: dict) -> None:
|
|
|
26
26
|
cfg.setdefault("exclude", [])
|
|
27
27
|
cfg.setdefault("log_file", str(Path.home() / ".local" / "share" / "github-sync" / "github-sync.log"))
|
|
28
28
|
cfg.setdefault("log_level", "INFO")
|
|
29
|
-
# Expand ~ in projects_root
|
|
30
|
-
if "projects_root" in cfg:
|
|
31
|
-
cfg["projects_root"] = str(Path(cfg["projects_root"]).expanduser())
|
|
32
29
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
if not cfg.get("github_user"):
|
|
31
|
+
raise ValueError("Missing required config key: 'github_user'")
|
|
32
|
+
|
|
33
|
+
# Normalize projects_root to a list of expanded absolute paths
|
|
34
|
+
raw = cfg.get("projects_root")
|
|
35
|
+
if not raw:
|
|
36
|
+
raise ValueError("Missing required config key: 'projects_root'")
|
|
37
|
+
if isinstance(raw, str):
|
|
38
|
+
raw = [raw]
|
|
39
|
+
cfg["projects_root"] = [str(Path(p).expanduser().resolve()) for p in raw]
|
|
@@ -112,8 +112,20 @@ CONFIG_TEMPLATE = """\
|
|
|
112
112
|
|
|
113
113
|
github_user: {github_user}
|
|
114
114
|
|
|
115
|
-
#
|
|
116
|
-
|
|
115
|
+
# Directories to scan for projects.
|
|
116
|
+
# Every subdirectory inside each root is treated as a project to sync.
|
|
117
|
+
# You can specify one path (string) or multiple paths (list):
|
|
118
|
+
#
|
|
119
|
+
# Single path:
|
|
120
|
+
# projects_root: ~/projects
|
|
121
|
+
#
|
|
122
|
+
# Multiple paths:
|
|
123
|
+
# projects_root:
|
|
124
|
+
# - ~/projects
|
|
125
|
+
# - ~/work
|
|
126
|
+
# - /opt/company-code
|
|
127
|
+
projects_root:
|
|
128
|
+
- {projects_root}
|
|
117
129
|
|
|
118
130
|
# Daily schedule time (used by the systemd timer / cron)
|
|
119
131
|
schedule_time: "02:00"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.1.1"
|
|
File without changes
|
|
File without changes
|
{github_sync_agent-0.1.1 → github_sync_agent-0.1.2}/github_sync_agent.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{github_sync_agent-0.1.1 → github_sync_agent-0.1.2}/github_sync_agent.egg-info/entry_points.txt
RENAMED
|
File without changes
|
|
File without changes
|
{github_sync_agent-0.1.1 → github_sync_agent-0.1.2}/github_sync_agent.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|