FossilRepo

Redesign sync: configuration wizard, per-repo setting, enable/disable - Sync tab shows "Setup Sync" when no remote configured, "Sync" when active - Configuration wizard: auto-detects remote from .fossil file, explains what sync does - Enable Sync button saves remote URL - Disable Sync button clears config and stops auto-sync - Pull button only shown when configured - Sync result display with success/failure and raw output - Three POST actions: configure, pull, disable

lmata 2026-04-07 02:27 trunk
Commit de85d0bf8b4206778ce8ae917e52204435b2619cdf67eed75b2f1d192a49552b
+42 -26
--- fossil/views.py
+++ fossil/views.py
@@ -960,34 +960,58 @@
960960
# --- Sync ---
961961
962962
963963
@login_required
964964
def sync_pull(request, slug):
965
- """Pull updates from upstream remote."""
965
+ """Sync configuration and pull from upstream remote."""
966966
P.PROJECT_CHANGE.check(request.user)
967967
project, fossil_repo, reader = _get_repo_and_reader(slug)
968968
969
+ from fossil.cli import FossilCLI
970
+
971
+ cli = FossilCLI()
969972
result = None
970
- if request.method == "POST":
971
- from fossil.cli import FossilCLI
972
-
973
- cli = FossilCLI()
974
- if cli.is_available():
975
- # Detect remote URL if not set
976
- if not fossil_repo.remote_url:
977
- remote = cli.get_remote_url(fossil_repo.full_path)
978
- if remote:
979
- fossil_repo.remote_url = remote
980
- fossil_repo.save(update_fields=["remote_url", "updated_at", "version"])
981
-
973
+ action = request.POST.get("action", "") if request.method == "POST" else ""
974
+
975
+ # Auto-detect remote from .fossil file if not saved yet
976
+ detected_remote = ""
977
+ if not fossil_repo.remote_url and cli.is_available():
978
+ detected_remote = cli.get_remote_url(fossil_repo.full_path)
979
+
980
+ if action == "configure":
981
+ # Save remote URL configuration
982
+ url = request.POST.get("remote_url", "").strip()
983
+ if url:
984
+ fossil_repo.remote_url = url
985
+ fossil_repo.save(update_fields=["remote_url", "updated_at", "version"])
986
+ from django.contrib import messages
987
+
988
+ messages.success(request, f"Sync configured: {url}")
989
+ from django.shortcuts import redirect
990
+
991
+ return redirect("fossil:sync", slug=slug)
992
+
993
+ elif action == "disable":
994
+ fossil_repo.remote_url = ""
995
+ fossil_repo.last_sync_at = None
996
+ fossil_repo.upstream_artifacts_available = 0
997
+ fossil_repo.save(update_fields=["remote_url", "last_sync_at", "upstream_artifacts_available", "updated_at", "version"])
998
+ from django.contrib import messages
999
+
1000
+ messages.info(request, "Sync disabled.")
1001
+ from django.shortcuts import redirect
1002
+
1003
+ return redirect("fossil:sync", slug=slug)
1004
+
1005
+ elif action == "pull" and fossil_repo.remote_url:
1006
+ if cli.is_available():
9821007
result = cli.pull(fossil_repo.full_path)
9831008
if result["success"]:
9841009
from django.utils import timezone
9851010
9861011
fossil_repo.last_sync_at = timezone.now()
9871012
if result["artifacts_received"] > 0:
988
- # Update metadata
9891013
with reader:
9901014
fossil_repo.checkin_count = reader.get_checkin_count()
9911015
fossil_repo.file_size_bytes = fossil_repo.full_path.stat().st_size
9921016
fossil_repo.upstream_artifacts_available = 0
9931017
fossil_repo.save(
@@ -1001,32 +1025,24 @@
10011025
]
10021026
)
10031027
from django.contrib import messages
10041028
10051029
if result["artifacts_received"] > 0:
1006
- messages.success(request, f"Pulled {result['artifacts_received']} new artifacts from upstream.")
1030
+ messages.success(request, f"Pulled {result['artifacts_received']} new artifacts.")
10071031
else:
10081032
messages.info(request, "Already up to date.")
10091033
1010
- # Get remote URL for display
1011
- remote_url = fossil_repo.remote_url
1012
- if not remote_url:
1013
- from fossil.cli import FossilCLI
1014
-
1015
- cli = FossilCLI()
1016
- if cli.is_available():
1017
- remote_url = cli.get_remote_url(fossil_repo.full_path)
1018
-
10191034
return render(
10201035
request,
10211036
"fossil/sync.html",
10221037
{
10231038
"project": project,
10241039
"fossil_repo": fossil_repo,
1025
- "remote_url": remote_url,
1040
+ "detected_remote": detected_remote,
1041
+ "sync_configured": bool(fossil_repo.remote_url),
10261042
"result": result,
1027
- "active_tab": "code",
1043
+ "active_tab": "sync",
10281044
},
10291045
)
10301046
10311047
10321048
# --- Technotes ---
10331049
--- fossil/views.py
+++ fossil/views.py
@@ -960,34 +960,58 @@
960 # --- Sync ---
961
962
963 @login_required
964 def sync_pull(request, slug):
965 """Pull updates from upstream remote."""
966 P.PROJECT_CHANGE.check(request.user)
967 project, fossil_repo, reader = _get_repo_and_reader(slug)
968
 
 
 
