rollup from next branch (#4)
All checks were successful
continuous-integration/drone/push Build is passing
All checks were successful
continuous-integration/drone/push Build is passing
Co-authored-by: sneak <sneak@sneak.berlin> Reviewed-on: #4
This commit was merged in pull request #4.
This commit is contained in:
3
mfer/gen.go
Normal file
3
mfer/gen.go
Normal file
@@ -0,0 +1,3 @@
|
||||
package mfer
|
||||
|
||||
//go:generate protoc --go_out=. --go_opt=paths=source_relative mf.proto
|
||||
114
mfer/manifest.go
Normal file
114
mfer/manifest.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package mfer
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/spf13/afero"
|
||||
)
|
||||
|
||||
type ManifestFile struct {
|
||||
Path string
|
||||
FileInfo fs.FileInfo
|
||||
}
|
||||
|
||||
func (m *ManifestFile) String() string {
|
||||
return fmt.Sprintf("<File \"%s\">", m.Path)
|
||||
}
|
||||
|
||||
type Manifest struct {
|
||||
SourceFS afero.Fs
|
||||
SourceFSRoot string
|
||||
Files []*ManifestFile
|
||||
ScanOptions *ManifestScanOptions
|
||||
TotalFileSize int64
|
||||
}
|
||||
|
||||
func (m *Manifest) String() string {
|
||||
return fmt.Sprintf("<Manifest count=%d totalSize=%d>", len(m.Files), m.TotalFileSize)
|
||||
}
|
||||
|
||||
type ManifestScanOptions struct {
|
||||
IgnoreDotfiles bool
|
||||
FollowSymLinks bool
|
||||
}
|
||||
|
||||
func NewFromPath(inputPath string, options *ManifestScanOptions) (*Manifest, error) {
|
||||
abs, err := filepath.Abs(inputPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
afs := afero.NewBasePathFs(afero.NewOsFs(), abs)
|
||||
m, err := NewFromFS(afs, options)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
m.SourceFSRoot = abs
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func NewFromFS(fs afero.Fs, options *ManifestScanOptions) (*Manifest, error) {
|
||||
m := &Manifest{
|
||||
SourceFS: fs,
|
||||
ScanOptions: options,
|
||||
}
|
||||
err := m.Scan()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return m, nil
|
||||
}
|
||||
|
||||
func (m *Manifest) Scan() error {
|
||||
// FIXME scan and whatever function does the hashing should take ctx
|
||||
oe := afero.Walk(m.SourceFS, "./", func(path string, info fs.FileInfo, err error) error {
|
||||
if m.ScanOptions.IgnoreDotfiles && strings.HasPrefix(path, ".") {
|
||||
// FIXME make this check all path components BUG
|
||||
return nil
|
||||
}
|
||||
|
||||
if info != nil && info.IsDir() {
|
||||
// manifests contain only files, directories are implied.
|
||||
return nil
|
||||
}
|
||||
|
||||
fileinfo, staterr := m.SourceFS.Stat(path)
|
||||
if staterr != nil {
|
||||
panic(staterr)
|
||||
}
|
||||
|
||||
nf := &ManifestFile{
|
||||
Path: path,
|
||||
FileInfo: fileinfo,
|
||||
}
|
||||
m.Files = append(m.Files, nf)
|
||||
m.TotalFileSize = m.TotalFileSize + info.Size()
|
||||
return nil
|
||||
})
|
||||
if oe != nil {
|
||||
return oe
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (m *Manifest) WriteToFile(path string) error {
|
||||
// FIXME refuse to overwrite without -f if file exists
|
||||
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
return m.Write(f)
|
||||
}
|
||||
|
||||
func (m *Manifest) Write(output io.Writer) error {
|
||||
// FIXME implement
|
||||
panic("nope")
|
||||
return nil // nolint:all
|
||||
}
|
||||
70
mfer/mf.proto
Normal file
70
mfer/mf.proto
Normal file
@@ -0,0 +1,70 @@
|
||||
syntax = "proto3";
|
||||
|
||||
option go_package = "git.eeqj.de/sneak/mfer";
|
||||
|
||||
message Timestamp {
|
||||
int64 seconds = 1;
|
||||
int32 nanos = 2;
|
||||
}
|
||||
|
||||
message MFFile {
|
||||
enum Version {
|
||||
NONE = 0;
|
||||
ONE = 1; // only one for now
|
||||
}
|
||||
|
||||
// required mffile root attributes 1xx
|
||||
Version version = 101;
|
||||
bytes innerMessage = 102;
|
||||
// these are used solely to detect corruption/truncation
|
||||
// and not for cryptographic integrity.
|
||||
int64 size = 103;
|
||||
bytes sha256 = 104;
|
||||
|
||||
// 2xx for optional manifest root attributes
|
||||
// think we might use gosignify instead of gpg:
|
||||
// github.com/frankbraun/gosignify
|
||||
|
||||
//detached signature, ascii or binary
|
||||
optional bytes signature = 201;
|
||||
//full GPG key id
|
||||
optional bytes signer = 202;
|
||||
//full GPG signing public key, ascii or binary
|
||||
optional bytes signingPubKey = 203;
|
||||
}
|
||||
|
||||
message MFFilePath {
|
||||
// required attributes:
|
||||
string path = 1;
|
||||
int64 size = 2;
|
||||
|
||||
// gotta have at least one:
|
||||
repeated MFFileChecksum hashes = 3;
|
||||
|
||||
// optional per-file metadata
|
||||
optional string mimeType = 301;
|
||||
optional Timestamp mtime = 302;
|
||||
optional Timestamp ctime = 303;
|
||||
optional Timestamp atime = 304;
|
||||
}
|
||||
|
||||
message MFFileChecksum {
|
||||
// 1.0 golang implementation must write a multihash here
|
||||
// it's ok to only ever use/verify sha256 multihash
|
||||
bytes multiHash = 1;
|
||||
}
|
||||
|
||||
message MFFileInner {
|
||||
enum Version {
|
||||
NONE = 0;
|
||||
ONE = 1; // only one for now
|
||||
}
|
||||
Version version = 100;
|
||||
|
||||
// required manifest attributes:
|
||||
repeated MFFilePath files = 101;
|
||||
|
||||
// optional manifest attributes 2xx:
|
||||
optional Timestamp createdAt = 201;
|
||||
}
|
||||
|
||||
12
mfer/mfer_test.go
Normal file
12
mfer/mfer_test.go
Normal file
@@ -0,0 +1,12 @@
|
||||
package mfer
|
||||
|
||||
import "testing"
|
||||
|
||||
// Add those variables as well
|
||||
var (
|
||||
existingFolder = "./testdata/a-folder-that-exists"
|
||||
nonExistingFolder = "./testdata/a-folder-that-does-not-exists"
|
||||
)
|
||||
|
||||
func TestManifestGeneration(t *testing.T) {
|
||||
}
|
||||
Reference in New Issue
Block a user