go/nix/nar: init
This commit is contained in:
parent
e772336dc5
commit
50520f0230
8 changed files with 600 additions and 0 deletions
|
@ -3,6 +3,7 @@
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
args: {
|
args: {
|
||||||
|
nix = import ./nix args;
|
||||||
twitterchiver = import ./twitterchiver args;
|
twitterchiver = import ./twitterchiver args;
|
||||||
twitternuke = import ./twitternuke args;
|
twitternuke = import ./twitternuke args;
|
||||||
minotarproxy = import ./minotarproxy args;
|
minotarproxy = import ./minotarproxy args;
|
||||||
|
|
|
@ -17,9 +17,11 @@ require (
|
||||||
github.com/hashicorp/vault/api v1.4.1
|
github.com/hashicorp/vault/api v1.4.1
|
||||||
github.com/jackc/pgtype v1.4.2
|
github.com/jackc/pgtype v1.4.2
|
||||||
github.com/jackc/pgx/v4 v4.8.1
|
github.com/jackc/pgx/v4 v4.8.1
|
||||||
|
github.com/numtide/go-nix v0.0.0-20210617093959-c74bbf961f08
|
||||||
github.com/pomerium/sdk-go v0.0.5
|
github.com/pomerium/sdk-go v0.0.5
|
||||||
github.com/prometheus/client_golang v1.12.1
|
github.com/prometheus/client_golang v1.12.1
|
||||||
gocloud.dev v0.24.0
|
gocloud.dev v0.24.0
|
||||||
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
|
golang.org/x/crypto v0.0.0-20210817164053-32db794688a5
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||||
|
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9
|
||||||
)
|
)
|
||||||
|
|
|
@ -119,6 +119,7 @@ github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj
|
||||||
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
|
github.com/armon/go-radix v1.0.0 h1:F4z6KzEeeQIMeLFa97iZU6vupzoecKdU5TX24SNppXI=
|
||||||
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
github.com/armon/go-radix v1.0.0/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||||
github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
|
github.com/aws/aws-sdk-go v1.15.27/go.mod h1:mFuSZ37Z9YOHbQEwBWztmVzqXrEkub65tZoCYDt7FT0=
|
||||||
|
github.com/aws/aws-sdk-go v1.33.13/go.mod h1:5zCpMtNQVjRREroY7sYe8lOMRSxkhG6MZveU8YkpAk0=
|
||||||
github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
github.com/aws/aws-sdk-go v1.37.0/go.mod h1:hcU610XS61/+aQV88ixoOzUoG7v3b31pl2zKMmprdro=
|
||||||
github.com/aws/aws-sdk-go v1.40.34 h1:SBYmodndE2d4AYucuuJnOXk4MD1SFbucoIdpwKVKeSA=
|
github.com/aws/aws-sdk-go v1.40.34 h1:SBYmodndE2d4AYucuuJnOXk4MD1SFbucoIdpwKVKeSA=
|
||||||
github.com/aws/aws-sdk-go v1.40.34/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
|
github.com/aws/aws-sdk-go v1.40.34/go.mod h1:585smgzpB/KqRA+K3y/NL/oYRqQvpNJYvLm+LY1U59Q=
|
||||||
|
@ -227,6 +228,7 @@ github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvSc
|
||||||
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
|
||||||
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
|
||||||
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
github.com/go-playground/validator/v10 v10.2.0/go.mod h1:uOYAAleCW8F/7oMFd6aG0GOhaH6EGOAJShg8Id5JGkI=
|
||||||
|
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||||
github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
|
github.com/go-test/deep v1.0.2 h1:onZX1rnHT3Wv6cqNgYyFOOlgVKJrksuCMCRvJStbMYw=
|
||||||
|
@ -452,6 +454,7 @@ github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJS
|
||||||
github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE=
|
github.com/jhump/protoreflect v1.6.0 h1:h5jfMVslIg6l29nsMs0D8Wj17RDVdNYti0vDN/PZZoE=
|
||||||
github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
|
github.com/jhump/protoreflect v1.6.0/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74=
|
||||||
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
github.com/jmespath/go-jmespath v0.0.0-20160202185014-0b12d6b521d8/go.mod h1:Nht3zPeWKUH0NzdCt2Blrr5ys8VGpn0CEB0cQHVjt7k=
|
||||||
|
github.com/jmespath/go-jmespath v0.3.0/go.mod h1:9QtRXoHjLGCJ5IBSaohpXITPlowMeeYCZ7fLUTSywik=
|
||||||
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg=
|
||||||
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
|
||||||
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
|
||||||
|
@ -530,6 +533,8 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
|
||||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||||
|
github.com/numtide/go-nix v0.0.0-20210617093959-c74bbf961f08 h1:mwf7VCrP2jkvqARQDqWdX2nsR4pv+UPWRGoO1jaVQkc=
|
||||||
|
github.com/numtide/go-nix v0.0.0-20210617093959-c74bbf961f08/go.mod h1:QdjLlYQnNqfJRhTF9YJTfIt902z6yufXmI1xy+PwGMg=
|
||||||
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
|
github.com/oklog/run v1.0.0 h1:Ru7dDtJNOyC66gQ5dQmaCa0qIsAUFY3sFpK1Xk8igrw=
|
||||||
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
github.com/oklog/run v1.0.0/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA=
|
||||||
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
|
||||||
|
|
8
go/nix/default.nix
Normal file
8
go/nix/default.nix
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
# SPDX-FileCopyrightText: 2020 Luke Granger-Brown <depot@lukegb.com>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
args:
|
||||||
|
{
|
||||||
|
nar = import ./nar args;
|
||||||
|
}
|
14
go/nix/nar/default.nix
Normal file
14
go/nix/nar/default.nix
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# SPDX-FileCopyrightText: 2020 Luke Granger-Brown <depot@lukegb.com>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
{ depot, ... }:
|
||||||
|
depot.third_party.buildGo.package {
|
||||||
|
name = "nar";
|
||||||
|
srcs = [
|
||||||
|
./narinfo.go
|
||||||
|
];
|
||||||
|
deps = with depot; [
|
||||||
|
third_party.gopkgs."github.com".numtide.go-nix.nixbase32
|
||||||
|
];
|
||||||
|
}
|
375
go/nix/nar/narinfo.go
Normal file
375
go/nix/nar/narinfo.go
Normal file
|
@ -0,0 +1,375 @@
|
||||||
|
// SPDX-FileCopyrightText: 2020 Luke Granger-Brown <depot@lukegb.com>
|
||||||
|
//
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
// Package nar implements a simple NAR read/writer.
|
||||||
|
package nar
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"crypto/md5"
|
||||||
|
"crypto/sha1"
|
||||||
|
"crypto/sha256"
|
||||||
|
"crypto/sha512"
|
||||||
|
"encoding/base64"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"hash"
|
||||||
|
"io"
|
||||||
|
"regexp"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/numtide/go-nix/nixbase32"
|
||||||
|
)
|
||||||
|
|
||||||
|
// CompressionMethod lists the supported compression methods.
|
||||||
|
type CompressionMethod int
|
||||||
|
|
||||||
|
const (
|
||||||
|
CompressionUnknown CompressionMethod = iota
|
||||||
|
CompressionNone
|
||||||
|
CompressionXz
|
||||||
|
CompressionBzip2
|
||||||
|
CompressionGzip
|
||||||
|
CompressionBrotli
|
||||||
|
)
|
||||||
|
|
||||||
|
func (c CompressionMethod) String() string {
|
||||||
|
switch c {
|
||||||
|
case CompressionNone:
|
||||||
|
return "none"
|
||||||
|
case CompressionXz:
|
||||||
|
return "xz"
|
||||||
|
case CompressionBzip2:
|
||||||
|
return "bzip2"
|
||||||
|
case CompressionGzip:
|
||||||
|
return "gzip"
|
||||||
|
case CompressionBrotli:
|
||||||
|
return "br"
|
||||||
|
}
|
||||||
|
return "!!unknown!!"
|
||||||
|
}
|
||||||
|
|
||||||
|
func compressionFromString(s string) CompressionMethod {
|
||||||
|
switch s {
|
||||||
|
case "none", "":
|
||||||
|
return CompressionNone
|
||||||
|
case "xz":
|
||||||
|
return CompressionXz
|
||||||
|
case "bzip2":
|
||||||
|
return CompressionBzip2
|
||||||
|
case "gzip":
|
||||||
|
return CompressionGzip
|
||||||
|
case "br":
|
||||||
|
return CompressionBrotli
|
||||||
|
default:
|
||||||
|
return CompressionUnknown
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// HashAlgorithm lists the supported hashing algorithms.
|
||||||
|
type HashAlgorithm int
|
||||||
|
|
||||||
|
const (
|
||||||
|
HashUnknown HashAlgorithm = iota
|
||||||
|
HashMd5
|
||||||
|
HashSha1
|
||||||
|
HashSha256
|
||||||
|
HashSha512
|
||||||
|
)
|
||||||
|
|
||||||
|
func (a HashAlgorithm) String() string {
|
||||||
|
switch a {
|
||||||
|
case HashMd5:
|
||||||
|
return "md5"
|
||||||
|
case HashSha1:
|
||||||
|
return "sha1"
|
||||||
|
case HashSha256:
|
||||||
|
return "sha256"
|
||||||
|
case HashSha512:
|
||||||
|
return "sha512"
|
||||||
|
}
|
||||||
|
return "!!unknown!!"
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a HashAlgorithm) hash() hash.Hash {
|
||||||
|
switch a {
|
||||||
|
case HashMd5:
|
||||||
|
return md5.New()
|
||||||
|
case HashSha1:
|
||||||
|
return sha1.New()
|
||||||
|
case HashSha256:
|
||||||
|
return sha256.New()
|
||||||
|
case HashSha512:
|
||||||
|
return sha512.New()
|
||||||
|
}
|
||||||
|
panic("unknown hash algorithm")
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a HashAlgorithm) sizes() sizes {
|
||||||
|
sz := a.hash().Size()
|
||||||
|
return sizes{
|
||||||
|
rawLen: sz,
|
||||||
|
base16Len: hex.EncodedLen(sz),
|
||||||
|
base32Len: nixbase32.EncodedLen(sz),
|
||||||
|
base64Len: base64.StdEncoding.EncodedLen(sz),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type sizes struct {
|
||||||
|
rawLen int
|
||||||
|
base16Len int
|
||||||
|
base32Len int
|
||||||
|
base64Len int
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hash represents a hash and its algorithm.
|
||||||
|
type Hash struct {
|
||||||
|
Algorithm HashAlgorithm
|
||||||
|
Hash []byte
|
||||||
|
IsSRI bool
|
||||||
|
}
|
||||||
|
|
||||||
|
// Valid returns if this Hash contains a valid hash.
|
||||||
|
func (h Hash) Valid() bool {
|
||||||
|
return h.Algorithm != HashUnknown && len(h.Hash) == h.Algorithm.sizes().rawLen
|
||||||
|
}
|
||||||
|
|
||||||
|
func (h Hash) String() string {
|
||||||
|
if h.IsSRI {
|
||||||
|
return fmt.Sprintf("%s-%s", h.Algorithm, base64.StdEncoding.EncodeToString(h.Hash))
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%s:%s", h.Algorithm, nixbase32.EncodeToString(h.Hash))
|
||||||
|
}
|
||||||
|
|
||||||
|
func hashFromString(s string) (Hash, error) {
|
||||||
|
var h Hash
|
||||||
|
idx := strings.IndexAny(s, "-:")
|
||||||
|
if idx == -1 {
|
||||||
|
return h, fmt.Errorf("no hashtype separator")
|
||||||
|
}
|
||||||
|
h.IsSRI = s[idx] == '-'
|
||||||
|
hashtype, hashdata := s[:idx], s[idx+1:]
|
||||||
|
switch hashtype {
|
||||||
|
case "md5":
|
||||||
|
h.Algorithm = HashMd5
|
||||||
|
case "sha1":
|
||||||
|
h.Algorithm = HashSha1
|
||||||
|
case "sha256":
|
||||||
|
h.Algorithm = HashSha256
|
||||||
|
case "sha512":
|
||||||
|
h.Algorithm = HashSha512
|
||||||
|
default:
|
||||||
|
return h, fmt.Errorf("unknown hash type %v", hashtype)
|
||||||
|
}
|
||||||
|
var hashbytes []byte
|
||||||
|
sz := h.Algorithm.sizes()
|
||||||
|
// We support the same things as Nix, which is base16 (hex), base32 (why), and base64 hashes.
|
||||||
|
// For SRI, base64 is required.
|
||||||
|
switch {
|
||||||
|
case !h.IsSRI && len(hashdata) == sz.base16Len:
|
||||||
|
var err error
|
||||||
|
hashbytes, err = hex.DecodeString(hashdata)
|
||||||
|
if err != nil {
|
||||||
|
return h, fmt.Errorf("decoding hash %v as hex", hashdata)
|
||||||
|
}
|
||||||
|
case !h.IsSRI && len(hashdata) == sz.base32Len:
|
||||||
|
var err error
|
||||||
|
hashbytes, err = nixbase32.DecodeString(hashdata)
|
||||||
|
if err != nil {
|
||||||
|
return h, fmt.Errorf("decoding hash %v as nix-base32", hashdata)
|
||||||
|
}
|
||||||
|
case len(hashdata) == sz.base64Len:
|
||||||
|
var err error
|
||||||
|
hashbytes, err = base64.StdEncoding.DecodeString(hashdata)
|
||||||
|
if err != nil {
|
||||||
|
return h, fmt.Errorf("decoding hash %v as base64", hashdata)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return h, fmt.Errorf("unknown hash type for length %d", len(hashdata))
|
||||||
|
}
|
||||||
|
h.Hash = hashbytes
|
||||||
|
return h, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// NarInfo represents the content of a .narinfo file.
|
||||||
|
type NarInfo struct {
|
||||||
|
StorePath string
|
||||||
|
URL string
|
||||||
|
Compression CompressionMethod
|
||||||
|
FileHash Hash
|
||||||
|
FileSize uint64
|
||||||
|
NarHash Hash
|
||||||
|
NarSize uint64
|
||||||
|
References []string
|
||||||
|
Deriver string
|
||||||
|
System string
|
||||||
|
Sig map[string][]byte
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ni NarInfo) WriteTo(w io.Writer) error {
|
||||||
|
if ni.StorePath != "" {
|
||||||
|
if _, err := fmt.Fprintf(w, "StorePath: %s\n", ni.StorePath); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ni.URL != "" {
|
||||||
|
if _, err := fmt.Fprintf(w, "URL: %s\n", ni.URL); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ni.Compression != CompressionUnknown {
|
||||||
|
if _, err := fmt.Fprintf(w, "Compression: %s\n", ni.Compression); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ni.FileHash.Valid() {
|
||||||
|
if _, err := fmt.Fprintf(w, "FileHash: %s\n", ni.FileHash); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ni.FileSize != 0 {
|
||||||
|
if _, err := fmt.Fprintf(w, "FileSize: %d\n", ni.FileSize); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ni.NarHash.Valid() {
|
||||||
|
if _, err := fmt.Fprintf(w, "NarHash: %s\n", ni.NarHash); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ni.NarSize != 0 {
|
||||||
|
if _, err := fmt.Fprintf(w, "NarSize: %d\n", ni.NarSize); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(ni.References) != 0 {
|
||||||
|
if _, err := fmt.Fprintf(w, "References: %s\n", strings.Join(ni.References, " ")); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ni.Deriver != "" {
|
||||||
|
if _, err := fmt.Fprintf(w, "Deriver: %s\n", ni.Deriver); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ni.System != "" {
|
||||||
|
if _, err := fmt.Fprintf(w, "System: %s\n", ni.System); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(ni.Sig) != 0 {
|
||||||
|
sigKeys := make([]string, 0, len(ni.Sig))
|
||||||
|
for k := range ni.Sig {
|
||||||
|
sigKeys = append(sigKeys, k)
|
||||||
|
}
|
||||||
|
sort.Strings(sigKeys)
|
||||||
|
for _, k := range sigKeys {
|
||||||
|
v := ni.Sig[k]
|
||||||
|
if _, err := fmt.Fprintf(w, "Sig: %s:%s\n", k, base64.StdEncoding.EncodeToString(v)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (ni NarInfo) String() string {
|
||||||
|
var b strings.Builder
|
||||||
|
ni.WriteTo(&b)
|
||||||
|
return b.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
var narLineRegexp = regexp.MustCompile(`^([A-Za-z]+): (.*)$`)
|
||||||
|
|
||||||
|
// ParseNarInfo parses NarInfo data from a Reader.
|
||||||
|
func ParseNarInfo(r io.Reader) (*NarInfo, error) {
|
||||||
|
ni := NarInfo{
|
||||||
|
Compression: CompressionBzip2,
|
||||||
|
}
|
||||||
|
s := bufio.NewScanner(r)
|
||||||
|
for s.Scan() {
|
||||||
|
submatches := narLineRegexp.FindStringSubmatch(s.Text())
|
||||||
|
switch {
|
||||||
|
case len(submatches) == 0:
|
||||||
|
continue
|
||||||
|
case len(submatches) != 3:
|
||||||
|
return nil, fmt.Errorf("failed to parse line %q: wrong number of submatches", s.Text())
|
||||||
|
}
|
||||||
|
name, value := submatches[1], submatches[2]
|
||||||
|
switch name {
|
||||||
|
case "StorePath":
|
||||||
|
ni.StorePath = value
|
||||||
|
case "URL":
|
||||||
|
ni.URL = value
|
||||||
|
case "Compression":
|
||||||
|
ni.Compression = compressionFromString(value)
|
||||||
|
if ni.Compression == CompressionUnknown {
|
||||||
|
return nil, fmt.Errorf("unknown compression method %q", value)
|
||||||
|
}
|
||||||
|
case "FileHash":
|
||||||
|
h, err := hashFromString(value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parsing %q as FileHash: %w", value, err)
|
||||||
|
}
|
||||||
|
ni.FileHash = h
|
||||||
|
case "FileSize":
|
||||||
|
var err error
|
||||||
|
ni.FileSize, err = strconv.ParseUint(value, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parsing %q as FileSize: %w", value, err)
|
||||||
|
}
|
||||||
|
case "NarHash":
|
||||||
|
h, err := hashFromString(value)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parsing %q as NarHash: %w", value, err)
|
||||||
|
}
|
||||||
|
ni.NarHash = h
|
||||||
|
case "NarSize":
|
||||||
|
var err error
|
||||||
|
ni.NarSize, err = strconv.ParseUint(value, 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parsing %q as NarSize: %w", value, err)
|
||||||
|
}
|
||||||
|
case "References":
|
||||||
|
ni.References = strings.Split(value, " ")
|
||||||
|
case "Deriver":
|
||||||
|
if value != "unknown-deriver" {
|
||||||
|
ni.Deriver = value
|
||||||
|
}
|
||||||
|
case "System":
|
||||||
|
ni.System = value
|
||||||
|
case "Sig":
|
||||||
|
if ni.Sig == nil {
|
||||||
|
ni.Sig = make(map[string][]byte)
|
||||||
|
}
|
||||||
|
bits := strings.SplitN(value, ":", 2)
|
||||||
|
if len(bits) != 2 {
|
||||||
|
return nil, fmt.Errorf("parsing %q as Sig: not enough pieces", value)
|
||||||
|
}
|
||||||
|
rawVal, err := base64.StdEncoding.DecodeString(bits[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("parsing %q as Sig: %w", value, err)
|
||||||
|
}
|
||||||
|
ni.Sig[bits[0]] = rawVal
|
||||||
|
case "CA":
|
||||||
|
return nil, fmt.Errorf("content-addressed NARs not supported yet")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := s.Err(); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
switch {
|
||||||
|
case ni.StorePath == "":
|
||||||
|
return nil, fmt.Errorf("StorePath missing")
|
||||||
|
case !ni.NarHash.Valid():
|
||||||
|
return nil, fmt.Errorf("NarHash missing or invalid")
|
||||||
|
case ni.URL == "":
|
||||||
|
return nil, fmt.Errorf("URL empty")
|
||||||
|
case ni.NarSize == 0:
|
||||||
|
return nil, fmt.Errorf("NAR is 0-size")
|
||||||
|
}
|
||||||
|
return &ni, nil
|
||||||
|
}
|
181
go/nix/nar/narinfo_test.go
Normal file
181
go/nix/nar/narinfo_test.go
Normal file
|
@ -0,0 +1,181 @@
|
||||||
|
package nar
|
||||||
|
|
||||||
|
import (
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/google/go-cmp/cmp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestRoundtripHash(t *testing.T) {
|
||||||
|
tcs := []struct {
|
||||||
|
base16 string
|
||||||
|
base32 string
|
||||||
|
base64 string
|
||||||
|
sriBase64 string
|
||||||
|
}{{
|
||||||
|
base16: "sha256:1fbeb031b3b03845e290a26b3727815f92b261f45ba68c082885f7950f2a8bc7",
|
||||||
|
base32: "sha256:1iwb587rbxw55048r9jvyihv54jzh4kkfsx2j3i4af5hncqv1ghz",
|
||||||
|
base64: "sha256:H76wMbOwOEXikKJrNyeBX5KyYfRbpowIKIX3lQ8qi8c=",
|
||||||
|
sriBase64: "sha256-H76wMbOwOEXikKJrNyeBX5KyYfRbpowIKIX3lQ8qi8c=",
|
||||||
|
}, {
|
||||||
|
base16: "sha256:42b0ead11a3146dcac102d56b63809ac405fe8d196a7748f7b6176ab44162879",
|
||||||
|
base32: "sha256:0y982r2anxk1gf7p99wns7l5yh5c14wbcmid22ndqiii3b8ymc22",
|
||||||
|
base64: "sha256:QrDq0RoxRtysEC1WtjgJrEBf6NGWp3SPe2F2q0QWKHk=",
|
||||||
|
sriBase64: "sha256-QrDq0RoxRtysEC1WtjgJrEBf6NGWp3SPe2F2q0QWKHk=",
|
||||||
|
}}
|
||||||
|
for _, tc := range tcs {
|
||||||
|
base16Got, err := hashFromString(tc.base16)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("hashFromString(%q): %v", tc.base16, err)
|
||||||
|
}
|
||||||
|
base32Got, err := hashFromString(tc.base32)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("hashFromString(%q): %v", tc.base32, err)
|
||||||
|
}
|
||||||
|
base64Got, err := hashFromString(tc.base64)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("hashFromString(%q): %v", tc.base64, err)
|
||||||
|
}
|
||||||
|
sriBase64Got, err := hashFromString(tc.sriBase64)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("hashFromString(%q): %v", tc.sriBase64, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if diff := cmp.Diff(base16Got, base32Got); diff != "" {
|
||||||
|
t.Errorf("hashFromString diff (-base16 +base32):\n%v", diff)
|
||||||
|
}
|
||||||
|
if diff := cmp.Diff(base16Got, base64Got); diff != "" {
|
||||||
|
t.Errorf("hashFromString diff (-base16 +base64):\n%v", diff)
|
||||||
|
}
|
||||||
|
sriBase64Got.IsSRI = false
|
||||||
|
if diff := cmp.Diff(base16Got, sriBase64Got); diff != "" {
|
||||||
|
t.Errorf("hashFromString diff (-base16 +sriBase64):\n%v", diff)
|
||||||
|
}
|
||||||
|
sriBase64Got.IsSRI = true
|
||||||
|
|
||||||
|
if base16Got.String() != tc.base32 {
|
||||||
|
t.Errorf("base16Got.String() = %q; want %q", base16Got.String(), tc.base32)
|
||||||
|
}
|
||||||
|
if base32Got.String() != tc.base32 {
|
||||||
|
t.Errorf("base32Got.String() = %q; want %q", base32Got.String(), tc.base32)
|
||||||
|
}
|
||||||
|
if base64Got.String() != tc.base32 {
|
||||||
|
t.Errorf("base64Got.String() = %q; want %q", base64Got.String(), tc.base32)
|
||||||
|
}
|
||||||
|
if sriBase64Got.String() != tc.sriBase64 {
|
||||||
|
t.Errorf("sriBase64Got.String() = %q; want %q", sriBase64Got.String(), tc.sriBase64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestParseNarInfo(t *testing.T) {
|
||||||
|
tcs := []struct {
|
||||||
|
name string
|
||||||
|
input string
|
||||||
|
wantErr bool
|
||||||
|
want *NarInfo
|
||||||
|
wantStr string
|
||||||
|
}{{
|
||||||
|
name: "empty",
|
||||||
|
input: "",
|
||||||
|
wantErr: true,
|
||||||
|
}, {
|
||||||
|
name: "bare minimum narinfo",
|
||||||
|
input: `StorePath: /nix/store/27albyvzh5j455fhm53q1wn697f6nzkh-hello-2.10
|
||||||
|
URL: nar/1iwb587rbxw55048r9jvyihv54jzh4kkfsx2j3i4af5hncqv1ghz.nar.bz2
|
||||||
|
NarHash: sha256:0y982r2anxk1gf7p99wns7l5yh5c14wbcmid22ndqiii3b8ymc22
|
||||||
|
NarSize: 206152
|
||||||
|
`,
|
||||||
|
want: &NarInfo{
|
||||||
|
StorePath: "/nix/store/27albyvzh5j455fhm53q1wn697f6nzkh-hello-2.10",
|
||||||
|
URL: "nar/1iwb587rbxw55048r9jvyihv54jzh4kkfsx2j3i4af5hncqv1ghz.nar.bz2",
|
||||||
|
Compression: CompressionBzip2,
|
||||||
|
NarHash: Hash{
|
||||||
|
Algorithm: HashSha256,
|
||||||
|
Hash: []byte{
|
||||||
|
0x42, 0xb0, 0xea, 0xd1, 0x1a, 0x31, 0x46, 0xdc, 0xac, 0x10, 0x2d, 0x56, 0xb6, 0x38, 0x09, 0xac,
|
||||||
|
0x40, 0x5f, 0xe8, 0xd1, 0x96, 0xa7, 0x74, 0x8f, 0x7b, 0x61, 0x76, 0xab, 0x44, 0x16, 0x28, 0x79,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
NarSize: 206152,
|
||||||
|
},
|
||||||
|
wantStr: `StorePath: /nix/store/27albyvzh5j455fhm53q1wn697f6nzkh-hello-2.10
|
||||||
|
URL: nar/1iwb587rbxw55048r9jvyihv54jzh4kkfsx2j3i4af5hncqv1ghz.nar.bz2
|
||||||
|
Compression: bzip2
|
||||||
|
NarHash: sha256:0y982r2anxk1gf7p99wns7l5yh5c14wbcmid22ndqiii3b8ymc22
|
||||||
|
NarSize: 206152
|
||||||
|
`,
|
||||||
|
}, {
|
||||||
|
name: "hello NAR",
|
||||||
|
input: `StorePath: /nix/store/27albyvzh5j455fhm53q1wn697f6nzkh-hello-2.10
|
||||||
|
URL: nar/1iwb587rbxw55048r9jvyihv54jzh4kkfsx2j3i4af5hncqv1ghz.nar.xz
|
||||||
|
Compression: xz
|
||||||
|
FileHash: sha256:1iwb587rbxw55048r9jvyihv54jzh4kkfsx2j3i4af5hncqv1ghz
|
||||||
|
FileSize: 41180
|
||||||
|
NarHash: sha256:0y982r2anxk1gf7p99wns7l5yh5c14wbcmid22ndqiii3b8ymc22
|
||||||
|
NarSize: 206152
|
||||||
|
References: 27albyvzh5j455fhm53q1wn697f6nzkh-hello-2.10 j5p0j1w27aqdzncpw73k95byvhh5prw2-glibc-2.33-47
|
||||||
|
Deriver: s1n7mahd61zgxk2w4b1q1ani0nzqfl47-hello-2.10.drv
|
||||||
|
Sig: cache.nixos.org-1:jdYq19bNeGQ7rqzMcJu6IQeSNmAgLVrZUbnVc6KOXw8aTevoZcMNOdDS8s+puPq4BDC1+A2jvH0aUgc9lkBzCw==
|
||||||
|
`,
|
||||||
|
want: &NarInfo{
|
||||||
|
StorePath: "/nix/store/27albyvzh5j455fhm53q1wn697f6nzkh-hello-2.10",
|
||||||
|
URL: "nar/1iwb587rbxw55048r9jvyihv54jzh4kkfsx2j3i4af5hncqv1ghz.nar.xz",
|
||||||
|
Compression: CompressionXz,
|
||||||
|
FileHash: Hash{
|
||||||
|
Algorithm: HashSha256,
|
||||||
|
Hash: []byte{
|
||||||
|
0x1f, 0xbe, 0xb0, 0x31, 0xb3, 0xb0, 0x38, 0x45, 0xe2, 0x90, 0xa2, 0x6b, 0x37, 0x27, 0x81, 0x5f,
|
||||||
|
0x92, 0xb2, 0x61, 0xf4, 0x5b, 0xa6, 0x8c, 0x08, 0x28, 0x85, 0xf7, 0x95, 0x0f, 0x2a, 0x8b, 0xc7,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
FileSize: 41180,
|
||||||
|
NarHash: Hash{
|
||||||
|
Algorithm: HashSha256,
|
||||||
|
Hash: []byte{
|
||||||
|
0x42, 0xb0, 0xea, 0xd1, 0x1a, 0x31, 0x46, 0xdc, 0xac, 0x10, 0x2d, 0x56, 0xb6, 0x38, 0x09, 0xac,
|
||||||
|
0x40, 0x5f, 0xe8, 0xd1, 0x96, 0xa7, 0x74, 0x8f, 0x7b, 0x61, 0x76, 0xab, 0x44, 0x16, 0x28, 0x79,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
NarSize: 206152,
|
||||||
|
References: []string{
|
||||||
|
"27albyvzh5j455fhm53q1wn697f6nzkh-hello-2.10",
|
||||||
|
"j5p0j1w27aqdzncpw73k95byvhh5prw2-glibc-2.33-47",
|
||||||
|
},
|
||||||
|
Deriver: "s1n7mahd61zgxk2w4b1q1ani0nzqfl47-hello-2.10.drv",
|
||||||
|
Sig: map[string][]uint8{
|
||||||
|
"cache.nixos.org-1": []uint8{
|
||||||
|
0x8d, 0xd6, 0x2a, 0xd7, 0xd6, 0xcd, 0x78, 0x64, 0x3b, 0xae, 0xac, 0xcc, 0x70, 0x9b, 0xba, 0x21,
|
||||||
|
0x07, 0x92, 0x36, 0x60, 0x20, 0x2d, 0x5a, 0xd9, 0x51, 0xb9, 0xd5, 0x73, 0xa2, 0x8e, 0x5f, 0x0f,
|
||||||
|
0x1a, 0x4d, 0xeb, 0xe8, 0x65, 0xc3, 0x0d, 0x39, 0xd0, 0xd2, 0xf2, 0xcf, 0xa9, 0xb8, 0xfa, 0xb8,
|
||||||
|
0x04, 0x30, 0xb5, 0xf8, 0x0d, 0xa3, 0xbc, 0x7d, 0x1a, 0x52, 0x07, 0x3d, 0x96, 0x40, 0x73, 0x0b,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
for _, tc := range tcs {
|
||||||
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
got, err := ParseNarInfo(strings.NewReader(tc.input))
|
||||||
|
if !tc.wantErr && err != nil {
|
||||||
|
t.Fatalf("ParseNarInfo: %v", err)
|
||||||
|
} else if tc.wantErr && err == nil {
|
||||||
|
t.Fatal("ParseNarInfo didn't return error (expected one)")
|
||||||
|
}
|
||||||
|
|
||||||
|
if diff := cmp.Diff(tc.want, got); diff != "" {
|
||||||
|
t.Errorf("ParseNarInfo mismatch (-want +got):\n%s", diff)
|
||||||
|
}
|
||||||
|
|
||||||
|
if got != nil {
|
||||||
|
wantStr := tc.wantStr
|
||||||
|
if wantStr == "" {
|
||||||
|
wantStr = tc.input
|
||||||
|
}
|
||||||
|
if got.String() != wantStr {
|
||||||
|
t.Errorf("roundtrip: got.String() = \n%v\n\nwant:\n%v", got.String(), wantStr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
14
third_party/gopkgs/github.com/numtide/go-nix/default.nix
vendored
Normal file
14
third_party/gopkgs/github.com/numtide/go-nix/default.nix
vendored
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# SPDX-FileCopyrightText: 2020 Luke Granger-Brown <depot@lukegb.com>
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
{ depot, ... }:
|
||||||
|
depot.third_party.buildGo.external {
|
||||||
|
path = "github.com/numtide/go-nix";
|
||||||
|
src = depot.third_party.nixpkgs.fetchFromGitHub {
|
||||||
|
owner = "numtide";
|
||||||
|
repo = "go-nix";
|
||||||
|
rev = "c74bbf961f08e7ee01d034305bc0cb8777c71592";
|
||||||
|
hash = "sha256:0d1nh5hw7mwr3qs6g8ijng54bb3rzfxmsnsmsc5b5ydb082mmqi6";
|
||||||
|
};
|
||||||
|
}
|
Loading…
Reference in a new issue