FossilRepo

Add FEATURE_RELEASES, FEATURE_SYNC, FEATURE_FILES flags (all default off)

ragelink 2026-04-14 04:47 UTC trunk
Commit f4111a3d4c048ff0f2d445261a41d15688e2b8987a19878195debc9cacc0c23b
--- config/settings.py
+++ config/settings.py
@@ -256,19 +256,22 @@
256256
"TURNSTILE_ENABLED": (False, "Enable Cloudflare Turnstile on the login page"),
257257
"TURNSTILE_SITE_KEY": ("", "Cloudflare Turnstile site key (public)"),
258258
"TURNSTILE_SECRET_KEY": ("", "Cloudflare Turnstile secret key (server-side verification)"),
259259
# Feature flags
260260
"FEATURE_CHAT": (False, "Enable project chat rooms"),
261
+ "FEATURE_RELEASES": (False, "Enable releases (tags + downloadable assets)"),
262
+ "FEATURE_SYNC": (False, "Enable Git sync / mirror configuration"),
263
+ "FEATURE_FILES": (False, "Enable unversioned file storage per project"),
261264
}
262265
CONSTANCE_CONFIG_FIELDSETS = {
263266
"General": ("SITE_NAME",),
264267
"Fossil Storage": ("FOSSIL_DATA_DIR", "FOSSIL_STORE_IN_DB", "FOSSIL_S3_TRACKING", "FOSSIL_S3_BUCKET", "FOSSIL_BINARY_PATH"),
265268
"Git Sync": ("GIT_SYNC_MODE", "GIT_SYNC_SCHEDULE", "GIT_MIRROR_DIR", "GIT_SSH_KEY_DIR"),
266269
"GitHub OAuth": ("GITHUB_OAUTH_CLIENT_ID", "GITHUB_OAUTH_CLIENT_SECRET"),
267270
"GitLab OAuth": ("GITLAB_OAUTH_CLIENT_ID", "GITLAB_OAUTH_CLIENT_SECRET"),
268271
"Cloudflare Turnstile": ("TURNSTILE_ENABLED", "TURNSTILE_SITE_KEY", "TURNSTILE_SECRET_KEY"),
269
- "Features": ("FEATURE_CHAT",),
272
+ "Features": ("FEATURE_CHAT", "FEATURE_RELEASES", "FEATURE_SYNC", "FEATURE_FILES"),
270273
}
271274
272275
# --- Sentry ---
273276
274277
SENTRY_DSN = env_str("SENTRY_DSN")
275278
--- config/settings.py
+++ config/settings.py
@@ -256,19 +256,22 @@
256 "TURNSTILE_ENABLED": (False, "Enable Cloudflare Turnstile on the login page"),
257 "TURNSTILE_SITE_KEY": ("", "Cloudflare Turnstile site key (public)"),
258 "TURNSTILE_SECRET_KEY": ("", "Cloudflare Turnstile secret key (server-side verification)"),
259 # Feature flags
260 "FEATURE_CHAT": (False, "Enable project chat rooms"),
 
 
 
