diff --git a/go/default.nix b/go/default.nix index 599513c7e5..237acf88dd 100644 --- a/go/default.nix +++ b/go/default.nix @@ -3,6 +3,7 @@ # SPDX-License-Identifier: Apache-2.0 args: { + nix = import ./nix args; twitterchiver = import ./twitterchiver args; twitternuke = import ./twitternuke args; minotarproxy = import ./minotarproxy args; diff --git a/go/go.mod b/go/go.mod index 1b470d1168..c7afdca851 100644 --- a/go/go.mod +++ b/go/go.mod @@ -17,9 +17,11 @@ require ( github.com/hashicorp/vault/api v1.4.1 github.com/jackc/pgtype v1.4.2 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/prometheus/client_golang v1.12.1 gocloud.dev v0.24.0 golang.org/x/crypto v0.0.0-20210817164053-32db794688a5 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c + golang.org/x/sys v0.0.0-20220114195835-da31bd327af9 ) diff --git a/go/go.sum b/go/go.sum index 4c35e315c5..01aaca8260 100644 --- a/go/go.sum +++ b/go/go.sum @@ -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/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.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.40.34 h1:SBYmodndE2d4AYucuuJnOXk4MD1SFbucoIdpwKVKeSA= 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/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-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-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 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/go.mod h1:eaTn3RZAmMBcV0fifFvlm6VHNz3wSkYyXYWUh7ymB74= 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/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= 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/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/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/go.mod h1:dlhp/R75TPv97u0XWUtDeV/lRKWPKSdTuV0TZvrmrQA= github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY= diff --git a/go/nix/default.nix b/go/nix/default.nix new file mode 100644 index 0000000000..7f4b56d657 --- /dev/null +++ b/go/nix/default.nix @@ -0,0 +1,8 @@ +# SPDX-FileCopyrightText: 2020 Luke Granger-Brown +# +# SPDX-License-Identifier: Apache-2.0 + +args: +{ + nar = import ./nar args; +} diff --git a/go/nix/nar/default.nix b/go/nix/nar/default.nix new file mode 100644 index 0000000000..5ac90af272 --- /dev/null +++ b/go/nix/nar/default.nix @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: 2020 Luke Granger-Brown +# +# 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 + ]; +} diff --git a/go/nix/nar/narinfo.go b/go/nix/nar/narinfo.go new file mode 100644 index 0000000000..ebac51edbb --- /dev/null +++ b/go/nix/nar/narinfo.go @@ -0,0 +1,375 @@ +// SPDX-FileCopyrightText: 2020 Luke Granger-Brown +// +// 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 +} diff --git a/go/nix/nar/narinfo_test.go b/go/nix/nar/narinfo_test.go new file mode 100644 index 0000000000..f54d8b40df --- /dev/null +++ b/go/nix/nar/narinfo_test.go @@ -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) + } + } + }) + } +} diff --git a/third_party/gopkgs/github.com/numtide/go-nix/default.nix b/third_party/gopkgs/github.com/numtide/go-nix/default.nix new file mode 100644 index 0000000000..6dbaf69611 --- /dev/null +++ b/third_party/gopkgs/github.com/numtide/go-nix/default.nix @@ -0,0 +1,14 @@ +# SPDX-FileCopyrightText: 2020 Luke Granger-Brown +# +# 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"; + }; +}