Bug fixes from v0.6.2 code scan
This commit is contained in:
@@ -1,316 +1,174 @@
|
|||||||
# TASK: Create CLAUDE.md + cleanup + statusIcon fix (felhom.eu repo)
|
# TASK: Bug fixes from v0.6.2 code scan
|
||||||
|
|
||||||
## Context
|
## Context
|
||||||
|
|
||||||
The `felhom.eu` repo lacks a CLAUDE.md with build instructions for the hub, has no `.gitignore` (so `hub.exe` got committed), and has a statusIcon rendering bug on the hub dashboard.
|
Comprehensive code scan of felhom-controller v0.6.2 found 4 minor bugs across templates, shell scripts, and Go code. None are critical, but all should be fixed for correctness.
|
||||||
|
|
||||||
**Current state:** Hub v0.1.2 running on k3s. Controller v0.6.2 on demo node.
|
**Current state:** Controller v0.6.2 running on demo-felhom.eu.
|
||||||
|
|
||||||
All changes in this task are in the **felhom.eu repo** only.
|
All changes in this task are in the **deploy-felhom-compose** repo only.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Task 1: Create CLAUDE.md
|
## Bug 1: Missing `require_arg` for `--hdd-path` in docker-setup.sh
|
||||||
|
|
||||||
Create `CLAUDE.md` in the repo root (`E:\git\felhom.eu\CLAUDE.md`) with the following content.
|
**File:** `scripts/docker-setup.sh`
|
||||||
Use the controller's `CLAUDE.md` (in `deploy-felhom-compose`) as a style reference.
|
|
||||||
|
|
||||||
The CLAUDE.md should include these sections:
|
**Problem:** The `--hdd-path` flag parsing doesn't use `require_arg` validation like all other flags do. Under `set -u`, if `--hdd-path` is the last argument and has no value, `$2` is unbound and the script crashes with a cryptic bash error instead of a friendly message.
|
||||||
|
|
||||||
### Project overview
|
**Current code** (in the argument parsing `while` loop):
|
||||||
|
|
||||||
This repo (`felhom.eu`) contains:
|
|
||||||
- **Website** (`website/`) — Static HTML pages at felhom.eu, served via k3s nginx + git-sync sidecar
|
|
||||||
- **Hub** (`hub/`) — Go application (felhom-hub) — centralized dashboard for monitoring customer controllers, runs on k3s at hub.felhom.eu
|
|
||||||
- **K8s manifests** (`manifests/`) — k3s deployment manifests for all felhom-system services
|
|
||||||
|
|
||||||
See `README.md` for full architecture, DNS, email, and SEO documentation.
|
|
||||||
See `TASK.md` for the current task to implement (if it exists).
|
|
||||||
|
|
||||||
### Code quality rules
|
|
||||||
|
|
||||||
Same as controller CLAUDE.md:
|
|
||||||
- Always double-check generated code for bugs, logic issues, syntax errors
|
|
||||||
- Handle edge cases without overcomplicating
|
|
||||||
- Add debug capabilities for troubleshooting
|
|
||||||
- Ask for more input rather than guessing
|
|
||||||
|
|
||||||
### Workspace layout
|
|
||||||
|
|
||||||
```
|
|
||||||
E:\git\felhom.eu\ (or /e/git/felhom.eu/ in Git Bash)
|
|
||||||
├── hub/ # felhom-hub Go application
|
|
||||||
│ ├── cmd/hub/ # Entry point (main.go)
|
|
||||||
│ ├── internal/
|
|
||||||
│ │ ├── api/ # Report ingestion API
|
|
||||||
│ │ ├── store/ # SQLite storage + queries
|
|
||||||
│ │ └── web/ # Dashboard UI
|
|
||||||
│ │ ├── server.go # Server, routing, template funcs
|
|
||||||
│ │ ├── embed.go # go:embed for templates
|
|
||||||
│ │ └── templates/ # HTML templates + CSS
|
|
||||||
│ ├── configs/ # Example config files
|
|
||||||
│ ├── Dockerfile
|
|
||||||
│ ├── Makefile
|
|
||||||
│ └── go.mod
|
|
||||||
├── manifests/ # k3s deployment manifests
|
|
||||||
│ ├── hub.yaml # Hub deployment (hub.felhom.eu)
|
|
||||||
│ ├── webpage.yaml # Website + FileBrowser + git-sync
|
|
||||||
│ ├── contact-mailer.yaml # Contact form email sender
|
|
||||||
│ ├── healthchecks.yaml # Healthchecks (status.felhom.eu)
|
|
||||||
│ └── umami.yaml # Analytics (stats.felhom.eu)
|
|
||||||
├── website/ # Static HTML pages (felhom.eu)
|
|
||||||
│ ├── index.html
|
|
||||||
│ ├── alkalmazasok.html
|
|
||||||
│ ├── ... (all Hungarian, UTF-8 with BOM)
|
|
||||||
│ └── assets/ # Logos, screenshots, OG images
|
|
||||||
├── CLAUDE.md # This file
|
|
||||||
├── README.md # Full project documentation
|
|
||||||
└── TASK.md # Current task (if exists)
|
|
||||||
```
|
|
||||||
|
|
||||||
Related repos (same parent directory):
|
|
||||||
```
|
|
||||||
E:\git\deploy-felhom-compose\ # felhom-controller Go app + deploy scripts
|
|
||||||
E:\git\app-catalog-felhom.eu\ # Docker Compose templates per app
|
|
||||||
E:\git\homelab-manifests\ # k3s cluster manifests (dooplex.hu services)
|
|
||||||
E:\git\misc-scripts\ # Helper scripts (build scripts, repo collector)
|
|
||||||
```
|
|
||||||
|
|
||||||
All repos hosted at `gitea.dooplex.hu/admin/`.
|
|
||||||
|
|
||||||
### SSH access
|
|
||||||
|
|
||||||
SSH key-based authentication configured. No password prompts.
|
|
||||||
|
|
||||||
| Host | IP | User | Role |
|
|
||||||
|------|----|------|------|
|
|
||||||
| Build server (k3s node) | 192.168.0.180 | kisfenyo | Build + push images, kubectl |
|
|
||||||
| Demo node | 192.168.0.162 | kisfenyo | Test deployment (demo-felhom.eu) |
|
|
||||||
|
|
||||||
**Note:** `kubectl` on the build server requires `sudo` (k3s kubeconfig permissions).
|
|
||||||
|
|
||||||
### Build & deploy workflow — Hub
|
|
||||||
|
|
||||||
After making code changes to `hub/`, you **MUST** build, push, and deploy the new image.
|
|
||||||
Do NOT leave code changes uncommitted or undeployed.
|
|
||||||
|
|
||||||
#### Step 1: Commit and push changes
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd /e/git/felhom.eu
|
--hdd-path) HDD_PATH="$2"; shift 2 ;;
|
||||||
git add -A && git commit -m "<descriptive message>" && git push
|
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Step 2: Build + push the container image on the build server
|
**Fix:** Add `require_arg` call, matching the pattern used by all other flags:
|
||||||
|
|
||||||
The build server (192.168.0.180) has the build toolchain. The build script lives at
|
|
||||||
`~/build/felhom-hub/build.sh` on the build server (NOT in this repo).
|
|
||||||
|
|
||||||
First, check the current running version:
|
|
||||||
```bash
|
|
||||||
ssh kisfenyo@192.168.0.180 "sudo kubectl get deploy -n felhom-system hub -o jsonpath='{.spec.template.spec.containers[0].image}'"
|
|
||||||
```
|
|
||||||
|
|
||||||
Then build with the next version (e.g., if current is 0.1.2, use 0.1.3):
|
|
||||||
```bash
|
|
||||||
ssh kisfenyo@192.168.0.180 "cd ~/build/felhom-hub && ./build.sh <NEW_VERSION> --push"
|
|
||||||
```
|
|
||||||
|
|
||||||
The build script:
|
|
||||||
- Pulls latest code from Gitea (`git pull` on the felhom.eu repo)
|
|
||||||
- Copies `hub/` source to a clean build workspace
|
|
||||||
- Builds Docker image with version + build-time ldflags
|
|
||||||
- Pushes to `gitea.dooplex.hu/admin/felhom-hub:<VERSION>` and `:latest`
|
|
||||||
|
|
||||||
#### Step 3: Deploy to k3s
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
ssh kisfenyo@192.168.0.180 "sudo kubectl set image -n felhom-system deploy/hub hub=gitea.dooplex.hu/admin/felhom-hub:<NEW_VERSION>"
|
--hdd-path)
|
||||||
|
require_arg "$1" "${2:-}"
|
||||||
|
HDD_PATH="$2"; shift 2 ;;
|
||||||
```
|
```
|
||||||
|
|
||||||
#### Step 4: Verify the deployment
|
**Verification:** Search for other flags in the same `while` loop — they all use `require_arg`. Confirm `require_arg` is defined earlier in the script (it is).
|
||||||
|
|
||||||
```bash
|
|
||||||
ssh kisfenyo@192.168.0.180 "sudo kubectl get pods -n felhom-system -l app=hub && echo '---' && sudo kubectl logs -n felhom-system -l app=hub --tail 10"
|
|
||||||
```
|
|
||||||
|
|
||||||
Should show pod Running and `[INFO] felhom-hub <VERSION> starting` in logs.
|
|
||||||
|
|
||||||
#### Build workflow summary
|
|
||||||
|
|
||||||
| Step | Command | Where |
|
|
||||||
|------|---------|-------|
|
|
||||||
| 1. Commit + push | `git add -A && git commit && git push` | Local (this repo) |
|
|
||||||
| 2. Build + push image | `ssh 192.168.0.180 "cd ~/build/felhom-hub && ./build.sh <VER> --push"` | Build server |
|
|
||||||
| 3. Deploy | `ssh 192.168.0.180 "sudo kubectl set image -n felhom-system deploy/hub hub=...:<VER>"` | Build server (kubectl) |
|
|
||||||
| 4. Verify | `ssh 192.168.0.180 "sudo kubectl get pods -n felhom-system -l app=hub"` | Build server |
|
|
||||||
|
|
||||||
### Build & deploy workflow — Website
|
|
||||||
|
|
||||||
The website auto-deploys via git-sync sidecar. Just push to `main`:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd /e/git/felhom.eu
|
|
||||||
git add -A && git commit -m "<message>" && git push
|
|
||||||
```
|
|
||||||
|
|
||||||
Changes are live within 1-2 minutes. No build step needed.
|
|
||||||
|
|
||||||
For emergency edits, use FileBrowser at `https://files.felhom.eu`.
|
|
||||||
|
|
||||||
### Build & deploy workflow — K8s Manifests
|
|
||||||
|
|
||||||
Manifests are applied manually:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ssh kisfenyo@192.168.0.180 "sudo kubectl apply -f /home/kisfenyo/git/felhom.eu/manifests/<manifest>.yaml"
|
|
||||||
```
|
|
||||||
|
|
||||||
Remember to `git pull` on the build server first if you pushed changes locally.
|
|
||||||
|
|
||||||
### Tech stack (Hub)
|
|
||||||
|
|
||||||
- **Language:** Go 1.24+
|
|
||||||
- **Web framework:** stdlib `net/http` + `html/template`
|
|
||||||
- **Database:** SQLite via `modernc.org/sqlite` (pure Go, no CGo)
|
|
||||||
- **Auth:** bcrypt password hash + basic auth
|
|
||||||
- **Deployment:** Docker container on k3s (felhom-system namespace)
|
|
||||||
- **Storage:** Longhorn PVC at `/data/` (SQLite DB)
|
|
||||||
- **Config:** YAML file mounted via k8s ConfigMap at `/etc/felhom-hub/hub.yaml`
|
|
||||||
|
|
||||||
### Key patterns
|
|
||||||
|
|
||||||
- Hub receives reports from customer controllers via `POST /api/v1/report` (Bearer token auth)
|
|
||||||
- Dashboard shows all customers in a table with status, CPU, memory, disk, containers, backup age
|
|
||||||
- Customer detail page shows system info, report history, full JSON report
|
|
||||||
- Status logic: OK (report < 30m), WARN (30m-1h or health=warn), DOWN (> 1h or health=fail)
|
|
||||||
- SQLite timestamps may vary in format — use `parseSQLiteTime()` for robust parsing
|
|
||||||
- Auto-refresh: dashboard and detail pages refresh every 60 seconds via `<meta http-equiv="refresh">`
|
|
||||||
- Geo-restricted to Hungary via nginx ingress annotation
|
|
||||||
|
|
||||||
### File encoding
|
|
||||||
|
|
||||||
All HTML files in `website/` are **UTF-8 with BOM**. Ensure your editor preserves this.
|
|
||||||
Hub Go source files are standard UTF-8 (no BOM).
|
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Task 2: Create .gitignore
|
## Bug 2: Implicit `event` variable in `stackAction()` (layout.html)
|
||||||
|
|
||||||
Create `.gitignore` in the repo root with appropriate entries:
|
**File:** `controller/internal/web/templates/layout.html`
|
||||||
|
|
||||||
```gitignore
|
**Problem:** The `stackAction` JavaScript function references `event.currentTarget` to get the clicked button, but `event` is never passed as a parameter. It relies on the implicit global `window.event` object, which is non-standard and deprecated. Works in Chrome/Firefox today but is not guaranteed.
|
||||||
# Go binaries
|
|
||||||
hub/hub
|
|
||||||
hub/hub.exe
|
|
||||||
hub/bin/
|
|
||||||
|
|
||||||
# Build artifacts
|
**Current code:**
|
||||||
*.exe
|
|
||||||
*.dll
|
|
||||||
*.so
|
|
||||||
*.dylib
|
|
||||||
|
|
||||||
# Test and coverage
|
```javascript
|
||||||
*.test
|
async function stackAction(name, action) {
|
||||||
*.out
|
const btn = event.currentTarget;
|
||||||
coverage.html
|
|
||||||
|
|
||||||
# IDE
|
|
||||||
.idea/
|
|
||||||
.vscode/
|
|
||||||
*.swp
|
|
||||||
*.swo
|
|
||||||
*~
|
|
||||||
|
|
||||||
# OS
|
|
||||||
.DS_Store
|
|
||||||
Thumbs.db
|
|
||||||
|
|
||||||
# Temporary files
|
|
||||||
*.tmp
|
|
||||||
*.bak
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Task 3: Remove hub.exe from git history
|
**Fix — Step 1:** Change the function signature to accept `event`:
|
||||||
|
|
||||||
After creating `.gitignore`, remove the committed binary:
|
```javascript
|
||||||
|
async function stackAction(event, name, action) {
|
||||||
```bash
|
const btn = event.currentTarget;
|
||||||
git rm --cached hub/hub.exe
|
|
||||||
```
|
```
|
||||||
|
|
||||||
This removes it from tracking but the `.gitignore` prevents re-adding. No need to rewrite
|
**Fix — Step 2:** Update ALL `onclick` call sites in the same file that call `stackAction` to pass `event` as the first argument. Search for `stackAction(` in the template — each call looks like:
|
||||||
history — just remove from current tree.
|
|
||||||
|
|
||||||
Also check for any other binaries that shouldn't be tracked:
|
```html
|
||||||
```bash
|
onclick="stackAction('{{.Name}}', 'start')"
|
||||||
find hub/ -name "*.exe" -o -name "hub" -type f -executable
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Task 4: Fix hub statusIcon rendering
|
Change each to:
|
||||||
|
|
||||||
**File:** `hub/internal/web/server.go`
|
```html
|
||||||
|
onclick="stackAction(event, '{{.Name}}', 'start')"
|
||||||
|
```
|
||||||
|
|
||||||
**Problem:** `statusIcon()` returns HTML entities (`🟢`), but Go's `html/template`
|
There are multiple call sites (start, stop, restart buttons in the stacks section). Update **all** of them.
|
||||||
auto-escapes them to literal text (`&#x1F7E2;`). Additionally, emoji don't respond to
|
|
||||||
CSS `color` — but the templates already apply `style="color: {{statusColor .OverallStatus}}"`.
|
|
||||||
|
|
||||||
**Fix:** Change `statusIcon()` to return `●` (U+25CF, BLACK CIRCLE) — a plain Unicode character
|
**Verification:** Search the entire file for `stackAction(` — every call site must pass `event` as the first argument. No other functions in the codebase call `stackAction`.
|
||||||
that responds to CSS color styling. The existing `statusColor()` function handles color differentiation.
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Bug 3: Missing separator in page title (layout.html)
|
||||||
|
|
||||||
|
**File:** `controller/internal/web/templates/layout.html`
|
||||||
|
|
||||||
|
**Problem:** The `<title>` tag concatenates `.Title` and "Felhom.eu" with no separator, rendering as e.g. `"VezérlőpultFelhom.eu"` instead of `"Vezérlőpult — Felhom.eu"`.
|
||||||
|
|
||||||
|
**Current code:**
|
||||||
|
|
||||||
|
```html
|
||||||
|
<title>{{.Title}}Felhom.eu</title>
|
||||||
|
```
|
||||||
|
|
||||||
|
**Fix:**
|
||||||
|
|
||||||
|
```html
|
||||||
|
<title>{{.Title}} — Felhom.eu</title>
|
||||||
|
```
|
||||||
|
|
||||||
|
Uses em dash (U+2014) with spaces on both sides. This is a single-character change in the template.
|
||||||
|
|
||||||
|
**Edge case:** If `.Title` is empty, the title becomes ` — Felhom.eu` (leading space + dash). Check if any handler sets an empty `.Title`. If so, consider using a conditional:
|
||||||
|
|
||||||
|
```html
|
||||||
|
<title>{{if .Title}}{{.Title}} — {{end}}Felhom.eu</title>
|
||||||
|
```
|
||||||
|
|
||||||
|
Check all handlers that call `render()` or `renderTemplate()` — if every handler always sets a non-empty `.Title`, the simple fix (without conditional) is fine.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Bug 4: `nextPruneLabel` edge case on Sunday before 4am (funcmap.go)
|
||||||
|
|
||||||
|
**File:** `controller/internal/web/funcmap.go`
|
||||||
|
|
||||||
|
**Problem:** The `nextPruneLabel` function calculates when the next weekly prune (Sunday 4:00) will occur. On Sunday before 4am, `daysUntilSunday` computes to 0, but the function returns the date in `"2006-01-02"` format instead of `"ma"` (Hungarian for "today"). Every other "today" scenario in the codebase uses the `"ma"` label.
|
||||||
|
|
||||||
|
**Current code:**
|
||||||
|
|
||||||
```go
|
```go
|
||||||
// BEFORE (broken):
|
daysUntilSunday := (7 - int(now.Weekday())) % 7
|
||||||
func statusIcon(status string) string {
|
if daysUntilSunday == 0 && now.Hour() >= 4 {
|
||||||
switch status {
|
daysUntilSunday = 7
|
||||||
case "ok":
|
|
||||||
return "🟢" // green circle
|
|
||||||
case "warn":
|
|
||||||
return "🟡" // yellow circle
|
|
||||||
case "down", "fail":
|
|
||||||
return "🔴" // red circle
|
|
||||||
default:
|
|
||||||
return "⚪" // white circle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// AFTER (works with CSS color):
|
|
||||||
func statusIcon(status string) string {
|
|
||||||
return "●"
|
|
||||||
}
|
}
|
||||||
|
next := time.Date(now.Year(), now.Month(), now.Day()+daysUntilSunday, 4, 0, 0, 0, now.Location())
|
||||||
|
return next.Format("2006-01-02")
|
||||||
```
|
```
|
||||||
|
|
||||||
No template changes needed — `statusColor()` already provides the correct color per status.
|
The logic breakdown:
|
||||||
|
- Sunday, hour >= 4: `daysUntilSunday` = 0 → set to 7 (next week). Correct.
|
||||||
|
- Sunday, hour < 4: `daysUntilSunday` = 0 → stays 0, returns today's date as `"2006-01-02"`. Should return `"ma"`.
|
||||||
|
- Any other day: `daysUntilSunday` > 0 → returns future date. Correct.
|
||||||
|
|
||||||
**Verification:**
|
**Fix:**
|
||||||
1. Dashboard: colored dot (green/yellow/red) before customer name, no `&#x` text
|
|
||||||
2. Customer detail: colored dot in header
|
```go
|
||||||
3. Colors match status (green=OK, yellow=WARN, red=DOWN)
|
daysUntilSunday := (7 - int(now.Weekday())) % 7
|
||||||
|
if daysUntilSunday == 0 {
|
||||||
|
if now.Hour() >= 4 {
|
||||||
|
daysUntilSunday = 7 // Already ran today, next week
|
||||||
|
} else {
|
||||||
|
return "ma" // Today (Sunday), hasn't run yet
|
||||||
|
}
|
||||||
|
}
|
||||||
|
next := time.Date(now.Year(), now.Month(), now.Day()+daysUntilSunday, 4, 0, 0, 0, now.Location())
|
||||||
|
return next.Format("2006-01-02")
|
||||||
|
```
|
||||||
|
|
||||||
|
**Verification:** Mentally walk through all cases:
|
||||||
|
- Monday–Saturday: `daysUntilSunday` is 1–6, returns future date ✓
|
||||||
|
- Sunday 03:00: returns `"ma"` ✓
|
||||||
|
- Sunday 04:00: `daysUntilSunday` = 7, returns next Sunday ✓
|
||||||
|
- Sunday 23:00: `daysUntilSunday` = 7, returns next Sunday ✓
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Build & Deploy
|
## Build & Deploy
|
||||||
|
|
||||||
After all changes, commit and deploy hub v0.1.3:
|
After all fixes, commit and deploy as v0.6.3:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# 1. Commit
|
# 1. Commit
|
||||||
cd /e/git/felhom.eu
|
cd /e/git/deploy-felhom-compose
|
||||||
git add -A && git commit -m "add CLAUDE.md, .gitignore, fix statusIcon rendering" && git push
|
git add -A && git commit -m "fix: require_arg for --hdd-path, explicit event in stackAction, title separator, nextPruneLabel Sunday edge case" && git push
|
||||||
|
|
||||||
# 2. Build
|
# 2. Build (only needed for bugs 2-4 which affect the controller binary/templates)
|
||||||
ssh kisfenyo@192.168.0.180 "cd ~/build/felhom-hub && ./build.sh 0.1.3 --push"
|
ssh kisfenyo@192.168.0.180 "cd ~/build/felhom-controller && ./build.sh 0.6.3 --push"
|
||||||
|
|
||||||
# 3. Deploy
|
# 3. Deploy to demo node
|
||||||
ssh kisfenyo@192.168.0.180 "sudo kubectl set image -n felhom-system deploy/hub hub=gitea.dooplex.hu/admin/felhom-hub:0.1.3"
|
ssh kisfenyo@192.168.0.162 "docker pull gitea.dooplex.hu/admin/felhom-controller:0.6.3 && cd /opt/docker && docker compose up -d"
|
||||||
|
|
||||||
# 4. Verify
|
# 4. Verify
|
||||||
ssh kisfenyo@192.168.0.180 "sudo kubectl rollout status -n felhom-system deploy/hub && sudo kubectl logs -n felhom-system -l app=hub --tail 5"
|
ssh kisfenyo@192.168.0.162 "docker logs felhom-controller --tail 5"
|
||||||
```
|
```
|
||||||
|
|
||||||
## Post-deploy checklist
|
## Post-deploy checklist
|
||||||
|
|
||||||
- [ ] `hub.felhom.eu` shows colored `●` dot, not `🟢` text
|
- [ ] Page title shows separator: "Vezérlőpult — Felhom.eu" (check browser tab)
|
||||||
- [ ] `hub.exe` no longer in repo (`git ls-files hub/hub.exe` returns empty)
|
- [ ] Stack start/stop/restart buttons still work (Bug 2 didn't break onclick handlers)
|
||||||
- [ ] `CLAUDE.md` exists in repo root
|
- [ ] `docker-setup.sh --hdd-path` without value shows friendly error (test locally)
|
||||||
- [ ] `.gitignore` exists in repo root
|
- [ ] Backup page shows "ma" on Sunday before 4am (only testable at that time, or adjust system clock)
|
||||||
Reference in New Issue
Block a user