Repository: aksiksi/compose2nix (go)

Issue:
Question about agenix encrypted env files
Hello! 

I'm trying to follow your instructions for adding age encrypted env files.

I've created a file with in the format of a .env file and encrypted it with agenix in my flake dir.

Then I run `compose2nix -output default.nix --env_files=/run/agenix/tailscale-keys.env --include_env_files=true`
and I get this error: ` failed to open file /run/agenix/tailscale-keys.env: open /run/agenix/tailscale-keys.env: no such file or directory`. Referencing the path to the encrypted file make no sense either.

I'm guessing that nix needs to have a reference to that age file for it to be in the store and mounted under `/run/agenix`, however , I'm running `compose2nix` on another machine from the one running the container, and that "build machine" has no config to reference and mount the secret. Chicken-and-egg methinks.

I'm guessing that you could blindly reference the path to /run/agenix env file in the resulting .nix file, but not sure.

Ideas on how to proceed?

Pull request:
Flag to ignore missing env files
Fixes #8.

Patch:
diff --git a/Makefile b/Makefile
index 1d4114e..c1e9ba5 100644
--- a/Makefile
+++ b/Makefile
@@ -4,6 +4,9 @@ build:
 test:
 	go test -v
 
+coverage:
+	go test -v -covermode=count
+
 flake:
 	nix build -L .#packages.x86_64-linux.default
 
@@ -13,4 +16,4 @@ nixos-test:
 	./nixos-test/update.sh
 	nix build -L .#checks.x86_64-linux.integrationTest
 
-.PHONY: build flake nixos-test test
+.PHONY: build coverage flake nixos-test test
diff --git a/README.md b/README.md
index a659245..04559f9 100644
--- a/README.md
+++ b/README.md
@@ -174,6 +174,8 @@ Usage of compose2nix:
         only use env file(s) in the NixOS container definitions.
   -generate_unused_resources
         if set, unused resources (e.g., networks) will be generated even if no containers use them.
+  -ignore_missing_env_files
+        if set, missing env files will be ignored.
   -include_env_files
         include env files in the NixOS container definition.
   -inputs string
diff --git a/compose.go b/compose.go
index 507e0df..a1b6c3b 100644
--- a/compose.go
+++ b/compose.go
@@ -56,6 +56,7 @@ type Generator struct {
 	EnvFiles               []string
 	IncludeEnvFiles        bool
 	EnvFilesOnly           bool
+	IgnoreMissingEnvFiles  bool
 	ServiceInclude         *regexp.Regexp
 	AutoStart              bool
 	UseComposeLogDriver    bool
@@ -70,7 +71,7 @@ type Generator struct {
 }
 
 func (g *Generator) Run(ctx context.Context) (*NixContainerConfig, error) {
-	env, err := ReadEnvFiles(g.EnvFiles, !g.EnvFilesOnly)
+	env, err := ReadEnvFiles(g.EnvFiles, !g.EnvFilesOnly, g.IgnoreMissingEnvFiles)
 	if err != nil {
 		return nil, err
 	}
diff --git a/helpers.go b/helpers.go
index 2dc187c..a39b548 100644
--- a/helpers.go
+++ b/helpers.go
@@ -3,6 +3,7 @@ package main
 import (
 	"bufio"
 	"fmt"
+	"log"
 	"os"
 	"slices"
 	"strings"
@@ -35,13 +36,24 @@ func mapToRepeatedFlag(flagName string, m map[string]string) []string {
 	return flags
 }
 
-func ReadEnvFiles(envFiles []string, mergeWithEnv bool) (env []string, _ error) {
+// ReadEnvFiles reads the given set of env files into a list of KEY=VAL entries.
+//
+// If mergeWithEnv is set, the running env is merged with the provided env files. Any
+// duplicate variables will be overridden by the running env.
+//
+// If ignoreMissing is set, any missing env files will be ignored. This is useful for cases
+// where an env file is not available during conversion to Nix.
+func ReadEnvFiles(envFiles []string, mergeWithEnv, ignoreMissing bool) (env []string, _ error) {
 	for _, p := range envFiles {
 		if strings.TrimSpace(p) == "" {
 			continue
 		}
 		f, err := os.Open(p)
 		if err != nil {
+			if ignoreMissing {
+				log.Printf("Ignoring mising env file %s...", p)
+				continue
+			}
 			return nil, fmt.Errorf("failed to open file %s: %w", p, err)
 		}
 		defer f.Close()
diff --git a/main.go b/main.go
index 8f6b51c..632021b 100644
--- a/main.go
+++ b/main.go
@@ -23,6 +23,7 @@ var inputs = flag.String("inputs", "docker-compose.yml", "one or more comma-sepa
 var envFiles = flag.String("env_files", "", "one or more comma-separated paths to .env file(s).")
 var includeEnvFiles = flag.Bool("include_env_files", false, "include env files in the NixOS container definition.")
 var envFilesOnly = flag.Bool("env_files_only", false, "only use env file(s) in the NixOS container definitions.")
+var ignoreMissingEnvFiles = flag.Bool("ignore_missing_env_files", false, "if set, missing env files will be ignored.")
 var output = flag.String("output", "docker-compose.nix", "path to output Nix file.")
 var project = flag.String("project", "", "project name used as a prefix for generated resources. this overrides any top-level \"name\" set in the Compose file(s).")
 var serviceInclude = flag.String("service_include", "", "regex pattern for services to include.")
@@ -78,6 +79,7 @@ func main() {
 		EnvFiles:               envFiles,
 		IncludeEnvFiles:        *includeEnvFiles,
 		EnvFilesOnly:           *envFilesOnly,
+		IgnoreMissingEnvFiles:  *ignoreMissingEnvFiles,
 		ServiceInclude:         serviceIncludeRegexp,
 		AutoStart:              *autoStart,
 		UseComposeLogDriver:    *useComposeLogDriver,


Test patch:
diff --git a/nix_test.go b/nix_test.go
index 820a71a..b439a7c 100644
--- a/nix_test.go
+++ b/nix_test.go
@@ -299,3 +299,19 @@ func TestDocker_EnvFilesOnly(t *testing.T) {
 		t.Errorf("output diff: %s\n", diff)
 	}
 }
+
+func TestDocker_IgnoreMissingEnvFiles(t *testing.T) {
+	ctx := context.Background()
+	composePath, envFilePath, _ := getPaths(t)
+	g := Generator{
+		Runtime:               ContainerRuntimeDocker,
+		Inputs:                []string{composePath},
+		EnvFiles:              []string{path.Join(t.TempDir(), "bad-path"), envFilePath},
+		IncludeEnvFiles:       true,
+		EnvFilesOnly:          true,
+		IgnoreMissingEnvFiles: true,
+	}
+	if _, err := g.Run(ctx); err != nil {
+		t.Fatal(err)
+	}
+}