Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 7f6ef11371 | |||
| 7713d071c2 |
@@ -7,9 +7,9 @@ ARCHITECTURE.md for design details.
|
||||
|
||||
## Current Status
|
||||
|
||||
**Phase:** Phase 5 complete. File exclusion feature added.
|
||||
**Phase:** Phase 5 complete. File exclusion feature added. Add is now idempotent.
|
||||
|
||||
**Last updated:** 2026-03-27
|
||||
**Last updated:** 2026-03-30
|
||||
|
||||
## Completed Steps
|
||||
|
||||
@@ -114,3 +114,4 @@ Phase 6: Manifest Signing (to be planned).
|
||||
| 2026-03-26 | — | `sgard list` remote support: uses `resolveRemoteConfig()` to list server manifest via `PullManifest` RPC. Client `List()` method added. |
|
||||
| 2026-03-26 | — | Version derived from git tags via `VERSION` file. flake.nix reads `VERSION`; Makefile `version` target syncs from latest tag, `build` injects via ldflags. |
|
||||
| 2026-03-27 | — | File exclusion: `sgard exclude`/`include` commands, `Manifest.Exclude` field, Add/MirrorUp/MirrorDown respect exclusions, directory exclusion support. 8 tests. |
|
||||
| 2026-03-30 | — | Idempotent add: `sgard add` silently skips already-tracked files/directories instead of erroring, enabling glob-based workflows. |
|
||||
|
||||
@@ -13,18 +13,11 @@ RUN CGO_ENABLED=0 go build -trimpath -ldflags="-s -w" -o /sgardd ./cmd/sgardd
|
||||
# Runtime stage
|
||||
FROM alpine:3.21
|
||||
|
||||
RUN apk add --no-cache ca-certificates tzdata \
|
||||
&& adduser -D -h /srv/sgard sgard
|
||||
RUN apk add --no-cache ca-certificates tzdata
|
||||
|
||||
COPY --from=builder /sgardd /usr/local/bin/sgardd
|
||||
|
||||
VOLUME /srv/sgard
|
||||
WORKDIR /srv/sgard
|
||||
EXPOSE 9473
|
||||
|
||||
USER sgard
|
||||
|
||||
ENTRYPOINT ["sgardd", \
|
||||
"--repo", "/srv/sgard", \
|
||||
"--authorized-keys", "/srv/sgard/authorized_keys", \
|
||||
"--tls-cert", "/srv/sgard/certs/sgard.pem", \
|
||||
"--tls-key", "/srv/sgard/certs/sgard.key"]
|
||||
ENTRYPOINT ["sgardd"]
|
||||
|
||||
@@ -239,7 +239,7 @@ func (g *Garden) Add(paths []string, opts ...AddOptions) error {
|
||||
// Track the directory itself as a structural entry.
|
||||
tilded := toTildePath(abs)
|
||||
if g.findEntry(tilded) != nil {
|
||||
return fmt.Errorf("already tracking %s", tilded)
|
||||
continue
|
||||
}
|
||||
entry := manifest.Entry{
|
||||
Path: tilded,
|
||||
@@ -277,7 +277,7 @@ func (g *Garden) Add(paths []string, opts ...AddOptions) error {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if err := g.addEntry(abs, info, now, false, o); err != nil {
|
||||
if err := g.addEntry(abs, info, now, true, o); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
@@ -200,7 +200,7 @@ func TestAddSymlink(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestAddDuplicateRejected(t *testing.T) {
|
||||
func TestAddDuplicateIsIdempotent(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
repoDir := filepath.Join(root, "repo")
|
||||
|
||||
@@ -218,8 +218,19 @@ func TestAddDuplicateRejected(t *testing.T) {
|
||||
t.Fatalf("first Add: %v", err)
|
||||
}
|
||||
|
||||
if err := g.Add([]string{testFile}); err == nil {
|
||||
t.Fatal("second Add of same path should fail")
|
||||
if err := g.Add([]string{testFile}); err != nil {
|
||||
t.Fatalf("second Add of same path should be idempotent: %v", err)
|
||||
}
|
||||
|
||||
entries := g.List()
|
||||
count := 0
|
||||
for _, e := range entries {
|
||||
if e.Path == toTildePath(testFile) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
if count != 1 {
|
||||
t.Fatalf("expected 1 entry, got %d", count)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user