261 }
262 CONSTANCE_CONFIG_FIELDSETS = {
263 "General": ("SITE_NAME",),
264 "Fossil Storage": ("FOSSIL_DATA_DIR", "FOSSIL_STORE_IN_DB", "FOSSIL_S3_TRACKING", "FOSSIL_S3_BUCKET", "FOSSIL_BINARY_PATH"),
265 "Git Sync": ("GIT_SYNC_MODE", "GIT_SYNC_SCHEDULE", "GIT_MIRROR_DIR", "GIT_SSH_KEY_DIR"),
266 "GitHub OAuth": ("GITHUB_OAUTH_CLIENT_ID", "GITHUB_OAUTH_CLIENT_SECRET"),
267 "GitLab OAuth": ("GITLAB_OAUTH_CLIENT_ID", "GITLAB_OAUTH_CLIENT_SECRET"),
268 "Cloudflare Turnstile": ("TURNSTILE_ENABLED", "TURNSTILE_SITE_KEY", "TURNSTILE_SECRET_KEY"),
269 "Features": ("FEATURE_CHAT",),
270 }
271
272 # --- Sentry ---
273
274 SENTRY_DSN = env_str("SENTRY_DSN")
275
--- config/settings.py
+++ config/settings.py
@@ -256,19 +256,22 @@
256 "TURNSTILE_ENABLED": (False, "Enable Cloudflare Turnstile on the login page"),
257 "TURNSTILE_SITE_KEY": ("", "Cloudflare Turnstile site key (public)"),
258 "TURNSTILE_SECRET_KEY": ("", "Cloudflare Turnstile secret key (server-side verification)"),
259 # Feature flags
260 "FEATURE_CHAT": (False, "Enable project chat rooms"),
261 "FEATURE_RELEASES": (False, "Enable releases (tags + downloadable assets)"),
262 "FEATURE_SYNC": (False, "Enable Git sync / mirror configuration"),
263 "FEATURE_FILES": (False, "Enable unversioned file storage per project"),
264 }
265 CONSTANCE_CONFIG_FIELDSETS = {
266 "General": ("SITE_NAME",),
267 "Fossil Storage": ("FOSSIL_DATA_DIR", "FOSSIL_STORE_IN_DB", "FOSSIL_S3_TRACKING", "FOSSIL_S3_BUCKET", "FOSSIL_BINARY_PATH"),
268 "Git Sync": ("GIT_SYNC_MODE", "GIT_SYNC_SCHEDULE", "GIT_MIRROR_DIR", "GIT_SSH_KEY_DIR"),
269 "GitHub OAuth": ("GITHUB_OAUTH_CLIENT_ID", "GITHUB_OAUTH_CLIENT_SECRET"),
270 "GitLab OAuth": ("GITLAB_OAUTH_CLIENT_ID", "GITLAB_OAUTH_CLIENT_SECRET"),
271 "Cloudflare Turnstile": ("TURNSTILE_ENABLED", "TURNSTILE_SITE_KEY", "TURNSTILE_SECRET_KEY"),
272 "Features": ("FEATURE_CHAT", "FEATURE_RELEASES", "FEATURE_SYNC", "FEATURE_FILES"),
273 }
274
275 # --- Sentry ---
276
277 SENTRY_DSN = env_str("SENTRY_DSN")
278
--- core/context_processors.py
+++ core/context_processors.py
@@ -41,6 +41,9 @@
4141
"sidebar_ungrouped": ungrouped_projects,
4242
"sidebar_pages": pages, # Keep for backwards compat
4343
"sidebar_product_docs": product_docs,
4444
"sidebar_kb_pages": kb_pages,
4545
"feature_chat": config.FEATURE_CHAT,
46
+ "feature_releases": config.FEATURE_RELEASES,
47
+ "feature_sync": config.FEATURE_SYNC,
48
+ "feature_files": config.FEATURE_FILES,
4649
}
4750
--- core/context_processors.py
+++ core/context_processors.py
@@ -41,6 +41,9 @@
41 "sidebar_ungrouped": ungrouped_projects,
42 "sidebar_pages": pages, # Keep for backwards compat
43 "sidebar_product_docs": product_docs,
44 "sidebar_kb_pages": kb_pages,
45 "feature_chat": config.FEATURE_CHAT,
 
 
 
