diff options
Diffstat (limited to 'tools/nixery')
25 files changed, 472 insertions, 784 deletions
diff --git a/tools/nixery/README.md b/tools/nixery/README.md index 03515939a9..a879d030b8 100644 --- a/tools/nixery/README.md +++ b/tools/nixery/README.md @@ -1,5 +1,5 @@ <div align="center"> - <img src="docs/src/nixery-logo.png"> + <img src="https://nixery.dev/nixery-logo.png"> </div> ----------------- diff --git a/tools/nixery/builder/archive.go b/tools/nixery/builder/archive.go index 3bc02ab4d5..8763e4cb85 100644 --- a/tools/nixery/builder/archive.go +++ b/tools/nixery/builder/archive.go @@ -16,6 +16,8 @@ import ( "io" "os" "path/filepath" + + "github.com/google/nixery/layers" ) // Create a new compressed tarball from each of the paths in the list @@ -23,7 +25,7 @@ import ( // // The uncompressed tarball is hashed because image manifests must // contain both the hashes of compressed and uncompressed layers. -func packStorePaths(l *layer, w io.Writer) (string, error) { +func packStorePaths(l *layers.Layer, w io.Writer) (string, error) { shasum := sha256.New() gz := gzip.NewWriter(w) multi := io.MultiWriter(shasum, gz) diff --git a/tools/nixery/builder/builder.go b/tools/nixery/builder/builder.go index 37c9b9fcb7..7f0bd7fffd 100644 --- a/tools/nixery/builder/builder.go +++ b/tools/nixery/builder/builder.go @@ -23,8 +23,10 @@ import ( "strings" "github.com/google/nixery/config" + "github.com/google/nixery/layers" "github.com/google/nixery/manifest" "github.com/google/nixery/storage" + "github.com/im7mortal/kmutex" log "github.com/sirupsen/logrus" ) @@ -36,10 +38,11 @@ const LayerBudget int = 94 // State holds the runtime state that is carried around in Nixery and // passed to builder functions. type State struct { - Storage storage.Backend - Cache *LocalCache - Cfg config.Config - Pop Popularity + Storage storage.Backend + Cache *LocalCache + Cfg config.Config + Pop layers.Popularity + UploadMutex *kmutex.Kmutex } // Architecture represents the possible CPU architectures for which @@ -117,7 +120,7 @@ type ImageResult struct { Pkgs []string `json:"pkgs"` // These fields are populated in case of success - Graph runtimeGraph `json:"runtimeGraph"` + Graph layers.RuntimeGraph `json:"runtimeGraph"` SymlinkLayer struct { Size int `json:"size"` TarHash string `json:"tarHash"` @@ -281,7 +284,7 @@ func prepareImage(s *State, image *Image) (*ImageResult, error) { // added only after successful uploads, which guarantees that entries // retrieved from the cache are present in the bucket. func prepareLayers(ctx context.Context, s *State, image *Image, result *ImageResult) ([]manifest.Entry, error) { - grouped := groupLayers(&result.Graph, &s.Pop, LayerBudget) + grouped := layers.GroupLayers(&result.Graph, &s.Pop, LayerBudget) var entries []manifest.Entry @@ -291,34 +294,23 @@ func prepareLayers(ctx context.Context, s *State, image *Image, result *ImageRes // Missing layers are built and uploaded to the storage // bucket. for _, l := range grouped { - if entry, cached := layerFromCache(ctx, s, l.Hash()); cached { - entries = append(entries, *entry) - } else { - lh := l.Hash() - - // While packing store paths, the SHA sum of - // the uncompressed layer is computed and - // written to `tarhash`. - // - // TODO(tazjin): Refactor this to make the - // flow of data cleaner. - var tarhash string - lw := func(w io.Writer) error { - var err error - tarhash, err = packStorePaths(&l, w) - return err - } - - entry, err := uploadHashLayer(ctx, s, lh, lw) + lh := l.Hash() + + // While packing store paths, the SHA sum of + // the uncompressed layer is computed and + // written to `tarhash`. + // + // TODO(tazjin): Refactor this to make the + // flow of data cleaner. + lw := func(w io.Writer) (string, error) { + tarhash, err := packStorePaths(&l, w) if err != nil { - return nil, err + return "", err } - entry.MergeRating = l.MergeRating - entry.TarHash = tarhash var pkgs []string for _, p := range l.Contents { - pkgs = append(pkgs, packageFromPath(p)) + pkgs = append(pkgs, layers.PackageFromPath(p)) } log.WithFields(log.Fields{ @@ -327,15 +319,21 @@ func prepareLayers(ctx context.Context, s *State, image *Image, result *ImageRes "tarhash": tarhash, }).Info("created image layer") - go cacheLayer(ctx, s, l.Hash(), *entry) - entries = append(entries, *entry) + return tarhash, err + } + + entry, err := uploadHashLayer(ctx, s, lh, l.MergeRating, lw) + if err != nil { + return nil, err } + + entries = append(entries, *entry) } // Symlink layer (built in the first Nix build) needs to be // included here manually: slkey := result.SymlinkLayer.TarHash - entry, err := uploadHashLayer(ctx, s, slkey, func(w io.Writer) error { + entry, err := uploadHashLayer(ctx, s, slkey, 0, func(w io.Writer) (string, error) { f, err := os.Open(result.SymlinkLayer.Path) if err != nil { log.WithError(err).WithFields(log.Fields{ @@ -344,7 +342,7 @@ func prepareLayers(ctx context.Context, s *State, image *Image, result *ImageRes "layer": slkey, }).Error("failed to open symlink layer") - return err + return "", err } defer f.Close() @@ -357,18 +355,16 @@ func prepareLayers(ctx context.Context, s *State, image *Image, result *ImageRes "layer": slkey, }).Error("failed to upload symlink layer") - return err + return "", err } - return gz.Close() + return "sha256:" + slkey, gz.Close() }) if err != nil { return nil, err } - entry.TarHash = "sha256:" + result.SymlinkLayer.TarHash - go cacheLayer(ctx, s, slkey, *entry) entries = append(entries, *entry) return entries, nil @@ -379,7 +375,7 @@ func prepareLayers(ctx context.Context, s *State, image *Image, result *ImageRes // // This type exists to avoid duplication between the handling of // symlink layers and store path layers. -type layerWriter func(w io.Writer) error +type layerWriter func(w io.Writer) (string, error) // byteCounter is a special io.Writer that counts all bytes written to // it and does nothing else. @@ -407,8 +403,16 @@ func (b *byteCounter) Write(p []byte) (n int, err error) { // // The return value is the layer's SHA256 hash, which is used in the // image manifest. -func uploadHashLayer(ctx context.Context, s *State, key string, lw layerWriter) (*manifest.Entry, error) { +func uploadHashLayer(ctx context.Context, s *State, key string, mrating uint64, lw layerWriter) (*manifest.Entry, error) { + s.UploadMutex.Lock(key) + defer s.UploadMutex.Unlock(key) + + if entry, cached := layerFromCache(ctx, s, key); cached { + return entry, nil + } + path := "staging/" + key + var tarhash string sha256sum, size, err := s.Storage.Persist(ctx, path, manifest.LayerType, func(sw io.Writer) (string, int64, error) { // Sets up a "multiwriter" that simultaneously runs both hash // algorithms and uploads to the storage backend. @@ -416,7 +420,8 @@ func uploadHashLayer(ctx context.Context, s *State, key string, lw layerWriter) counter := &byteCounter{} multi := io.MultiWriter(sw, shasum, counter) - err := lw(multi) + var err error + tarhash, err = lw(multi) sha256sum := fmt.Sprintf("%x", shasum.Sum([]byte{})) return sha256sum, counter.count, err @@ -448,10 +453,14 @@ func uploadHashLayer(ctx context.Context, s *State, key string, lw layerWriter) }).Info("created and persisted layer") entry := manifest.Entry{ - Digest: "sha256:" + sha256sum, - Size: size, + Digest: "sha256:" + sha256sum, + Size: size, + TarHash: tarhash, + MergeRating: mrating, } + cacheLayer(ctx, s, key, entry) + return &entry, nil } @@ -492,13 +501,13 @@ func BuildImage(ctx context.Context, s *State, image *Image) (*BuildResult, erro } m, c := manifest.Manifest(image.Arch.imageArch, layers, cmd) - lw := func(w io.Writer) error { + lw := func(w io.Writer) (string, error) { r := bytes.NewReader(c.Config) _, err := io.Copy(w, r) - return err + return "", err } - if _, err = uploadHashLayer(ctx, s, c.SHA256, lw); err != nil { + if _, err = uploadHashLayer(ctx, s, c.SHA256, 0, lw); err != nil { log.WithError(err).WithFields(log.Fields{ "image": image.Name, "tag": image.Tag, diff --git a/tools/nixery/main.go b/tools/nixery/cmd/server/main.go index 2e633e0898..24aec6391c 100644 --- a/tools/nixery/main.go +++ b/tools/nixery/cmd/server/main.go @@ -26,9 +26,11 @@ import ( "github.com/google/nixery/builder" "github.com/google/nixery/config" + "github.com/google/nixery/layers" "github.com/google/nixery/logs" mf "github.com/google/nixery/manifest" "github.com/google/nixery/storage" + "github.com/im7mortal/kmutex" log "github.com/sirupsen/logrus" ) @@ -52,7 +54,7 @@ var ( // Downloads the popularity information for the package set from the // URL specified in Nixery's configuration. -func downloadPopularity(url string) (builder.Popularity, error) { +func downloadPopularity(url string) (layers.Popularity, error) { resp, err := http.Get(url) if err != nil { return nil, err @@ -67,7 +69,7 @@ func downloadPopularity(url string) (builder.Popularity, error) { return nil, err } - var pop builder.Popularity + var pop layers.Popularity err = json.Unmarshal(j, &pop) if err != nil { return nil, err @@ -246,7 +248,7 @@ func main() { log.WithError(err).Fatal("failed to instantiate build cache") } - var pop builder.Popularity + var pop layers.Popularity if cfg.PopUrl != "" { pop, err = downloadPopularity(cfg.PopUrl) if err != nil { @@ -256,10 +258,11 @@ func main() { } state := builder.State{ - Cache: &cache, - Cfg: cfg, - Pop: pop, - Storage: s, + Cache: &cache, + Cfg: cfg, + Pop: pop, + Storage: s, + UploadMutex: kmutex.New(), } log.WithFields(log.Fields{ diff --git a/tools/nixery/default.nix b/tools/nixery/default.nix index b5575be507..91eabca960 100644 --- a/tools/nixery/default.nix +++ b/tools/nixery/default.nix @@ -19,106 +19,111 @@ with pkgs; let - inherit (pkgs) buildGoModule; + inherit (pkgs) buildGoModule lib; # Avoid extracting this from git until we have a way to plumb # through revision numbers. nixery-commit-hash = "depot"; +in +depot.nix.readTree.drvTargets rec { + # Implementation of the Nix image building logic + nixery-prepare-image = import ./prepare-image { inherit pkgs; }; + + # Include the Nixery website into the Nix store, unless its being + # overridden to something else. Nixery will serve this as its front + # page when visited from a browser. + nixery-web = ./web; - # Go implementation of the Nixery server which implements the - # container registry interface. + nixery-popcount = callPackage ./popcount { }; + + # Build Nixery's Go code, resulting in the binaries used for various + # bits of functionality. # - # Users should use the nixery-bin derivation below instead as it - # provides the paths of files needed at runtime. - nixery-server = buildGoModule rec { - name = "nixery-server"; + # The server binary is wrapped to ensure that required environment + # variables are set at runtime. + nixery = buildGoModule rec { + name = "nixery"; src = ./.; doCheck = true; # Needs to be updated after every modification of go.mod/go.sum - vendorSha256 = "1xnmyz2a5s5sck0fzhcz51nds4s80p0jw82dhkf4v2c4yzga83yk"; + vendorHash = "sha256-io9NCeZmjCZPLmII3ajXIsBWbT40XiW8ncXOuUDabbo="; - buildFlagsArray = [ - "-ldflags=-s -w -X main.version=${nixery-commit-hash}" + ldflags = [ + "-s" + "-w" + "-X" + "main.version=${nixery-commit-hash}" ]; - }; -in -depot.nix.readTree.drvTargets rec { - # Implementation of the Nix image building logic - nixery-prepare-image = import ./prepare-image { inherit pkgs; }; - # Use mdBook to build a static asset page which Nixery can then - # serve. This is primarily used for the public instance at - # nixery.dev. - nixery-book = callPackage ./docs { }; + nativeBuildInputs = [ makeWrapper ]; + postInstall = '' + wrapProgram $out/bin/server \ + --set-default WEB_DIR "${nixery-web}" \ + --prefix PATH : ${nixery-prepare-image}/bin + ''; + + # Nixery is mirrored to Github at tazjin/nixery; this is + # automatically updated from CI for canon builds. + passthru.meta.ci.extraSteps.github = depot.tools.releases.filteredGitPush { + filter = ":/tools/nixery"; + remote = "git@github.com:tazjin/nixery.git"; + ref = "refs/heads/master"; + }; + }; - # Wrapper script running the Nixery server with the above two data - # dependencies configured. + # Wrapper script for the wrapper script (meta!) which configures + # the container environment appropriately. # - # In most cases, this will be the derivation a user wants if they - # are installing Nixery directly. - nixery-bin = writeShellScriptBin "nixery" '' - export WEB_DIR="${nixery-book}" - export PATH="${nixery-prepare-image}/bin:$PATH" - exec ${nixery-server}/bin/nixery + # Most importantly, sandboxing is disabled to avoid privilege + # issues in containers. + nixery-launch-script = writeShellScriptBin "nixery" '' + set -e + export PATH=${coreutils}/bin:$PATH + export NIX_SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt + mkdir -p /tmp + + # Create the build user/group required by Nix + echo 'nixbld:x:30000:nixbld' >> /etc/group + echo 'nixbld:x:30000:30000:nixbld:/tmp:/bin/bash' >> /etc/passwd + echo 'root:x:0:0:root:/root:/bin/bash' >> /etc/passwd + echo 'root:x:0:' >> /etc/group + + # Disable sandboxing to avoid running into privilege issues + mkdir -p /etc/nix + echo 'sandbox = false' >> /etc/nix/nix.conf + + # In some cases users building their own image might want to + # customise something on the inside (e.g. set up an environment + # for keys or whatever). + # + # This can be achieved by setting a 'preLaunch' script. + ${preLaunch} + + exec ${nixery}/bin/server ''; - nixery-popcount = callPackage ./popcount { }; - # Container image containing Nixery and Nix itself. This image can # be run on Kubernetes, published on AppEngine or whatever else is # desired. - nixery-image = - let - # Wrapper script for the wrapper script (meta!) which configures - # the container environment appropriately. - # - # Most importantly, sandboxing is disabled to avoid privilege - # issues in containers. - nixery-launch-script = writeShellScriptBin "nixery" '' - set -e - export PATH=${coreutils}/bin:$PATH - export NIX_SSL_CERT_FILE=/etc/ssl/certs/ca-bundle.crt - mkdir -p /tmp - - # Create the build user/group required by Nix - echo 'nixbld:x:30000:nixbld' >> /etc/group - echo 'nixbld:x:30000:30000:nixbld:/tmp:/bin/bash' >> /etc/passwd - echo 'root:x:0:0:root:/root:/bin/bash' >> /etc/passwd - echo 'root:x:0:' >> /etc/group - - # Disable sandboxing to avoid running into privilege issues - mkdir -p /etc/nix - echo 'sandbox = false' >> /etc/nix/nix.conf - - # In some cases users building their own image might want to - # customise something on the inside (e.g. set up an environment - # for keys or whatever). - # - # This can be achieved by setting a 'preLaunch' script. - ${preLaunch} - - exec ${nixery-bin}/bin/nixery - ''; - in - dockerTools.buildLayeredImage { - name = "nixery"; - config.Cmd = [ "${nixery-launch-script}/bin/nixery" ]; - - inherit maxLayers; - contents = [ - bashInteractive - cacert - coreutils - git - gnutar - gzip - iana-etc - nix - nixery-prepare-image - nixery-launch-script - openssh - zlib - ] ++ extraPackages; - }; + nixery-image = dockerTools.buildLayeredImage { + name = "nixery"; + config.Cmd = [ "${nixery-launch-script}/bin/nixery" ]; + + inherit maxLayers; + contents = [ + bashInteractive + cacert + coreutils + git + gnutar + gzip + iana-etc + nix + nixery-prepare-image + nixery-launch-script + openssh + zlib + ] ++ extraPackages; + }; } diff --git a/tools/nixery/docs/.gitignore b/tools/nixery/docs/.gitignore deleted file mode 100644 index 7585238efe..0000000000 --- a/tools/nixery/docs/.gitignore +++ /dev/null @@ -1 +0,0 @@ -book diff --git a/tools/nixery/docs/book.toml b/tools/nixery/docs/book.toml deleted file mode 100644 index bf6ccbb27f..0000000000 --- a/tools/nixery/docs/book.toml +++ /dev/null @@ -1,8 +0,0 @@ -[book] -authors = ["Vincent Ambo <tazjin@google.com>"] -language = "en" -multilingual = false -src = "src" - -[output.html] -additional-css = ["theme/nixery.css"] diff --git a/tools/nixery/docs/default.nix b/tools/nixery/docs/default.nix deleted file mode 100644 index 876a34dcf1..0000000000 --- a/tools/nixery/docs/default.nix +++ /dev/null @@ -1,26 +0,0 @@ -# Copyright 2022 The TVL Contributors -# SPDX-License-Identifier: Apache-2.0 - -# Builds the documentation page using the Rust project's 'mdBook' -# tool. -# -# Some of the documentation is pulled in and included from other -# sources. - -{ fetchFromGitHub, mdbook, runCommand, rustPlatform }: - -let - nix-1p = fetchFromGitHub { - owner = "tazjin"; - repo = "nix-1p"; - rev = "9f0baf5e270128d9101ba4446cf6844889e399a2"; - sha256 = "1pf9i90gn98vz67h296w5lnwhssk62dc6pij983dff42dbci7lhj"; - }; -in -runCommand "nixery-book" { } '' - mkdir -p $out - cp -r ${./.}/* . - chmod -R a+w src - cp ${nix-1p}/README.md src/nix-1p.md - ${mdbook}/bin/mdbook build -d $out -'' diff --git a/tools/nixery/docs/src/SUMMARY.md b/tools/nixery/docs/src/SUMMARY.md deleted file mode 100644 index f1d68a3ac4..0000000000 --- a/tools/nixery/docs/src/SUMMARY.md +++ /dev/null @@ -1,8 +0,0 @@ -# Summary - -- [Nixery](./nixery.md) - - [Under the hood](./under-the-hood.md) - - [Caching](./caching.md) - - [Run your own Nixery](./run-your-own.md) -- [Nix](./nix.md) - - [Nix, the language](./nix-1p.md) diff --git a/tools/nixery/docs/src/caching.md b/tools/nixery/docs/src/caching.md deleted file mode 100644 index 05ea68ef60..0000000000 --- a/tools/nixery/docs/src/caching.md +++ /dev/null @@ -1,69 +0,0 @@ -# Caching in Nixery - -This page gives a quick overview over the caching done by Nixery. All cache data -is written to Nixery's storage bucket and is based on deterministic identifiers -or content-addressing, meaning that cache entries under the same key *never -change*. - -## Manifests - -Manifests of builds are cached at `$BUCKET/manifests/$KEY`. The effect of this -cache is that multiple instances of Nixery do not need to rebuild the same -manifest from scratch. - -Since the manifest cache is populated only *after* layers are uploaded, Nixery -can immediately return the manifest to its clients without needing to check -whether layers have been uploaded already. - -`$KEY` is generated by creating a SHA1 hash of the requested content of a -manifest plus the package source specification. - -Manifests are *only* cached if the package source specification is *not* a -moving target. - -Manifest caching *only* applies in the following cases: - -* package source specification is a specific git commit -* package source specification is a specific NixOS/nixpkgs commit - -Manifest caching *never* applies in the following cases: - -* package source specification is a local file path (i.e. `NIXERY_PKGS_PATH`) -* package source specification is a NixOS channel (e.g. `NIXERY_CHANNEL=nixos-20.09`) -* package source specification is a git branch or tag (e.g. `staging`, `master` or `latest`) - -It is thus always preferable to request images from a fully-pinned package -source. - -Manifests can be removed from the manifest cache without negative consequences. - -## Layer tarballs - -Layer tarballs are the files that Nixery clients retrieve from the storage -bucket to download an image. - -They are stored content-addressably at `$BUCKET/layers/$SHA256HASH` and layer -requests sent to Nixery will redirect directly to this storage location. - -The effect of this cache is that Nixery does not need to upload identical layers -repeatedly. When Nixery notices that a layer already exists in GCS it will skip -uploading this layer. - -Removing layers from the cache is *potentially problematic* if there are cached -manifests or layer builds referencing those layers. - -To clean up layers, a user must ensure that no other cached resources still -reference these layers. - -## Layer builds - -Layer builds are cached at `$BUCKET/builds/$HASH`, where `$HASH` is a SHA1 of -the Nix store paths included in the layer. - -The content of the cached entries is a JSON-object that contains the SHA256 -hashes and sizes of the built layer. - -The effect of this cache is that different instances of Nixery will not build, -hash and upload layers that have identical contents across different instances. - -Layer builds can be removed from the cache without negative consequences. diff --git a/tools/nixery/docs/src/nix-1p.md b/tools/nixery/docs/src/nix-1p.md deleted file mode 100644 index a21234150f..0000000000 --- a/tools/nixery/docs/src/nix-1p.md +++ /dev/null @@ -1,2 +0,0 @@ -This page is a placeholder. During the build process, it is replaced by the -actual `nix-1p` guide from https://github.com/tazjin/nix-1p diff --git a/tools/nixery/docs/src/nix.md b/tools/nixery/docs/src/nix.md deleted file mode 100644 index 2bfd75a692..0000000000 --- a/tools/nixery/docs/src/nix.md +++ /dev/null @@ -1,31 +0,0 @@ -# Nix - -These sections are designed to give some background information on what Nix is. -If you've never heard of Nix before looking at Nixery, this might just be the -page for you! - -[Nix][] is a functional package-manager that comes with a number of advantages -over traditional package managers, such as side-by-side installs of different -package versions, atomic updates, easy customisability, simple binary caching -and much more. Feel free to explore the [Nix website][Nix] for an overview of -Nix itself. - -Nix uses a custom programming language also called Nix, which is explained here -[on its own page][nix-1p]. - -In addition to the package manager and language, the Nix project also maintains -[NixOS][] - a Linux distribution built entirely on Nix. On NixOS, users can -declaratively describe the *entire* configuration of their system and perform -updates/rollbacks to other system configurations with ease. - -Most Nix packages are tracked in the [Nix package set][nixpkgs], usually simply -referred to as `nixpkgs`. It contains tens of thousands of packages already! - -Nixery (which you are looking at!) provides an easy & simple way to get started -with Nix, in fact you don't even need to know that you're using Nix to make use -of Nixery. - -[Nix]: https://nixos.org/nix/ -[nix-1p]: nix-1p.html -[NixOS]: https://nixos.org/ -[nixpkgs]: https://github.com/nixos/nixpkgs diff --git a/tools/nixery/docs/src/nixery.md b/tools/nixery/docs/src/nixery.md deleted file mode 100644 index d9ba179010..0000000000 --- a/tools/nixery/docs/src/nixery.md +++ /dev/null @@ -1,80 +0,0 @@ -![Nixery](./nixery-logo.png) - ------------- - -Welcome to this instance of [Nixery][]. It provides ad-hoc container images that -contain packages from the [Nix][] package manager. Images with arbitrary -packages can be requested via the image name. - -Nix not only provides the packages to include in the images, but also builds the -images themselves by using a special [layering strategy][] that optimises for -cache efficiency. - -For general information on why using Nix makes sense for container images, check -out [this blog post][layers]. - -## Demo - -<script src="https://asciinema.org/a/262583.js" id="asciicast-262583" async data-autoplay="true" data-loop="true"></script> - -## Quick start - -Simply pull an image from this registry, separating each package you want -included by a slash: - - docker pull nixery.dev/shell/git/htop - -This gives you an image with `git`, `htop` and an interactively configured -shell. You could run it like this: - - docker run -ti nixery.dev/shell/git/htop bash - -Each path segment corresponds either to a key in the Nix package set, or a -meta-package that automatically expands to several other packages. - -Meta-packages **must** be the first path component if they are used. Currently -there are only two meta-packages: -- `shell`, which provides a `bash`-shell with interactive configuration and - standard tools like `coreutils`. -- `arm64`, which provides ARM64 binaries. - -**Tip:** When pulling from a private Nixery instance, replace `nixery.dev` in -the above examples with your registry address. - -## FAQ - -If you have a question that is not answered here, feel free to file an issue on -Github so that we can get it included in this section. The volume of questions -is quite low, thus by definition your question is already frequently asked. - -### Where is the source code for this? - -Over [on Github][Nixery]. It is licensed under the Apache 2.0 license. Consult -the documentation entries in the sidebar for information on how to set up your -own instance of Nixery. - -### Which revision of `nixpkgs` is used for the builds? - -The instance at `nixery.dev` tracks a recent NixOS channel, currently NixOS -20.09. The channel is updated several times a day. - -Private registries might be configured to track a different channel (such as -`nixos-unstable`) or even track a git repository with custom packages. - -### Should I depend on `nixery.dev` in production? - -While we appreciate the enthusiasm, if you would like to use Nixery in your -production project we recommend setting up a private instance. The public Nixery -at `nixery.dev` is run on a best-effort basis and we make no guarantees about -availability. - -### Who made this? - -Nixery was written by [tazjin][], but many people have contributed to Nix over -time, maybe you could become one of them? - -[Nixery]: https://github.com/tazjin/nixery -[Nix]: https://nixos.org/nix -[layering strategy]: https://storage.googleapis.com/nixdoc/nixery-layers.html -[layers]: https://grahamc.com/blog/nix-and-layered-docker-images -[tazjin]: https://tazj.in diff --git a/tools/nixery/docs/src/run-your-own.md b/tools/nixery/docs/src/run-your-own.md deleted file mode 100644 index 7ed8bdd0bc..0000000000 --- a/tools/nixery/docs/src/run-your-own.md +++ /dev/null @@ -1,194 +0,0 @@ -## Run your own Nixery - -<!-- markdown-toc start - Don't edit this section. Run M-x markdown-toc-refresh-toc --> - -- [0. Prerequisites](#0-prerequisites) -- [1. Choose a package set](#1-choose-a-package-set) -- [2. Build Nixery itself](#2-build-nixery-itself) -- [3. Prepare configuration](#3-prepare-configuration) -- [4. Deploy Nixery](#4-deploy-nixery) -- [5. Productionise](#5-productionise) - -<!-- markdown-toc end --> - - ---------- - -⚠ This page is still under construction! ⚠ - --------- - -Running your own Nixery is not difficult, but requires some setup. Follow the -steps below to get up & running. - -*Note:* Nixery can be run inside of a [GKE][] cluster, providing a local service -from which images can be requested. Documentation for how to set this up is -forthcoming, please see [nixery#4][]. - -## 0. Prerequisites - -To run Nixery, you must have: - -* [Nix][] (to build Nixery itself) -* Somewhere to run it (your own server, Google AppEngine, a Kubernetes cluster, - whatever!) -* *Either* a [Google Cloud Storage][gcs] bucket in which to store & serve layers, - *or* a comfortable amount of disk space - -Note that while the main Nixery process is a server written in Go, -it invokes a script that itself relies on Nix to be available. -You can compile the main Nixery daemon without Nix, but it won't -work without Nix. - -(If you are completely new to Nix and don't know how to get -started, check the [Nix installation documentation][nixinstall].) - -## 1. Choose a package set - -When running your own Nixery you need to decide which package set you want to -serve. By default, Nixery builds packages from a recent NixOS channel which -ensures that most packages are cached upstream and no expensive builds need to -be performed for trivial things. - -However if you are running a private Nixery, chances are high that you intend to -use it with your own packages. There are three options available: - -1. Specify an upstream Nix/NixOS channel[^1], such as `nixos-20.09` or - `nixos-unstable`. -2. Specify your own git-repository with a custom package set[^2]. This makes it - possible to pull different tags, branches or commits by modifying the image - tag. -3. Specify a local file path containing a Nix package set. Where this comes from - or what it contains is up to you. - -## 2. Build Nixery itself - -### 2.1. With a container image - -The easiest way to run Nixery is to build a container image. This -section assumes that the container runtime used is Docker, please -modify instructions accordingly if you are using something else. - -With a working Nix installation, you can clone and build the Nixery -image like this: - -``` -git clone https://code.tvl.fyi/depot.git:/tools/nixery.git -nix-build -A nixery-image -``` - -This will create a `result`-symlink which points to a tarball containing the -image. In Docker, this tarball can be loaded by using `docker load -i result`. - -### 2.2. Without a container image - -*This method might be more convenient if you intend to work on -the code of the Nixery server itself, because you won't have to -rebuild (and reload) an image each time to test your changes.* - -You will need to run the two following commands at the root of the repo: - -* `go build` to build the `nixery` binary; -* `nix-env --install --file prepare-image/default.nix` to build - the required helpers. - -## 3. Prepare configuration - -Nixery is configured via environment variables. - -You must set *all* of these: - -* `NIXERY_STORAGE_BACKEND` (must be set to `gcs` or `filesystem`) -* `PORT`: HTTP port on which Nixery should listen -* `WEB_DIR`: directory containing static files (see below) - -You must set *one* of these: - -* `NIXERY_CHANNEL`: The name of a [Nix/NixOS channel][nixchannel] to use for building, - for instance `nixos-21.05` -* `NIXERY_PKGS_REPO`: URL of a git repository containing a package set (uses - locally configured SSH/git credentials) -* `NIXERY_PKGS_PATH`: A local filesystem path containing a Nix package set to use - for building - -If `NIXERY_STORAGE_BACKEND` is set to `filesystem`, then `STORAGE_PATH` -must be set to the directory that will hold the registry blobs. -That directory must be located on a filesystem that supports extended -attributes (which means that on most systems, `/tmp` won't work). - -If `NIXERY_STORAGE_BACKEND` is set to `gcs`, then `GCS_BUCKET` -must be set to the [Google Cloud Storage][gcs] bucket that will be -used to store & serve image layers. - -You may set *all* of these: - -* `NIX_TIMEOUT`: Number of seconds that any Nix builder is allowed to run - (defaults to 60) - -To authenticate to the configured GCS bucket, Nixery uses Google's [Application -Default Credentials][ADC]. Depending on your environment this may require -additional configuration. - -If the `GOOGLE_APPLICATION_CREDENTIALS` environment is configured, the service -account's private key will be used to create [signed URLs for -layers][signed-urls]. - -## 4. Start Nixery - -Run the image that was built in step 2.1 with all the environment variables -mentioned above. Alternatively, set all the environment variables and run -the Nixery server that was built in step 2.2. - -Once Nixery is running you can immediately start requesting images from it. - -## 5. Productionise - -(⚠ Here be dragons! ⚠) - -Nixery is still an early project and has not yet been deployed in any production -environments and some caveats apply. - -Notably, Nixery currently does not support any authentication methods, so anyone -with network access to the registry can retrieve images. - -Running a Nixery inside of a fenced-off environment (such as internal to a -Kubernetes cluster) should be fine, but you should consider to do all of the -following: - -* Issue a TLS certificate for the hostname you are assigning to Nixery. In fact, - Docker will refuse to pull images from registries that do not use TLS (with - the exception of `.local` domains). -* Configure signed GCS URLs to avoid having to make your bucket world-readable. -* Configure request timeouts for Nixery if you have your own web server in front - of it. This will be natively supported by Nixery in the future. - -## 6. `WEB_DIR` - -All the URLs accessed by Docker registry clients start with `/v2/`. -This means that it is possible to serve a static website from Nixery -itself (as long as you don't want to serve anything starting with `/v2`). -This is how, for instance, https://nixery.dev shows the website for Nixery, -while it is also possible to e.g. `docker pull nixery.dev/shell`. - -When running Nixery, you must set the `WEB_DIR` environment variable. -When Nixery receives requests that don't look like registry requests, -it tries to serve them using files in the directory indicated by `WEB_DIR`. -If the directory doesn't exist, Nixery will run fine but serve 404. - -------- - -[^1]: Nixery will not work with Nix channels older than `nixos-19.03`. - -[^2]: This documentation will be updated with instructions on how to best set up - a custom Nix repository. Nixery expects custom package sets to be a superset - of `nixpkgs`, as it uses `lib` and other features from `nixpkgs` - extensively. - -[GKE]: https://cloud.google.com/kubernetes-engine/ -[nixery#4]: https://github.com/tazjin/nixery/issues/4 -[Nix]: https://nixos.org/nix -[gcs]: https://cloud.google.com/storage/ -[signed-urls]: under-the-hood.html#5-image-layers-are-requested -[ADC]: https://cloud.google.com/docs/authentication/production#finding_credentials_automatically -[nixinstall]: https://nixos.org/manual/nix/stable/installation/installing-binary.html -[nixchannel]: https://nixos.wiki/wiki/Nix_channels diff --git a/tools/nixery/docs/src/under-the-hood.md b/tools/nixery/docs/src/under-the-hood.md deleted file mode 100644 index 4b79830010..0000000000 --- a/tools/nixery/docs/src/under-the-hood.md +++ /dev/null @@ -1,129 +0,0 @@ -# Under the hood - -This page serves as a quick explanation of what happens under-the-hood when an -image is requested from Nixery. - -<!-- markdown-toc start - Don't edit this section. Run M-x markdown-toc-refresh-toc --> - -- [1. The image manifest is requested](#1-the-image-manifest-is-requested) -- [2. Nix fetches and prepares image content](#2-nix-fetches-and-prepares-image-content) -- [3. Layers are grouped, created, hashed, and persisted](#3-layers-are-grouped-created-hashed-and-persisted) -- [4. The manifest is assembled and returned to the client](#4-the-manifest-is-assembled-and-returned-to-the-client) -- [5. Image layers are requested](#5-image-layers-are-requested) - -<!-- markdown-toc end --> - --------- - -## 1. The image manifest is requested - -When container registry clients such as Docker pull an image, the first thing -they do is ask for the image manifest. This is a JSON document describing which -layers are contained in an image, as well as some additional auxiliary -information. - -This request is of the form `GET /v2/$imageName/manifests/$imageTag`. - -Nixery receives this request and begins by splitting the image name into its -path components and substituting meta-packages (such as `shell`) for their -contents. - -For example, requesting `shell/htop/git` results in Nixery expanding the image -name to `["bashInteractive", "coreutils", "htop", "git"]`. - -If Nixery is configured with a private Nix repository, it also looks at the -image tag and substitutes `latest` with `master`. - -It then invokes Nix with three parameters: - -1. image contents (as above) -2. image tag -3. configured package set source - -## 2. Nix fetches and prepares image content - -Using the parameters above, Nix imports the package set and begins by mapping -the image names to attributes in the package set. - -A special case during this process is packages with uppercase characters in -their name, for example anything under `haskellPackages`. The registry protocol -does not allow uppercase characters, so the Nix code will translate something -like `haskellpackages` (lowercased) to the correct attribute name. - -After identifying all contents, Nix uses the `symlinkJoin` function to -create a special layer with the "symlink farm" required to let the -image function like a normal disk image. - -Nix then returns information about the image contents as well as the -location of the special layer to Nixery. - -## 3. Layers are grouped, created, hashed, and persisted - -With the information received from Nix, Nixery determines the contents -of each layer while optimising for the best possible cache efficiency -(see the [layering design doc][] for details). - -With the grouped layers, Nixery then begins to create compressed -tarballs with all required contents for each layer. As these tarballs -are being created, they are simultaneously being hashed (as the image -manifest must contain the content-hashes of all layers) and persisted -to storage. - -Storage can be either a remote [Google Cloud Storage][gcs] bucket, or -a local filesystem path. - -During this step, Nixery checks its build cache (see [Caching][]) to -determine whether a layer needs to be built or is already cached from -a previous build. - -*Note:* While this step is running (which can take some time in the case of -large first-time image builds), the registry client is left hanging waiting for -an HTTP response. Unfortunately the registry protocol does not allow for any -feedback back to the user at this point, so from the user's perspective things -just ... hang, for a moment. - -## 4. The manifest is assembled and returned to the client - -Once armed with the hashes of all required layers, Nixery assembles -the OCI Container Image manifest which describes the structure of the -built image and names all of its layers by their content hash. - -This manifest is returned to the client. - -## 5. Image layers are requested - -The client now inspects the manifest and determines which of the -layers it is currently missing based on their content hashes. Note -that different container runtimes will handle this differently, and in -the case of certain engine and storage driver combinations (e.g. -Docker with OverlayFS) layers might be downloaded again even if they -are already present. - -For each of the missing layers, the client now issues a request to -Nixery that looks like this: - -`GET /v2/${imageName}/blob/sha256:${layerHash}` - -Nixery receives these requests and handles them based on the -configured storage backend. - -If the storage backend is GCS, it *redirects* them to Google Cloud -Storage URLs, responding with an `HTTP 303 See Other` status code and -the actual download URL of the layer. - -Nixery supports using private buckets which are not generally world-readable, in -which case [signed URLs][] are constructed using a private key. These allow the -registry client to download each layer without needing to care about how the -underlying authentication works. - -If the storage backend is the local filesystem, Nixery will attempt to -serve the layer back to the client from disk. - ---------- - -That's it. After these five steps the registry client has retrieved all it needs -to run the image produced by Nixery. - -[gcs]: https://cloud.google.com/storage/ -[signed URLs]: https://cloud.google.com/storage/docs/access-control/signed-urls -[layering design doc]: https://storage.googleapis.com/nixdoc/nixery-layers.html diff --git a/tools/nixery/docs/theme/favicon.png b/tools/nixery/docs/theme/favicon.png deleted file mode 100644 index f510bde197..0000000000 --- a/tools/nixery/docs/theme/favicon.png +++ /dev/null Binary files differdiff --git a/tools/nixery/docs/theme/nixery.css b/tools/nixery/docs/theme/nixery.css deleted file mode 100644 index c240e693d5..0000000000 --- a/tools/nixery/docs/theme/nixery.css +++ /dev/null @@ -1,3 +0,0 @@ -h2, h3 { - margin-top: 1em; -} diff --git a/tools/nixery/go.mod b/tools/nixery/go.mod index dfaeb72068..9e896ffb40 100644 --- a/tools/nixery/go.mod +++ b/tools/nixery/go.mod @@ -3,22 +3,12 @@ module github.com/google/nixery go 1.15 require ( - cloud.google.com/go/storage v1.18.2 - github.com/census-instrumentation/opencensus-proto v0.3.0 // indirect - github.com/cespare/xxhash/v2 v2.1.2 // indirect - github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4 // indirect - github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1 // indirect - github.com/envoyproxy/go-control-plane v0.10.0 // indirect - github.com/envoyproxy/protoc-gen-validate v0.6.2 // indirect + cloud.google.com/go/storage v1.22.1 github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/google/go-cmp v0.5.6 - github.com/pkg/xattr v0.4.4 + github.com/google/go-cmp v0.5.8 + github.com/im7mortal/kmutex v1.0.1 // indirect + github.com/pkg/xattr v0.4.7 github.com/sirupsen/logrus v1.8.1 - golang.org/x/net v0.0.0-20211029160332-540bb53d3b2e // indirect - golang.org/x/oauth2 v0.0.0-20211028175245-ba495a64dcb5 - golang.org/x/sys v0.0.0-20211029162942-c1bf0bb051ef // indirect - gonum.org/v1/gonum v0.9.3 - google.golang.org/api v0.60.0 // indirect - google.golang.org/genproto v0.0.0-20211029142109-e255c875f7c7 // indirect - google.golang.org/grpc v1.41.0 // indirect + golang.org/x/oauth2 v0.0.0-20220524215830-622c5d57e401 + gonum.org/v1/gonum v0.11.0 ) diff --git a/tools/nixery/go.sum b/tools/nixery/go.sum index 312cbfaa2e..5b6054fb60 100644 --- a/tools/nixery/go.sum +++ b/tools/nixery/go.sum @@ -24,16 +24,25 @@ cloud.google.com/go v0.87.0/go.mod h1:TpDYlFy7vuLzZMMZ+B6iRiELaY7z/gJPaqbMx6mlWc cloud.google.com/go v0.90.0/go.mod h1:kRX0mNRHe0e2rC6oNakvwQqzyDmg57xJ+SZU1eT2aDQ= cloud.google.com/go v0.93.3/go.mod h1:8utlLll2EF5XMAV15woO4lSbWQlk8rer9aLOfLh7+YI= cloud.google.com/go v0.94.1/go.mod h1:qAlAugsXlC+JWO+Bke5vCtc9ONxjQT3drlTTnAplMW4= -cloud.google.com/go v0.97.0 h1:3DXvAyifywvq64LfkKaMOmkWPS1CikIQdMe2lY9vxU8= cloud.google.com/go v0.97.0/go.mod h1:GF7l59pYBVlXQIBLx3a761cZ41F9bBH3JUlihCt2Udc= +cloud.google.com/go v0.99.0/go.mod h1:w0Xx2nLzqWJPuozYQX+hFfCSI8WioryfRDzkoI/Y2ZA= +cloud.google.com/go v0.100.2 h1:t9Iw5QH5v4XtlEQaCtUY7x6sCABps8sW0acw7e2WQ6Y= +cloud.google.com/go v0.100.2/go.mod h1:4Xra9TjzAeYHrl5+oeLlzbM2k3mjVhZh4UqTZ//w99A= cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o= cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE= cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc= cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg= cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc= cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ= +cloud.google.com/go/compute v0.1.0/go.mod h1:GAesmwr110a34z04OlxYkATPBEfVhkymfTBXtfbBFow= +cloud.google.com/go/compute v1.3.0/go.mod h1:cCZiE1NHEtai4wiufUhW8I8S1JKkAnhnQJWM7YD99wM= +cloud.google.com/go/compute v1.5.0/go.mod h1:9SMHyhJlzhlkJqrPAc839t2BZFTSk6Jdj6mkzQJeu0M= +cloud.google.com/go/compute v1.6.0 h1:XdQIN5mdPTSBVwSIVDuY5e8ZzVAccsHvD3qTEz4zIps= +cloud.google.com/go/compute v1.6.0/go.mod h1:T29tfhtVbq1wvAPo0E3+7vhgmkOYeXjhFvz/FMzPu0s= cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE= cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk= +cloud.google.com/go/iam v0.3.0 h1:exkAomrVUuzx9kWFI1wm3KI0uoDeUFPB4kKGzx6x+Gc= +cloud.google.com/go/iam v0.3.0/go.mod h1:XzJPvDayI+9zsASAFO68Hk07u3z+f+JrT2xXNdp4bnY= cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I= cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw= cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA= @@ -43,24 +52,24 @@ cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0Zeo cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk= cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs= cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0= -cloud.google.com/go/storage v1.18.2 h1:5NQw6tOn3eMm0oE8vTkfjau18kjL79FlMjy/CHTpmoY= -cloud.google.com/go/storage v1.18.2/go.mod h1:AiIj7BWXyhO5gGVmYJ+S8tbkCx3yb0IMjua8Aw4naVM= +cloud.google.com/go/storage v1.22.1 h1:F6IlQJZrZM++apn9V5/VfS3gbTUYg98PS3EMQAzqtfg= +cloud.google.com/go/storage v1.22.1/go.mod h1:S8N1cAStu7BOeFfE8KAQzmyyLkK8p/vmRq6kuBTW58Y= dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= gioui.org v0.0.0-20210308172011-57750fc8a0a6/go.mod h1:RSH6KIUZ0p2xy5zHDxgAM4zumjgTw83q2ge/PI+yyw8= +git.sr.ht/~sbinet/gg v0.3.1/go.mod h1:KGYtlADtqsqANL9ueOFkWymvzUvLMQllU5Ixo+8v3pc= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9/go.mod h1:JynElWSGnm/4RlzPXRlREEwqTHAN3T56Bv2ITsFT3gY= +github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19/go.mod h1:T13YZdzov6OU0A1+RfKZiZN9ca6VeKdBdyDV+BY97Tk= github.com/ajstarks/svgo v0.0.0-20180226025133-644b8db467af/go.mod h1:K08gAheRH3/J6wwsYMMT4xOr94bZjxIelGM0+d/wbFw= +github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b/go.mod h1:1KcenG0jGWcpt8ov532z81sp/kMMUG485J2InIOyADM= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/boombuler/barcode v1.0.0/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= +github.com/boombuler/barcode v1.0.1/go.mod h1:paBWMcWSl3LHKBqUq+rly7CNSldXjb2rDl3JlRe0mD8= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/census-instrumentation/opencensus-proto v0.3.0 h1:t/LhUZLVitR1Ow2YOnduCsavhwFUklBMoGVYUCqmCqk= -github.com/census-instrumentation/opencensus-proto v0.3.0/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= -github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE= -github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= @@ -68,13 +77,11 @@ github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDk github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= -github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4 h1:hzAQntlaYRkVSFEfj9OTWlVV1H155FMD8BTKktLv0QI= github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI= github.com/cncf/xds/go v0.0.0-20210312221358-fbca930ec8ed/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= -github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1 h1:zH8ljVhhq7yC0MIeUL/IviMtY8hx2mK8cN9wEYb8ggw= github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= @@ -87,22 +94,23 @@ github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.m github.com/envoyproxy/go-control-plane v0.9.9-0.20210217033140-668b12f5399d/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk= github.com/envoyproxy/go-control-plane v0.9.9-0.20210512163311-63b5d3c536b0/go.mod h1:hliV/p42l8fGbc6Y9bQ70uLwIvmJyVE5k4iMKlh8wCQ= github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0= -github.com/envoyproxy/go-control-plane v0.10.0 h1:WVt4HEPbdRbRD/PKKPbPnIVavO6gk/h673jWyIJ016k= -github.com/envoyproxy/go-control-plane v0.10.0/go.mod h1:AY7fTTXNdv/aJ2O5jwpxAPOWUZ7hQAEvzN5Pf27BkQQ= +github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= -github.com/envoyproxy/protoc-gen-validate v0.6.2 h1:JiO+kJTpmYGjEodY7O1Zk8oZcNz1+f30UtwtXoFUPzE= -github.com/envoyproxy/protoc-gen-validate v0.6.2/go.mod h1:2t7qjJNvHPx8IjnBOzl9E9/baC+qXE/TeeyBRzgJDws= github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-fonts/dejavu v0.1.0/go.mod h1:4Wt4I4OU2Nq9asgDCteaAaWZOV24E+0/Pwo0gppep4g= github.com/go-fonts/latin-modern v0.2.0/go.mod h1:rQVLdDMK+mK1xscDwsqM5J8U2jrRa3T0ecnM9pNujks= github.com/go-fonts/liberation v0.1.1/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= +github.com/go-fonts/liberation v0.2.0/go.mod h1:K6qoJYypsmfVjWg8KOVDQhLc8UDgIK2HYqyqAO9z7GY= github.com/go-fonts/stix v0.1.0/go.mod h1:w/c1f0ldAUlJmLBvlbkvVXLAD+tAMqobIIQpmnUIzUY= github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-latex/latex v0.0.0-20210118124228-b3d85cf34e07/go.mod h1:CO1AlKB2CSIqUrmQPqA0gdRIlnLEY0gK5JGjh37zN5U= +github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81/go.mod h1:SX0U8uGpxhq9o2S/CELCSUxEWWAuoCUcVCQWv7G2OCk= +github.com/go-pdf/fpdf v0.5.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= +github.com/go-pdf/fpdf v0.6.0/go.mod h1:HzcnA+A23uwogo0tp9yU+l3V+KXhiESpt1PMayhOh5M= github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGwJL78qG/PmXZO1EjYhfJinVAhrmmHX6Z8B9k= github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -152,8 +160,10 @@ github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.3/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.7/go.mod h1:n+brtR0CgQNWTVd5ZUFpTBC8YFBDLK/h/bpaJ8/DtOE= +github.com/google/go-cmp v0.5.8 h1:e6P7q2lk1O+qJJb4BtCQXlK8vWEO8V1ZeuEdJNOqZyg= +github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs= github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0= @@ -180,44 +190,46 @@ github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+ github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg= github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk= github.com/googleapis/gax-go/v2 v2.1.0/go.mod h1:Q3nei7sK6ybPYH7twZdmQpAd1MKb7pfu6SK+H1/DsU0= -github.com/googleapis/gax-go/v2 v2.1.1 h1:dp3bWCh+PPO1zjRRiCSczJav13sBvG4UhNyVTa1KqdU= github.com/googleapis/gax-go/v2 v2.1.1/go.mod h1:hddJymUZASv3XPyGkUpKj8pPO47Rmb0eJc8R6ouapiM= +github.com/googleapis/gax-go/v2 v2.2.0/go.mod h1:as02EH8zWkzwUoLbBaFeQ+arQaj/OthfcblKl4IGNaM= +github.com/googleapis/gax-go/v2 v2.3.0 h1:nRJtk3y8Fm770D42QV6T90ZnvFZyk7agSo3Q+Z9p3WI= +github.com/googleapis/gax-go/v2 v2.3.0/go.mod h1:b8LNqSzNabLiUpXKkY7HAR5jr6bIT99EXz9pXxye9YM= +github.com/googleapis/go-type-adapters v1.0.0 h1:9XdMn+d/G57qq1s8dNc5IesGCXHf6V2HZ2JwRxfA2tA= +github.com/googleapis/go-type-adapters v1.0.0/go.mod h1:zHW75FOG2aur7gAO2B+MLby+cLsWGBF62rFAi7WjWO4= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8= -github.com/iancoleman/strcase v0.2.0/go.mod h1:iwCmte+B7n89clKwxIoIXy/HfoL7AsD47ZCWhYzw7ho= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= +github.com/im7mortal/kmutex v1.0.1 h1:zAACzjwD+OEknDqnLdvRa/BhzFM872EBwKijviGLc9Q= +github.com/im7mortal/kmutex v1.0.1/go.mod h1:f71c/Ugk/+58OHRAgvgzPP3QEiWGUjK13fd8ozfKWdo= github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU= github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk= github.com/jung-kurt/gofpdf v1.0.0/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5/go.mod h1:7Id9E/uU8ce6rXgefFLlgrJj/GYY22cpxn+r32jIOes= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= -github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/lyft/protoc-gen-star v0.5.3/go.mod h1:V0xaHgaf5oCCqmcxYcWiDfTiKsZsRc87/1qhoTACD8w= github.com/phpdave11/gofpdf v1.4.2/go.mod h1:zpO6xFn9yxo3YLyMvW8HcKWVdbNqgIfOOp2dXMnm1mY= github.com/phpdave11/gofpdi v1.0.12/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= +github.com/phpdave11/gofpdi v1.0.13/go.mod h1:vBmVV0Do6hSBHC8uKUQ71JGW+ZGQq74llk/7bXwjDoI= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= -github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= -github.com/pkg/xattr v0.4.4 h1:FSoblPdYobYoKCItkqASqcrKCxRn9Bgurz0sCBwzO5g= -github.com/pkg/xattr v0.4.4/go.mod h1:sBD3RAqlr8Q+RC3FutZcikpT8nyDrIEEBw2J744gVWs= +github.com/pkg/xattr v0.4.7 h1:XoA3KzmFvyPlH4RwX5eMcgtzcaGBaSvgt3IoFQfbrmQ= +github.com/pkg/xattr v0.4.7/go.mod h1:di8WF84zAKk8jzR1UBTEWh9AUlIZZ7M/JNt8e9B6ktU= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= github.com/ruudk/golang-pdf417 v0.0.0-20181029194003-1af4ab5afa58/go.mod h1:6lfFZQK844Gfx8o5WFuvpxWRwnSoipWe/p622j1v06w= +github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245/go.mod h1:pQAZKsJ8yyVxGRWYNEm9oFB8ieLgKFnamEyDmSA0BRk= github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE= github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA= -github.com/spf13/afero v1.3.3/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= @@ -230,6 +242,7 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU= go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -242,7 +255,6 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/exp v0.0.0-20180321215751-8460e604b9de/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= @@ -269,6 +281,10 @@ golang.org/x/image v0.0.0-20200430140353-33d19683fad8/go.mod h1:FeLwcggjj3mMvU+o golang.org/x/image v0.0.0-20200618115811-c13761719519/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20201208152932-35266b937fa6/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= golang.org/x/image v0.0.0-20210216034530-4410531fe030/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0= +golang.org/x/image v0.0.0-20210607152325-775e3b0c77b9/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20210628002857-a66eb6448b8d/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20211028202545-6944b10bf410/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= +golang.org/x/image v0.0.0-20220302094943-723b81ca9867/go.mod h1:023OzeP/+EPmXeapQh35lcL3II3LrY8Ic+EFFKVhULM= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= @@ -292,7 +308,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.5.0/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= +golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -328,9 +344,11 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v golang.org/x/net v0.0.0-20210316092652-d523dce5a7f4/go.mod h1:RBQZq4jEuRlivfhVLdyRGr576XBO4/greRjx4P4O3yc= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20210813160813-60bc85c4be6d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= -golang.org/x/net v0.0.0-20211029160332-540bb53d3b2e h1:2lVrcCMRP9p7tfk4KUpV1ESqtf49jpihlUtYnSj67k4= -golang.org/x/net v0.0.0-20211029160332-540bb53d3b2e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= +golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= +golang.org/x/net v0.0.0-20220325170049-de3da57026de h1:pZB1TWnKi+o4bENlbzAgLrEbY4RMYmUIRobMcSmfeYc= +golang.org/x/net v0.0.0-20220325170049-de3da57026de/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= @@ -346,9 +364,12 @@ golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ golang.org/x/oauth2 v0.0.0-20210628180205-a41e5a781914/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210805134026-6f1e6394065a/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= golang.org/x/oauth2 v0.0.0-20210819190943-2bc19b11175f/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211005180243-6b3c2da341f1/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= -golang.org/x/oauth2 v0.0.0-20211028175245-ba495a64dcb5 h1:v79phzBz03tsVCUTbvTBmmC3CUXF5mKYt7DA4ZVldpM= -golang.org/x/oauth2 v0.0.0-20211028175245-ba495a64dcb5/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20211104180415-d3ed0bb246c8/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= +golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220309155454-6242fa91716a/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220411215720-9780585627b5/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= +golang.org/x/oauth2 v0.0.0-20220524215830-622c5d57e401 h1:zwrSfklXn0gxyLRX/aR+q6cgHbV/ItVyzbPlbA+dkAw= +golang.org/x/oauth2 v0.0.0-20220524215830-622c5d57e401/go.mod h1:DAh4E804XQdzx2j+YRIaUnCqCV2RuMz24cGBJ5QYIrc= golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -388,7 +409,6 @@ golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200905004654-be1d3432aa8f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20201101102859-da207088b7d1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -403,17 +423,24 @@ golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210514084401-e8d321eab015/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210603125802-9665404d3644/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210816183151-1e6c022a8912/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210908233432-aa78b53d3365/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20210917161153-d61c044b1678/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211025201205-69cdffdb9359/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.0.0-20211029162942-c1bf0bb051ef h1:1ZMK6QI8sz0q1ficx9/snrJ8E/PeRW7Oagamf+0xp10= -golang.org/x/sys v0.0.0-20211029162942-c1bf0bb051ef/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211124211545-fe61309f8881/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211210111614-af8b64212486/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211216021012-1d35b9e2eb4e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220128215802-99c3d69c2c27/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220328115105-d36c6a25d886/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f h1:8w7RhxzTVgUzw/AH/9mUV5q0vMgy40SQRursCcfmkCw= +golang.org/x/sys v0.0.0-20220408201424-a24fb2fb8a0f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= @@ -481,19 +508,22 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.3/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.4/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= +golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f h1:GGU+dLjvlC3qDwqYgL6UgRmHXhOOgns0bZu2Ty5mm6U= +golang.org/x/xerrors v0.0.0-20220411194840-2f41105eb62f/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gonum.org/v1/gonum v0.0.0-20180816165407-929014505bf4/go.mod h1:Y+Yx5eoAFn32cQvJDxZx5Dpnq+c3wtXuadVZAcxbbBo= gonum.org/v1/gonum v0.8.2/go.mod h1:oe/vMfY3deqTw+1EZJhuvEW2iwGF1bW9wwu7XCu0+v0= -gonum.org/v1/gonum v0.9.3 h1:DnoIG+QAMaF5NvxnGe/oKsgKcAc6PcUyl8q0VetfQ8s= gonum.org/v1/gonum v0.9.3/go.mod h1:TZumC3NeyVQskjXqmyWt4S3bINhy7B4eYwW69EbyX+0= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc= +gonum.org/v1/gonum v0.11.0 h1:f1IJhK4Km5tBJmaiJXtk/PkL4cdVX6J+tGiM187uT5E= +gonum.org/v1/gonum v0.11.0/go.mod h1:fSG4YDCxxUZQJ7rKsQrj0gMOg00Il0Z96/qMA4bVQhA= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/plot v0.0.0-20190515093506-e2840ee46a6b/go.mod h1:Wt8AAjI+ypCyYX3nZBvf6cAIx93T+c/OS2HFAYskSZc= gonum.org/v1/plot v0.9.0/go.mod h1:3Pcqqmp6RHvJI72kgb8fThyUnav364FOsdDo2aGW5lY= +gonum.org/v1/plot v0.10.1/go.mod h1:VZW5OlhkL1mysU9vaqNHnsy86inf6Ot+jB3r+BczCEo= google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE= google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M= google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg= @@ -523,9 +553,13 @@ google.golang.org/api v0.54.0/go.mod h1:7C4bFFOvVDGXjfDTAsgGwDgAxRDeQ4X8NvUedIt6 google.golang.org/api v0.55.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= google.golang.org/api v0.56.0/go.mod h1:38yMfeP1kfjsl8isn0tliTjIb1rJXcQi4UXlbqivdVE= google.golang.org/api v0.57.0/go.mod h1:dVPlbZyBo2/OjBpmvNdpn2GRm6rPy75jyU7bmhdrMgI= -google.golang.org/api v0.58.0/go.mod h1:cAbP2FsxoGVNwtgNAmmn3y5G1TWAiVYRmg4yku3lv+E= -google.golang.org/api v0.60.0 h1:eq/zs5WPH4J9undYM9IP1O7dSr7Yh8Y0GtSCpzGzIUk= -google.golang.org/api v0.60.0/go.mod h1:d7rl65NZAkEQ90JFzqBjcRq1TVeG5ZoGV3sSpEnnVb4= +google.golang.org/api v0.61.0/go.mod h1:xQRti5UdCmoCEqFxcz93fTl338AVqDgyaDRuOZ3hg9I= +google.golang.org/api v0.63.0/go.mod h1:gs4ij2ffTRXwuzzgJl/56BdwJaA194ijkfn++9tDuPo= +google.golang.org/api v0.67.0/go.mod h1:ShHKP8E60yPsKNw/w8w+VYaj9H6buA5UqDp8dhbQZ6g= +google.golang.org/api v0.70.0/go.mod h1:Bs4ZM2HGifEvXwd50TtW70ovgJffJYw2oRCOFU/SkfA= +google.golang.org/api v0.71.0/go.mod h1:4PyU6e6JogV1f9eA4voyrTY2batOLdgZ5qZ5HOCc4j8= +google.golang.org/api v0.74.0 h1:ExR2D+5TYIrMphWgs5JCgwRhEDlPDXXrLwHHMgPHTXE= +google.golang.org/api v0.74.0/go.mod h1:ZpfMZOVRMywNyvJFeqL9HRWBgAuRfSjJFpe9QtRRyDs= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= @@ -573,6 +607,7 @@ google.golang.org/genproto v0.0.0-20210222152913-aa3ee6e6a81c/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210303154014-9728d6b83eeb/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20210329143202-679c6ae281ee/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210513213006-bf773b8c8384/go.mod h1:P3QM42oQyzQSnHPnZ/vqoCdDmzH28fzWByN9asMeM8A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= @@ -589,12 +624,22 @@ google.golang.org/genproto v0.0.0-20210828152312-66f60bf46e71/go.mod h1:eFjDcFEc google.golang.org/genproto v0.0.0-20210831024726-fe130286e0e2/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210903162649-d08c68adba83/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210909211513-a8c4777a87af/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= -google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4/go.mod h1:eFjDcFEctNawg4eG61bRv87N7iHBWyVhJu7u1kqDUXY= google.golang.org/genproto v0.0.0-20210924002016-3dee208752a0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211016002631-37fc39342514/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211021150943-2b146023228c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= -google.golang.org/genproto v0.0.0-20211029142109-e255c875f7c7 h1:aaSaYY/DIDJy3f/JLXWv6xJ1mBQSRnQ1s5JhAFTnzO4= -google.golang.org/genproto v0.0.0-20211029142109-e255c875f7c7/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211118181313-81c1377c94b1/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211206160659-862468c7d6e0/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211208223120-3a66f561d7aa/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20211221195035-429b39de9b1c/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220126215142-9970aeb2e350/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220207164111-0872dc986b00/go.mod h1:5CzLGKJ67TSI2B9POpiiyGha0AjJvZIUgRMt1dSmuhc= +google.golang.org/genproto v0.0.0-20220218161850-94dd64e39d7c/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220222213610-43724f9ea8cf/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220304144024-325a89244dc8/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220310185008-1973136f34c6/go.mod h1:kGP+zUP2Ddo0ayMi4YuN7C3WZyJvGLZRh8Z5wnAqvEI= +google.golang.org/genproto v0.0.0-20220324131243-acbaeb5b85eb/go.mod h1:hAL49I2IFola2sVEjAn7MEwsja0xp51I0tlGAf9hz4E= +google.golang.org/genproto v0.0.0-20220407144326-9054f6ed7bac/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220413183235-5e96e2839df9/go.mod h1:8w6bsBMX6yCPbAVTeqQHvzxW0EIFigd5lZyahWgyfDo= +google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335 h1:2D0OT6tPVdrQTOnVe1VQjfJPTED6EZ7fdJ/f6Db6OsY= +google.golang.org/genproto v0.0.0-20220518221133-4f43b3371335/go.mod h1:RAyBrSAP7Fh3Nc84ghnVLDPuV51xc9agzmm4Ph6i0Q4= google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38= google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM= @@ -620,8 +665,11 @@ google.golang.org/grpc v1.38.0/go.mod h1:NREThFqKR1f3iQ6oBuvc5LadQuXVGo9rkm5ZGrQ google.golang.org/grpc v1.39.0/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.39.1/go.mod h1:PImNr+rS9TWYb2O4/emRugxiyHZ5JyHW5F+RPnDzfrE= google.golang.org/grpc v1.40.0/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= -google.golang.org/grpc v1.41.0 h1:f+PlOh7QV4iIJkPrx5NQ7qaNGFQ3OTse67yaDHfju4E= -google.golang.org/grpc v1.41.0/go.mod h1:U3l9uK9J0sini8mHphKoXyaqDA/8VyGnDee1zzIUK6k= +google.golang.org/grpc v1.40.1/go.mod h1:ogyxbiOoUXAkP+4+xa6PZSE9DZgIHtSpzjDTB9KAK34= +google.golang.org/grpc v1.44.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU= +google.golang.org/grpc v1.45.0/go.mod h1:lN7owxKUQEqMfSyQikvvk5tf/6zMPsrK+ONuO11+0rQ= +google.golang.org/grpc v1.46.0 h1:oCjezcn6g6A75TGoKYBPgKmVBLexhYLM6MebdrPApP8= +google.golang.org/grpc v1.46.0/go.mod h1:vN9eftEi1UMyUsIF80+uQXhHjbXYbm0uXoFCACuMGWk= google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0/go.mod h1:6Kw0yEErY5E/yWrBtf03jp27GLLJujG4z/JK95pnjjw= google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= @@ -635,8 +683,9 @@ google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGj google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ= google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.28.0 h1:w43yiav+6bVFTBQFZX0r7ipe9JQ1QsbMgHwbBziscLw= +google.golang.org/protobuf v1.28.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -652,6 +701,7 @@ honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWh honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +honnef.co/go/tools v0.1.3/go.mod h1:NgwopIslSNH47DimFoV78dnkksY2EFtX0ajyb3K/las= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= diff --git a/tools/nixery/builder/layers.go b/tools/nixery/layers/layers.go index 5e37e62681..7251c61a84 100644 --- a/tools/nixery/builder/layers.go +++ b/tools/nixery/layers/layers.go @@ -11,10 +11,10 @@ // // # Inputs // -// * a graph of Nix runtime dependencies, generated via exportReferenceGraph -// * popularity values of each package in the Nix package set (in the form of a -// direct reference count) -// * a maximum number of layers to allocate for the image (the "layer budget") +// - a graph of Nix runtime dependencies, generated via exportReferenceGraph +// - popularity values of each package in the Nix package set (in the form of a +// direct reference count) +// - a maximum number of layers to allocate for the image (the "layer budget") // // # Algorithm // @@ -30,14 +30,15 @@ // │ │ // │ v // └───> D ───> F -// │ -// └────> G +// +// │ +// └────> G // // Each node (i.e. package) is then visited to determine how important // it is to separate this node into its own layer, specifically: // -// 1. Is the node within a certain threshold percentile of absolute -// popularity within all of nixpkgs? (e.g. `glibc`, `openssl`) +// 1. Is the node within a certain threshold percentile of absolute +// popularity within all of nixpkgs? (e.g. `glibc`, `openssl`) // // 2. Is the node's runtime closure above a threshold size? (e.g. 100MB) // @@ -103,7 +104,7 @@ // // Layer budget: 10 // Layers: { E }, { D, F }, { A }, { B }, { C } -package builder +package layers import ( "crypto/sha1" @@ -121,7 +122,7 @@ import ( // dependencies of a derivation. // // This is generated in Nix by using the exportReferencesGraph feature. -type runtimeGraph struct { +type RuntimeGraph struct { References struct { Graph []string `json:"graph"` } `json:"exportReferencesGraph"` @@ -142,19 +143,19 @@ type Popularity = map[string]int // Layer represents the data returned for each layer that Nix should // build for the container image. -type layer struct { +type Layer struct { Contents []string `json:"contents"` MergeRating uint64 } // Hash the contents of a layer to create a deterministic identifier that can be // used for caching. -func (l *layer) Hash() string { +func (l *Layer) Hash() string { sum := sha1.Sum([]byte(strings.Join(l.Contents, ":"))) return fmt.Sprintf("%x", sum) } -func (a layer) merge(b layer) layer { +func (a Layer) merge(b Layer) Layer { a.Contents = append(a.Contents, b.Contents...) a.MergeRating += b.MergeRating return a @@ -177,7 +178,7 @@ var nixRegexp = regexp.MustCompile(`^/nix/store/[a-z0-9]+-`) // PackageFromPath returns the name of a Nix package based on its // output store path. -func packageFromPath(path string) string { +func PackageFromPath(path string) string { return nixRegexp.ReplaceAllString(path, "") } @@ -185,7 +186,7 @@ func packageFromPath(path string) string { // the dot format used by GraphViz, into which the dependency graph // can be rendered. func (c *closure) DOTID() string { - return packageFromPath(c.Path) + return PackageFromPath(c.Path) } // bigOrPopular checks whether this closure should be considered for @@ -228,7 +229,7 @@ func insertEdges(graph *simple.DirectedGraph, cmap *map[string]*closure, node *c } // Create a graph structure from the references supplied by Nix. -func buildGraph(refs *runtimeGraph, pop *Popularity) *simple.DirectedGraph { +func buildGraph(refs *RuntimeGraph, pop *Popularity) *simple.DirectedGraph { cmap := make(map[string]*closure) graph := simple.NewDirectedGraph() @@ -288,7 +289,7 @@ func buildGraph(refs *runtimeGraph, pop *Popularity) *simple.DirectedGraph { // Extracts a subgraph starting at the specified root from the // dominator tree. The subgraph is converted into a flat list of // layers, each containing the store paths and merge rating. -func groupLayer(dt *flow.DominatorTree, root *closure) layer { +func groupLayer(dt *flow.DominatorTree, root *closure) Layer { size := root.Size contents := []string{root.Path} children := dt.DominatedBy(root.ID()) @@ -305,7 +306,7 @@ func groupLayer(dt *flow.DominatorTree, root *closure) layer { // Contents are sorted to ensure that hashing is consistent sort.Strings(contents) - return layer{ + return Layer{ Contents: contents, MergeRating: uint64(root.Popularity) * size, } @@ -316,10 +317,10 @@ func groupLayer(dt *flow.DominatorTree, root *closure) layer { // // Layers are merged together until they fit into the layer budget, // based on their merge rating. -func dominate(budget int, graph *simple.DirectedGraph) []layer { +func dominate(budget int, graph *simple.DirectedGraph) []Layer { dt := flow.Dominators(graph.Node(0), graph) - var layers []layer + var layers []Layer for _, n := range dt.DominatedBy(dt.Root().ID()) { layers = append(layers, groupLayer(&dt, n.(*closure))) } @@ -347,7 +348,7 @@ func dominate(budget int, graph *simple.DirectedGraph) []layer { // groupLayers applies the algorithm described above the its input and returns a // list of layers, each consisting of a list of Nix store paths that it should // contain. -func groupLayers(refs *runtimeGraph, pop *Popularity, budget int) []layer { +func GroupLayers(refs *RuntimeGraph, pop *Popularity, budget int) []Layer { graph := buildGraph(refs, pop) return dominate(budget, graph) } diff --git a/tools/nixery/manifest/manifest.go b/tools/nixery/manifest/manifest.go index d61514d2f6..5638b576eb 100644 --- a/tools/nixery/manifest/manifest.go +++ b/tools/nixery/manifest/manifest.go @@ -54,8 +54,8 @@ type imageConfig struct { } `json:"rootfs"` Config struct { - Cmd []string `json:"cmd,omitempty"` - Env []string `json:"env,omitempty"` + Cmd []string `json:",omitempty"` + Env []string `json:",omitempty"` } `json:"config"` } diff --git a/tools/nixery/popcount/README.md b/tools/nixery/popcount/README.md index 8485a4d30e..3e56f99d57 100644 --- a/tools/nixery/popcount/README.md +++ b/tools/nixery/popcount/README.md @@ -34,6 +34,6 @@ It currently does not evaluate nested attribute sets (such as ``` In essence, this will trim Nix's store paths and hashes from the output, - count the occurences of each package and return the output as JSON. All + count the occurrences of each package and return the output as JSON. All packages that have no references other than themselves are removed from the output. diff --git a/tools/nixery/prepare-image/prepare-image.nix b/tools/nixery/prepare-image/prepare-image.nix index bb88983cf6..28022fe42f 100644 --- a/tools/nixery/prepare-image/prepare-image.nix +++ b/tools/nixery/prepare-image/prepare-image.nix @@ -13,7 +13,7 @@ { # Description of the package set to be used (will be loaded by load-pkgs.nix) srcType ? "nixpkgs" -, srcArgs ? "nixos-20.09" +, srcArgs ? "nixos-unstable" , system ? "x86_64-linux" , importArgs ? { } , # Path to load-pkgs.nix @@ -89,6 +89,19 @@ let in attrByPath path fetchLower s; + # Workaround for a workaround in nixpkgs: Unquoted language + # identifiers can not start with numbers in Nix, but some package + # names start with numbers (such as `1password`). + # + # In nixpkgs convention, these identifiers are prefixed with + # underscores (e.g. `_1password`), however this is not accepted by + # the Docker registry protocol. + # + # To make this work, we detect these kinds of packages and add the + # missing underscore. + needsUnderscore = pkg: (builtins.match "^[0-9].*" pkg) != null; + normalisedPackages = map (p: if needsUnderscore p then "_${p}" else p) (fromJSON packages); + # allContents contains all packages successfully retrieved by name # from the package set, as well as any errors encountered while # attempting to fetch a package. @@ -104,7 +117,7 @@ let then attrs // { errors = attrs.errors ++ [ res ]; } else attrs // { contents = attrs.contents ++ [ res ]; }; init = { contents = [ ]; errors = [ ]; }; - fetched = (map (deepFetch pkgs) (fromJSON packages)); + fetched = (map (deepFetch pkgs) normalisedPackages); in foldl' splitter init fetched; @@ -155,7 +168,7 @@ let # Metadata about the symlink layer which is required for serving it. # Two different hashes are computed for different usages (inclusion # in manifest vs. content-checking in the layer cache). - symlinkLayerMeta = fromJSON (readFile (runCommand "symlink-layer-meta.json" + symlinkLayerMeta = fromJSON (builtins.unsafeDiscardStringContext (readFile (runCommand "symlink-layer-meta.json" { buildInputs = [ coreutils jq openssl ]; } '' @@ -164,11 +177,11 @@ let jq -n -c --arg tarHash $tarHash --arg size $layerSize --arg path ${symlinkLayer} \ '{ size: ($size | tonumber), tarHash: $tarHash, path: $path }' >> $out - '')); + ''))); # Final output structure returned to Nixery if the build succeeded buildOutput = { - runtimeGraph = fromJSON (readFile runtimeGraph); + runtimeGraph = fromJSON (builtins.unsafeDiscardStringContext (readFile runtimeGraph)); symlinkLayer = symlinkLayerMeta; }; diff --git a/tools/nixery/web/index.html b/tools/nixery/web/index.html new file mode 100644 index 0000000000..354c4913b2 --- /dev/null +++ b/tools/nixery/web/index.html @@ -0,0 +1,166 @@ +<!DOCTYPE html> +<head> + <meta charset="utf-8"> + <meta name="viewport" content="width=device-width, initial-scale=1"> + <meta name="description" content="The Virus Lounge"> + <link rel="stylesheet" type="text/css" href="https://static.tvl.fyi/latest/tvl.css" media="all"> + <link rel="icon" type="image/webp" href="/favicon.webp"> + <title>Nixery</title> +</head> +<body class="light"> + <img src="./nixery-logo.png" alt="Nixery"> + <hr> + + <p> + Welcome to this instance of Nixery, an ad-hoc container image registry that provides + packages from the <a href="https://nixos.org/nix">Nix</a> package manager. + </p> + + <p> + You can pull container images from this registry + at <code><span class="registry-hostname">nixery.dev</span></code> by appending any + packages that you need in the URL, separated by slashes. + </p> + + <noscript> + <p class="cheddar-callout cheddar-tip"> + <strong>NOTE:</strong> When pulling from a private Nixery instance, + replace <code>nixery.dev</code> in the above examples with your registry address. + </p> + </noscript> + + <h2><a href="#demo" aria-hidden="true" class="anchor" id="demo"></a>Demo</h2> + + <noscript> + <p> + The interactive demo needs Javascript to run, but you can just read the Usage + instructions below instead + </p> + </noscript> + + <script src="https://asciinema.org/a/262583.js" id="asciicast-262583" async data-autoplay="true" data-loop="true"></script> + + <h2><a href="#usage" aria-hidden="true" class="anchor" id="usage"></a>Usage</h2> + + <p> + These usage examples assume that you use Docker, but should not be much different for + other OCI-compatible platforms. + </p> + + <p> + Pull an image from this registry, separating each package you want included by a + slash: + </p> + + <pre style="background-color:#f6f8fa;padding:16px;"><span style="color:#323232;">docker pull <span class="registry-hostname">nixery.dev</span>/shell/git/htop</span></pre> + + <p> + This gives you an image with <code>git</code>, <code>htop</code> and an interactively + configured shell. You could run it like this: + </p> + + <pre style="background-color:#f6f8fa;padding:16px;"><span style="color:#323232;">docker run -ti <span class="registry-hostname">nixery.dev</span>/shell/git/htop bash</span></pre> + + <p> + Each path segment corresponds either to a key in the Nix package set, or a + meta-package that automatically expands to several other packages. + </p> + + <p> + Meta-packages <strong>must</strong> be the first path component if they are used. + Currently there are only two meta-packages: + </p> + + <ul> + <li> + <p> + <code>shell</code>, which provides a <code>bash</code>-shell with interactive + configuration and standard tools like <code>coreutils</code></p> + </li> + <li> + <p><code>arm64</code>, which provides ARM64 binaries</p> + </li> + </ul> + + <h2><a href="#faq" aria-hidden="true" class="anchor" id="faq"></a>FAQ</h2> + + <h3> + <a href="#how-does-this-work" aria-hidden="true" class="anchor" id="how-does-this-work"></a> + How does this work? + </h3> + + <p> + The short version is that we use the Nix package manager and an optimised + <a href="https://tazj.in/blog/nixery-layers">layering strategy</a>. + </p> + + <p> + Check out <a href="https://www.youtube.com/watch?v=pOI9H4oeXqA">the Nixery talk</a> + from NixCon 2019 for more information. + </p> + + <h3> + <a href="#should-i-depend-on-nixerydev-in-production" aria-hidden="true" class="anchor" id="should-i-depend-on-nixerydev-in-production"></a> + Should I depend on <code>nixery.dev</code> in production? + </h3> + + <p> + While we appreciate the enthusiasm, if you would like to use Nixery in your production + project we recommend setting up a private instance. The public Nixery + at <code>nixery.dev</code> is run on a best-effort basis and we make no guarantees + about availability. + </p> + + <h3> + <a href="#who-made-this" aria-hidden="true" class="anchor" id="who-made-this"></a> + Who made this? + </h3> + + <p> + Nixery was written by <a href="https://tazj.in">tazjin</a>, originally at Google. + These days Nixery is maintained by <a href="https://tvl.su">TVL</a>. + </p> + <p> + Nixery would not be possible without the many people that have contributed to Nix and + nixpkgs over time, maybe you could become one of them? + </p> + + <h3> + <a href="#where-is-the-source-code-for-this" aria-hidden="true" class="anchor" id="where-is-the-source-code-for-this"></a> + Where is the source code for this? + </h3> + + <p> + Nixery lives in the <a href="https://cs.tvl.fyi/depot/-/tree/tools/nixery">TVL + monorepo</a>. All development happens there and follows + the <a href="https://cs.tvl.fyi/depot/-/blob/docs/CONTRIBUTING.md">TVL contribution + guidelines</a>. + </p> + + <p> + We <em>mirror</em> the source code <a href="https://github.com/tazjin/nixery">to + Github</a> but do not guarantee that anyone will look at PRs or issues there. + </p> + + <hr> + <footer> + <p class="footer"> + <a class="uncoloured-link" href="https://at.tvl.fyi/?q=//tools/nixery">code</a> + | + <a class="uncoloured-link" href="https://cl.tvl.fyi/q/file:%2522%255Etools/nixery/.*%2522">reviews</a> + | + <a class="uncoloured-link" href="https://b.tvl.fyi/">bugs</a> + </p> + <p class="lod">ಠ_ಠ</p> + </footer> + + <script> + /* Replace the hostnames above with the one at which this page runs. */ + let hostname = window.location.hostname; + if (hostname != '') { + for (span of document.getElementsByClassName("registry-hostname")) { + span.textContent = hostname; + } + } + </script> +</body> diff --git a/tools/nixery/docs/src/nixery-logo.png b/tools/nixery/web/nixery-logo.png index fcf77df3d6..fcf77df3d6 100644 --- a/tools/nixery/docs/src/nixery-logo.png +++ b/tools/nixery/web/nixery-logo.png Binary files differ |