969 result = None
970 if request.method == "POST":
971 from fossil.cli import FossilCLI
972
973 cli = FossilCLI()
974 if cli.is_available():
975 # Detect remote URL if not set
976 if not fossil_repo.remote_url:
977 remote = cli.get_remote_url(fossil_repo.full_path)
978 if remote:
979 fossil_repo.remote_url = remote
980 fossil_repo.save(update_fields=["remote_url", "updated_at", "version"])
981
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
982 result = cli.pull(fossil_repo.full_path)
983 if result["success"]:
984 from django.utils import timezone
985
986 fossil_repo.last_sync_at = timezone.now()
987 if result["artifacts_received"] > 0:
988 # Update metadata
989 with reader:
990 fossil_repo.checkin_count = reader.get_checkin_count()
991 fossil_repo.file_size_bytes = fossil_repo.full_path.stat().st_size
992 fossil_repo.upstream_artifacts_available = 0
993 fossil_repo.save(
@@ -1001,32 +1025,24 @@
1001 ]
1002 )
1003 from django.contrib import messages
1004
1005 if result["artifacts_received"] > 0:
1006 messages.success(request, f"Pulled {result['artifacts_received']} new artifacts from upstream.")
1007 else:
1008 messages.info(request, "Already up to date.")
1009
1010 # Get remote URL for display
1011 remote_url = fossil_repo.remote_url
1012 if not remote_url:
1013 from fossil.cli import FossilCLI
1014
1015 cli = FossilCLI()
1016 if cli.is_available():
1017 remote_url = cli.get_remote_url(fossil_repo.full_path)
1018
1019 return render(
1020 request,
1021 "fossil/sync.html",
1022 {
1023 "project": project,
1024 "fossil_repo": fossil_repo,
1025 "remote_url": remote_url,
 
1026 "result": result,
1027 "active_tab": "code",
1028 },
1029 )
1030
1031
1032 # --- Technotes ---
1033
--- fossil/views.py
+++ fossil/views.py
@@ -960,34 +960,58 @@
960 # --- Sync ---
961
962
963 @login_required
964 def sync_pull(request, slug):
965 """Sync configuration and pull from upstream remote."""
966 P.PROJECT_CHANGE.check(request.user)
967 project, fossil_repo, reader = _get_repo_and_reader(slug)
968
969 from fossil.cli import FossilCLI
970
971 cli = FossilCLI()
972 result = None
973 action = request.POST.get("action", "") if request.method == "POST" else ""
974
975 # Auto-detect remote from .fossil file if not saved yet
976 detected_remote = ""
977 if not fossil_repo.remote_url and cli.is_available():
978 detected_remote = cli.get_remote_url(fossil_repo.full_path)
979
980 if action == "configure":
981 # Save remote URL configuration
982 url = request.POST.get("remote_url", "").strip()
983 if url:
984 fossil_repo.remote_url = url
985 fossil_repo.save(update_fields=["remote_url", "updated_at", "version"])
986 from django.contrib import messages
987
988 messages.success(request, f"Sync configured: {url}")
989 from django.shortcuts import redirect
990
991 return redirect("fossil:sync", slug=slug)
992
993 elif action == "disable":
994 fossil_repo.remote_url = ""
995 fossil_repo.last_sync_at = None
996 fossil_repo.upstream_artifacts_available = 0
997 fossil_repo.save(update_fields=["remote_url", "last_sync_at", "upstream_artifacts_available", "updated_at", "version"])
998 from django.contrib import messages
999
1000 messages.info(request, "Sync disabled.")
1001 from django.shortcuts import redirect
1002
1003 return redirect("fossil:sync", slug=slug)
1004
1005 elif action == "pull" and fossil_repo.remote_url:
1006 if cli.is_available():
1007 result = cli.pull(fossil_repo.full_path)
1008 if result["success"]:
1009 from django.utils import timezone
1010
1011 fossil_repo.last_sync_at = timezone.now()
1012 if result["artifacts_received"] > 0:
 
