All checks were successful
check / check (push) Successful in 5s
Clears the final 80 golangci-lint findings under the canonical .golangci.yml (sha256 021cc83f4e6fc7c31b95b34b846723dfcf20b66b7baeea1dc40406e643346bcb), taking the repo from red to green: script/cibuild exits 0. - wsl_v5 (60): blank line above defer/go statements sharing no variable with the line above; blank-line-only diff. - sqlclosecheck (10): the package-local CloseRows helper hid the close from the analyzer. Helper removed; all 18 call sites now defer an inline rows.Close(), preserving the fatal-on-close-error path. No resource leak existed - the rows were always being closed. - prealloc (3): append targets given a starting capacity. - revive (3): package-name findings suppressed with per-site directives pending the naming decision tracked in #76. No gosec suppressions are needed under the pinned linter. .golangci.yml, Dockerfile, Makefile, .gitea/ and script/ are byte-identical to main. Verified with script/cibuild (digest-pinned golangci-lint v2.12.2), not make check - the latter resolves the linter from PATH and is not a trustworthy gate here; see #78. Closes #59.
90 lines
2.0 KiB
Go
90 lines
2.0 KiB
Go
// Package blobgen implements the blob data pipeline: streaming zstd
|
|
// compression, age encryption, and SHA256 content hashing for blob
|
|
// creation, plus the matching decrypt/decompress/verify reader.
|
|
package blobgen
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/hex"
|
|
"fmt"
|
|
"io"
|
|
)
|
|
|
|
// CompressResult contains the results of compression
|
|
type CompressResult struct {
|
|
Data []byte
|
|
UncompressedSize int64
|
|
CompressedSize int64
|
|
SHA256 string
|
|
}
|
|
|
|
// CompressData compresses and encrypts data, returning the result with hash
|
|
func CompressData(
|
|
data []byte, compressionLevel int, recipients []string,
|
|
) (*CompressResult, error) {
|
|
var buf bytes.Buffer
|
|
|
|
// Create writer
|
|
w, err := NewWriter(&buf, compressionLevel, recipients)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("creating writer: %w", err)
|
|
}
|
|
|
|
// Write data
|
|
_, err = w.Write(data)
|
|
if err != nil {
|
|
_ = w.Close()
|
|
|
|
return nil, fmt.Errorf("writing data: %w", err)
|
|
}
|
|
|
|
// Close to flush
|
|
err = w.Close()
|
|
if err != nil {
|
|
return nil, fmt.Errorf("closing writer: %w", err)
|
|
}
|
|
|
|
return &CompressResult{
|
|
Data: buf.Bytes(),
|
|
UncompressedSize: int64(len(data)),
|
|
CompressedSize: int64(buf.Len()),
|
|
SHA256: hex.EncodeToString(w.Sum256()),
|
|
}, nil
|
|
}
|
|
|
|
// CompressStream compresses and encrypts from reader to writer, returning
|
|
// the number of uncompressed bytes written and the content hash.
|
|
func CompressStream(
|
|
dst io.Writer, src io.Reader, compressionLevel int, recipients []string,
|
|
) (int64, string, error) {
|
|
// Create writer
|
|
w, err := NewWriter(dst, compressionLevel, recipients)
|
|
if err != nil {
|
|
return 0, "", fmt.Errorf("creating writer: %w", err)
|
|
}
|
|
|
|
closed := false
|
|
|
|
defer func() {
|
|
if !closed {
|
|
_ = w.Close()
|
|
}
|
|
}()
|
|
|
|
// Copy data
|
|
_, err = io.Copy(w, src)
|
|
if err != nil {
|
|
return 0, "", fmt.Errorf("copying data: %w", err)
|
|
}
|
|
|
|
// Close to flush
|
|
err = w.Close()
|
|
if err != nil {
|
|
return 0, "", fmt.Errorf("closing writer: %w", err)
|
|
}
|
|
|
|
closed = true
|
|
|
|
return w.BytesWritten(), hex.EncodeToString(w.Sum256()), nil
|
|
}
|