Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: option to auto-format Nix output using nixfmt #33

Merged
merged 2 commits into from
Aug 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 20 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,26 @@ jobs:
go-version: '>=1.21'
- run: go version
- name: Test
run: go test -v -covermode=count -coverprofile=coverage.out
run: make coverage
- name: Upload coverage
uses: codecov/codecov-action@v3
uses: codecov/codecov-action@v4
with:
fail_ci_if_error: true
files: coverage.out
token: ${{ secrets.CODECOV_TOKEN }}
linux-test-with-nixfmt:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: cachix/install-nix-action@v22
with:
github_access_token: ${{ secrets.GITHUB_TOKEN }}
nix_path: nixpkgs=channel:nixos-unstable
- uses: DeterminateSystems/magic-nix-cache-action@main
- name: Run tests with nixfmt
run: nix develop --command make coverage
- name: Upload coverage
uses: codecov/codecov-action@v4
with:
fail_ci_if_error: true
files: coverage.out
Expand All @@ -37,3 +54,4 @@ jobs:
nix_path: nixpkgs=channel:nixos-unstable
- uses: DeterminateSystems/magic-nix-cache-action@main
- run: make flake

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,4 @@ out.nix*
*.env
result
.pre-commit-config.yaml
coverage.out
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ test:
go test -v

coverage:
go test -v -covermode=count
go test -v -covermode=count -coverprofile=coverage.out

flake:
nix build -L .#packages.x86_64-linux.default
Expand Down
42 changes: 22 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -209,46 +209,48 @@ Discussion: https://github.com/aksiksi/compose2nix/issues/24
```
$ compose2nix -h
Usage of compose2nix:
-auto_format
if true, Nix output will be formatted using "nixfmt" (must be present in $PATH).
-auto_start
auto-start setting for generated service(s). this applies to all services, not just containers. (default true)
auto-start setting for generated service(s). this applies to all services, not just containers. (default true)
-check_systemd_mounts
if set, volume paths will be checked against systemd mount paths on the current machine and marked as container dependencies.
if set, volume paths will be checked against systemd mount paths on the current machine and marked as container dependencies.
-create_root_target
if set, a root systemd target will be created, which when stopped tears down all resources. (default true)
if set, a root systemd target will be created, which when stopped tears down all resources. (default true)
-default_stop_timeout duration
default stop timeout for generated container services. (default 1m30s)
default stop timeout for generated container services. (default 1m30s)
-env_files string
one or more comma-separated paths to .env file(s).
one or more comma-separated paths to .env file(s).
-env_files_only
only use env file(s) in the NixOS container definitions.
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.
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.
if set, missing env files will be ignored.
-include_env_files
include env files in the NixOS container definition.
include env files in the NixOS container definition.
-inputs string
one or more comma-separated path(s) to Compose file(s). (default "docker-compose.yml")
one or more comma-separated path(s) to Compose file(s). (default "docker-compose.yml")
-output string
path to output Nix file. (default "docker-compose.nix")
path to output Nix file. (default "docker-compose.nix")
-project string
project name used as a prefix for generated resources. this overrides any top-level "name" set in the Compose file(s).
project name used as a prefix for generated resources. this overrides any top-level "name" set in the Compose file(s).
-remove_volumes
if set, volumes will be removed on systemd service stop.
if set, volumes will be removed on systemd service stop.
-root_path string
root path to use for any relative paths in the Compose file (e.g., volumes). if unset, the current working directory will be used.
root path to use for any relative paths in the Compose file (e.g., volumes). if unset, the current working directory will be used.
-runtime string
one of: ["podman", "docker"]. (default "podman")
one of: ["podman", "docker"]. (default "podman")
-service_include string
regex pattern for services to include.
regex pattern for services to include.
-use_compose_log_driver
if set, always use the Docker Compose log driver.
if set, always use the Docker Compose log driver.
-use_upheld_by
if set, upheldBy will be used for service dependencies (NixOS 24.05+).
if set, upheldBy will be used for service dependencies (NixOS 24.05+).
-version
display version and exit
display version and exit
-write_nix_setup
if true, Nix setup code is written to output (runtime, DNS, autoprune, etc.) (default true)
if true, Nix setup code is written to output (runtime, DNS, autoprune, etc.) (default true)
```

### Supported Docker Compose Features
Expand Down
2 changes: 2 additions & 0 deletions compose.go
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ type Generator struct {
UseUpheldBy bool
RemoveVolumes bool
NoCreateRootTarget bool
AutoFormat bool
WriteHeader bool
NoWriteNixSetup bool
DefaultStopTimeout time.Duration
Expand Down Expand Up @@ -164,6 +165,7 @@ func (g *Generator) Run(ctx context.Context) (*NixContainerConfig, error) {
CreateRootTarget: !g.NoCreateRootTarget,
AutoStart: g.AutoStart,
WriteNixSetup: !g.NoWriteNixSetup,
AutoFormat: g.AutoFormat,
}, nil
}