46 }
47
--- core/context_processors.py
+++ core/context_processors.py
@@ -41,6 +41,9 @@
41 "sidebar_ungrouped": ungrouped_projects,
42 "sidebar_pages": pages, # Keep for backwards compat
43 "sidebar_product_docs": product_docs,
44 "sidebar_kb_pages": kb_pages,
45 "feature_chat": config.FEATURE_CHAT,
46 "feature_releases": config.FEATURE_RELEASES,
47 "feature_sync": config.FEATURE_SYNC,
48 "feature_files": config.FEATURE_FILES,
49 }
50
--- fossil/views.py
+++ fossil/views.py
@@ -1499,10 +1499,14 @@
14991499
15001500
15011501
@login_required
15021502
def sync_pull(request, slug):
15031503
"""Sync configuration and pull from upstream remote."""
1504
+ from constance import config
1505
+
1506
+ if not config.FEATURE_SYNC:
1507
+ raise Http404
15041508
project, fossil_repo, reader = _get_repo_and_reader(slug, request, "write")
15051509
15061510
from fossil.cli import FossilCLI
15071511
15081512
cli = FossilCLI()
@@ -2263,10 +2267,14 @@
22632267
22642268
# --- Unversioned Content ---
22652269
22662270
22672271
def unversioned_list(request, slug):
2272
+ from constance import config
2273
+
2274
+ if not config.FEATURE_FILES:
2275
+ raise Http404
22682276
from projects.access import can_admin_project
22692277
22702278
project, fossil_repo, reader = _get_repo_and_reader(slug, request)
22712279
22722280
with reader:
@@ -3059,10 +3067,14 @@
30593067
fossil_repo = get_object_or_404(FossilRepository, project=project, deleted_at__isnull=True)
30603068
return project, fossil_repo
30613069
30623070
30633071
def release_list(request, slug):
3072
+ from constance import config
3073
+
3074
+ if not config.FEATURE_RELEASES:
3075
+ raise Http404
30643076
from projects.access import can_write_project
30653077
30663078
project, fossil_repo = _get_project_and_repo(slug, request, "read")
30673079
30683080
from fossil.releases import Release
30693081
--- fossil/views.py
+++ fossil/views.py
@@ -1499,10 +1499,14 @@
1499
1500
1501 @login_required
1502 def sync_pull(request, slug):
1503 """Sync configuration and pull from upstream remote."""
 
 
 
 
1504 project, fossil_repo, reader = _get_repo_and_reader(slug, request, "write")
1505
1506 from fossil.cli import FossilCLI
1507
1508 cli = FossilCLI()
@@ -2263,10 +2267,14 @@
2263
2264 # --- Unversioned Content ---
2265
2266
2267 def unversioned_list(request, slug):
 
 
 
 
2268 from projects.access import can_admin_project
2269
2270 project, fossil_repo, reader = _get_repo_and_reader(slug, request)
2271
2272 with reader:
@@ -3059,10 +3067,14 @@
3059 fossil_repo = get_object_or_404(FossilRepository, project=project, deleted_at__isnull=True)
3060 return project, fossil_repo
3061
3062
3063 def release_list(request, slug):
 
 
 
 
3064 from projects.access import can_write_project
3065
3066 project, fossil_repo = _get_project_and_repo(slug, request, "read")
3067
3068 from fossil.releases import Release
3069
--- fossil/views.py
+++ fossil/views.py
@@ -1499,10 +1499,14 @@
1499
1500
1501 @login_required
1502 def sync_pull(request, slug):
1503 """Sync configuration and pull from upstream remote."""
1504 from constance import config
1505
1506 if not config.FEATURE_SYNC:
1507 raise Http404
1508 project, fossil_repo, reader = _get_repo_and_reader(slug, request, "write")
1509
1510 from fossil.cli import FossilCLI
1511
1512 cli = FossilCLI()
@@ -2263,10 +2267,14 @@
2267
2268 # --- Unversioned Content ---
2269
2270
2271 def unversioned_list(request, slug):
2272 from constance import config
2273
2274 if not config.FEATURE_FILES:
2275 raise Http404
2276 from projects.access import can_admin_project
2277
2278 project, fossil_repo, reader = _get_repo_and_reader(slug, request)
2279
2280 with reader:
@@ -3059,10 +3067,14 @@
3067 fossil_repo = get_object_or_404(FossilRepository, project=project, deleted_at__isnull=True)
3068 return project, fossil_repo
3069
3070
3071 def release_list(request, slug):
3072 from constance import config
3073
3074 if not config.FEATURE_RELEASES:
3075 raise Http404
3076 from projects.access import can_write_project
3077
3078 project, fossil_repo = _get_project_and_repo(slug, request, "read")
3079
3080 from fossil.releases import Release
3081
--- templates/fossil/_project_nav.html
+++ templates/fossil/_project_nav.html
@@ -31,23 +31,29 @@
3131
<a href="{% url 'fossil:chat' slug=project.slug %}"
3232
class="px-3 py-2.5 sm:px-4 sm:py-2 text-sm font-medium rounded-t-md whitespace-nowrap {% if active_tab == 'chat' %}bg-gray-800 text-gray-100 border-b-2 border-brand{% else %}text-gray-400 hover:text-gray-200 hover:bg-gray-800/50 transition-colors{% endif %}">
3333
Chat
3434
</a>
3535
{% endif %}
36
+ {% if feature_releases %}
3637
<a href="{% url 'fossil:releases' slug=project.slug %}"
3738
class="px-3 py-2.5 sm:px-4 sm:py-2 text-sm font-medium rounded-t-md whitespace-nowrap {% if active_tab == 'releases' %}bg-gray-800 text-gray-100 border-b-2 border-brand{% else %}text-gray-400 hover:text-gray-200 hover:bg-gray-800/50 transition-colors{% endif %}">
3839
Releases
3940
</a>
41
+ {% endif %}
42
+ {% if feature_files %}
4043
<a href="{% url 'fossil:unversioned' slug=project.slug %}"
4144
class="px-3 py-2.5 sm:px-4 sm:py-2 text-sm font-medium rounded-t-md whitespace-nowrap {% if active_tab == 'files' %}bg-gray-800 text-gray-100 border-b-2 border-brand{% else %}text-gray-400 hover:text-gray-200 hover:bg-gray-800/50 transition-colors{% endif %}">
4245
Files
4346
</a>
47
+ {% endif %}
4448
{% if perms.projects.change_project %}
49
+ {% if feature_sync %}
4550
<a href="{% url 'fossil:sync' slug=project.slug %}"
4651
class="px-3 py-2.5 sm:px-4 sm: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 transition-colors{% endif %}">
4752
{% if fossil_repo.remote_url %}Sync{% else %}Setup Sync{% endif %}
4853
</a>
54
+ {% endif %}
4955
<a href="{% url 'fossil:repo_settings' slug=project.slug %}"
5056
class="px-3 py-2.5 sm:px-4 sm:py-2 text-sm font-medium rounded-t-md whitespace-nowrap {% if active_tab == 'settings' %}bg-gray-800 text-gray-100 border-b-2 border-brand{% else %}text-gray-400 hover:text-gray-200 hover:bg-gray-800/50 transition-colors{% endif %}">
5157
Settings
5258
</a>
5359
<a href="{% url 'fossil:explorer' slug=project.slug %}"
5460
--- templates/fossil/_project_nav.html
+++ templates/fossil/_project_nav.html
@@ -31,23 +31,29 @@
31 <a href="{% url 'fossil:chat' slug=project.slug %}"
32 class="px-3 py-2.5 sm:px-4 sm:py-2 text-sm font-medium rounded-t-md whitespace-nowrap {% if active_tab == 'chat' %}bg-gray-800 text-gray-100 border-b-2 border-brand{% else %}text-gray-400 hover:text-gray-200 hover:bg-gray-800/50 transition-colors{% endif %}">
33 Chat
34 </a>
35 {% endif %}
 