1013 with reader:
1014 fossil_repo.checkin_count = reader.get_checkin_count()
1015 fossil_repo.file_size_bytes = fossil_repo.full_path.stat().st_size
1016 fossil_repo.upstream_artifacts_available = 0
1017 fossil_repo.save(
@@ -1001,32 +1025,24 @@
1025 ]
1026 )
1027 from django.contrib import messages
1028
1029 if result["artifacts_received"] > 0:
1030 messages.success(request, f"Pulled {result['artifacts_received']} new artifacts.")
1031 else:
1032 messages.info(request, "Already up to date.")
1033
 
 
 
 
 
 
 
 
 
1034 return render(
1035 request,
1036 "fossil/sync.html",
1037 {
1038 "project": project,
1039 "fossil_repo": fossil_repo,
1040 "detected_remote": detected_remote,
1041 "sync_configured": bool(fossil_repo.remote_url),
1042 "result": result,
1043 "active_tab": "sync",
1044 },
1045 )
1046
1047
1048 # --- Technotes ---
1049
--- templates/fossil/_project_nav.html
+++ templates/fossil/_project_nav.html
@@ -28,9 +28,9 @@
2828
Forum
2929
</a>
3030
{% if perms.projects.change_project %}
3131
<a href="{% url 'fossil:sync' slug=project.slug %}"
3232
class="px-4 py-2 text-sm font-medium rounded-t-md whitespace-nowrap {% if active_tab == 'sync' %}bg-gray-800 text-gray-100 border-b-2 border-brand{% else %}text-gray-400 hover:text-gray-200 hover:bg-gray-800/50{% endif %}">
33
- Sync
33
+ {% if fossil_repo.remote_url %}Sync{% else %}Setup Sync{% endif %}
3434
</a>
3535
{% endif %}
3636
</nav>
3737
--- templates/fossil/_project_nav.html
+++ templates/fossil/_project_nav.html
@@ -28,9 +28,9 @@
28 Forum
29 </a>
30 {% if perms.projects.change_project %}
31 <a href="{% url 'fossil:sync' slug=project.slug %}"
32 class="px-4 py-2 text-sm font-medium rounded-t-md whitespace-nowrap {% if active_tab == 'sync' %}bg-gray-800 text-gray-100 border-b-2 border-brand{% else %}text-gray-400 hover:text-gray-200 hover:bg-gray-800/50{% endif %}">
33 Sync
34 </a>
35 {% endif %}
36 </nav>
37
--- templates/fossil/_project_nav.html
+++ templates/fossil/_project_nav.html
@@ -28,9 +28,9 @@
28 Forum
29 </a>
30 {% if perms.projects.change_project %}
31 <a href="{% url 'fossil:sync' slug=project.slug %}"
32 class="px-4 py-2 text-sm font-medium rounded-t-md whitespace-nowrap {% if active_tab == 'sync' %}bg-gray-800 text-gray-100 border-b-2 border-brand{% else %}text-gray-400 hover:text-gray-200 hover:bg-gray-800/50{% endif %}">
33 {% if fossil_repo.remote_url %}Sync{% else %}Setup Sync{% endif %}
34 </a>
35 {% endif %}
36 </nav>
37
--- templates/fossil/sync.html
+++ templates/fossil/sync.html
@@ -4,17 +4,21 @@
44
{% block content %}
55
<h1 class="text-2xl font-bold text-gray-100 mb-2">{{ project.name }}</h1>
66
{% include "fossil/_project_nav.html" %}
77
88
<div class="max-w-2xl">
9
- <h2 class="text-lg font-semibold text-gray-200 mb-4">Repository Sync</h2>
10
-
9
+ {% if sync_configured %}
10
+ <!-- Sync is configured — show status and pull button -->
1111
<div class="rounded-lg bg-gray-800 border border-gray-700 p-5 mb-6">
12
+ <div class="flex items-center justify-between mb-4">
13
+ <h2 class="text-lg font-semibold text-gray-200">Upstream Sync</h2>
14
+ <span class="inline-flex rounded-full bg-green-900/50 px-2 py-0.5 text-xs font-semibold text-green-300">Configured</span>
15
+ </div>
1216
<dl class="space-y-3">
1317
<div class="flex items-center justify-between">
14
- <dt class="text-sm text-gray-400">Remote</dt>
15
- <dd class="text-sm text-gray-200 font-mono">{{ remote_url|default:"No remote configured" }}</dd>
18
+ <dt class="text-sm text-gray-400">Remote URL</dt>
19
+ <dd class="text-sm text-gray-200 font-mono truncate max-w-xs">{{ fossil_repo.remote_url }}</dd>
1620
</div>
1721
<div class="flex items-center justify-between">
1822
<dt class="text-sm text-gray-400">Last synced</dt>
1923
<dd class="text-sm text-gray-200">{% if fossil_repo.last_sync_at %}{{ fossil_repo.last_sync_at|timesince }} ago{% else %}Never{% endif %}</dd>
2024
</div>
@@ -25,41 +29,88 @@
2529
<div class="flex items-center justify-between">
2630
<dt class="text-sm text-gray-400">Repository size</dt>
2731
<dd class="text-sm text-gray-200">{{ fossil_repo.file_size_bytes|filesizeformat }}</dd>
2832
</div>
2933
</dl>
30
- </div>
31
-
32
- {% if remote_url %}
33
- <form method="post">
34
- {% csrf_token %}
35
- <button type="submit" class="inline-flex items-center gap-2 rounded-md bg-brand px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-brand-hover">
36
- <svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
37
- <path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182" />
38
- </svg>
39
- Pull from Upstream
40
- </button>
41
- </form>
42
- {% else %}
43
- <p class="text-sm text-gray-500">No remote URL configured. This repository was created locally.</p>
44
- {% endif %}
34
+
35
+ <div class="mt-5 flex items-center gap-3">
36
+ <form method="post">
37
+ {% csrf_token %}
38
+ <input type="hidden" name="action" value="pull">
39
+ <button type="submit" class="inline-flex items-center gap-2 rounded-md bg-brand px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-brand-hover">
40
+ <svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
41
+ <path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182" />
42
+ </svg>
43
+ Pull from Upstream
44
+ </button>
45
+ </form>
46
+ <form method="post">
47
+ {% csrf_token %}
48
+ <input type="hidden" name="action" value="disable">
49
+ <button type="submit" class="rounded-md bg-gray-700 px-3 py-2 text-sm text-gray-400 hover:text-white ring-1 ring-inset ring-gray-600 hover:bg-gray-600">
50
+ Disable Sync
51
+ </button>
52
+ </form>
53
+ </div>
54
+ </div>
4555
4656
{% if result %}
47
- <div class="mt-4 rounded-lg {% if result.success %}bg-green-900/30 border border-green-800{% else %}bg-red-900/30 border border-red-800{% endif %} p-4">
57
+ <div class="rounded-lg {% if result.success %}bg-green-900/20 border border-green-800{% else %}bg-red-900/20 border border-red-800{% endif %} p-4">
4858
<div class="text-sm {% if result.success %}text-green-300{% else %}text-red-300{% endif %}">
4959
{% if result.success %}
50
- {% if result.artifacts_received > 0 %}
51
- Pulled {{ result.artifacts_received }} new artifacts.
52
- {% else %}
53
- Already up to date.
54
- {% endif %}
60
+ {% if result.artifacts_received > 0 %}Pulled {{ result.artifacts_received }} new artifacts.{% else %}Already up to date.{% endif %}
5561
{% else %}
5662
Sync failed: {{ result.message }}
5763
{% endif %}
5864
</div>
59
- {% if result.message %}
60
- <pre class="mt-2 text-xs text-gray-500 font-mono">{{ result.message }}</pre>
61
- {% endif %}
65
+ {% if result.message %}<pre class="mt-2 text-xs text-gray-500 font-mono">{{ result.message }}</pre>{% endif %}
66
+ </div>
67
+ {% endif %}
68
+
69
+ {% else %}
70
+ <!-- Sync not configured — show setup wizard -->
71
+ <div class="rounded-lg bg-gray-800 border border-gray-700 p-6">
72
+ <div class="text-center mb-6">
73
+ <svg class="mx-auto h-12 w-12 text-gray-600" fill="none" viewBox="0 0 24 24" stroke-width="1" stroke="currentColor">
74
+ <path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182" />
75
+ </svg>
76
+ <h2 class="mt-3 text-lg font-semibold text-gray-200">Configure Upstream Sync</h2>
77
+ <p class="mt-1 text-sm text-gray-400">Connect this repository to an upstream Fossil server to pull updates automatically.</p>
78
+ </div>
79
+
80
+ <form method="post" class="space-y-4">
81
+ {% csrf_token %}
82
+ <input type="hidden" name="action" value="configure">
83
+
84
+ <div>
85
+ <label class="block text-sm font-medium text-gray-300 mb-1">Remote URL</label>
86
+ <input type="url" name="remote_url" required
87
+ value="{{ detected_remote }}"
88
+ placeholder="https://fossil-scm.org/home"
89
+ class="w-full rounded-md border-gray-700 bg-gray-900 text-gray-100 shadow-sm focus:border-brand focus:ring-brand sm:text-sm px-3 py-2 font-mono">
90
+ {% if detected_remote %}
91
+ <p class="mt-1 text-xs text-green-400">Auto-detected from repository: {{ detected_remote }}</p>
92
+ {% else %}
93
+ <p class="mt-1 text-xs text-gray-500">Enter the URL of the upstream Fossil server (e.g. https://fossil-scm.org/home)</p>
94
+ {% endif %}
95
+ </div>
96
+
97
+ <div class="bg-gray-900/50 rounded-md p-4 text-sm text-gray-400">
98
+ <h4 class="font-medium text-gray-300 mb-2">What sync does:</h4>
99
+ <ul class="space-y-1 text-xs">
100
+ <li>Pulls new checkins, wiki pages, tickets, and forum posts from the remote</li>
101
+ <li>Runs automatically every 15 minutes via background task</li>
102
+ <li>You can also pull manually at any time</li>
103
+ <li>Your local data is never overwritten — only new artifacts are added</li>
104
+ </ul>
105
+ </div>
106
+
107
+ <div class="flex justify-end">
108
+ <button type="submit" class="rounded-md bg-brand px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-brand-hover">
109
+ Enable Sync
110
+ </button>
111
+ </div>
112
+ </form>
62113
</div>
63114
{% endif %}
64115
</div>
65116
{% endblock %}
66117
--- templates/fossil/sync.html
+++ templates/fossil/sync.html
@@ -4,17 +4,21 @@
4 {% block content %}
5 <h1 class="text-2xl font-bold text-gray-100 mb-2">{{ project.name }}</h1>
6 {% include "fossil/_project_nav.html" %}
7
8 <div class="max-w-2xl">
9 <h2 class="text-lg font-semibold text-gray-200 mb-4">Repository Sync</h2>
10
11 <div class="rounded-lg bg-gray-800 border border-gray-700 p-5 mb-6">
 
 
 
 
12 <dl class="space-y-3">
13 <div class="flex items-center justify-between">
14 <dt class="text-sm text-gray-400">Remote</dt>
15 <dd class="text-sm text-gray-200 font-mono">{{ remote_url|default:"No remote configured" }}</dd>
16 </div>
17 <div class="flex items-center justify-between">
18 <dt class="text-sm text-gray-400">Last synced</dt>
19 <dd class="text-sm text-gray-200">{% if fossil_repo.last_sync_at %}{{ fossil_repo.last_sync_at|timesince }} ago{% else %}Never{% endif %}</dd>
20 </div>
@@ -25,41 +29,88 @@
25 <div class="flex items-center justify-between">
26 <dt class="text-sm text-gray-400">Repository size</dt>
27 <dd class="text-sm text-gray-200">{{ fossil_repo.file_size_bytes|filesizeformat }}</dd>
28 </div>
29 </dl>
30 </div>
31
32 {% if remote_url %}
33 <form method="post">
34 {% csrf_token %}
35 <button type="submit" class="inline-flex items-center gap-2 rounded-md bg-brand px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-brand-hover">
36 <svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
37 <path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182" />
38 </svg>
39 Pull from Upstream
40 </button>
41 </form>
42 {% else %}
43 <p class="text-sm text-gray-500">No remote URL configured. This repository was created locally.</p>
44 {% endif %}
 
 
 
 
 
 
45
46 {% if result %}
47 <div class="mt-4 rounded-lg {% if result.success %}bg-green-900/30 border border-green-800{% else %}bg-red-900/30 border border-red-800{% endif %} p-4">
48 <div class="text-sm {% if result.success %}text-green-300{% else %}text-red-300{% endif %}">
49 {% if result.success %}
50 {% if result.artifacts_received > 0 %}
51 Pulled {{ result.artifacts_received }} new artifacts.
52 {% else %}
53 Already up to date.
54 {% endif %}
55 {% else %}
56 Sync failed: {{ result.message }}
57 {% endif %}
58 </div>
59 {% if result.message %}
60 <pre class="mt-2 text-xs text-gray-500 font-mono">{{ result.message }}</pre>
61 {% endif %}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62 </div>
63 {% endif %}
64 </div>
65 {% endblock %}
66
--- templates/fossil/sync.html
+++ templates/fossil/sync.html
@@ -4,17 +4,21 @@
4 {% block content %}
5 <h1 class="text-2xl font-bold text-gray-100 mb-2">{{ project.name }}</h1>
6 {% include "fossil/_project_nav.html" %}
7
8 <div class="max-w-2xl">
9 {% if sync_configured %}
10 <!-- Sync is configured — show status and pull button -->
11 <div class="rounded-lg bg-gray-800 border border-gray-700 p-5 mb-6">
12 <div class="flex items-center justify-between mb-4">
13 <h2 class="text-lg font-semibold text-gray-200">Upstream Sync</h2>
14 <span class="inline-flex rounded-full bg-green-900/50 px-2 py-0.5 text-xs font-semibold text-green-300">Configured</span>
15 </div>
16 <dl class="space-y-3">
17 <div class="flex items-center justify-between">
18 <dt class="text-sm text-gray-400">Remote URL</dt>
19 <dd class="text-sm text-gray-200 font-mono truncate max-w-xs">{{ fossil_repo.remote_url }}</dd>
20 </div>
21 <div class="flex items-center justify-between">
22 <dt class="text-sm text-gray-400">Last synced</dt>
23 <dd class="text-sm text-gray-200">{% if fossil_repo.last_sync_at %}{{ fossil_repo.last_sync_at|timesince }} ago{% else %}Never{% endif %}</dd>
24 </div>
@@ -25,41 +29,88 @@
29 <div class="flex items-center justify-between">
30 <dt class="text-sm text-gray-400">Repository size</dt>
31 <dd class="text-sm text-gray-200">{{ fossil_repo.file_size_bytes|filesizeformat }}</dd>
32 </div>
33 </dl>
34
35 <div class="mt-5 flex items-center gap-3">
36 <form method="post">
37 {% csrf_token %}
38 <input type="hidden" name="action" value="pull">
39 <button type="submit" class="inline-flex items-center gap-2 rounded-md bg-brand px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-brand-hover">
40 <svg class="h-4 w-4" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor">
41 <path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182" />
42 </svg>
43 Pull from Upstream
44 </button>
45 </form>
46 <form method="post">
47 {% csrf_token %}
48 <input type="hidden" name="action" value="disable">
49 <button type="submit" class="rounded-md bg-gray-700 px-3 py-2 text-sm text-gray-400 hover:text-white ring-1 ring-inset ring-gray-600 hover:bg-gray-600">
50 Disable Sync
51 </button>
52 </form>
53 </div>
54 </div>
55
56 {% if result %}
57 <div class="rounded-lg {% if result.success %}bg-green-900/20 border border-green-800{% else %}bg-red-900/20 border border-red-800{% endif %} p-4">
58 <div class="text-sm {% if result.success %}text-green-300{% else %}text-red-300{% endif %}">
59 {% if result.success %}
60 {% if result.artifacts_received > 0 %}Pulled {{ result.artifacts_received }} new artifacts.{% else %}Already up to date.{% endif %}
 
 
 
 
61 {% else %}
62 Sync failed: {{ result.message }}
63 {% endif %}
64 </div>
65 {% if result.message %}<pre class="mt-2 text-xs text-gray-500 font-mono">{{ result.message }}</pre>{% endif %}
66 </div>
67 {% endif %}
68
69 {% else %}
70 <!-- Sync not configured — show setup wizard -->
71 <div class="rounded-lg bg-gray-800 border border-gray-700 p-6">
72 <div class="text-center mb-6">
73 <svg class="mx-auto h-12 w-12 text-gray-600" fill="none" viewBox="0 0 24 24" stroke-width="1" stroke="currentColor">
74 <path stroke-linecap="round" stroke-linejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182" />
75 </svg>
76 <h2 class="mt-3 text-lg font-semibold text-gray-200">Configure Upstream Sync</h2>
77 <p class="mt-1 text-sm text-gray-400">Connect this repository to an upstream Fossil server to pull updates automatically.</p>
78 </div>
79
80 <form method="post" class="space-y-4">
81 {% csrf_token %}
82 <input type="hidden" name="action" value="configure">
83
84 <div>
85 <label class="block text-sm font-medium text-gray-300 mb-1">Remote URL</label>
86 <input type="url" name="remote_url" required
87 value="{{ detected_remote }}"
88 placeholder="https://fossil-scm.org/home"
89 class="w-full rounded-md border-gray-700 bg-gray-900 text-gray-100 shadow-sm focus:border-brand focus:ring-brand sm:text-sm px-3 py-2 font-mono">
90 {% if detected_remote %}
91 <p class="mt-1 text-xs text-green-400">Auto-detected from repository: {{ detected_remote }}</p>
92 {% else %}
93 <p class="mt-1 text-xs text-gray-500">Enter the URL of the upstream Fossil server (e.g. https://fossil-scm.org/home)</p>
94 {% endif %}
95 </div>
96
97 <div class="bg-gray-900/50 rounded-md p-4 text-sm text-gray-400">
98 <h4 class="font-medium text-gray-300 mb-2">What sync does:</h4>
99 <ul class="space-y-1 text-xs">
100 <li>Pulls new checkins, wiki pages, tickets, and forum posts from the remote</li>
101 <li>Runs automatically every 15 minutes via background task</li>
102 <li>You can also pull manually at any time</li>
103 <li>Your local data is never overwritten — only new artifacts are added</li>
104 </ul>
105 </div>
106
107 <div class="flex justify-end">
108 <button type="submit" class="rounded-md bg-brand px-4 py-2 text-sm font-semibold text-white shadow-sm hover:bg-brand-hover">
109 Enable Sync
110 </button>
111 </div>
112 </form>
113 </div>
114 {% endif %}
115 </div>
116 {% endblock %}
117

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button