Reimplement dnsmasq-entrypoint in go
This allowed for deleting all script utilities and environment variable logic.
This commit is contained in:
parent
2200d85992
commit
03618ba72c
16
default.nix
16
default.nix
@ -57,9 +57,11 @@ in rec {
|
|||||||
|
|
||||||
entrypoint = pkgs.callPackage ./entrypoint {};
|
entrypoint = pkgs.callPackage ./entrypoint {};
|
||||||
|
|
||||||
dnsmasq = (pkgs.callPackage ./dnsmasq {
|
dnsmasq = (pkgs.callPackage ./nix/dnsmasq.nix {
|
||||||
glibcStatic = pkgs.glibc.static;
|
glibcStatic = pkgs.glibc.static;
|
||||||
}).env;
|
});
|
||||||
|
|
||||||
|
nebula = pkgs.callPackage ./nix/nebula.nix {};
|
||||||
|
|
||||||
garage = (pkgs.callPackage ./nix/garage.nix {}).env;
|
garage = (pkgs.callPackage ./nix/garage.nix {}).env;
|
||||||
|
|
||||||
@ -69,18 +71,10 @@ in rec {
|
|||||||
name = "cryptic-net-AppDir";
|
name = "cryptic-net-AppDir";
|
||||||
paths = [
|
paths = [
|
||||||
|
|
||||||
pkgs.pkgsStatic.bash
|
|
||||||
pkgs.pkgsStatic.coreutils
|
|
||||||
pkgs.pkgsStatic.gnutar
|
|
||||||
pkgs.pkgsStatic.gzip
|
|
||||||
|
|
||||||
# custom packages from ./pkgs.nix
|
|
||||||
pkgs.yq-go
|
|
||||||
pkgs.nebula
|
|
||||||
|
|
||||||
./AppDir
|
./AppDir
|
||||||
version
|
version
|
||||||
dnsmasq
|
dnsmasq
|
||||||
|
nebula
|
||||||
garage
|
garage
|
||||||
entrypoint
|
entrypoint
|
||||||
|
|
||||||
|
@ -1,36 +0,0 @@
|
|||||||
# TODO implement this in go
|
|
||||||
|
|
||||||
set -e -o pipefail
|
|
||||||
cd "$APPDIR"
|
|
||||||
|
|
||||||
conf_path="$_RUNTIME_DIR_PATH"/dnsmasq.conf
|
|
||||||
|
|
||||||
cat etc/dnsmasq/base.conf > "$conf_path"
|
|
||||||
|
|
||||||
tmp="$(mktemp -d -t cryptic-net-dnsmasq-entrypoint-XXX)"
|
|
||||||
|
|
||||||
( trap "rm -rf '$tmp'" EXIT
|
|
||||||
|
|
||||||
tar xzf "$_BOOTSTRAP_PATH" -C "$tmp" ./hosts
|
|
||||||
|
|
||||||
thisHostName=$(tar xzf "$_BOOTSTRAP_PATH" --to-stdout ./hostname)
|
|
||||||
thisHostIP=$(cat "$tmp"/hosts/"$thisHostName".yml | yq '.nebula.ip')
|
|
||||||
|
|
||||||
domain=$(tar xzf "$_BOOTSTRAP_PATH" --to-stdout ./admin/creation-params.yml | yq '.domain')
|
|
||||||
|
|
||||||
echo "listen-address=$thisHostIP" >> "$conf_path"
|
|
||||||
|
|
||||||
ls -1 "$tmp"/hosts | while read hostYml; do
|
|
||||||
|
|
||||||
hostName=$(echo "$hostYml" | cut -d. -f1)
|
|
||||||
hostIP=$(cat "$tmp"/hosts/"$hostYml" | yq '.nebula.ip')
|
|
||||||
echo "address=/${hostName}.hosts.$domain/$hostIP" >> "$conf_path"
|
|
||||||
|
|
||||||
done
|
|
||||||
)
|
|
||||||
|
|
||||||
cat "$_DAEMON_YML_PATH" | \
|
|
||||||
yq '.dns.resolvers | .[] | "server=" + .' \
|
|
||||||
>> "$conf_path"
|
|
||||||
|
|
||||||
exec bin/dnsmasq -d -C "$conf_path"
|
|
@ -1,39 +0,0 @@
|
|||||||
{
|
|
||||||
|
|
||||||
stdenv,
|
|
||||||
buildEnv,
|
|
||||||
glibcStatic,
|
|
||||||
rebase,
|
|
||||||
|
|
||||||
}: rec {
|
|
||||||
|
|
||||||
dnsmasq = stdenv.mkDerivation rec {
|
|
||||||
pname = "dnsmasq";
|
|
||||||
version = "2.85";
|
|
||||||
|
|
||||||
src = builtins.fetchurl {
|
|
||||||
url = "https://www.thekelleys.org.uk/dnsmasq/${pname}-${version}.tar.xz";
|
|
||||||
sha256 = "sha256-rZjTgD32h+W5OAgPPSXGKP5ByHh1LQP7xhmXh/7jEvo=";
|
|
||||||
};
|
|
||||||
|
|
||||||
nativeBuildInputs = [ glibcStatic ];
|
|
||||||
|
|
||||||
makeFlags = [
|
|
||||||
"LDFLAGS=-static"
|
|
||||||
"DESTDIR="
|
|
||||||
"BINDIR=$(out)/bin"
|
|
||||||
"MANDIR=$(out)/man"
|
|
||||||
"LOCALEDIR=$(out)/share/locale"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
env = buildEnv {
|
|
||||||
name = "cryptic-net-dnsmasq";
|
|
||||||
paths = [
|
|
||||||
(rebase "cryptic-net-dnsmasq-bin" ./bin "bin")
|
|
||||||
(rebase "cryptic-net-dnsmasq-etc" ./etc "etc/dnsmasq")
|
|
||||||
dnsmasq
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
@ -1,41 +0,0 @@
|
|||||||
# Configuration file for dnsmasq.
|
|
||||||
#
|
|
||||||
# Format is one option per line, legal options are the same
|
|
||||||
# as the long options legal on the command line. See
|
|
||||||
# "/usr/sbin/dnsmasq --help" or "man 8 dnsmasq" for details.
|
|
||||||
|
|
||||||
# Listen on this specific port instead of the standard DNS port
|
|
||||||
# (53). Setting this to zero completely disables DNS function,
|
|
||||||
# leaving only DHCP and/or TFTP.
|
|
||||||
port=53
|
|
||||||
|
|
||||||
# If you don't want dnsmasq to read /etc/resolv.conf or any other
|
|
||||||
# file, getting its servers from this file instead (see below), then
|
|
||||||
# uncomment this.
|
|
||||||
no-resolv
|
|
||||||
|
|
||||||
# On systems which support it, dnsmasq binds the wildcard address,
|
|
||||||
# even when it is listening on only some interfaces. It then discards
|
|
||||||
# requests that it shouldn't reply to. This has the advantage of
|
|
||||||
# working even when interfaces come and go and change address. If you
|
|
||||||
# want dnsmasq to really bind only the interfaces it is listening on,
|
|
||||||
# uncomment this option. About the only time you may need this is when
|
|
||||||
# running another nameserver on the same machine.
|
|
||||||
bind-interfaces
|
|
||||||
|
|
||||||
# If you don't want dnsmasq to read /etc/hosts, uncomment the
|
|
||||||
# following line.
|
|
||||||
no-hosts
|
|
||||||
|
|
||||||
# Unset user and group so that dnsmasq doesn't drop privileges to another user.
|
|
||||||
# If this isn't done then dnsmasq fails to start up, since it fails to access
|
|
||||||
# /etc/passwd correctly, probably due to nix.
|
|
||||||
user=
|
|
||||||
group=
|
|
||||||
|
|
||||||
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
||||||
#
|
|
||||||
# Everything below is generated dynamically based on runtime configuration
|
|
||||||
#
|
|
||||||
# !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
||||||
|
|
@ -94,16 +94,14 @@ func runDaemonPmuxOnce(env crypticnet.Env) (crypticnet.Env, error) {
|
|||||||
return crypticnet.Env{}, fmt.Errorf("generating nebula config: %w", err)
|
return crypticnet.Env{}, fmt.Errorf("generating nebula config: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dnsmasqPmuxProcConfig, err := dnsmasqPmuxProcConfig(env)
|
||||||
|
if err != nil {
|
||||||
|
return crypticnet.Env{}, fmt.Errorf("generating dnsmasq config: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
pmuxProcConfigs := []pmuxlib.ProcessConfig{
|
pmuxProcConfigs := []pmuxlib.ProcessConfig{
|
||||||
nebulaPmuxProcConfig,
|
nebulaPmuxProcConfig,
|
||||||
{
|
dnsmasqPmuxProcConfig,
|
||||||
Name: "dnsmasq",
|
|
||||||
Cmd: "bash",
|
|
||||||
Args: []string{"dnsmasq-entrypoint"},
|
|
||||||
StartAfterFunc: func(ctx context.Context) error {
|
|
||||||
return waitForNebula(ctx, env)
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(thisDaemon.Storage.Allocations) > 0 {
|
if len(thisDaemon.Storage.Allocations) > 0 {
|
||||||
@ -302,15 +300,6 @@ var subCmdDaemon = subCmd{
|
|||||||
return fmt.Errorf("merging daemon.yml into bootstrap data: %w", err)
|
return fmt.Errorf("merging daemon.yml into bootstrap data: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO once dnsmasq entrypoint is written in go and the config
|
|
||||||
// generation is inlined into this process then this Setenv won't be
|
|
||||||
// necessary.
|
|
||||||
for key, val := range env.ToMap() {
|
|
||||||
if err := os.Setenv(key, val); err != nil {
|
|
||||||
return fmt.Errorf("failed to set %q to %q: %w", key, val, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for {
|
for {
|
||||||
|
|
||||||
if env, err = runDaemonPmuxOnce(env); errors.Is(err, context.Canceled) {
|
if env, err = runDaemonPmuxOnce(env); errors.Is(err, context.Canceled) {
|
||||||
|
45
entrypoint/src/cmd/entrypoint/dnsmasq_util.go
Normal file
45
entrypoint/src/cmd/entrypoint/dnsmasq_util.go
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
crypticnet "cryptic-net"
|
||||||
|
"cryptic-net/bootstrap"
|
||||||
|
"cryptic-net/dnsmasq"
|
||||||
|
"fmt"
|
||||||
|
"path/filepath"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"code.betamike.com/cryptic-io/pmux/pmuxlib"
|
||||||
|
)
|
||||||
|
|
||||||
|
func dnsmasqPmuxProcConfig(env crypticnet.Env) (pmuxlib.ProcessConfig, error) {
|
||||||
|
|
||||||
|
thisDaemon := env.ThisDaemon()
|
||||||
|
|
||||||
|
confPath := filepath.Join(env.RuntimeDirPath, "dnsmasq.conf")
|
||||||
|
|
||||||
|
hostsSlice := make([]bootstrap.Host, 0, len(env.Bootstrap.Hosts))
|
||||||
|
for _, host := range env.Bootstrap.Hosts {
|
||||||
|
hostsSlice = append(hostsSlice, host)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(hostsSlice, func(i, j int) bool {
|
||||||
|
return hostsSlice[i].Nebula.IP < hostsSlice[j].Nebula.IP
|
||||||
|
})
|
||||||
|
|
||||||
|
confData := dnsmasq.ConfData{
|
||||||
|
Resolvers: thisDaemon.DNS.Resolvers,
|
||||||
|
Domain: env.Bootstrap.AdminCreationParams.Domain,
|
||||||
|
IP: env.Bootstrap.ThisHost().Nebula.IP,
|
||||||
|
Hosts: hostsSlice,
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := dnsmasq.WriteConfFile(confPath, confData); err != nil {
|
||||||
|
return pmuxlib.ProcessConfig{}, fmt.Errorf("writing dnsmasq.conf to %q: %w", confPath, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return pmuxlib.ProcessConfig{
|
||||||
|
Name: "dnsmasq",
|
||||||
|
Cmd: "dnsmasq",
|
||||||
|
Args: []string{"-d", "-C", confPath},
|
||||||
|
}, nil
|
||||||
|
}
|
3
entrypoint/src/dnsmasq/dnsmasq.go
Normal file
3
entrypoint/src/dnsmasq/dnsmasq.go
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
// Package dnsmasq contains helper functions and types which are useful for
|
||||||
|
// setting up dnsmasq configs, processes, and deployments.
|
||||||
|
package dnsmasq
|
58
entrypoint/src/dnsmasq/tpl.go
Normal file
58
entrypoint/src/dnsmasq/tpl.go
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
package dnsmasq
|
||||||
|
|
||||||
|
import (
|
||||||
|
"cryptic-net/bootstrap"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"text/template"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ConfData describes all the data needed to populate a dnsmasq.conf file.
|
||||||
|
type ConfData struct {
|
||||||
|
Resolvers []string
|
||||||
|
Domain string
|
||||||
|
IP string
|
||||||
|
Hosts []bootstrap.Host
|
||||||
|
}
|
||||||
|
|
||||||
|
var confTpl = template.Must(template.New("").Parse(`
|
||||||
|
port=53
|
||||||
|
|
||||||
|
bind-interfaces
|
||||||
|
listen-address={{ .IP }}
|
||||||
|
|
||||||
|
no-resolv
|
||||||
|
no-hosts
|
||||||
|
|
||||||
|
user=
|
||||||
|
group=
|
||||||
|
|
||||||
|
{{- range $host := .Hosts }}
|
||||||
|
address=/{{ $host.Name }}.hosts.{{ .Domain }}/{{ $host.Nebula.IP }}
|
||||||
|
{{ end -}}
|
||||||
|
|
||||||
|
{{- range .Resolvers }}
|
||||||
|
server={{ . }}
|
||||||
|
{{ end -}}
|
||||||
|
`))
|
||||||
|
|
||||||
|
// WriteConfFile renders a dnsmasq.conf using the given data to a new
|
||||||
|
// file at the given path.
|
||||||
|
func WriteConfFile(path string, data ConfData) error {
|
||||||
|
|
||||||
|
file, err := os.OpenFile(
|
||||||
|
path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0640,
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("creating file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
if err := confTpl.Execute(file, data); err != nil {
|
||||||
|
return fmt.Errorf("rendering template to file: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
@ -19,14 +19,6 @@ import (
|
|||||||
"github.com/adrg/xdg"
|
"github.com/adrg/xdg"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Names of various environment variables which get set by the entrypoint.
|
|
||||||
const (
|
|
||||||
DaemonYmlPathEnvVar = "_DAEMON_YML_PATH"
|
|
||||||
BootstrapPathEnvVar = "_BOOTSTRAP_PATH"
|
|
||||||
RuntimeDirPathEnvVar = "_RUNTIME_DIR_PATH"
|
|
||||||
DataDirPathEnvVar = "_DATA_DIR_PATH"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Env contains the values of environment variables, as well as other entities
|
// Env contains the values of environment variables, as well as other entities
|
||||||
// which are useful across all processes.
|
// which are useful across all processes.
|
||||||
type Env struct {
|
type Env struct {
|
||||||
@ -74,40 +66,6 @@ func NewEnv(bootstrapOptional bool) (Env, error) {
|
|||||||
return env.init(bootstrapOptional)
|
return env.init(bootstrapOptional)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ReadEnv reads an Env from the process's environment variables, rather than
|
|
||||||
// calculating like NewEnv does.
|
|
||||||
func ReadEnv() (Env, error) {
|
|
||||||
|
|
||||||
var err error
|
|
||||||
|
|
||||||
readEnv := func(key string) string {
|
|
||||||
if err != nil {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
val := os.Getenv(key)
|
|
||||||
|
|
||||||
if val == "" {
|
|
||||||
err = fmt.Errorf("envvar %q not set", key)
|
|
||||||
}
|
|
||||||
|
|
||||||
return val
|
|
||||||
}
|
|
||||||
|
|
||||||
env := Env{
|
|
||||||
AppDirPath: getAppDirPath(),
|
|
||||||
DaemonYmlPath: readEnv(DaemonYmlPathEnvVar),
|
|
||||||
RuntimeDirPath: readEnv(RuntimeDirPathEnvVar),
|
|
||||||
DataDirPath: readEnv(DataDirPathEnvVar),
|
|
||||||
}
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return Env{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
return env.init(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
// DataDirBootstrapPath returns the path to the bootstrap file within the user's
|
// DataDirBootstrapPath returns the path to the bootstrap file within the user's
|
||||||
// data dir. If the file does not exist there it will be found in the AppDirPath
|
// data dir. If the file does not exist there it will be found in the AppDirPath
|
||||||
// by ReloadBootstrap.
|
// by ReloadBootstrap.
|
||||||
@ -197,17 +155,6 @@ func (e Env) init(bootstrapOptional bool) (Env, error) {
|
|||||||
return e.initBootstrap(bootstrapOptional)
|
return e.initBootstrap(bootstrapOptional)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ToMap returns the Env as a map of key/value strings. If this map is set into
|
|
||||||
// a process's environment, then that process can read it back using ReadEnv.
|
|
||||||
func (e Env) ToMap() map[string]string {
|
|
||||||
return map[string]string{
|
|
||||||
DaemonYmlPathEnvVar: e.DaemonYmlPath,
|
|
||||||
BootstrapPathEnvVar: e.BootstrapPath,
|
|
||||||
RuntimeDirPathEnvVar: e.RuntimeDirPath,
|
|
||||||
DataDirPathEnvVar: e.DataDirPath,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// ThisDaemon returns the DaemonYml (loaded from DaemonYmlPath) for the
|
// ThisDaemon returns the DaemonYml (loaded from DaemonYmlPath) for the
|
||||||
// currently running process.
|
// currently running process.
|
||||||
func (e Env) ThisDaemon() DaemonYml {
|
func (e Env) ThisDaemon() DaemonYml {
|
||||||
|
@ -67,9 +67,7 @@ func WriteGarageTomlFile(path string, data GarageTomlData) error {
|
|||||||
|
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
err = RenderGarageToml(file, data)
|
if err := garageTomlTpl.Execute(file, data); err != nil {
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("rendering template to file: %w", err)
|
return fmt.Errorf("rendering template to file: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
25
nix/dnsmasq.nix
Normal file
25
nix/dnsmasq.nix
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
{
|
||||||
|
|
||||||
|
stdenv,
|
||||||
|
glibcStatic,
|
||||||
|
|
||||||
|
}: stdenv.mkDerivation rec {
|
||||||
|
|
||||||
|
pname = "dnsmasq";
|
||||||
|
version = "2.85";
|
||||||
|
|
||||||
|
src = builtins.fetchurl {
|
||||||
|
url = "https://www.thekelleys.org.uk/dnsmasq/${pname}-${version}.tar.xz";
|
||||||
|
sha256 = "sha256-rZjTgD32h+W5OAgPPSXGKP5ByHh1LQP7xhmXh/7jEvo=";
|
||||||
|
};
|
||||||
|
|
||||||
|
nativeBuildInputs = [ glibcStatic ];
|
||||||
|
|
||||||
|
makeFlags = [
|
||||||
|
"LDFLAGS=-static"
|
||||||
|
"DESTDIR="
|
||||||
|
"BINDIR=$(out)/bin"
|
||||||
|
"MANDIR=$(out)/man"
|
||||||
|
"LOCALEDIR=$(out)/share/locale"
|
||||||
|
];
|
||||||
|
}
|
13
nix/pkgs.nix
13
nix/pkgs.nix
@ -2,7 +2,8 @@ rec {
|
|||||||
|
|
||||||
overlays = [
|
overlays = [
|
||||||
|
|
||||||
# Make both buildGoModules use static compilation by default.
|
# Make buildGoModules use static compilation by default, and use go 1.18
|
||||||
|
# everywhere.
|
||||||
(final: prev:
|
(final: prev:
|
||||||
|
|
||||||
let
|
let
|
||||||
@ -17,19 +18,11 @@ rec {
|
|||||||
in {
|
in {
|
||||||
|
|
||||||
go = prev.go_1_18;
|
go = prev.go_1_18;
|
||||||
buildGoModule = args: prev.buildGoModule (buildArgs // args);
|
buildGoModule = args: prev.buildGo118Module (buildArgs // args);
|
||||||
buildGo118Module = args: prev.buildGo118Module (buildArgs // args);
|
buildGo118Module = args: prev.buildGo118Module (buildArgs // args);
|
||||||
|
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
(final: prev: { rebase = prev.callPackage ./rebase.nix {}; })
|
|
||||||
(final: prev: { yq-go = prev.callPackage ./yq-go.nix {}; })
|
|
||||||
|
|
||||||
(final: prev: { nebula = prev.callPackage ./nebula.nix {
|
|
||||||
buildGoModule = prev.buildGo118Module;
|
|
||||||
}; })
|
|
||||||
|
|
||||||
];
|
];
|
||||||
|
|
||||||
version = "22-05";
|
version = "22-05";
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
# rebase is a helper which takes all files/dirs under oldroot, and
|
|
||||||
# creates a new derivation with those files/dirs copied under newroot
|
|
||||||
# (where newroot is a relative path to the root of the derivation).
|
|
||||||
|
|
||||||
{
|
|
||||||
|
|
||||||
stdenv,
|
|
||||||
|
|
||||||
}: name: oldroot: newroot: stdenv.mkDerivation {
|
|
||||||
|
|
||||||
inherit name oldroot newroot;
|
|
||||||
|
|
||||||
builder = builtins.toFile "builder.sh" ''
|
|
||||||
source $stdenv/setup
|
|
||||||
mkdir -p "$out"/"$newroot"
|
|
||||||
cp -rL "$oldroot"/* "$out"/"$newroot"
|
|
||||||
'';
|
|
||||||
}
|
|
@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
|
|
||||||
buildGoModule,
|
|
||||||
fetchFromGitHub,
|
|
||||||
|
|
||||||
}: buildGoModule rec {
|
|
||||||
|
|
||||||
pname = "yq-go";
|
|
||||||
version = "4.21.1";
|
|
||||||
|
|
||||||
src = fetchFromGitHub {
|
|
||||||
owner = "mikefarah";
|
|
||||||
repo = "yq";
|
|
||||||
rev = "v${version}";
|
|
||||||
sha256 = "sha256-283xe7FVHYSsRl4cZD7WDzIW1gqNAFsNrWYJkthZheU=";
|
|
||||||
};
|
|
||||||
|
|
||||||
vendorSha256 = "sha256-F11FnDYJ59aKrdRXDPpKlhX52yQXdaN1sblSkVI2j9w=";
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user