36 <a href="{% url 'fossil:releases' slug=project.slug %}"
37 class="px-3 py-2.5 sm:px-4 sm:py-2 text-sm font-medium rounded-t-md whitespace-nowrap {% if active_tab == 'releases' %}bg-gray-800 text-gray-100 border-b-2 border-brand{% else %}text-gray-400 hover:text-gray-200 hover:bg-gray-800/50 transition-colors{% endif %}">
38 Releases
39 </a>
 
 
40 <a href="{% url 'fossil:unversioned' slug=project.slug %}"
41 class="px-3 py-2.5 sm:px-4 sm:py-2 text-sm font-medium rounded-t-md whitespace-nowrap {% if active_tab == 'files' %}bg-gray-800 text-gray-100 border-b-2 border-brand{% else %}text-gray-400 hover:text-gray-200 hover:bg-gray-800/50 transition-colors{% endif %}">
42 Files
43 </a>
 
44 {% if perms.projects.change_project %}
 
45 <a href="{% url 'fossil:sync' slug=project.slug %}"
46 class="px-3 py-2.5 sm:px-4 sm: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 transition-colors{% endif %}">
47 {% if fossil_repo.remote_url %}Sync{% else %}Setup Sync{% endif %}
48 </a>
 
49 <a href="{% url 'fossil:repo_settings' slug=project.slug %}"
50 class="px-3 py-2.5 sm:px-4 sm:py-2 text-sm font-medium rounded-t-md whitespace-nowrap {% if active_tab == 'settings' %}bg-gray-800 text-gray-100 border-b-2 border-brand{% else %}text-gray-400 hover:text-gray-200 hover:bg-gray-800/50 transition-colors{% endif %}">
51 Settings
52 </a>
53 <a href="{% url 'fossil:explorer' slug=project.slug %}"
54
--- templates/fossil/_project_nav.html
+++ templates/fossil/_project_nav.html
@@ -31,23 +31,29 @@
31 <a href="{% url 'fossil:chat' slug=project.slug %}"
32 class="px-3 py-2.5 sm:px-4 sm:py-2 text-sm font-medium rounded-t-md whitespace-nowrap {% if active_tab == 'chat' %}bg-gray-800 text-gray-100 border-b-2 border-brand{% else %}text-gray-400 hover:text-gray-200 hover:bg-gray-800/50 transition-colors{% endif %}">
33 Chat
34 </a>
35 {% endif %}
36 {% if feature_releases %}
37 <a href="{% url 'fossil:releases' slug=project.slug %}"
38 class="px-3 py-2.5 sm:px-4 sm:py-2 text-sm font-medium rounded-t-md whitespace-nowrap {% if active_tab == 'releases' %}bg-gray-800 text-gray-100 border-b-2 border-brand{% else %}text-gray-400 hover:text-gray-200 hover:bg-gray-800/50 transition-colors{% endif %}">
39 Releases
40 </a>
41 {% endif %}
42 {% if feature_files %}
43 <a href="{% url 'fossil:unversioned' slug=project.slug %}"
44 class="px-3 py-2.5 sm:px-4 sm:py-2 text-sm font-medium rounded-t-md whitespace-nowrap {% if active_tab == 'files' %}bg-gray-800 text-gray-100 border-b-2 border-brand{% else %}text-gray-400 hover:text-gray-200 hover:bg-gray-800/50 transition-colors{% endif %}">
45 Files
46 </a>
47 {% endif %}
48 {% if perms.projects.change_project %}
49 {% if feature_sync %}
50 <a href="{% url 'fossil:sync' slug=project.slug %}"
51 class="px-3 py-2.5 sm:px-4 sm: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 transition-colors{% endif %}">
52 {% if fossil_repo.remote_url %}Sync{% else %}Setup Sync{% endif %}
53 </a>
54 {% endif %}
55 <a href="{% url 'fossil:repo_settings' slug=project.slug %}"
56 class="px-3 py-2.5 sm:px-4 sm:py-2 text-sm font-medium rounded-t-md whitespace-nowrap {% if active_tab == 'settings' %}bg-gray-800 text-gray-100 border-b-2 border-brand{% else %}text-gray-400 hover:text-gray-200 hover:bg-gray-800/50 transition-colors{% endif %}">
57 Settings
58 </a>
59 <a href="{% url 'fossil:explorer' slug=project.slug %}"
60

Keyboard Shortcuts

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