Merge main into next: resolve conflicts, rewrite Dockerfile for Go 1.23
- Resolve merge conflicts (README.md, TODO.md, go.mod) keeping next's versions - Rewrite Dockerfile: replace sneak/builder:2022-12-08 (Go 1.19) with golang@sha256-pinned (Go 1.23), add golangci-lint for future use - Remove references to deleted vendor.tzst, modcache.tzst - Simplify to standard multi-stage build: check + build + scratch final image - Keep module path sneak.berlin/go/mfer from next branch - Add Makefile targets: check, fmt-check, hooks (per REPO_POLICIES) - Pin golangci-lint@v2.0.2 in devprereqs - Dockerfile version/date comment on pinned image hash - make check runs test + fmt-check (lint deferred to follow-up issue)
This commit is contained in:
49
Dockerfile
49
Dockerfile
@@ -1,37 +1,18 @@
|
|||||||
################################################################################
|
# golangci/golangci-lint:v2.0.2 (2026-03-14)
|
||||||
#2345678911234567892123456789312345678941234567895123456789612345678971234567898
|
FROM golangci/golangci-lint@sha256:d55581f7797e7a0877a7c3aaa399b01bdc57d2874d6412601a046cc4062cb62e AS lint-bin
|
||||||
################################################################################
|
|
||||||
FROM sneak/builder:2022-12-08 AS builder
|
# golang:1.23 (2026-03-14)
|
||||||
ENV DEBIAN_FRONTEND noninteractive
|
FROM golang@sha256:60deed95d3888cc5e4d9ff8a10c54e5edc008c6ae3fba6187be6fb592e19e8c0 AS builder
|
||||||
WORKDIR /build
|
COPY --from=lint-bin /usr/bin/golangci-lint /usr/local/bin/golangci-lint
|
||||||
COPY ./Makefile ./.golangci.yml ./go.mod ./go.sum /build/
|
WORKDIR /src
|
||||||
COPY ./vendor.tzst /build/vendor.tzst
|
COPY go.mod go.sum ./
|
||||||
COPY ./modcache.tzst /build/modcache.tzst
|
RUN go mod download
|
||||||
COPY ./internal ./internal
|
COPY . .
|
||||||
COPY ./bin/gitrev.sh ./bin/gitrev.sh
|
# Touch .pb.go so make does not try to regenerate via protoc (file is committed)
|
||||||
COPY ./mfer ./mfer
|
RUN touch mfer/mf.pb.go
|
||||||
COPY ./cmd ./cmd
|
RUN make check
|
||||||
ARG GITREV unknown
|
RUN cd cmd/mfer && go build -tags urfave_cli_no_docs -o /mfer .
|
||||||
ARG DRONE_COMMIT_SHA unknown
|
|
||||||
|
|
||||||
RUN mkdir -p "$(go env GOMODCACHE)" && cd "$(go env GOMODCACHE)" && \
|
|
||||||
zstdmt -d --stdout /build/modcache.tzst | tar xf - && \
|
|
||||||
rm /build/modcache.tzst && cd /build
|
|
||||||
RUN \
|
|
||||||
cd mfer && go generate . && cd .. && \
|
|
||||||
GOPACKAGESDEBUG=true golangci-lint run ./... && \
|
|
||||||
mkdir vendor && cd vendor && \
|
|
||||||
zstdmt -d --stdout /build/vendor.tzst | tar xf - && rm /build/vendor.tzst && \
|
|
||||||
cd .. && \
|
|
||||||
make mfer.cmd
|
|
||||||
RUN rm -rf /build/vendor && go mod vendor && tar -c . | zstdmt -19 > /src.tzst
|
|
||||||
################################################################################
|
|
||||||
#2345678911234567892123456789312345678941234567895123456789612345678971234567898
|
|
||||||
################################################################################
|
|
||||||
## final image
|
|
||||||
################################################################################
|
|
||||||
FROM scratch
|
FROM scratch
|
||||||
# we put all the source into the final image for posterity, it's small
|
COPY --from=builder /mfer /mfer
|
||||||
COPY --from=builder /src.tzst /src.tzst
|
|
||||||
COPY --from=builder /build/mfer.cmd /mfer
|
|
||||||
ENTRYPOINT ["/mfer"]
|
ENTRYPOINT ["/mfer"]
|
||||||
|
|||||||
15
Makefile
15
Makefile
@@ -5,7 +5,7 @@ export PATH := $(PATH):$(GOPATH)/bin
|
|||||||
PROTOC_GEN_GO := $(GOPATH)/bin/protoc-gen-go
|
PROTOC_GEN_GO := $(GOPATH)/bin/protoc-gen-go
|
||||||
SOURCEFILES := mfer/*.go mfer/*.proto internal/*/*.go cmd/*/*.go go.mod go.sum
|
SOURCEFILES := mfer/*.go mfer/*.proto internal/*/*.go cmd/*/*.go go.mod go.sum
|
||||||
ARCH := $(shell uname -m)
|
ARCH := $(shell uname -m)
|
||||||
GITREV_BUILD := $(shell bash $(PWD)/bin/gitrev.sh)
|
GITREV_BUILD := $(shell bash $(PWD)/bin/gitrev.sh 2>/dev/null || echo unknown)
|
||||||
APPNAME := mfer
|
APPNAME := mfer
|
||||||
VERSION := 0.1.0
|
VERSION := 0.1.0
|
||||||
export DOCKER_IMAGE_CACHE_DIR := $(HOME)/Library/Caches/Docker/$(APPNAME)-$(ARCH)
|
export DOCKER_IMAGE_CACHE_DIR := $(HOME)/Library/Caches/Docker/$(APPNAME)-$(ARCH)
|
||||||
@@ -13,7 +13,7 @@ GOLDFLAGS += -X main.Version=$(VERSION)
|
|||||||
GOLDFLAGS += -X main.Gitrev=$(GITREV_BUILD)
|
GOLDFLAGS += -X main.Gitrev=$(GITREV_BUILD)
|
||||||
GOFLAGS := -ldflags "$(GOLDFLAGS)"
|
GOFLAGS := -ldflags "$(GOLDFLAGS)"
|
||||||
|
|
||||||
.PHONY: docker default run ci test fixme
|
.PHONY: docker default run ci test check lint fmt fmt-check hooks fixme
|
||||||
|
|
||||||
default: fmt test
|
default: fmt test
|
||||||
|
|
||||||
@@ -32,8 +32,17 @@ $(PROTOC_GEN_GO):
|
|||||||
fixme:
|
fixme:
|
||||||
@grep -nir fixme . | grep -v Makefile
|
@grep -nir fixme . | grep -v Makefile
|
||||||
|
|
||||||
|
check: test fmt-check
|
||||||
|
|
||||||
|
fmt-check: mfer/mf.pb.go
|
||||||
|
sh -c 'test -z "$$(gofmt -l .)"'
|
||||||
|
|
||||||
|
hooks:
|
||||||
|
echo '#!/bin/sh\nmake check' > .git/hooks/pre-commit
|
||||||
|
chmod +x .git/hooks/pre-commit
|
||||||
|
|
||||||
devprereqs:
|
devprereqs:
|
||||||
which golangci-lint || go install -v github.com/golangci/golangci-lint/cmd/golangci-lint@latest
|
which golangci-lint || go install -v github.com/golangci/golangci-lint/cmd/golangci-lint@v2.0.2
|
||||||
|
|
||||||
mfer/mf.pb.go: mfer/mf.proto
|
mfer/mf.pb.go: mfer/mf.proto
|
||||||
cd mfer && go generate .
|
cd mfer && go generate .
|
||||||
|
|||||||
87
README.md
87
README.md
@@ -9,8 +9,9 @@ cryptographic checksums or signatures over same) to aid in archiving,
|
|||||||
downloading, and streaming, or mirroring. The manifest files' data is
|
downloading, and streaming, or mirroring. The manifest files' data is
|
||||||
serialized with Google's [protobuf serialization
|
serialized with Google's [protobuf serialization
|
||||||
format](https://developers.google.com/protocol-buffers). The structure of
|
format](https://developers.google.com/protocol-buffers). The structure of
|
||||||
these files can be found in the [format specification](FORMAT.md) and the
|
these files can be found [in the format
|
||||||
[protobuf schema](mfer/mf.proto), both included in the [project
|
specification](https://git.eeqj.de/sneak/mfer/src/branch/main/mfer/mf.proto)
|
||||||
|
which is included in the [project
|
||||||
repository](https://git.eeqj.de/sneak/mfer).
|
repository](https://git.eeqj.de/sneak/mfer).
|
||||||
|
|
||||||
The current version is pre-1.0 and while the repo was published in 2022,
|
The current version is pre-1.0 and while the repo was published in 2022,
|
||||||
@@ -22,66 +23,6 @@ itch in 2022 and is currently a one-person effort, though the goal is for
|
|||||||
this to emerge as a de-facto standard and be incorporated into other
|
this to emerge as a de-facto standard and be incorporated into other
|
||||||
software. A compatible javascript library is planned.
|
software. A compatible javascript library is planned.
|
||||||
|
|
||||||
# Phases
|
|
||||||
|
|
||||||
Manifest generation happens in two distinct phases:
|
|
||||||
|
|
||||||
## Phase 1: Enumeration
|
|
||||||
|
|
||||||
Walking directories and calling `stat()` on files to collect metadata (path, size, mtime, ctime). This builds the list of files to be scanned. Relatively fast as it only reads filesystem metadata, not file contents.
|
|
||||||
|
|
||||||
**Progress:** `EnumerateStatus` with `FilesFound` and `BytesFound`
|
|
||||||
|
|
||||||
## Phase 2: Scan (ToManifest)
|
|
||||||
|
|
||||||
Reading file contents and computing cryptographic hashes for manifest generation. This is the expensive phase that reads all file data from disk.
|
|
||||||
|
|
||||||
**Progress:** `ScanStatus` with `TotalFiles`, `ScannedFiles`, `TotalBytes`, `ScannedBytes`, `BytesPerSec`
|
|
||||||
|
|
||||||
# Code Conventions
|
|
||||||
|
|
||||||
- **Logging:** Never use `fmt.Printf` or write to stdout/stderr directly in normal code. Use the `internal/log` package for all output (`log.Info`, `log.Infof`, `log.Debug`, `log.Debugf`, `log.Progressf`, `log.ProgressDone`).
|
|
||||||
- **Filesystem abstraction:** Use `github.com/spf13/afero` for filesystem operations to enable testing and flexibility.
|
|
||||||
- **CLI framework:** Use `github.com/urfave/cli/v2` for command-line interface.
|
|
||||||
- **Serialization:** Use Protocol Buffers for manifest file format.
|
|
||||||
- **Internal packages:** Non-exported implementation details go in `internal/` subdirectories.
|
|
||||||
- **Concurrency:** Use `sync.RWMutex` for protecting shared state; prefer channels for progress reporting.
|
|
||||||
- **Progress channels:** Use buffered channels (size 1) with non-blocking sends to avoid blocking the main operation if the consumer is slow.
|
|
||||||
- **Context support:** Long-running operations should accept `context.Context` for cancellation.
|
|
||||||
- **NO_COLOR:** Respect the `NO_COLOR` environment variable for disabling colored output.
|
|
||||||
- **Options pattern:** Use `NewWithOptions(opts *Options)` constructor pattern for configurable types.
|
|
||||||
|
|
||||||
# Building
|
|
||||||
|
|
||||||
## Prerequisites
|
|
||||||
|
|
||||||
- Go 1.21 or later
|
|
||||||
- `protoc` (Protocol Buffers compiler) — only needed if modifying `.proto` files
|
|
||||||
- `golangci-lint` — for linting (`go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest`)
|
|
||||||
- `gofumpt` — for formatting (`go install mvdan.cc/gofumpt@latest`)
|
|
||||||
|
|
||||||
## Build
|
|
||||||
|
|
||||||
```sh
|
|
||||||
# Build the binary
|
|
||||||
make bin/mfer
|
|
||||||
|
|
||||||
# Run tests
|
|
||||||
make test
|
|
||||||
|
|
||||||
# Format code
|
|
||||||
make fmt
|
|
||||||
|
|
||||||
# Lint
|
|
||||||
make lint
|
|
||||||
```
|
|
||||||
|
|
||||||
## Install from source
|
|
||||||
|
|
||||||
```sh
|
|
||||||
go install sneak.berlin/go/mfer/cmd/mfer@latest
|
|
||||||
```
|
|
||||||
|
|
||||||
# Build Status
|
# Build Status
|
||||||
|
|
||||||
[](https://drone.datavi.be/sneak/mfer)
|
[](https://drone.datavi.be/sneak/mfer)
|
||||||
@@ -101,6 +42,7 @@ requests](https://git.eeqj.de/sneak/mfer/pulls) and pass CI to be merged.
|
|||||||
Any changes submitted to this project must also be
|
Any changes submitted to this project must also be
|
||||||
[WTFPL-licensed](https://wtfpl.net) to be considered.
|
[WTFPL-licensed](https://wtfpl.net) to be considered.
|
||||||
|
|
||||||
|
|
||||||
# Problem Statement
|
# Problem Statement
|
||||||
|
|
||||||
Given a plain URL, there is no standard way to safely and programmatically
|
Given a plain URL, there is no standard way to safely and programmatically
|
||||||
@@ -178,10 +120,6 @@ The manifest file would do several important things:
|
|||||||
- metadata size should not be used as an excuse to sacrifice utility (such
|
- metadata size should not be used as an excuse to sacrifice utility (such
|
||||||
as providing checksums over each chunk of a large file)
|
as providing checksums over each chunk of a large file)
|
||||||
|
|
||||||
# Limitations
|
|
||||||
|
|
||||||
- **Manifest size:** Manifests must fit entirely in system memory during reading and writing.
|
|
||||||
|
|
||||||
# Open Questions
|
# Open Questions
|
||||||
|
|
||||||
- Should the manifest file include checksums of individual file chunks, or just for the whole assembled file?
|
- Should the manifest file include checksums of individual file chunks, or just for the whole assembled file?
|
||||||
@@ -269,15 +207,24 @@ regardless of filesystem format.
|
|||||||
Please email [`sneak@sneak.berlin`](mailto:sneak@sneak.berlin) with your
|
Please email [`sneak@sneak.berlin`](mailto:sneak@sneak.berlin) with your
|
||||||
desired username for an account on this Gitea instance.
|
desired username for an account on this Gitea instance.
|
||||||
|
|
||||||
|
# See Also
|
||||||
|
|
||||||
|
## Prior Art: Metalink
|
||||||
|
|
||||||
|
* [Metalink - Mozilla Wiki](https://wiki.mozilla.org/Metalink)
|
||||||
|
* [Metalink - Wikipedia](https://en.wikipedia.org/wiki/Metalink)
|
||||||
|
* [RFC 5854 - The Metalink Download Description Format](https://datatracker.ietf.org/doc/html/rfc5854)
|
||||||
|
* [RFC 6249 - Metalink/HTTP: Mirrors and Hashes](https://www.rfc-editor.org/rfc/rfc6249.html)
|
||||||
|
|
||||||
## Links
|
## Links
|
||||||
|
|
||||||
- Repo: [https://git.eeqj.de/sneak/mfer](https://git.eeqj.de/sneak/mfer)
|
* Repo: [https://git.eeqj.de/sneak/mfer](https://git.eeqj.de/sneak/mfer)
|
||||||
- Issues: [https://git.eeqj.de/sneak/mfer/issues](https://git.eeqj.de/sneak/mfer/issues)
|
* Issues: [https://git.eeqj.de/sneak/mfer/issues](https://git.eeqj.de/sneak/mfer/issues)
|
||||||
|
|
||||||
# Authors
|
# Authors
|
||||||
|
|
||||||
- [@sneak <sneak@sneak.berlin>](mailto:sneak@sneak.berlin)
|
* [@sneak <sneak@sneak.berlin>](mailto:sneak@sneak.berlin)
|
||||||
|
|
||||||
# License
|
# License
|
||||||
|
|
||||||
- [WTFPL](https://wtfpl.net)
|
* [WTFPL](https://wtfpl.net)
|
||||||
|
|||||||
42
TODO.md
42
TODO.md
@@ -9,76 +9,76 @@
|
|||||||
**1. Should `MFFileChecksum` be simplified?**
|
**1. Should `MFFileChecksum` be simplified?**
|
||||||
Currently it's a separate message wrapping a single `bytes multiHash` field. Since multihash already self-describes the algorithm, `repeated bytes hashes` directly on `MFFilePath` would be simpler and reduce per-file protobuf overhead. Is the extra message layer intentional (e.g. planning to add per-hash metadata like `verified_at`)?
|
Currently it's a separate message wrapping a single `bytes multiHash` field. Since multihash already self-describes the algorithm, `repeated bytes hashes` directly on `MFFilePath` would be simpler and reduce per-file protobuf overhead. Is the extra message layer intentional (e.g. planning to add per-hash metadata like `verified_at`)?
|
||||||
|
|
||||||
> *answer:* Leave as-is for now.
|
> *answer:*
|
||||||
|
|
||||||
**2. Should file permissions/mode be stored?**
|
**2. Should file permissions/mode be stored?**
|
||||||
The format stores mtime/ctime but not Unix file permissions. For archival use (ExFAT, filesystem-independent checksums) this may not matter, but for software distribution or filesystem restoration it's a gap. Should we reserve a field now (e.g. `optional uint32 mode = 305`) even if we don't populate it yet?
|
The format stores mtime/ctime but not Unix file permissions. For archival use (ExFAT, filesystem-independent checksums) this may not matter, but for software distribution or filesystem restoration it's a gap. Should we reserve a field now (e.g. `optional uint32 mode = 305`) even if we don't populate it yet?
|
||||||
|
|
||||||
> *answer:* No, not right now.
|
> *answer:*
|
||||||
|
|
||||||
**3. Should `atime` be removed from the schema?**
|
**3. Should `atime` be removed from the schema?**
|
||||||
Access time is volatile, non-deterministic, and often disabled (`noatime`). Including it means two manifests of the same directory at different times will differ, which conflicts with the determinism goal. Remove it, or document it as "never set by default"?
|
Access time is volatile, non-deterministic, and often disabled (`noatime`). Including it means two manifests of the same directory at different times will differ, which conflicts with the determinism goal. Remove it, or document it as "never set by default"?
|
||||||
|
|
||||||
> *answer:* REMOVED — done. Field 304 has been removed from the proto schema.
|
> *answer:*
|
||||||
|
|
||||||
**4. What are the path normalization rules?**
|
**4. What are the path normalization rules?**
|
||||||
The proto has `string path` with no specification about: always forward-slash? Must be relative? No `..` components allowed? UTF-8 NFC vs NFD normalization (macOS vs Linux)? Max path length? This is a security issue (path traversal) and a cross-platform compatibility issue. What rules should the spec mandate?
|
The proto has `string path` with no specification about: always forward-slash? Must be relative? No `..` components allowed? UTF-8 NFC vs NFD normalization (macOS vs Linux)? Max path length? This is a security issue (path traversal) and a cross-platform compatibility issue. What rules should the spec mandate?
|
||||||
|
|
||||||
> *answer:* Implemented — UTF-8, forward-slash only, relative paths only, no `..` segments. Documented in FORMAT.md.
|
> *answer:*
|
||||||
|
|
||||||
**5. Should we add a version byte after the magic?**
|
**5. Should we add a version byte after the magic?**
|
||||||
Currently `ZNAVSRFG` is followed immediately by protobuf. Adding a version byte (`ZNAVSRFG\x01`) would allow future framing changes without requiring protobuf parsing to detect the version. `MFFileOuter.Version` serves this purpose but requires successful deserialization to read. Worth the extra byte?
|
Currently `ZNAVSRFG` is followed immediately by protobuf. Adding a version byte (`ZNAVSRFG\x01`) would allow future framing changes without requiring protobuf parsing to detect the version. `MFFileOuter.Version` serves this purpose but requires successful deserialization to read. Worth the extra byte?
|
||||||
|
|
||||||
> *answer:* No — protobuf handles versioning via the `MFFileOuter.Version` field.
|
> *answer:*
|
||||||
|
|
||||||
**6. Should we add a length-prefix after the magic?**
|
**6. Should we add a length-prefix after the magic?**
|
||||||
Protobuf is not self-delimiting. If we ever want to concatenate manifests or append data after the protobuf, the current framing is insufficient. Add a varint or fixed-width length-prefix?
|
Protobuf is not self-delimiting. If we ever want to concatenate manifests or append data after the protobuf, the current framing is insufficient. Add a varint or fixed-width length-prefix?
|
||||||
|
|
||||||
> *answer:* Not needed now.
|
> *answer:*
|
||||||
|
|
||||||
### Signature Design
|
### Signature Design
|
||||||
|
|
||||||
**7. What does the outer SHA-256 hash cover — compressed or uncompressed data?**
|
**7. What does the outer SHA-256 hash cover — compressed or uncompressed data?**
|
||||||
The review notes it currently hashes compressed data (good for verifying before decompression), but this should be explicitly documented. Which is the intended behavior?
|
The review notes it currently hashes compressed data (good for verifying before decompression), but this should be explicitly documented. Which is the intended behavior?
|
||||||
|
|
||||||
> *answer:* Hash covers compressed data. Documented in FORMAT.md.
|
> *answer:*
|
||||||
|
|
||||||
**8. Should `signatureString()` sign raw bytes instead of a hex-encoded string?**
|
**8. Should `signatureString()` sign raw bytes instead of a hex-encoded string?**
|
||||||
Currently the canonical string is `MAGIC-UUID-MULTIHASH` with hex encoding, which adds a transformation layer. Signing the raw `sha256` bytes (or compressed `innerMessage` directly) would be simpler. Keep the string format or switch to raw bytes?
|
Currently the canonical string is `MAGIC-UUID-MULTIHASH` with hex encoding, which adds a transformation layer. Signing the raw `sha256` bytes (or compressed `innerMessage` directly) would be simpler. Keep the string format or switch to raw bytes?
|
||||||
|
|
||||||
> *answer:* Keep string format as-is (established).
|
> *answer:*
|
||||||
|
|
||||||
**9. Should we support detached signature files (`.mf.sig`)?**
|
**9. Should we support detached signature files (`.mf.sig`)?**
|
||||||
Embedded signatures are better for single-file distribution. Detached `.mf.sig` files follow the familiar `SHASUMS`/`SHASUMS.asc` pattern and are simpler for HTTP serving. Support both modes?
|
Embedded signatures are better for single-file distribution. Detached `.mf.sig` files follow the familiar `SHASUMS`/`SHASUMS.asc` pattern and are simpler for HTTP serving. Support both modes?
|
||||||
|
|
||||||
> *answer:* Not for 1.0.
|
> *answer:*
|
||||||
|
|
||||||
**10. GPG vs pure-Go crypto for signatures?**
|
**10. GPG vs pure-Go crypto for signatures?**
|
||||||
Shelling out to `gpg` is fragile (may not be installed, version-dependent output). `github.com/ProtonMail/go-crypto` provides pure-Go OpenPGP, or we could go Ed25519/signify (simpler, no key management). Which direction?
|
Shelling out to `gpg` is fragile (may not be installed, version-dependent output). `github.com/ProtonMail/go-crypto` provides pure-Go OpenPGP, or we could go Ed25519/signify (simpler, no key management). Which direction?
|
||||||
|
|
||||||
> *answer:* Keep GPG shelling for now (established).
|
> *answer:*
|
||||||
|
|
||||||
### Implementation Design
|
### Implementation Design
|
||||||
|
|
||||||
**11. Should manifests be deterministic by default?**
|
**11. Should manifests be deterministic by default?**
|
||||||
This means: sort file entries by path, omit `createdAt` timestamp (or make it opt-in), no `atime`. Should determinism be the default, with a `--include-timestamps` flag to opt in?
|
This means: sort file entries by path, omit `createdAt` timestamp (or make it opt-in), no `atime`. Should determinism be the default, with a `--include-timestamps` flag to opt in?
|
||||||
|
|
||||||
> *answer:* YES — implemented, default behavior.
|
> *answer:*
|
||||||
|
|
||||||
**12. Should we consolidate or keep both scanner/checker implementations?**
|
**12. Should we consolidate or keep both scanner/checker implementations?**
|
||||||
There are two parallel implementations: `mfer/scanner.go` + `mfer/checker.go` (typed with `FileSize`, `RelFilePath`) and `internal/scanner/` + `internal/checker/` (raw `int64`, `string`). The `mfer/` versions are superior. Delete the `internal/` versions?
|
There are two parallel implementations: `mfer/scanner.go` + `mfer/checker.go` (typed with `FileSize`, `RelFilePath`) and `internal/scanner/` + `internal/checker/` (raw `int64`, `string`). The `mfer/` versions are superior. Delete the `internal/` versions?
|
||||||
|
|
||||||
> *answer:* Consolidated — done (PR#27).
|
> *answer:*
|
||||||
|
|
||||||
**13. Should the `manifest` type be exported?**
|
**13. Should the `manifest` type be exported?**
|
||||||
Currently unexported with exported constructors (`New`, `NewFromPaths`, etc.). Consumers can't declare `var m *mfer.manifest`. Export the type, or define an interface?
|
Currently unexported with exported constructors (`New`, `NewFromPaths`, etc.). Consumers can't declare `var m *mfer.manifest`. Export the type, or define an interface?
|
||||||
|
|
||||||
> *answer:* Keep unexported.
|
> *answer:*
|
||||||
|
|
||||||
**14. What should the Go module path be for 1.0?**
|
**14. What should the Go module path be for 1.0?**
|
||||||
Currently mixed between `sneak.berlin/go/mfer` and `git.eeqj.de/sneak/mfer`. Which is canonical?
|
Currently mixed between `sneak.berlin/go/mfer` and `git.eeqj.de/sneak/mfer`. Which is canonical?
|
||||||
|
|
||||||
> *answer:* `sneak.berlin/go/mfer`
|
> *answer:*
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -86,19 +86,19 @@ Currently mixed between `sneak.berlin/go/mfer` and `git.eeqj.de/sneak/mfer`. Whi
|
|||||||
|
|
||||||
### Phase 1: Foundation (format correctness)
|
### Phase 1: Foundation (format correctness)
|
||||||
|
|
||||||
- [x] Delete `internal/scanner/` and `internal/checker/` — consolidate on `mfer/` package versions; update CLI code
|
- [ ] Delete `internal/scanner/` and `internal/checker/` — consolidate on `mfer/` package versions; update CLI code
|
||||||
- [x] Add deterministic file ordering — sort entries by path (lexicographic, byte-order) in `Builder.Build()`; add test asserting byte-identical output from two runs
|
- [ ] Add deterministic file ordering — sort entries by path (lexicographic, byte-order) in `Builder.Build()`; add test asserting byte-identical output from two runs
|
||||||
- [x] Add decompression size limit — `io.LimitReader` in `deserializeInner()` with `m.pbOuter.Size` as bound
|
- [ ] Add decompression size limit — `io.LimitReader` in `deserializeInner()` with `m.pbOuter.Size` as bound
|
||||||
- [ ] Fix `errors.Is` dead code in checker — replace with `os.IsNotExist(err)` or `errors.Is(err, fs.ErrNotExist)`
|
- [ ] Fix `errors.Is` dead code in checker — replace with `os.IsNotExist(err)` or `errors.Is(err, fs.ErrNotExist)`
|
||||||
- [ ] Fix `AddFile` to verify size — check `totalRead == size` after reading, return error on mismatch
|
- [ ] Fix `AddFile` to verify size — check `totalRead == size` after reading, return error on mismatch
|
||||||
- [x] Specify path invariants — add proto comments (UTF-8, forward-slash, relative, no `..`, no leading `/`); validate in `Builder.AddFile` and `Builder.AddFileWithHash`
|
- [ ] Specify path invariants — add proto comments (UTF-8, forward-slash, relative, no `..`, no leading `/`); validate in `Builder.AddFile` and `Builder.AddFileWithHash`
|
||||||
|
|
||||||
### Phase 2: CLI polish
|
### Phase 2: CLI polish
|
||||||
|
|
||||||
- [ ] Fix flag naming — all CLI flags use kebab-case as primary (`--include-dotfiles`, `--follow-symlinks`)
|
- [ ] Fix flag naming — all CLI flags use kebab-case as primary (`--include-dotfiles`, `--follow-symlinks`)
|
||||||
- [ ] Fix URL construction in fetch — use `BaseURL.JoinPath()` or `url.JoinPath()` instead of string concatenation
|
- [ ] Fix URL construction in fetch — use `BaseURL.JoinPath()` or `url.JoinPath()` instead of string concatenation
|
||||||
- [ ] Add progress rate-limiting to Checker — throttle to once per second, matching Scanner
|
- [ ] Add progress rate-limiting to Checker — throttle to once per second, matching Scanner
|
||||||
- [x] Add `--deterministic` flag (or make it default) — omit `createdAt`, sort files
|
- [ ] Add `--deterministic` flag (or make it default) — omit `createdAt`, sort files
|
||||||
|
|
||||||
### Phase 3: Robustness
|
### Phase 3: Robustness
|
||||||
|
|
||||||
@@ -109,10 +109,10 @@ Currently mixed between `sneak.berlin/go/mfer` and `git.eeqj.de/sneak/mfer`. Whi
|
|||||||
|
|
||||||
### Phase 4: Format finalization
|
### Phase 4: Format finalization
|
||||||
|
|
||||||
- [x] Remove or deprecate `atime` from proto (pending design question answer)
|
- [ ] Remove or deprecate `atime` from proto (pending design question answer)
|
||||||
- [ ] Reserve `optional uint32 mode = 305` in `MFFilePath` for future file permissions
|
- [ ] Reserve `optional uint32 mode = 305` in `MFFilePath` for future file permissions
|
||||||
- [ ] Add version byte after magic — `ZNAVSRFG\x01` for format version 1
|
- [ ] Add version byte after magic — `ZNAVSRFG\x01` for format version 1
|
||||||
- [x] Write format specification document — separate from README: magic, outer structure, compression, inner structure, path invariants, signature scheme, canonical serialization
|
- [ ] Write format specification document — separate from README: magic, outer structure, compression, inner structure, path invariants, signature scheme, canonical serialization
|
||||||
|
|
||||||
### Phase 5: Release prep
|
### Phase 5: Release prep
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user