Skip to content

Commit

Permalink
fix(action): check for existing non-empty output directory
Browse files Browse the repository at this point in the history
- it can easily happen that the output directory will be empty, for
  example when build succeeds but the output files are not found
- with this patch, to skip the build the directory can exists, but must
  not be empty

Signed-off-by: AtomicFS <vojtech.vesely@9elements.com>
  • Loading branch information
AtomicFS committed Dec 11, 2024
1 parent 04337ab commit f1f9418
Show file tree
Hide file tree
Showing 2 changed files with 35 additions and 2 deletions.
26 changes: 25 additions & 1 deletion action/recipes/recipes.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"context"
"errors"
"fmt"
"io"
"log/slog"
"os"
"path/filepath"
Expand Down Expand Up @@ -140,6 +141,25 @@ func Build(
return builds, err
}

// IsDirEmpty returns whether given directory is empty or not
func IsDirEmpty(path string) (bool, error) {
// Source: https://stackoverflow.com/questions/30697324/how-to-check-if-directory-on-path-is-empty
f, err := os.Open(path)
if err != nil {
return false, err
}
defer f.Close()

// File.Readdirnames() take a parameter which is used to limit the number of returned values
// It is enough to query only 1 child
// File.Readdirnames() is faster than File.Readdir()
_, err = f.Readdirnames(1)
if err == io.EOF {
return true, nil
}
return false, err // Either not empty or error, suits both cases
}

// Execute a build step
func Execute(ctx context.Context, target string, config *Config, interactive bool) error {
// Setup dagger client
Expand All @@ -152,8 +172,12 @@ func Execute(ctx context.Context, target string, config *Config, interactive boo
// Find requested target
modules := config.AllModules()
if _, ok := modules[target]; ok {
if _, err := os.Stat(modules[target].GetOutputDir()); err == nil {
// Check if output directory already exist
// We want to skip build if the output directory exists and is not empty
// If it is empty, then just continue with the building
_, errExists := os.Stat(modules[target].GetOutputDir())
empty, _ := IsDirEmpty(modules[target].GetOutputDir())
if errExists == nil && !empty {
slog.Warn(fmt.Sprintf("Output directory for '%s' already exists, skipping build", target))
return ErrBuildSkipped
}
Expand Down
11 changes: 10 additions & 1 deletion action/recipes/recipes_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package recipes
import (
"context"
"os"
"path/filepath"
"testing"

"dagger.io/dagger"
Expand Down Expand Up @@ -95,10 +96,18 @@ func TestExecuteSkipAndMissing(t *testing.T) {
assert.ErrorIs(t, err, ErrDependencyOutputMissing)

// Create the output directory
// Should build because the directory is empty
err = os.Mkdir(outputDir, os.ModePerm)
assert.NoError(t, err)
err = Execute(ctx, target, &myConfig, interactive)
assert.ErrorIs(t, err, ErrDependencyOutputMissing)

// Create file inside output directory
myfile, err := os.Create(filepath.Join(outputDir, "dummy.txt"))
assert.NoError(t, err)
myfile.Close()

// Since there is now existing output directory, it should skip the build
// Since there is now existing non-empty output directory, it should skip the build
err = Execute(ctx, target, &myConfig, interactive)
assert.ErrorIs(t, err, ErrBuildSkipped)
}
Expand Down

0 comments on commit f1f9418

Please sign in to comment.