Expand Down
2 changes: 1 addition & 1 deletion flake.nix
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
devShells = forAllSystems (system:
let pkgs = pkgsFor system; in {
default = pkgs.mkShell {
buildInputs = [ pkgs.go pkgs.gopls ];
buildInputs = [ pkgs.go pkgs.gopls pkgs.nixfmt-rfc-style ];
# Add a Git pre-commit hook.
shellHook = onchg.shellHook.${system};
};
Expand Down
35 changes: 35 additions & 0 deletions helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"log"
"os"
"os/exec"
"slices"
"strings"
)
Expand Down Expand Up @@ -61,3 +62,37 @@ func ReadEnvFiles(envFiles []string, mergeWithEnv, ignoreMissing bool) (env []st

return env, nil
}

// formatNixCode will format Nix code by calling 'nixfmt' and passing in the
// given code via stdin.
func formatNixCode(contents []byte) ([]byte, error) {
// Check for existence of 'nixfmt' in $PATH.
nixfmtPath, err := exec.LookPath("nixfmt")
if err != nil {
return nil, fmt.Errorf("'nixfmt' not found in $PATH: %w", err)
}

cmd := exec.Command(nixfmtPath)

stdin, err := cmd.StdinPipe()
if err != nil {
return nil, fmt.Errorf("failed to setup stdin pipe: %w", err)
}
if err := func() error {
defer stdin.Close()
if _, err := stdin.Write(contents); err != nil {
return fmt.Errorf("failed to write content to stdin: %w", err)
}
return nil
}(); err != nil {
return nil, err
}

// Overwrite contents with formatted output.
contents, err = cmd.Output()
if err != nil {
return nil, fmt.Errorf("failed to run 'nixfmt' on contents: %w", err)
}

return contents, nil
}
25 changes: 16 additions & 9 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ var removeVolumes = flag.Bool("remove_volumes", false, "if set, volumes will be
var createRootTarget = flag.Bool("create_root_target", true, "if set, a root systemd target will be created, which when stopped tears down all resources.")
var defaultStopTimeout = flag.Duration("default_stop_timeout", defaultSystemdStopTimeout, "default stop timeout for generated container services.")
var writeNixSetup = flag.Bool("write_nix_setup", true, "if true, Nix setup code is written to output (runtime, DNS, autoprune, etc.)")
var autoFormat = flag.Bool("auto_format", false, `if true, Nix output will be formatted using "nixfmt" (must be present in $PATH).`)
var version = flag.Bool("version", false, "display version and exit")

func main() {
Expand All @@ -47,6 +48,9 @@ func main() {
fmt.Printf("compose2nix v%s\n", appVersion)
return
}
if *output == "" {
log.Fatal("No output path specified.")
}

ctx := context.Background()

Expand Down Expand Up @@ -95,6 +99,7 @@ func main() {
NoCreateRootTarget: !*createRootTarget,
WriteHeader: true,
NoWriteNixSetup: !*writeNixSetup,
AutoFormat: *autoFormat,
DefaultStopTimeout: *defaultStopTimeout,
}
containerConfig, err := g.Run(ctx)
Expand All @@ -103,14 +108,16 @@ func main() {
}
fmt.Printf("Generated NixOS config in %v\n", time.Since(start))

if *output != "" {
dir := path.Dir(*output)
if _, err := os.Stat(dir); err != nil {
log.Fatalf("Directory %q does not exist", dir)
}
if err := os.WriteFile(*output, []byte(containerConfig.String()), 0644); err != nil {
log.Fatal(err)
}
fmt.Printf("Wrote NixOS config to %s\n", *output)
dir := path.Dir(*output)
if _, err := os.Stat(dir); err != nil {
log.Fatalf("Directory %q does not exist: %v", dir, err)
}
f, err := os.Create(*output)
if err != nil {
log.Fatalf("Failed to create file %q: %v", *output, err)
}
if err := containerConfig.Write(f); err != nil {
log.Fatal(err)
}
fmt.Printf("Wrote NixOS config to %s\n", *output)
}
25 changes: 25 additions & 0 deletions nix.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package main

import (
"fmt"
"io"
"strings"
"text/template"
)
Expand Down Expand Up @@ -203,6 +204,7 @@ type NixContainerConfig struct {
Volumes []*NixVolume
CreateRootTarget bool
WriteNixSetup bool
AutoFormat bool
AutoStart bool
}

Expand All @@ -221,6 +223,29 @@ func (c *NixContainerConfig) String() string {
return s.String()
}

// Write writes out the Nix config to the provided Writer.
//
// If the AutoFormat option on this struct is set to "true", this method will
// attempt to format the Nix config by calling "nixfmt" and passing in the
// fully built config via stdin.
func (c *NixContainerConfig) Write(out io.Writer) error {
config := []byte(c.String())

if c.AutoFormat {
formatted, err := formatNixCode(config)
if err != nil {
return err
}
config = formatted
}

if _, err := out.Write(config); err != nil {
return fmt.Errorf("failed to write Nix code: %w", err)
}

return nil
}

func rootTarget(runtime ContainerRuntime, project *Project) string {
return fmt.Sprintf("%s-compose-%s", runtime, project.With("root"))
}
Expand Down
23 changes: 22 additions & 1 deletion nix_test.go
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
package main

import (
"bytes"
"context"
"flag"
"fmt"
"os"
"os/exec"
"path"
"strings"
"testing"
Expand Down Expand Up @@ -39,7 +41,11 @@ func runSubtestsWithGenerator(t *testing.T, g *Generator) {
if err != nil {
t.Fatal(err)
}
got := c.String()
gotBuf := new(bytes.Buffer)
if err := c.Write(gotBuf); err != nil {
t.Fatal(err)
}
got := gotBuf.String()
if *update {
if err := os.WriteFile(outFilePath, []byte(got), 0644); err != nil {
t.Fatal(err)
Expand Down Expand Up @@ -68,6 +74,21 @@ func TestBasic(t *testing.T) {
runSubtestsWithGenerator(t, g)
}

func TestBasicAutoFormat(t *testing.T) {
if _, err := exec.LookPath("nixfmt"); err != nil {
t.Skip()
}
composePath, envFilePath := getPaths(t, true)
g := &Generator{
Inputs: []string{composePath},
EnvFiles: []string{envFilePath},
AutoStart: true,
GenerateUnusedResources: true,
AutoFormat: true,
}
runSubtestsWithGenerator(t, g)
}

func TestProject(t *testing.T) {
composePath, envFilePath := getPaths(t, true)
g := &Generator{
Expand Down
Loading
Loading