Skip to content
Open
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
48 changes: 38 additions & 10 deletions cmd/earthly/app/run.go
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,6 @@ func (app *EarthlyApp) handleError(ctx context.Context, err error, args []string
hintErr.Hint(),
hintErr.Message(),
)
app.BaseCLI.Console().HelpPrint(hintErr.Hint())

return 1
case errors.As(err, &autoSkipErr):
Expand Down Expand Up @@ -218,7 +217,6 @@ func (app *EarthlyApp) handleError(ctx context.Context, err error, args []string

helpMsg = "Are you using --platform to target a different architecture? You may have to manually install QEMU.\n" +
"For more information see https://docs.earthbuild.dev/guides/multi-platform\n"
app.BaseCLI.Console().HelpPrint(helpMsg)
app.BaseCLI.Logbus().Run().SetGenericFatalError(
time.Now(),
logstream.FailureType_FAILURE_TYPE_OTHER,
Expand All @@ -236,7 +234,6 @@ func (app *EarthlyApp) handleError(ctx context.Context, err error, args []string
args = stringutil.FilterElementsFromList(args, "--ci")
msg := "To debug your build, you can use the --interactive (-i) flag to drop into a shell of the failing RUN step"
helpMsg = fmt.Sprintf("%s: %q\n", msg, strings.Join(args, " "))
app.BaseCLI.Console().HelpPrint(helpMsg)
}
// This error would have been displayed earlier from the SolverMonitor.
// This SetGenericFatalError is a catch-all just in case that hasn't happened.
Expand All @@ -249,14 +246,49 @@ func (app *EarthlyApp) handleError(ctx context.Context, err error, args []string

return 1
case strings.Contains(err.Error(), "security.insecure is not allowed"):
helpMsg := "earth --allow-privileged (earth -P) flag is required\n"
// Extract target info from error if available
targetInfo := ""

if ie != nil && isInterpreterError {
targetInfo = ie.TargetID
}

// If no target info from interpreter error, try to extract from args
if targetInfo == "" && len(args) > 1 {
Comment thread
kmannislands marked this conversation as resolved.
for _, arg := range args[1:] {
if strings.HasPrefix(arg, "+") {
targetInfo = arg
break
}
}
}

userMsg := "This build requires privileged mode."

if targetInfo != "" {
userMsg = "Target " + targetInfo + " requires privileged mode."
}

// Create help message with actual target if available
flagExample := "earth -P +your-target"

if targetInfo != "" {
flagExample = "earth -P " + targetInfo
}

helpMsg := "To fix this, use one of the following:\n" +
" • Run with the -P flag: " + flagExample + "\n" +
" • Set environment variable: export EARTHLY_ALLOW_PRIVILEGED=true\n" +
" • Add to config: earth config global.allow_privileged true"

app.BaseCLI.Logbus().Run().SetGenericFatalError(
time.Now(),
logstream.FailureType_FAILURE_TYPE_NEEDS_PRIVILEGED,
helpMsg,
err.Error(),
userMsg,
)
app.BaseCLI.Console().HelpPrint(helpMsg)

app.BaseCLI.Console().VerboseWarnf("Error: %s\n", err.Error())

return 9
case strings.Contains(err.Error(), errutil.EarthlyGitStdErrMagicString):
Expand All @@ -277,8 +309,6 @@ func (app *EarthlyApp) handleError(ctx context.Context, err error, args []string
app.BaseCLI.Console().VerboseWarnf("Error: %v\n", err.Error())
}

app.BaseCLI.Console().HelpPrint(helpMsg)

return 1
case strings.Contains(err.Error(), "failed to compute cache key") && strings.Contains(err.Error(), ": not found"):
matches := notFoundRegex.FindStringSubmatch(err.Error())
Expand Down Expand Up @@ -310,7 +340,6 @@ func (app *EarthlyApp) handleError(ctx context.Context, err error, args []string
helpMsg := fmt.Sprintf("%s responded with a rate limit error. This is usually because you are not logged in.\n"+
"You can login using the command:\n"+
" docker login%s", registryName, registryHost)
app.BaseCLI.Console().HelpPrint(helpMsg)
app.BaseCLI.Logbus().Run().SetGenericFatalError(
time.Now(),
logstream.FailureType_FAILURE_TYPE_RATE_LIMITED,
Expand Down Expand Up @@ -338,7 +367,6 @@ func (app *EarthlyApp) handleError(ctx context.Context, err error, args []string
"Verify your account to lift this restriction."
app.BaseCLI.Logbus().Run().
SetGenericFatalError(time.Now(), logstream.FailureType_FAILURE_TYPE_OTHER, helpMsg, grpcErr.Message())
app.BaseCLI.Console().HelpPrint(helpMsg)

return 1
case grpcErrOK && grpcErr.Code() != codes.Canceled:
Expand Down
68 changes: 68 additions & 0 deletions cmd/earthly/app/run_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package app

import (
"strings"
"testing"

"github.com/stretchr/testify/require"
Expand Down Expand Up @@ -43,3 +44,70 @@ func TestRedactSecretsFromArgs(t *testing.T) {
require.ElementsMatch(t, testCase.expected, actual)
}
}

func TestExtractTargetFromArgs(t *testing.T) {
t.Parallel()

for _, testCase := range []struct {
name string
expected string
args []string
}{
{
name: "single target",
args: []string{"earthly", "+target"},
expected: "+target",
},
{
name: "target with flags before",
args: []string{"earthly", "--verbose", "+build"},
expected: "+build",
},
{
name: "target with flags after",
args: []string{"earthly", "+test", "--ci"},
expected: "+test",
},
{
name: "multiple targets - returns first",
args: []string{"earthly", "+first", "+second"},
expected: "+first",
},
{
name: "no target",
args: []string{"earthly", "--version"},
expected: "",
},
{
name: "empty args",
args: []string{},
expected: "",
},
{
name: "only command",
args: []string{"earthly"},
expected: "",
},
{
name: "target-like string but not target",
args: []string{"earthly", "not-a-target", "+actual-target"},
expected: "+actual-target",
},
} {
t.Run(testCase.name, func(t *testing.T) {
t.Parallel()
// Simulate the target extraction logic from handleError
var targetInfo string
if len(testCase.args) > 1 {
for _, arg := range testCase.args[1:] {
if strings.HasPrefix(arg, "+") {
targetInfo = arg
break
}
}
}

require.Equal(t, testCase.expected, targetInfo)
})
}
}
4 changes: 4 additions & 0 deletions logbus/formatter/formatter.go
Original file line number Diff line number Diff line change
Expand Up @@ -540,6 +540,10 @@ func (f *Formatter) printBuildFailure() {

c.Printf("%s%s\n", msgPrefix, failure.GetErrorMessage())

if failure.GetHelpMessage() != "" {
c.HelpPrint(failure.GetHelpMessage())
}
Comment thread
danielschlegel marked this conversation as resolved.

f.lastOutputWasOngoingUpdate = false
f.lastOutputWasProgress = false
f.lastCommandOutput = nil
Expand Down
63 changes: 63 additions & 0 deletions tests/Earthfile
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ ga-no-qemu-group4:
BUILD +push-arg-test
BUILD +ci-arg-test
BUILD +gen-dockerfile-test
BUILD +all-privileged-tests
BUILD +chown-test
BUILD +env-test
BUILD +env-home-test
Expand Down Expand Up @@ -595,6 +596,68 @@ reject-privileged-import-test:
DO +RUN_EARTHLY --earthfile=reject-privileged-import.earth --should_fail=true --extra_args="--allow-privileged" --target=+test-reject-copy
DO +RUN_EARTHLY --earthfile=reject-privileged-import.earth --should_fail=true --extra_args="--allow-privileged" --target=+test-reject-cmd

all-privileged-tests:
BUILD +privileged-run-test
BUILD +privileged-with-docker-test
BUILD +privileged-if-test
BUILD +privileged-for-test

privileged-run-test:
# Test that RUN --privileged without -P flag shows improved error message
RUN echo "VERSION 0.8
test:
FROM alpine:latest
RUN --privileged echo 'this requires privileged mode'
" > Earthfile
DO +RUN_EARTHLY \
--should_fail=true \
--target=+test \
--output_contains="To fix this, use one of the following:"

privileged-with-docker-test:
# Test that WITH DOCKER without -P flag shows improved error message
RUN echo "VERSION 0.8
test:
FROM earthbuild/dind:alpine-3.22-docker-28.3.3-r4
WITH DOCKER
RUN docker ps
END
" > Earthfile
DO +RUN_EARTHLY \
--should_fail=true \
--target=+test \
--output_contains="To fix this, use one of the following:"

privileged-if-test:
# Test that IF --privileged without -P flag shows improved error message
RUN echo "VERSION 0.8
test:
FROM alpine:latest
IF --privileged [ -f /proc/self/status ]
RUN echo 'found status'
END
" > Earthfile
DO +RUN_EARTHLY \
--should_fail=true \
--target=+test \
--output_contains="To fix this, use one of the following:"

privileged-for-test:
# Test that FOR --privileged without -P flag shows improved error message
RUN echo "VERSION 0.8
test:
FROM alpine:latest
FOR --privileged file IN \$(ls)
RUN echo \$file
END
" > Earthfile
DO +RUN_EARTHLY \
--should_fail=true \
--target=+test \
--output_contains="To fix this, use one of the following:"



pass-args-test:
COPY pass-args-sub-dir.earth subdir/Earthfile
COPY pass-args-root.earth Earthfile
Expand Down
2 changes: 1 addition & 1 deletion tests/with-docker/Earthfile
Original file line number Diff line number Diff line change
Expand Up @@ -178,4 +178,4 @@ pre-script-test:
COPY pre-script.sh /usr/share/earthly/dockerd-wrapper-pre-script
WITH DOCKER
RUN test -f /the-prescript-was-run
END
END
Loading