Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
32b468af8a |
@@ -1,12 +0,0 @@
|
|||||||
root = true
|
|
||||||
|
|
||||||
[*]
|
|
||||||
indent_style = space
|
|
||||||
indent_size = 4
|
|
||||||
end_of_line = lf
|
|
||||||
charset = utf-8
|
|
||||||
trim_trailing_whitespace = true
|
|
||||||
insert_final_newline = true
|
|
||||||
|
|
||||||
[Makefile]
|
|
||||||
indent_style = tab
|
|
||||||
+2
-21
@@ -10,24 +10,5 @@ modcache.tzst
|
|||||||
# Generated manifest files
|
# Generated manifest files
|
||||||
.index.mf
|
.index.mf
|
||||||
|
|
||||||
# Secrets
|
# Stale files
|
||||||
.env
|
.drone.yml
|
||||||
.env.*
|
|
||||||
*.key
|
|
||||||
*.pem
|
|
||||||
|
|
||||||
# OS files
|
|
||||||
.DS_Store
|
|
||||||
Thumbs.db
|
|
||||||
|
|
||||||
# Editor files
|
|
||||||
*.swp
|
|
||||||
*.swo
|
|
||||||
*~
|
|
||||||
.idea/
|
|
||||||
.vscode/
|
|
||||||
|
|
||||||
# Go build artifacts
|
|
||||||
*.log
|
|
||||||
*.out
|
|
||||||
*.test
|
|
||||||
|
|||||||
+12
@@ -11,6 +11,10 @@ COPY . .
|
|||||||
# Touch .pb.go so make does not try to regenerate via protoc (file is committed)
|
# Touch .pb.go so make does not try to regenerate via protoc (file is committed)
|
||||||
RUN touch mfer/mf.pb.go
|
RUN touch mfer/mf.pb.go
|
||||||
|
|
||||||
|
# Changing value from script/cibuild; forces the check steps below to
|
||||||
|
# re-run instead of being served from a stale layer cache.
|
||||||
|
ARG CHECK_EPOCH
|
||||||
|
|
||||||
# Go half of fmt-check only: this image has no node, so no prettier. The
|
# Go half of fmt-check only: this image has no node, so no prettier. The
|
||||||
# markdown half runs in the mdfmt stage below.
|
# markdown half runs in the mdfmt stage below.
|
||||||
RUN make fmt-check-go
|
RUN make fmt-check-go
|
||||||
@@ -27,6 +31,10 @@ RUN yarn install --frozen-lockfile
|
|||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
|
# Changing value from script/cibuild; forces the check step below to
|
||||||
|
# re-run instead of being served from a stale layer cache.
|
||||||
|
ARG CHECK_EPOCH
|
||||||
|
|
||||||
# No make in this image; call the script entrypoint directly.
|
# No make in this image; call the script entrypoint directly.
|
||||||
RUN script/prettier --check
|
RUN script/prettier --check
|
||||||
|
|
||||||
@@ -47,6 +55,10 @@ COPY . .
|
|||||||
# Touch .pb.go so make does not try to regenerate via protoc (file is committed)
|
# Touch .pb.go so make does not try to regenerate via protoc (file is committed)
|
||||||
RUN touch mfer/mf.pb.go
|
RUN touch mfer/mf.pb.go
|
||||||
|
|
||||||
|
# Changing value from script/cibuild; forces the check steps below to
|
||||||
|
# re-run instead of being served from a stale layer cache.
|
||||||
|
ARG CHECK_EPOCH
|
||||||
|
|
||||||
RUN make test
|
RUN make test
|
||||||
RUN cd cmd/mfer && go build -tags urfave_cli_no_docs -o /mfer .
|
RUN cd cmd/mfer && go build -tags urfave_cli_no_docs -o /mfer .
|
||||||
|
|
||||||
|
|||||||
@@ -24,12 +24,8 @@ only thing left of the `chore/align-repo-policies` branch is the list below.
|
|||||||
|
|
||||||
# Completed Steps
|
# Completed Steps
|
||||||
|
|
||||||
- 2026-09-21: rewrote `script/test` to the canonical pattern (30s timeout,
|
- 2026-09-21: made `script/cibuild` re-run the checks on an unchanged tree via a
|
||||||
`-race -cover`, quiet-first with verbose-on-failure rerun) and fixed the
|
changing `CHECK_EPOCH` build arg in each Dockerfile check stage (#89)
|
||||||
process-global logger data race it surfaced (#67)
|
|
||||||
- 2026-09-21: added the canonical `.editorconfig`, made `.gitignore` cover
|
|
||||||
secrets, OS, editor, and Go artifacts, and removed the dead Drone CI
|
|
||||||
references from `.gitignore` and `bin/gitrev.sh` (#72)
|
|
||||||
- 2026-08-09: added `.prettierrc`/`.prettierignore`, gave `script/fmt` and
|
- 2026-08-09: added `.prettierrc`/`.prettierignore`, gave `script/fmt` and
|
||||||
`script/fmt-check` one shared prettier file set via `script/prettier`, dropped
|
`script/fmt-check` one shared prettier file set via `script/prettier`, dropped
|
||||||
the `|| true` that hid prettier failures, and added a node-based Dockerfile
|
the `|| true` that hid prettier failures, and added a node-based Dockerfile
|
||||||
@@ -68,6 +64,7 @@ only thing left of the `chore/align-repo-policies` branch is the list below.
|
|||||||
- Move FORMAT.md from repo root to docs/ and update the AGENTS.md reference
|
- Move FORMAT.md from repo root to docs/ and update the AGENTS.md reference
|
||||||
- Pin Makefile-installed Go tools (`protoc-gen-go@v1.28.1`,
|
- Pin Makefile-installed Go tools (`protoc-gen-go@v1.28.1`,
|
||||||
`golangci-lint@v2.12.2`) by module hash, not mutable tag
|
`golangci-lint@v2.12.2`) by module hash, not mutable tag
|
||||||
|
- Set `make test` timeout to 30s (currently 10s)
|
||||||
- Add explicit README "Rationale" heading (content exists under other
|
- Add explicit README "Rationale" heading (content exists under other
|
||||||
names); name the author in the README Description first line
|
names); name the author in the README Description first line
|
||||||
- Reconcile root-level AGENTS.md with directory-hygiene policy (keep or
|
- Reconcile root-level AGENTS.md with directory-hygiene policy (keep or
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
#
|
#
|
||||||
|
if [[ ! -z "$DRONE_COMMIT_SHA" ]]; then
|
||||||
|
echo "${DRONE_COMMIT_SHA:0:7}"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
if [[ ! -z "$GITREV" ]]; then
|
if [[ ! -z "$GITREV" ]]; then
|
||||||
echo $GITREV
|
echo $GITREV
|
||||||
else
|
else
|
||||||
|
|||||||
@@ -679,13 +679,11 @@ func TestCheckDetectsManifestCorruption(t *testing.T) {
|
|||||||
fs := afero.NewMemMapFs()
|
fs := afero.NewMemMapFs()
|
||||||
rng := rand.New(rand.NewSource(42)) //nolint:gosec // deterministic test data
|
rng := rand.New(rand.NewSource(42)) //nolint:gosec // deterministic test data
|
||||||
|
|
||||||
// Create many small files with random names so the manifest has many
|
// Create many small files with random names to generate a ~1MB manifest
|
||||||
// entries and random single-byte flips land at varied offsets. Each
|
// Each manifest entry is roughly 50-60 bytes, so we need ~20000 files
|
||||||
// manifest entry is roughly 50-60 bytes. Kept modest so the suite stays
|
|
||||||
// within its wall-clock budget under -race.
|
|
||||||
require.NoError(t, fs.MkdirAll(testDir, 0o755))
|
require.NoError(t, fs.MkdirAll(testDir, 0o755))
|
||||||
|
|
||||||
numFiles := 1500
|
numFiles := 20000
|
||||||
for range numFiles {
|
for range numFiles {
|
||||||
// Generate random filename
|
// Generate random filename
|
||||||
filename := fmt.Sprintf("/testdir/%08x%08x%08x.dat",
|
filename := fmt.Sprintf("/testdir/%08x%08x%08x.dat",
|
||||||
@@ -701,11 +699,11 @@ func TestCheckDetectsManifestCorruption(t *testing.T) {
|
|||||||
exitCode := runCLI(opts)
|
exitCode := runCLI(opts)
|
||||||
require.Equal(t, 0, exitCode, "generate should succeed")
|
require.Equal(t, 0, exitCode, "generate should succeed")
|
||||||
|
|
||||||
// Read the valid manifest and verify it has real size.
|
// Read the valid manifest and verify it's approximately 1MB
|
||||||
validManifest, err := afero.ReadFile(fs, testManifest)
|
validManifest, err := afero.ReadFile(fs, testManifest)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
require.GreaterOrEqual(t, len(validManifest), 64*1024,
|
require.GreaterOrEqual(t, len(validManifest), 1024*1024,
|
||||||
"manifest should be at least 64KB, got %d bytes", len(validManifest))
|
"manifest should be at least 1MB, got %d bytes", len(validManifest))
|
||||||
t.Logf("manifest size: %d bytes (%d files)", len(validManifest), numFiles)
|
t.Logf("manifest size: %d bytes (%d files)", len(validManifest), numFiles)
|
||||||
|
|
||||||
// First corruption: truncate the manifest
|
// First corruption: truncate the manifest
|
||||||
@@ -728,8 +726,8 @@ func TestCheckDetectsManifestCorruption(t *testing.T) {
|
|||||||
exitCode = runCLI(opts)
|
exitCode = runCLI(opts)
|
||||||
require.Equal(t, 0, exitCode, "check should pass with valid manifest")
|
require.Equal(t, 0, exitCode, "check should pass with valid manifest")
|
||||||
|
|
||||||
// Now do 100 random corruption iterations
|
// Now do 500 random corruption iterations
|
||||||
for i := range 100 {
|
for i := range 500 {
|
||||||
// Corrupt: write a random byte at a random offset
|
// Corrupt: write a random byte at a random offset
|
||||||
corrupted := make([]byte, len(validManifest))
|
corrupted := make([]byte, len(validManifest))
|
||||||
copy(corrupted, validManifest)
|
copy(corrupted, validManifest)
|
||||||
|
|||||||
@@ -357,6 +357,6 @@ func (mfa *CLIApp) run(args []string) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
mfa.exitCode = 1
|
mfa.exitCode = 1
|
||||||
|
|
||||||
log.Errorf("%s", err)
|
log.WithError(err).Debugf("exiting")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+41
-34
@@ -112,16 +112,13 @@ func DisableStyling() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Init initializes the logger with the CLI handler and default log level.
|
// Init initializes the logger with the CLI handler and default log level.
|
||||||
//
|
|
||||||
// It reconfigures the process-global apex/log logger under the write lock so
|
|
||||||
// the global is never mutated while another goroutine holds the read lock to
|
|
||||||
// read it in emit. Without this, parallel callers (e.g. the test suite) race
|
|
||||||
// Init's SetLevel/SetHandler against concurrent log calls.
|
|
||||||
func Init() {
|
func Init() {
|
||||||
mu.Lock()
|
mu.RLock()
|
||||||
defer mu.Unlock()
|
|
||||||
|
|
||||||
log.SetHandler(acli.New(stderr))
|
w := stderr
|
||||||
|
|
||||||
|
mu.RUnlock()
|
||||||
|
log.SetHandler(acli.New(w))
|
||||||
log.SetLevel(log.DebugLevel) // Let apex/log pass everything; we filter ourselves
|
log.SetLevel(log.DebugLevel) // Let apex/log pass everything; we filter ourselves
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,66 +130,74 @@ func isEnabled(l Level) bool {
|
|||||||
return l >= currentLevel
|
return l >= currentLevel
|
||||||
}
|
}
|
||||||
|
|
||||||
// emit calls fn while holding the read lock if messages at level l are
|
|
||||||
// enabled. Holding the read lock across the apex/log call keeps the global
|
|
||||||
// logger from being read while Init reconfigures it under the write lock.
|
|
||||||
func emit(l Level, fn func()) {
|
|
||||||
mu.RLock()
|
|
||||||
defer mu.RUnlock()
|
|
||||||
|
|
||||||
if l >= currentLevel {
|
|
||||||
fn()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fatalf logs a formatted message at fatal level.
|
// Fatalf logs a formatted message at fatal level.
|
||||||
func Fatalf(format string, args ...any) {
|
func Fatalf(format string, args ...any) {
|
||||||
emit(FatalLevel, func() { log.Fatalf(format, args...) })
|
if isEnabled(FatalLevel) {
|
||||||
|
log.Fatalf(format, args...)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fatal logs a message at fatal level.
|
// Fatal logs a message at fatal level.
|
||||||
func Fatal(arg string) {
|
func Fatal(arg string) {
|
||||||
emit(FatalLevel, func() { log.Fatal(arg) })
|
if isEnabled(FatalLevel) {
|
||||||
|
log.Fatal(arg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Errorf logs a formatted message at error level.
|
// Errorf logs a formatted message at error level.
|
||||||
func Errorf(format string, args ...any) {
|
func Errorf(format string, args ...any) {
|
||||||
emit(ErrorLevel, func() { log.Errorf(format, args...) })
|
if isEnabled(ErrorLevel) {
|
||||||
|
log.Errorf(format, args...)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error logs a message at error level.
|
// Error logs a message at error level.
|
||||||
func Error(arg string) {
|
func Error(arg string) {
|
||||||
emit(ErrorLevel, func() { log.Error(arg) })
|
if isEnabled(ErrorLevel) {
|
||||||
|
log.Error(arg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Warnf logs a formatted message at warn level.
|
// Warnf logs a formatted message at warn level.
|
||||||
func Warnf(format string, args ...any) {
|
func Warnf(format string, args ...any) {
|
||||||
emit(WarnLevel, func() { log.Warnf(format, args...) })
|
if isEnabled(WarnLevel) {
|
||||||
|
log.Warnf(format, args...)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Warn logs a message at warn level.
|
// Warn logs a message at warn level.
|
||||||
func Warn(arg string) {
|
func Warn(arg string) {
|
||||||
emit(WarnLevel, func() { log.Warn(arg) })
|
if isEnabled(WarnLevel) {
|
||||||
|
log.Warn(arg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Infof logs a formatted message at info level.
|
// Infof logs a formatted message at info level.
|
||||||
func Infof(format string, args ...any) {
|
func Infof(format string, args ...any) {
|
||||||
emit(InfoLevel, func() { log.Infof(format, args...) })
|
if isEnabled(InfoLevel) {
|
||||||
|
log.Infof(format, args...)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Info logs a message at info level.
|
// Info logs a message at info level.
|
||||||
func Info(arg string) {
|
func Info(arg string) {
|
||||||
emit(InfoLevel, func() { log.Info(arg) })
|
if isEnabled(InfoLevel) {
|
||||||
|
log.Info(arg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verbosef logs a formatted message at verbose level.
|
// Verbosef logs a formatted message at verbose level.
|
||||||
func Verbosef(format string, args ...any) {
|
func Verbosef(format string, args ...any) {
|
||||||
emit(VerboseLevel, func() { log.Infof(format, args...) })
|
if isEnabled(VerboseLevel) {
|
||||||
|
log.Infof(format, args...)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verbose logs a message at verbose level.
|
// Verbose logs a message at verbose level.
|
||||||
func Verbose(arg string) {
|
func Verbose(arg string) {
|
||||||
emit(VerboseLevel, func() { log.Info(arg) })
|
if isEnabled(VerboseLevel) {
|
||||||
|
log.Info(arg)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Debugf logs a formatted message at debug level with caller location.
|
// Debugf logs a formatted message at debug level with caller location.
|
||||||
@@ -211,10 +216,7 @@ func Debug(arg string) {
|
|||||||
|
|
||||||
// DebugReal logs at debug level with caller info from the specified stack depth.
|
// DebugReal logs at debug level with caller info from the specified stack depth.
|
||||||
func DebugReal(arg string, cs int) {
|
func DebugReal(arg string, cs int) {
|
||||||
mu.RLock()
|
if !isEnabled(DebugLevel) {
|
||||||
defer mu.RUnlock()
|
|
||||||
|
|
||||||
if DebugLevel < currentLevel {
|
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -273,6 +275,11 @@ func GetLevel() Level {
|
|||||||
return currentLevel
|
return currentLevel
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithError returns a log entry with the error attached.
|
||||||
|
func WithError(e error) *log.Entry {
|
||||||
|
return log.Log.WithError(e)
|
||||||
|
}
|
||||||
|
|
||||||
// Progressf prints a progress message that overwrites the current line.
|
// Progressf prints a progress message that overwrites the current line.
|
||||||
// Use ProgressDone() when progress is complete to move to the next line.
|
// Use ProgressDone() when progress is complete to move to the next line.
|
||||||
func Progressf(format string, args ...any) {
|
func Progressf(format string, args ...any) {
|
||||||
|
|||||||
+9
-4
@@ -1,14 +1,19 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# script/cibuild: run the CI build. The Dockerfile runs script/check
|
# script/cibuild: run the CI build. The Dockerfile runs make fmt-check-go,
|
||||||
# (via make check), so a successful build implies all checks pass.
|
# make lint, the prettier check, and make test as build steps. A bare
|
||||||
# Generic: needs no adaptation. The Gitea workflow runs this on push.
|
# `docker build .` would serve those steps from Docker's layer cache on an
|
||||||
|
# unchanged tree, exiting 0 without running them. CHECK_EPOCH is a build
|
||||||
|
# arg placed above the check steps in every check stage; passing a fresh
|
||||||
|
# value each run busts the cache for those steps (and nothing above them),
|
||||||
|
# so a green build means the checks actually ran. Dependency layers above
|
||||||
|
# the arg stay cached. The Gitea workflow runs this on push.
|
||||||
set -eu
|
set -eu
|
||||||
|
|
||||||
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
ROOT="$(cd "$(dirname "$0")/.." && pwd -P)"
|
||||||
|
|
||||||
main() {
|
main() {
|
||||||
cd "$ROOT"
|
cd "$ROOT"
|
||||||
docker build .
|
docker build --build-arg CHECK_EPOCH="$(date +%s)" .
|
||||||
}
|
}
|
||||||
|
|
||||||
main "$@"
|
main "$@"
|
||||||
|
|||||||
+1
-6
@@ -17,12 +17,7 @@ ensure_pb() {
|
|||||||
main() {
|
main() {
|
||||||
cd "$ROOT"
|
cd "$ROOT"
|
||||||
ensure_pb
|
ensure_pb
|
||||||
go test -timeout 30s -race -cover ./... ||
|
go test -v --timeout 10s ./...
|
||||||
{
|
|
||||||
echo "--- Rerunning with -v for details ---"
|
|
||||||
go test -timeout 30s -race -v ./...
|
|
||||||
exit 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
main "$@"
|
main "$@"
|
||||||
|
|||||||
Reference in New Issue
Block a user