diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 2a7d652794..fae089a58a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -240,7 +240,7 @@ To contribute improvements to documentation related to currently released featur ### Config Starting with [v0.6.30](CHANGELOG.md#v0630---2022-11-22), the default location of the built binary's config file has -changed to `~/.earthly-dev/config.yml`. The standard location is not used as a fallback; it is possible to `export EARTHLY_CONFIG=~/.earthly/config.yml`, or create a symlink if required. +changed to `~/.earthly-dev/config.yml`. The standard location is not used as a fallback; it is possible to `export EARTH_CONFIG=~/.earthly/config.yml`, or create a symlink if required. ## Prereleases diff --git a/buildcontext/git.go b/buildcontext/git.go index d6a3c727ea..fea8c71bef 100644 --- a/buildcontext/git.go +++ b/buildcontext/git.go @@ -48,6 +48,8 @@ type resolvedGitProject struct { hash string // shortHash is the short git hash. shortHash string + // contentHash is the git tree hash (content-addressable). + contentHash string // branches is the git branches. branches []string // tags is the git tags. @@ -227,6 +229,7 @@ func (gr *gitResolver) resolveEarthProject( RemoteURL: gitURL, Hash: rgp.hash, ShortHash: rgp.shortHash, + ContentHash: rgp.contentHash, BranchOverrideTagArg: gr.gitBranchOverride != "", Branch: rgp.branches, Tags: rgp.tags, @@ -306,6 +309,7 @@ func (gr *gitResolver) resolveGitProject( "git rev-parse HEAD >/dest/git-hash ; " + "uname -m >/dest/uname-m ;" + "git rev-parse --short=8 HEAD >/dest/git-short-hash ; " + + "git rev-parse HEAD^{tree} >/dest/git-content-hash ; " + "git rev-parse --abbrev-ref HEAD >/dest/git-branch || touch /dest/git-branch ; " + "ls .git/refs/heads/ | head -n 1 >/dest/git-default-branch || touch /dest/git-default-branch ; " + "git describe --exact-match --tags >/dest/git-tags || touch /dest/git-tags ; " + @@ -362,22 +366,23 @@ func (gr *gitResolver) resolveGitProject( gitImage, string(unameM), platr.LLBNative().Architecture) } - var gitHashBytes []byte - - gitHashBytes, err = gitMetaRef.ReadFile(ctx, gwclient.ReadRequest{ - Filename: "git-hash", - }) - if err != nil { - return nil, errors.Wrap(err, "read git-hash") + metaFiles := []string{ + "git-hash", "git-short-hash", "git-content-hash", + "git-default-branch", "git-tags", + "git-committer-ts", "git-author-ts", + "git-author-email", "git-author-name", + "git-body", "git-refs", "Earthfile-paths", } - var gitShortHashBytes []byte + meta := make(map[string][]byte, len(metaFiles)) - gitShortHashBytes, err = gitMetaRef.ReadFile(ctx, gwclient.ReadRequest{ - Filename: "git-short-hash", - }) - if err != nil { - return nil, errors.Wrap(err, "read git-short-hash") + for _, name := range metaFiles { + meta[name], err = gitMetaRef.ReadFile(ctx, gwclient.ReadRequest{ + Filename: name, + }) + if err != nil { + return nil, errors.Wrap(err, "read "+name) + } } var gitBranch string @@ -387,97 +392,17 @@ func (gr *gitResolver) resolveGitProject( return nil, errors.Wrap(err, "read git-branch") } - var gitDefaultBranchBytes []byte - - gitDefaultBranchBytes, err = gitMetaRef.ReadFile(ctx, gwclient.ReadRequest{ - Filename: "git-default-branch", - }) - if err != nil { - return nil, errors.Wrap(err, "read git-default-branch") - } - - var gitTagsBytes []byte - - gitTagsBytes, err = gitMetaRef.ReadFile(ctx, gwclient.ReadRequest{ - Filename: "git-tags", - }) - if err != nil { - return nil, errors.Wrap(err, "read git-tags") - } - - var gitCommitterTsBytes []byte - - gitCommitterTsBytes, err = gitMetaRef.ReadFile(ctx, gwclient.ReadRequest{ - Filename: "git-committer-ts", - }) - if err != nil { - return nil, errors.Wrap(err, "read git-committer-ts") - } - - var gitAuthorTsBytes []byte - - gitAuthorTsBytes, err = gitMetaRef.ReadFile(ctx, gwclient.ReadRequest{ - Filename: "git-author-ts", - }) - if err != nil { - return nil, errors.Wrap(err, "read git-author-ts") - } - - var gitAuthorEmailBytes []byte - - gitAuthorEmailBytes, err = gitMetaRef.ReadFile(ctx, gwclient.ReadRequest{ - Filename: "git-author-email", - }) - if err != nil { - return nil, errors.Wrap(err, "read git-author-email") - } - - var gitAuthorNameBytes []byte - - gitAuthorNameBytes, err = gitMetaRef.ReadFile(ctx, gwclient.ReadRequest{ - Filename: "git-author-name", - }) - if err != nil { - return nil, errors.Wrap(err, "read git-author-name") - } - - var gitBodyBytes []byte - - gitBodyBytes, err = gitMetaRef.ReadFile(ctx, gwclient.ReadRequest{ - Filename: "git-body", - }) - if err != nil { - return nil, errors.Wrap(err, "read git-body") - } - - var gitRefsBytes []byte - - gitRefsBytes, err = gitMetaRef.ReadFile(ctx, gwclient.ReadRequest{ - Filename: "git-refs", - }) - if err != nil { - return nil, errors.Wrap(err, "read git-refs") - } - - var earthfilePathsRaw []byte - - earthfilePathsRaw, err = gitMetaRef.ReadFile(ctx, gwclient.ReadRequest{ - Filename: "Earthfile-paths", - }) - if err != nil { - return nil, errors.Wrap(err, "read Earthfile-paths") - } - isNotHead := func(s string) bool { return s != "" && s != "HEAD" } - gitHash := strings.SplitN(string(gitHashBytes), "\n", 2)[0] - gitShortHash := strings.SplitN(string(gitShortHashBytes), "\n", 2)[0] + gitHash := strings.SplitN(string(meta["git-hash"]), "\n", 2)[0] + gitShortHash := strings.SplitN(string(meta["git-short-hash"]), "\n", 2)[0] + gitContentHash := strings.SplitN(string(meta["git-content-hash"]), "\n", 2)[0] gitBranches := strings.SplitN(gitBranch, "\n", 2) - gitAuthorEmail := strings.SplitN(string(gitAuthorEmailBytes), "\n", 2)[0] - gitAuthorName := strings.SplitN(string(gitAuthorNameBytes), "\n", 2)[0] - gitCoAuthors := gitutil.ParseCoAuthorsFromBody(string(gitBodyBytes)) + gitAuthorEmail := strings.SplitN(string(meta["git-author-email"]), "\n", 2)[0] + gitAuthorName := strings.SplitN(string(meta["git-author-name"]), "\n", 2)[0] + gitCoAuthors := gitutil.ParseCoAuthorsFromBody(string(meta["git-body"])) var gitBranches2 []string @@ -495,11 +420,11 @@ func (gr *gitResolver) resolveGitProject( gitBranches2 = []string{gitRef} } } else { - gitBranches2 = []string{strings.SplitN(string(gitDefaultBranchBytes), "\n", 2)[0]} + gitBranches2 = []string{strings.SplitN(string(meta["git-default-branch"]), "\n", 2)[0]} } } - gitTags := strings.SplitN(string(gitTagsBytes), "\n", 2) + gitTags := strings.SplitN(string(meta["git-tags"]), "\n", 2) var gitTags2 []string @@ -509,9 +434,9 @@ func (gr *gitResolver) resolveGitProject( } } - gitCommitterTs := strings.SplitN(string(gitCommitterTsBytes), "\n", 2)[0] - gitAuthorTs := strings.SplitN(string(gitAuthorTsBytes), "\n", 2)[0] - gitRefs := strings.Split(string(gitRefsBytes), "\n") + gitCommitterTs := strings.SplitN(string(meta["git-committer-ts"]), "\n", 2)[0] + gitAuthorTs := strings.SplitN(string(meta["git-author-ts"]), "\n", 2)[0] + gitRefs := strings.Split(string(meta["git-refs"]), "\n") var gitRefs2 []string @@ -537,6 +462,7 @@ func (gr *gitResolver) resolveGitProject( rgp = &resolvedGitProject{ hash: gitHash, shortHash: gitShortHash, + contentHash: gitContentHash, branches: gitBranches2, tags: gitTags2, committerTs: gitCommitterTs, @@ -545,7 +471,7 @@ func (gr *gitResolver) resolveGitProject( authorName: gitAuthorName, coAuthors: gitCoAuthors, refs: gitRefs2, - earthfilePaths: strings.Split(strings.TrimSpace(string(earthfilePathsRaw)), "\n"), + earthfilePaths: strings.Split(strings.TrimSpace(string(meta["Earthfile-paths"])), "\n"), state: pllb.Git( gitURL, gitHash, diff --git a/cmd/earthly/app/run.go b/cmd/earthly/app/run.go index e8cb4c0590..9f5a755edc 100644 --- a/cmd/earthly/app/run.go +++ b/cmd/earthly/app/run.go @@ -19,6 +19,7 @@ import ( "github.com/EarthBuild/earthbuild/buildkitd" "github.com/EarthBuild/earthbuild/cmd/earthly/common" "github.com/EarthBuild/earthbuild/cmd/earthly/helper" + "github.com/EarthBuild/earthbuild/util/envutil" "github.com/EarthBuild/earthbuild/conslogging" "github.com/EarthBuild/earthbuild/earthfile2llb" "github.com/EarthBuild/earthbuild/inputgraph" @@ -68,12 +69,12 @@ func (app *EarthlyApp) unhideFlags() error { // TODO delete this check after 2022-03-01 if os.Getenv("EARTHLY_AUTOCOMPLETE_HIDDEN") != "" && os.Getenv("COMP_POINT") == "" { // only display warning when NOT under complete mode (otherwise we break auto completion) - app.BaseCLI.Console().Warn("Warning: EARTHLY_AUTOCOMPLETE_HIDDEN has been renamed to EARTHLY_SHOW_HIDDEN\n") + app.BaseCLI.Console().Warn("Warning: EARTHLY_AUTOCOMPLETE_HIDDEN has been renamed to EARTH_SHOW_HIDDEN\n") } showHidden := false - showHiddenStr := os.Getenv("EARTHLY_SHOW_HIDDEN") + showHiddenStr, _ := envutil.LookupEnv("EARTH_SHOW_HIDDEN", "EARTHLY_SHOW_HIDDEN") if showHiddenStr != "" { showHidden, err = strconv.ParseBool(showHiddenStr) if err != nil { diff --git a/cmd/earthly/flag/global.go b/cmd/earthly/flag/global.go index 90063638c7..bdcb6e32e6 100644 --- a/cmd/earthly/flag/global.go +++ b/cmd/earthly/flag/global.go @@ -95,7 +95,7 @@ func (global *Global) RootFlags(installName string, bkImage string) []cli.Flag { &cli.StringFlag{ Name: "installation-name", Value: defaultInstallationName, - EnvVars: []string{"EARTHLY_INSTALLATION_NAME"}, + EnvVars: []string{"EARTH_INSTALLATION_NAME", "EARTHLY_INSTALLATION_NAME"}, Usage: "The earth installation name to use when naming the buildkit container, " + "the docker volume and the ~/.earthly directory", Destination: &global.InstallationName, @@ -104,14 +104,14 @@ func (global *Global) RootFlags(installName string, bkImage string) []cli.Flag { &cli.StringFlag{ Name: "config", Value: "", // the default value will be applied in the "Before" fn, after flag.installationName is set. - EnvVars: []string{"EARTHLY_CONFIG"}, + EnvVars: []string{"EARTH_CONFIG", "EARTHLY_CONFIG"}, Usage: "Path to config file", Destination: &global.ConfigPath, }, &cli.StringFlag{ Name: "ssh-auth-sock", Value: os.Getenv("SSH_AUTH_SOCK"), - EnvVars: []string{"EARTHLY_SSH_AUTH_SOCK"}, + EnvVars: []string{"EARTH_SSH_AUTH_SOCK", "EARTHLY_SSH_AUTH_SOCK"}, Usage: "The SSH auth socket to use for ssh-agent forwarding", Destination: &global.SSHAuthSock, }, @@ -129,7 +129,7 @@ func (global *Global) RootFlags(installName string, bkImage string) []cli.Flag { }, &cli.StringFlag{ Name: "git-branch", - EnvVars: []string{"EARTHLY_GIT_BRANCH_OVERRIDE"}, + EnvVars: []string{"EARTH_GIT_BRANCH_OVERRIDE", "EARTHLY_GIT_BRANCH_OVERRIDE"}, Usage: "The git branch the build should be considered running in", Destination: &global.GitBranchOverride, Hidden: true, // primarily used by CI to pass branch context @@ -137,14 +137,14 @@ func (global *Global) RootFlags(installName string, bkImage string) []cli.Flag { &cli.BoolFlag{ Name: "verbose", Aliases: []string{"V"}, - EnvVars: []string{"EARTHLY_VERBOSE"}, + EnvVars: []string{"EARTH_VERBOSE", "EARTHLY_VERBOSE"}, Usage: "Enable verbose logging", Destination: &global.Verbose, }, &cli.BoolFlag{ Name: "debug", Aliases: []string{"D"}, - EnvVars: []string{"EARTHLY_DEBUG"}, + EnvVars: []string{"EARTH_DEBUG", "EARTHLY_DEBUG"}, Usage: "Enable debug mode. This flag also turns on the debug mode of buildkitd, " + "which may cause it to restart", Destination: &global.Debug, @@ -152,21 +152,21 @@ func (global *Global) RootFlags(installName string, bkImage string) []cli.Flag { }, &cli.BoolFlag{ Name: "exec-stats", - EnvVars: []string{"EARTHLY_EXEC_STATS"}, + EnvVars: []string{"EARTH_EXEC_STATS", "EARTHLY_EXEC_STATS"}, Usage: "Display container stats (e.g. cpu and memory usage)", Destination: &global.DisplayExecStats, Hidden: true, // Experimental }, &cli.StringFlag{ Name: "exec-stats-summary", - EnvVars: []string{"EARTHLY_EXEC_STATS_SUMMARY"}, + EnvVars: []string{"EARTH_EXEC_STATS_SUMMARY", "EARTHLY_EXEC_STATS_SUMMARY"}, Usage: "Output summarized container stats (e.g. cpu and memory usage) to the specified file", Destination: &global.ExecStatsSummary, Hidden: true, // Experimental }, &cli.BoolFlag{ Name: "profiler", - EnvVars: []string{"EARTHLY_PROFILER"}, + EnvVars: []string{"EARTH_PROFILER", "EARTHLY_PROFILER"}, Usage: "Enable the profiler", Destination: &global.EnableProfiler, Hidden: true, // Dev purposes only. @@ -174,21 +174,21 @@ func (global *Global) RootFlags(installName string, bkImage string) []cli.Flag { &cli.StringFlag{ Name: "buildkit-host", Value: "", - EnvVars: []string{"EARTHLY_BUILDKIT_HOST"}, + EnvVars: []string{"EARTH_BUILDKIT_HOST", "EARTHLY_BUILDKIT_HOST"}, Usage: `The URL to use for connecting to a buildkit host If empty, earthly will attempt to start a buildkitd instance via docker run`, Destination: &global.BuildkitHost, }, &cli.BoolFlag{ Name: "no-buildkit-update", - EnvVars: []string{"EARTHLY_NO_BUILDKIT_UPDATE"}, + EnvVars: []string{"EARTH_NO_BUILDKIT_UPDATE", "EARTHLY_NO_BUILDKIT_UPDATE"}, Usage: "Disable the automatic update of buildkitd", Destination: &global.NoBuildkitUpdate, Hidden: true, // Internal. }, &cli.StringFlag{ Name: "version-flag-overrides", - EnvVars: []string{"EARTHLY_VERSION_FLAG_OVERRIDES"}, + EnvVars: []string{"EARTH_VERSION_FLAG_OVERRIDES", "EARTHLY_VERSION_FLAG_OVERRIDES"}, Usage: "Apply additional flags after each VERSION command across all Earthfiles, " + "multiple flags can be separated by commas", Destination: &global.FeatureFlagOverrides, @@ -196,7 +196,7 @@ func (global *Global) RootFlags(installName string, bkImage string) []cli.Flag { }, &cli.StringFlag{ Name: EnvFileFlag, - EnvVars: []string{"EARTHLY_ENV_FILE_PATH"}, + EnvVars: []string{"EARTH_ENV_FILE_PATH", "EARTHLY_ENV_FILE_PATH"}, Usage: "Use values from this file as earth environment variables; " + "values are no longer used as --build-arg's or --secret's", Value: DefaultEnvFile, @@ -204,28 +204,28 @@ func (global *Global) RootFlags(installName string, bkImage string) []cli.Flag { }, &cli.StringFlag{ Name: ArgFileFlag, - EnvVars: []string{"EARTHLY_ARG_FILE_PATH"}, + EnvVars: []string{"EARTH_ARG_FILE_PATH", "EARTHLY_ARG_FILE_PATH"}, Usage: "Use values from this file as earth buildargs", Value: DefaultArgFile, Destination: &global.ArgFile, }, &cli.StringFlag{ Name: SecretFileFlag, - EnvVars: []string{"EARTHLY_SECRET_FILE_PATH"}, + EnvVars: []string{"EARTH_SECRET_FILE_PATH", "EARTHLY_SECRET_FILE_PATH"}, Usage: "Use values from this file as earth secrets", Value: DefaultSecretFile, Destination: &global.SecretFile, }, &cli.StringFlag{ Name: "logstream-debug-file", - EnvVars: []string{"EARTHLY_LOGSTREAM_DEBUG_FILE"}, + EnvVars: []string{"EARTH_LOGSTREAM_DEBUG_FILE", "EARTHLY_LOGSTREAM_DEBUG_FILE"}, Usage: "Enable log streaming debugging output to a file", Destination: &global.LogstreamDebugFile, Hidden: true, // Internal. }, &cli.StringFlag{ Name: "logstream-debug-manifest-file", - EnvVars: []string{"EARTHLY_LOGSTREAM_DEBUG_MANIFEST_FILE"}, + EnvVars: []string{"EARTH_LOGSTREAM_DEBUG_MANIFEST_FILE", "EARTHLY_LOGSTREAM_DEBUG_MANIFEST_FILE"}, Usage: "Enable log streaming manifest debugging output to a file", Destination: &global.LogstreamDebugManifestFile, Hidden: true, // Internal. @@ -233,7 +233,7 @@ func (global *Global) RootFlags(installName string, bkImage string) []cli.Flag { &cli.DurationFlag{ Name: "server-conn-timeout", Usage: "EarthBuild API server connection timeout value", - EnvVars: []string{"EARTHLY_SERVER_CONN_TIMEOUT"}, + EnvVars: []string{"EARTH_SERVER_CONN_TIMEOUT", "EARTHLY_SERVER_CONN_TIMEOUT"}, Hidden: true, // Internal. Value: 5 * time.Second, Destination: &global.ServerConnTimeout, @@ -251,76 +251,76 @@ func (global *Global) RootFlags(installName string, bkImage string) []cli.Flag { }, &cli.BoolFlag{ Name: "pull", - EnvVars: []string{"EARTHLY_PULL"}, + EnvVars: []string{"EARTH_PULL", "EARTHLY_PULL"}, Usage: "Force pull any referenced Docker images", Destination: &global.Pull, Hidden: true, // Experimental }, &cli.BoolFlag{ Name: "push", - EnvVars: []string{"EARTHLY_PUSH"}, + EnvVars: []string{"EARTH_PUSH", "EARTHLY_PUSH"}, Usage: "Push docker images and execute RUN --push commands", Destination: &global.Push, }, &cli.BoolFlag{ Name: "ci", - EnvVars: []string{"EARTHLY_CI"}, + EnvVars: []string{"EARTH_CI", "EARTHLY_CI"}, Usage: common.Wrap("Execute in CI mode. ", "Implies --no-output --strict"), Destination: &global.CI, }, &cli.BoolFlag{ Name: "ticktock", - EnvVars: []string{"EARTHLY_TICKTOCK"}, + EnvVars: []string{"EARTH_TICKTOCK", "EARTHLY_TICKTOCK"}, Usage: "Use earthbuild's experimental buildkit ticktock codebase", Destination: &global.UseTickTockBuildkitImage, Hidden: true, // Experimental }, &cli.BoolFlag{ Name: "output", - EnvVars: []string{"EARTHLY_OUTPUT"}, + EnvVars: []string{"EARTH_OUTPUT", "EARTHLY_OUTPUT"}, Usage: "Allow artifacts or images to be output, even when running under --ci mode", Destination: &global.Output, }, &cli.BoolFlag{ Name: "no-output", - EnvVars: []string{"EARTHLY_NO_OUTPUT"}, + EnvVars: []string{"EARTH_NO_OUTPUT", "EARTHLY_NO_OUTPUT"}, Usage: common.Wrap("Do not output artifacts or images", "(using --push is still allowed)"), Destination: &global.NoOutput, }, &cli.BoolFlag{ Name: "no-cache", - EnvVars: []string{"EARTHLY_NO_CACHE"}, + EnvVars: []string{"EARTH_NO_CACHE", "EARTHLY_NO_CACHE"}, Usage: "Do not use cache while building", Destination: &global.NoCache, }, &cli.BoolFlag{ Name: "auto-skip", - EnvVars: []string{"EARTHLY_AUTO_SKIP"}, + EnvVars: []string{"EARTH_AUTO_SKIP", "EARTHLY_AUTO_SKIP"}, Usage: "Skip buildkit if target has already been built", Destination: &global.SkipBuildkit, }, &cli.BoolFlag{ Name: "allow-privileged", Aliases: []string{"P"}, - EnvVars: []string{"EARTHLY_ALLOW_PRIVILEGED"}, + EnvVars: []string{"EARTH_ALLOW_PRIVILEGED", "EARTHLY_ALLOW_PRIVILEGED"}, Usage: "Allow build to use the --privileged flag in RUN commands", Destination: &global.AllowPrivileged, }, &cli.BoolFlag{ Name: "max-remote-cache", - EnvVars: []string{"EARTHLY_MAX_REMOTE_CACHE"}, + EnvVars: []string{"EARTH_MAX_REMOTE_CACHE", "EARTHLY_MAX_REMOTE_CACHE"}, Usage: "Saves all intermediate images too in the remote cache", Destination: &global.MaxRemoteCache, }, &cli.BoolFlag{ Name: "save-inline-cache", - EnvVars: []string{"EARTHLY_SAVE_INLINE_CACHE"}, + EnvVars: []string{"EARTH_SAVE_INLINE_CACHE", "EARTHLY_SAVE_INLINE_CACHE"}, Usage: "Enable cache inlining when pushing images", Destination: &global.SaveInlineCache, }, &cli.BoolFlag{ Name: "use-inline-cache", - EnvVars: []string{"EARTHLY_USE_INLINE_CACHE"}, + EnvVars: []string{"EARTH_USE_INLINE_CACHE", "EARTHLY_USE_INLINE_CACHE"}, Usage: common.Wrap("Attempt to use any inline cache that may have been previously pushed ", "uses image tags referenced by SAVE IMAGE --push or SAVE IMAGE --cache-from"), Destination: &global.UseInlineCache, @@ -328,33 +328,33 @@ func (global *Global) RootFlags(installName string, bkImage string) []cli.Flag { &cli.BoolFlag{ Name: "interactive", Aliases: []string{"i"}, - EnvVars: []string{"EARTHLY_INTERACTIVE"}, + EnvVars: []string{"EARTH_INTERACTIVE", "EARTHLY_INTERACTIVE"}, Usage: "Enable interactive debugging", Destination: &global.InteractiveDebugging, }, &cli.BoolFlag{ Name: "no-fake-dep", - EnvVars: []string{"EARTHLY_NO_FAKE_DEP"}, + EnvVars: []string{"EARTH_NO_FAKE_DEP", "EARTHLY_NO_FAKE_DEP"}, Usage: "Internal feature flag for fake-dep", Destination: &global.NoFakeDep, Hidden: true, // Internal. }, &cli.BoolFlag{ Name: "strict", - EnvVars: []string{"EARTHLY_STRICT"}, + EnvVars: []string{"EARTH_STRICT", "EARTHLY_STRICT"}, Usage: "Disallow usage of features that may create unrepeatable builds", Destination: &global.Strict, }, &cli.BoolFlag{ Name: "global-wait-end", - EnvVars: []string{"EARTHLY_GLOBAL_WAIT_END"}, + EnvVars: []string{"EARTH_GLOBAL_WAIT_END", "EARTHLY_GLOBAL_WAIT_END"}, Usage: "enables global wait-end code in place of builder code", Destination: &global.GlobalWaitEnd, Hidden: true, // used to force code-coverage of future builder.go refactor (once we remove support for 0.6) }, &cli.StringFlag{ Name: "git-lfs-pull-include", - EnvVars: []string{"EARTHLY_GIT_LFS_PULL_INCLUDE"}, + EnvVars: []string{"EARTH_GIT_LFS_PULL_INCLUDE", "EARTHLY_GIT_LFS_PULL_INCLUDE"}, Usage: "When referencing a remote target, perform a git lfs pull include prior to running the target. " + "Note that this flag is (hopefully) temporary, " + "see https://github.com/earthly/earthly/issues/2921 for details.", @@ -363,21 +363,21 @@ func (global *Global) RootFlags(installName string, bkImage string) []cli.Flag { }, &cli.StringFlag{ Name: "auto-skip-db-path", - EnvVars: []string{"EARTHLY_AUTO_SKIP_DB_PATH"}, + EnvVars: []string{"EARTH_AUTO_SKIP_DB_PATH", "EARTHLY_AUTO_SKIP_DB_PATH"}, Usage: "use a local database for auto-skip", Destination: &global.LocalSkipDB, }, &cli.StringFlag{ Name: "buildkit-image", Value: bkImage, - EnvVars: []string{"EARTHLY_BUILDKIT_IMAGE"}, + EnvVars: []string{"EARTH_BUILDKIT_IMAGE", "EARTHLY_BUILDKIT_IMAGE"}, Usage: "The docker image to use for the buildkit daemon", Destination: &global.BuildkitdImage, }, &cli.StringFlag{ Name: "buildkit-container-name", Value: defaultInstallationName + DefaultBuildkitdContainerSuffix, - EnvVars: []string{"EARTHLY_CONTAINER_NAME"}, + EnvVars: []string{"EARTH_CONTAINER_NAME", "EARTHLY_CONTAINER_NAME"}, Usage: "The docker container name to use for the buildkit daemon", Destination: &global.ContainerName, Hidden: true, @@ -385,28 +385,28 @@ func (global *Global) RootFlags(installName string, bkImage string) []cli.Flag { &cli.StringFlag{ Name: "buildkit-volume-name", Value: defaultInstallationName + DefaultBuildkitdVolumeSuffix, - EnvVars: []string{"EARTHLY_VOLUME_NAME"}, + EnvVars: []string{"EARTH_VOLUME_NAME", "EARTHLY_VOLUME_NAME"}, Usage: "The docker volume name to use for the buildkit daemon cache", Destination: &global.BuildkitdSettings.VolumeName, Hidden: true, }, &cli.StringFlag{ Name: "remote-cache", - EnvVars: []string{"EARTHLY_REMOTE_CACHE"}, + EnvVars: []string{"EARTH_REMOTE_CACHE", "EARTHLY_REMOTE_CACHE"}, Usage: "A remote docker image tag use as explicit cache and optionally additional attributes " + "to set in the image (Format: \"[,=,=,...]\")", Destination: &global.RemoteCache, }, &cli.BoolFlag{ Name: "disable-remote-registry-proxy", - EnvVars: []string{"EARTHLY_DISABLE_REMOTE_REGISTRY_PROXY"}, + EnvVars: []string{"EARTH_DISABLE_REMOTE_REGISTRY_PROXY", "EARTHLY_DISABLE_REMOTE_REGISTRY_PROXY"}, Usage: "Don't use the Docker registry proxy when transferring images", Destination: &global.DisableRemoteRegistryProxy, Value: false, }, &cli.BoolFlag{ Name: "no-auto-skip", - EnvVars: []string{"EARTHLY_NO_AUTO_SKIP"}, + EnvVars: []string{"EARTH_NO_AUTO_SKIP", "EARTHLY_NO_AUTO_SKIP"}, Usage: "Disable auto-skip functionality", Destination: &global.NoAutoSkip, Value: false, diff --git a/cmd/earthly/main.go b/cmd/earthly/main.go index 187835f9fe..376b634bb9 100644 --- a/cmd/earthly/main.go +++ b/cmd/earthly/main.go @@ -106,7 +106,7 @@ func run() (code int) { envFile := eFlag.DefaultEnvFile envFileOverride := false - if envFileFromEnv, ok := os.LookupEnv("EARTHLY_ENV_FILE"); ok { + if envFileFromEnv, ok := envutil.LookupEnv("EARTH_ENV_FILE", "EARTHLY_ENV_FILE"); ok { envFile = envFileFromEnv envFileOverride = true } @@ -165,7 +165,7 @@ func run() (code int) { padding := conslogging.DefaultPadding - customPadding, ok := os.LookupEnv("EARTHLY_TARGET_PADDING") + customPadding, ok := envutil.LookupEnv("EARTH_TARGET_PADDING", "EARTHLY_TARGET_PADDING") if ok { targetPadding, err := strconv.Atoi(customPadding) if err == nil { @@ -173,7 +173,7 @@ func run() (code int) { } } - if envutil.IsTrue("EARTHLY_FULL_TARGET") { + if envutil.IsTrueWithFallback("EARTH_FULL_TARGET", "EARTHLY_FULL_TARGET") { padding = conslogging.NoPadding } diff --git a/cmd/earthly/subcmd/bootstrap_cmds.go b/cmd/earthly/subcmd/bootstrap_cmds.go index 05f21e86a7..bb4b9c09c6 100644 --- a/cmd/earthly/subcmd/bootstrap_cmds.go +++ b/cmd/earthly/subcmd/bootstrap_cmds.go @@ -76,7 +76,7 @@ func (b *Bootstrap) Cmds() []*cli.Command { &cli.StringFlag{ Name: "certs-hostname", Usage: "Hostname to generate certificates for", - EnvVars: []string{"EARTHLY_CERTS_HOSTNAME"}, + EnvVars: []string{"EARTH_CERTS_HOSTNAME", "EARTHLY_CERTS_HOSTNAME"}, Value: "localhost", Destination: &b.certsHostName, }, diff --git a/cmd/earthly/subcmd/build_cmd.go b/cmd/earthly/subcmd/build_cmd.go index 03d69bd5e7..d4d7a19fa3 100644 --- a/cmd/earthly/subcmd/build_cmd.go +++ b/cmd/earthly/subcmd/build_cmd.go @@ -100,7 +100,7 @@ func (a *Build) Cmds() []*cli.Command { &cli.StringFlag{ Name: "dockerfile", Aliases: []string{"f"}, - EnvVars: []string{"EARTHLY_DOCKER_FILE"}, + EnvVars: []string{"EARTH_DOCKER_FILE", "EARTHLY_DOCKER_FILE"}, Usage: "Path to dockerfile input", Value: "Dockerfile", Destination: &a.cli.Flags().DockerfilePath, @@ -108,13 +108,13 @@ func (a *Build) Cmds() []*cli.Command { &cli.StringSliceFlag{ Name: "tag", Aliases: []string{"t"}, - EnvVars: []string{"EARTHLY_DOCKER_TAGS"}, + EnvVars: []string{"EARTH_DOCKER_TAGS", "EARTHLY_DOCKER_TAGS"}, Usage: "Name and tag for the built image; formatted as 'name:tag'", Destination: &a.dockerTags, }, &cli.StringFlag{ Name: "target", - EnvVars: []string{"EARTHLY_DOCKER_TARGET"}, + EnvVars: []string{"EARTH_DOCKER_TARGET", "EARTHLY_DOCKER_TARGET"}, Usage: "The docker target to build in the specified dockerfile", Destination: &a.dockerTarget, }, diff --git a/cmd/earthly/subcmd/build_flags.go b/cmd/earthly/subcmd/build_flags.go index 2dda690d18..c72f1fffdb 100644 --- a/cmd/earthly/subcmd/build_flags.go +++ b/cmd/earthly/subcmd/build_flags.go @@ -10,13 +10,13 @@ func (a *Build) buildFlags() []cli.Flag { return []cli.Flag{ &cli.StringSliceFlag{ Name: "platform", - EnvVars: []string{"EARTHLY_PLATFORMS"}, + EnvVars: []string{"EARTH_PLATFORMS", "EARTHLY_PLATFORMS"}, Usage: "Specify the target platform to build for or this can be read from ENV VAR", Destination: &a.platformsStr, }, &cli.StringSliceFlag{ Name: "build-arg", - EnvVars: []string{"EARTHLY_BUILD_ARGS"}, + EnvVars: []string{"EARTH_BUILD_ARGS", "EARTHLY_BUILD_ARGS"}, Usage: "A build arg override, specified as =[]", Destination: &a.buildArgs, Hidden: true, // Deprecated @@ -24,19 +24,19 @@ func (a *Build) buildFlags() []cli.Flag { &cli.StringSliceFlag{ Name: "secret", Aliases: []string{"s"}, - EnvVars: []string{"EARTHLY_SECRETS"}, + EnvVars: []string{"EARTH_SECRETS", "EARTHLY_SECRETS"}, Usage: "A secret override, specified as =[]", Destination: &a.secrets, }, &cli.StringSliceFlag{ Name: "secret-file", - EnvVars: []string{"EARTHLY_SECRET_FILES"}, + EnvVars: []string{"EARTH_SECRET_FILES", "EARTHLY_SECRET_FILES"}, Usage: "A secret override, specified as =", Destination: &a.secretFiles, }, &cli.StringSliceFlag{ Name: "cache-from", - EnvVars: []string{"EARTHLY_CACHE_FROM"}, + EnvVars: []string{"EARTH_CACHE_FROM", "EARTHLY_CACHE_FROM"}, Usage: "Remote docker image tags to use as readonly explicit cache (experimental)", Destination: &a.cacheFrom, Hidden: true, // Experimental diff --git a/cmd/earthly/subcmd/prune_cmds.go b/cmd/earthly/subcmd/prune_cmds.go index a0e359171d..f99f3e8278 100644 --- a/cmd/earthly/subcmd/prune_cmds.go +++ b/cmd/earthly/subcmd/prune_cmds.go @@ -44,13 +44,13 @@ func (a *Prune) Cmds() []*cli.Command { &cli.BoolFlag{ Name: "all", Aliases: []string{"a"}, - EnvVars: []string{"EARTHLY_PRUNE_ALL"}, + EnvVars: []string{"EARTH_PRUNE_ALL", "EARTHLY_PRUNE_ALL"}, Usage: "Prune all cache via BuildKit daemon", Destination: &a.all, }, &cli.BoolFlag{ Name: "reset", - EnvVars: []string{"EARTHLY_PRUNE_RESET"}, + EnvVars: []string{"EARTH_PRUNE_RESET", "EARTHLY_PRUNE_RESET"}, Usage: `Reset cache entirely by restarting BuildKit daemon and wiping cache dir.`, Destination: &a.reset, }, diff --git a/docs/caching/caching-in-earthfiles.md b/docs/caching/caching-in-earthfiles.md index bf883cc781..2f03b8f5c2 100644 --- a/docs/caching/caching-in-earthfiles.md +++ b/docs/caching/caching-in-earthfiles.md @@ -105,7 +105,7 @@ As auto-skip relies on statically analyzing the structure of the build upfront, For basic `ARG` operations, auto-skip is able to infer the value of the `ARG` statically, and therefore, it is able to support it. Here is a practical example. -``` +```Earthfile # Supported ARG MY_ARG=foo BUILD $MY_ARG @@ -119,7 +119,7 @@ In the first case, the value of `MY_ARG` is known statically as its value can be Similarly, the auto-skip algorithm is able to propagate `ARG`s across targets, as long as the value of the `ARG` is known statically. Here is a practical example: -``` +```Earthfile # Supported ARG MY_ARG=foo BUILD +target --arg=$MY_ARG @@ -135,7 +135,7 @@ BUILD +target --arg=$MY_ARG Here is a practical example: -``` +```Earthfile # Supported and efficient (only +target2 is analyzed) ARG MY_ARG=bar IF [ $MY_ARG = "foo" ] @@ -201,7 +201,7 @@ In Earthly, like in Dockerfiles, ARGs declared in Earthfiles also behave as envi For this reason, it is best to declare ARGs as late as possible within the target they are used in, and try to avoid declaring `--global` ARGs as much as possible. If an ARG is not yet declared, it will not influence the cache state of a layer, allowing for more cache hits. Limiting the scope of ARGs as much as possible will yield better cache performance. -Watch out especially for ARGs that change often, such as the built-in ARG `EARTHLY_GIT_HASH`. Declaring this ARG as late as possible in the build will cause less cache misses. +Watch out especially for ARGs that change often, such as the built-in ARG `EARTH_GIT_HASH`. Declaring this ARG as late as possible in the build will cause less cache misses. If you need a git-derived identifier that doesn't change as often, consider `EARTH_GIT_CONTENT_HASH` instead — it only changes when the file tree actually changes, not on every commit. ### Secrets diff --git a/docs/ci-integration/overview.md b/docs/ci-integration/overview.md index e77a987846..28d9f02854 100644 --- a/docs/ci-integration/overview.md +++ b/docs/ci-integration/overview.md @@ -73,7 +73,7 @@ Some options that may make sense in a CI environment are: |----------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | `CNI_MTU` | In some environments, the MTU externally may be different than the MTU on the internal CNI network, causing the internet to be unavailable. This lets you configure the internal network for when `earthly` auto-configures the MTU incorrectly. | | `NO_COLOR` / `FORCE_COLOR` | Lets you force on/off the ANSI color codes. Use this when `earthly` misinterprets the presence of a terminal. Set either one to `1` to enable or disable colors. | -| `EARTHLY_BUILDKIT_HOST` | Use this when you have an external BuildKit instance you would like to use instead of the one `earthly` manages. | +| `EARTH_BUILDKIT_HOST` | Use this when you have an external BuildKit instance you would like to use instead of the one `earthly` manages. | Earthly also has some special command-line switches to ensure best practices are followed within your CI. These come *highly* recommended. Enable these with the [`--ci`](../earthly-command/earthly-command.md#--ci) option, which is shorthand for [`--save-inline-cache`](../earthly-command/earthly-command.md#save-inline-cache) [`--strict`](../earthly-command/earthly-command.md#strict) [`--no-output`](../earthly-command/earthly-command.md#no-output). diff --git a/docs/docker-images/buildkit-standalone.md b/docs/docker-images/buildkit-standalone.md index 3c9510bc9c..3fbadb7eeb 100644 --- a/docs/docker-images/buildkit-standalone.md +++ b/docs/docker-images/buildkit-standalone.md @@ -23,7 +23,7 @@ Heres a quick breakdown: - `-t` tells Docker to emulate a TTY. This makes the `buildkit` log output colorized. - `-v earthbuild-tmp:/tmp/earthbuild:rw` mounts (and creates, if necessary) the `earthbuild-tmp` Docker volume into the containers `/tmp/earthbuild`. This is used as a temporary/working directory for `buildkitd` during builds. -Assuming you are running this on your machine, you could use this `buildkitd` by setting `EARTHLY_BUILDKIT_HOST=docker-container://`, or by specifying the appropriate values in `config.yml`. +Assuming you are running this on your machine, you could use this `buildkitd` by setting `EARTH_BUILDKIT_HOST=docker-container://`, or by specifying the appropriate values in `config.yml`. ### Usage (Use As Remote) @@ -36,7 +36,7 @@ Omitting the options already discussed from the simple example: - `-e BUILDKIT_TCP_TRANSPORT_ENABLED=true` makes `buildkitd` listen on a TCP port instead of a Unix socket. - `-p 8372:8372` forwards the hosts port 8372 to the containers port 8372. When using TCP, `buildkit` will always listen on 8372, but you can configure the apparent port by forwarding a different port on your host. -Assuming you ran this on another machine named `fast-builder`, you could use this remote `buildkitd` by setting `EARTHLY_BUILDKIT_HOST=tcp://fast-builder:8372`, or by specifying the address in your `config.yml`. +Assuming you ran this on another machine named `fast-builder`, you could use this remote `buildkitd` by setting `EARTH_BUILDKIT_HOST=tcp://fast-builder:8372`, or by specifying the address in your `config.yml`. ## Using This Image @@ -62,7 +62,7 @@ rm: can't remove '/var/earthbuild/dind/...': Resource busy To use this image externally, it requires you to forward a port on your machine to the containers port 8372. You will need to ensure that external access to the machine on the port you chose is possible as well. -When using this container locally with `earthly`, please note that setting `EARTHLY_BUILDKIT_HOST` values with hosts `127.0.0.1`, ` ::1/128`, or `localhost` are considered local and will result in Earthly attempting to manage the BuildKit container itself. Consider using your hostname, or another alternative name in these cases. +When using this container locally with `earthly`, please note that setting `EARTH_BUILDKIT_HOST` values with hosts `127.0.0.1`, ` ::1/128`, or `localhost` are considered local and will result in Earthly attempting to manage the BuildKit container itself. Consider using your hostname, or another alternative name in these cases. ### Supported Environment Variables diff --git a/docs/earthfile/builtin-args.md b/docs/earthfile/builtin-args.md index ca0de631eb..7f00d296e4 100644 --- a/docs/earthfile/builtin-args.md +++ b/docs/earthfile/builtin-args.md @@ -5,64 +5,76 @@ Builtin args are variables with values automatically filled-in by Earthly. The value of a builtin arg can never be overridden. However, you can always have an additional `ARG`, which takes as the default value, the value of the builtin arg. The additional arg can be overridden. Example ```Dockerfile -ARG EARTHLY_TARGET_TAG -ARG TAG=$EARTHLY_TARGET_TAG +ARG EARTH_TARGET_TAG +ARG TAG=$EARTH_TARGET_TAG SAVE IMAGE --push some/name:$TAG ``` {% hint style='danger' %} + ##### Important + Earthly builtin args need to be pre-declared before they can be used. For example ```Dockerfile -ARG EARTHLY_TARGET -RUN echo "The current target is $EARTHLY_TARGET" +ARG EARTH_TARGET +RUN echo "The current target is $EARTH_TARGET" ``` + +{% endhint %} + +{% hint style='info' %} + +##### Note + +The legacy `EARTHLY_*` prefix is also accepted for backward compatibility. + {% endhint %} ### General args -| Name | Description | Example value | -| --- | --- | --- | -| `EARTHLY_CI` | Whether the build is being executed in --ci mode. | `true`, `false` | -| `EARTHLY_BUILD_SHA` | The git hash of the commit which built the currently running version of Earthly. | `1a9eda7a83af0e2ec122720e93ff6dbe9231fc0c` | -| `EARTHLY_LOCALLY` | Whether the target is being executed `LOCALLY`. | `true`, `false` | -| `EARTHLY_PUSH` | Whether `earthly` was called with the `--push` flag, or not. | `true`, `false` | -| `EARTHLY_VERSION` | The version of Earthly currently running. | `v0.8.0` | +| Name | Description | Example value | +| ------------------ | ------------------------------------------------------------------------------- | ------------------------------------------ | +| `EARTH_CI` | Whether the build is being executed in --ci mode. | `true`, `false` | +| `EARTH_BUILD_SHA` | The git hash of the commit which built the currently running version of Earthly. | `1a9eda7a83af0e2ec122720e93ff6dbe9231fc0c` | +| `EARTH_LOCALLY` | Whether the target is being executed `LOCALLY`. | `true`, `false` | +| `EARTH_PUSH` | Whether `earthly` was called with the `--push` flag, or not. | `true`, `false` | +| `EARTH_VERSION` | The version of Earthly currently running. | `v0.8.0` | ### Target-related args -| Name | Description | Example value | -| --- | --- | --- | -| `EARTHLY_TARGET_NAME` | The name part of the canonical reference of the current target. | For the target `github.com/bar/buz/src:john/work+foo`, the name would be `foo` | -| `EARTHLY_TARGET_PROJECT_NO_TAG` | The project part of the canonical reference of the current target, but without the tag. | For the target `github.com/bar/buz/src:john/work+foo`, this would be `github.com/bar/buz/src` | -| `EARTHLY_TARGET_PROJECT` | The project part of the canonical reference of the current target. | For the target `github.com/bar/buz/src:john/work+foo`, the canonical project would be `github.com/bar/buz/src:john` | -| `EARTHLY_TARGET_TAG_DOCKER` | The tag part of the canonical reference of the current target, sanitized for safe use as a docker tag. This is guaranteed to be a valid docker tag, even if no canonical form exists, in which case, `latest` is used. | For the target `github.com/bar/buz/src:john/work+foo`, the docker tag would be `john_work` | -| `EARTHLY_TARGET_TAG` | The tag part of the canonical reference of the current target. Note that if the target has no [canonical form](../guides/importing.md#canonical-form), the value is an empty string. | For the target `github.com/bar/buz/src:john/work+foo`, the tag would be `john/work` | -| `EARTHLY_TARGET` | The canonical reference of the current target. | For example, for a target named `foo`, which exists on `john/work` branch, in a repository at `github.com/bar/buz`, in a subdirectory `src`, the canonical reference would be `github.com/bar/buz/src:john/work+foo`. For more information about canonical references, see [importing guide](../guides/importing.md). | +| Name | Description | Example value | +| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `EARTH_TARGET_NAME` | The name part of the canonical reference of the current target. | For the target `github.com/bar/buz/src:john/work+foo`, the name would be `foo` | +| `EARTH_TARGET_PROJECT_NO_TAG` | The project part of the canonical reference of the current target, but without the tag. | For the target `github.com/bar/buz/src:john/work+foo`, this would be `github.com/bar/buz/src` | +| `EARTH_TARGET_PROJECT` | The project part of the canonical reference of the current target. | For the target `github.com/bar/buz/src:john/work+foo`, the canonical project would be `github.com/bar/buz/src:john` | +| `EARTH_TARGET_TAG_DOCKER` | The tag part of the canonical reference of the current target, sanitized for safe use as a docker tag. This is guaranteed to be a valid docker tag, even if no canonical form exists, in which case, `latest` is used. | For the target `github.com/bar/buz/src:john/work+foo`, the docker tag would be `john_work` | +| `EARTH_TARGET_TAG` | The tag part of the canonical reference of the current target. Note that if the target has no [canonical form](../guides/importing.md#canonical-form), the value is an empty string. | For the target `github.com/bar/buz/src:john/work+foo`, the tag would be `john/work` | +| `EARTH_TARGET` | The canonical reference of the current target. | For example, for a target named `foo`, which exists on `john/work` branch, in a repository at `github.com/bar/buz`, in a subdirectory `src`, the canonical reference would be `github.com/bar/buz/src:john/work+foo`. See [importing guide](../guides/importing.md). | ### Git-related args -| Name | Description | Example value | Feature Flag | -| --- | --- | --- |----------------------------------------| -| `EARTHLY_GIT_AUTHOR` | The git author detected within the build context directory. If no git directory is detected, then the value is an empty string. This is currently the author's email address but the feature flag adds the name as well | `john@example.com` (or `John Doe ` with flag turned on) | `--earthly-git-author-individual-args` | -| `EARTHLY_GIT_AUTHOR_EMAIL` | The git author email detected within the build context directory. If no git directory is detected, then the value is an empty string. | `john@example.com` | `--earthly-git-author-individual-args` | -| `EARTHLY_GIT_AUTHOR_NAME` | The git author name detected within the build context directory. If no git directory is detected, then the value is an empty string. | `John Doe` | `--earthly-git-author-individual-args` | -| `EARTHLY_GIT_CO_AUTHORS` | The git co-authors detected within the build context directory, separated by space. If no git directory is detected, then the value is an empty string. | `Jane Doe ` | | -| `EARTHLY_GIT_COMMIT_AUTHOR_TIMESTAMP` | The author timestamp, as unix seconds, of the git commit detected within the build context directory. If no git directory is detected, then the value is an empty string. | `1626881847` | | -| `EARTHLY_GIT_BRANCH` | The git branch of the git commit detected within the build context directory. If no git directory is detected, then the value is an empty string. | `main` | | -| `EARTHLY_GIT_COMMIT_TIMESTAMP` | The committer timestamp, as unix seconds, of the git commit detected within the build context directory. If no git directory is detected, then the value is an empty string. | `1626881847` | | -| `EARTHLY_GIT_HASH` | The git hash detected within the build context directory. If no git directory is detected, then the value is an empty string. Take care when using this arg, as the frequently changing git hash may be cause for not using the cache. | `41cb5666ade67b29e42bef121144456d3977a67a` | | -| `EARTHLY_GIT_ORIGIN_URL` | The git URL detected within the build context directory. If no git directory is detected, then the value is an empty string. Please note that this may be inconsistent, depending on whether an HTTPS or SSH URL was used. | `git@github.com:bar/buz.git` or `https://github.com/bar/buz.git` | | -| `EARTHLY_GIT_PROJECT_NAME` | The git project name from within the git URL detected within the build context directory. If no git directory is detected, then the value is an empty string. | `bar/buz` | | -| `EARTHLY_GIT_REFS` | The git references of the git commit detected within the build context directory, separated by space. If no git directory is detected, then the value is an empty string. | `issue-2735-git-ref main` | | -| `EARTHLY_GIT_SHORT_HASH` | The first 8 characters of the git hash detected within the build context directory. If no git directory is detected, then the value is an empty string. Take care when using this arg, as the frequently changing git hash may be cause for not using the cache. | `41cb5666` | | -| `EARTHLY_SOURCE_DATE_EPOCH` | The timestamp, as unix seconds, of the git commit detected within the build context directory. If no git directory is detected, then the value is `0` (the unix epoch) | `1626881847`, `0` | | +| Name | Description | Example value | Feature Flag | +| ---- | ----------- | ------------- | ------------ | +| `EARTH_GIT_AUTHOR` | The git author detected within the build context directory. If no git directory is detected, then the value is an empty string. This is currently the author's email address but the feature flag adds the name as well | `john@example.com` (or `John Doe ` with flag turned on) | `--earthly-git-author-individual-args` | +| `EARTH_GIT_AUTHOR_EMAIL` | The git author email detected within the build context directory. If no git directory is detected, then the value is an empty string. | `john@example.com` | `--earthly-git-author-individual-args` | +| `EARTH_GIT_AUTHOR_NAME` | The git author name detected within the build context directory. If no git directory is detected, then the value is an empty string. | `John Doe` | `--earthly-git-author-individual-args` | +| `EARTH_GIT_BRANCH` | The git branch of the git commit detected within the build context directory. If no git directory is detected, then the value is an empty string. | `main` | | +| `EARTH_GIT_CO_AUTHORS` | The git co-authors detected within the build context directory, separated by space. If no git directory is detected, then the value is an empty string. | `Jane Doe ` | | +| `EARTH_GIT_COMMIT_AUTHOR_TIMESTAMP` | The author timestamp, as unix seconds, of the git commit detected within the build context directory. If no git directory is detected, then the value is an empty string. | `1626881847` | | +| `EARTH_GIT_COMMIT_TIMESTAMP` | The committer timestamp, as unix seconds, of the git commit detected within the build context directory. If no git directory is detected, then the value is an empty string. | `1626881847` | | +| `EARTH_GIT_CONTENT_HASH` | The git tree hash (`git rev-parse HEAD^{tree}`) detected within the build context directory. Unlike `EARTH_GIT_HASH`, this is content-addressable: it remains stable across amends, rebases, or cherry-picks that don't change the file tree. If no git directory is detected, then the value is an empty string. | `aaa96ced2d9a1c8e72c56b253a0e2fe78393feb7` | | +| `EARTH_GIT_HASH` | The git hash detected within the build context directory. If no git directory is detected, then the value is an empty string. Take care when using this arg, as the frequently changing git hash may be cause for not using the cache. | `41cb5666ade67b29e42bef121144456d3977a67a` | | +| `EARTH_GIT_ORIGIN_URL` | The git URL detected within the build context directory. If no git directory is detected, then the value is an empty string. Please note that this may be inconsistent, depending on whether an HTTPS or SSH URL was used. | `git@github.com:bar/buz.git` or `https://github.com/bar/buz.git` | | +| `EARTH_GIT_PROJECT_NAME` | The git project name from within the git URL detected within the build context directory. If no git directory is detected, then the value is an empty string. | `bar/buz` | | +| `EARTH_GIT_REFS` | The git references of the git commit detected within the build context directory, separated by space. If no git directory is detected, then the value is an empty string. | `issue-2735-git-ref main` | | +| `EARTH_GIT_SHORT_HASH` | The first 8 characters of the git hash detected within the build context directory. If no git directory is detected, then the value is an empty string. Take care when using this arg, as the frequently changing git hash may be cause for not using the cache. | `41cb5666` | | +| `EARTH_SOURCE_DATE_EPOCH` | The timestamp, as unix seconds, of the git commit detected within the build context directory. If no git directory is detected, then the value is `0` (the unix epoch) | `1626881847`, `0` | | ### Platform-related args | Name | Description | Example value | -| --- | --- | --- | +| ---- | ----------- | ------------- | | `NATIVEARCH` | The native processor architecture of the build runner. | `arm`, `amd64`, `arm64` | | `NATIVEOS` | The native OS of the build runner. | `linux` | | `NATIVEPLATFORM` | The native platform of the build runner. | `linux/arm/v7`, `linux/amd64`, `darwin/arm64` | @@ -81,7 +93,9 @@ The default value of the `TARGETPLATFORM` arg is the native platform of the runn Under `LOCALLY`, the `TARGETPLATFORM` arg is always set to the user platform (the environment the `earthly` binary is invoked from) and it is not overridden by the `--platform` flag. {% hint style='info' %} + ##### Note + Under `LOCALLY` targets, it is important to declare the `TARGETPLATFORM` arg **after** the `LOCALLY` command, to ensure that it gets the appropriate user platform value. For example: ```Dockerfile @@ -90,4 +104,5 @@ my-target: ARG TARGETPLATFORM RUN echo "The target platform under LOCALLY is $TARGETPLATFORM" ``` + {% endhint %} diff --git a/docs/earthfile/features.md b/docs/earthfile/features.md index d216d927a4..d6d2f6e495 100644 --- a/docs/earthfile/features.md +++ b/docs/earthfile/features.md @@ -59,7 +59,7 @@ VERSION [...] | `--for-in` | 0.6 | Enables support for `FOR ... IN ...` commands | | `--require-force-for-unsafe-saves` | 0.6 | Requires `--force` for saving artifacts locally outside the Earthfile's directory | | `--no-implicit-ignore` | 0.6 | Eliminates implicit `.earthlyignore` entries, such as `Earthfile` and `.tmp-earthly-out` | -| `--earthly-version-arg` | 0.7 | Enables builtin ARGs: `EARTHLY_VERSION` and `EARTHLY_BUILD_SHA` | +| `--earthly-version-arg` | 0.7 | Enables builtin ARGs: `EARTH_VERSION` and `EARTH_BUILD_SHA` | | `--shell-out-anywhere` | 0.7 | Allows shelling-out in any earthly command (including in the middle of `ARG`) | | `--explicit-global` | 0.7 | Base target args must have a `--global` flag in order to be considered global args | | `--check-duplicate-images` | 0.7 | Check for duplicate images during output | @@ -70,9 +70,9 @@ VERSION [...] | `--no-tar-build-output` | 0.7 | Do not print output when creating a tarball to load into `WITH DOCKER` | | `--use-no-manifest-list` | 0.7 | Enable the `SAVE IMAGE --no-manifest-list` option | | `--use-chmod` | 0.7 | Enable the `COPY --chmod` option | -| `--earthly-locally-arg` | 0.7 | Enable the `EARTHLY_LOCALLY` arg | +| `--earthly-locally-arg` | 0.7 | Enable the `EARTH_LOCALLY` arg | | `--use-project-secrets` | 0.7 | Enable project-based secret resolution | -| `--earthly-git-author-args` | 0.7 | Enable the `EARTHLY_GIT_AUTHOR` and `EARTHLY_GIT_CO_AUTHORS` args | +| `--earthly-git-author-args` | 0.7 | Enable the `EARTH_GIT_AUTHOR` and `EARTH_GIT_CO_AUTHORS` args | | `--wait-block` | 0.7 | Enable the `WAIT` / `END` block commands | | `--no-network` | 0.8 | Allow the use of `RUN --network=none` commands | | `--arg-scope-and-set` | 0.8 | Enable the `LET` / `SET` commands and nested `ARG` scoping | @@ -84,7 +84,7 @@ VERSION [...] | `--use-visited-upfront-hash-collection` | 0.8 | Switches to a newer target parallelization algorithm | | `--no-use-registry-for-with-docker` | Experimental | Disable `use-registry-for-with-docker` | | `--try` | Experimental | Enable the `TRY` / `FINALLY` / `END` block commands | -| `--earthly-ci-runner-arg` | Experimental | Enable the `EARTHLY_CI_RUNNER` builtin ARG | +| `--earthly-ci-runner-arg` | Experimental | Enable the `EARTH_CI_RUNNER` builtin ARG | | `--wildcard-builds` | Experimental | Allow for the expansion of wildcard (glob) paths for BUILD commands | | `--build-auto-skip` | Experimental | Allow for `--auto-skip` to be used on individual BUILD commands | | `--allow-privileged-from-dockerfile` | Experimental | Allow the use of the `--allow-privileged` flag in the `FROM DOCKERFILE` command | diff --git a/docs/earthly-command/earthly-command.md b/docs/earthly-command/earthly-command.md index 2db2b07002..29f5660240 100644 --- a/docs/earthly-command/earthly-command.md +++ b/docs/earthly-command/earthly-command.md @@ -65,7 +65,7 @@ Synopsis: - Artifact form `earthly --artifact / [--=...]` - Image form `earthly --image [--=...]` -Also available as an env var setting: `EARTHLY_BUILD_ARGS="=,=,..."`. +Also available as an env var setting: `EARTH_BUILD_ARGS="=,..."`. Build arg overrides may be specified as part of the Earthly command. The value of the build arg `` is set to ``. @@ -120,10 +120,12 @@ Lines beginning with `#` are treated as comments. Blank lines are allowed. Here ```.env # Settings -EARTHLY_ALLOW_PRIVILEGED=true -EARTHLY_VERBOSE=true +EARTH_ALLOW_PRIVILEGED=true +EARTH_VERBOSE=true ``` +The legacy `EARTHLY_` prefix is also accepted for backward compatibility. + ### Global Options ##### `--help` @@ -141,19 +143,19 @@ Prints help information about earthly. ##### `--config ` -Also available as an env var setting: `EARTHLY_CONFIG=`. +Also available as an env var setting: `EARTH_CONFIG=`. Overrides the earthly [configuration file](../earthly-config/earthly-config.md), defaults to `~/.earthly/config.yml`. ##### `--installation-name ` -Also available as an env var setting: `EARTHLY_INSTALLATION_NAME=`. +Also available as an env var setting: `EARTH_INSTALLATION_NAME=`. Overrides the Earthly installation name. The installation name is used for the BuildKit Daemon name, the cache volume name, the configuration directory (`~/.`) and for the ports used by BuildKit. Using multiple installation names on the same system allows Earthly to run as multiple isolated instances, each with its own configuration, cache and daemon. Defaults to `earthly`. ##### `--ssh-auth-sock ` -Also available as an env var setting: `EARTHLY_SSH_AUTH_SOCK=`. +Also available as an env var setting: `EARTH_SSH_AUTH_SOCK=`. Sets the path to the SSH agent sock, which can be used for SSH authentication. SSH authentication is used by Earthly in order to perform git clone's underneath. @@ -165,7 +167,7 @@ For more information see the [Authentication page](../guides/auth.md). ##### `--verbose` -Also available as an env var setting: `EARTHLY_VERBOSE=1`. +Also available as an env var setting: `EARTH_VERBOSE=1`. Enables verbose logging. @@ -194,7 +196,7 @@ Build options are specific to executing Earthly builds; they are simply listed i ##### `--secret|-s [=]` -Also available as an env var setting: `EARTHLY_SECRETS="=,=,..."`. +Also available as an env var setting: `EARTH_SECRETS="=,=,..."`. Passes a secret with ID `` to the build environments. If `` is not specified, then the value becomes the value of the environment variable with the same name as ``. @@ -204,7 +206,7 @@ Secrets can also be stored in a `.secret` file using the same syntax as an `.arg ##### `--secret-file =` -Also available as an env var setting: `EARTHLY_SECRET_FILES="=,=,..."`. +Also available as an env var setting: `EARTH_SECRET_FILES="=,=,..."`. Loads the contents of a file located at `` into a secret with ID `` for use within the build environments. @@ -212,7 +214,7 @@ The secret can be referenced within Earthfile recipes as `RUN --secret ` -Also available as an env var setting: `EARTHLY_PLATFORMS=`. +Also available as an env var setting: `EARTH_PLATFORMS=`. Sets the platform to build for. @@ -287,13 +289,13 @@ build: This option has been deprecated in favor of the new build arg syntax `earthly --=`. -Also available as an env var setting: `EARTHLY_BUILD_ARGS="=,=,..."`. +Also available as an env var setting: `EARTH_BUILD_ARGS="=,=,..."`. Overrides the value of the build arg ``. If `` is not specified, then the value becomes the value of the environment variable with the same name as ``. For more information see the [`ARG` Earthfile command](../earthfile/earthfile.md#arg). ##### `--interactive|-i` -Also available as an env var setting: `EARTHLY_INTERACTIVE=true`. +Also available as an env var setting: `EARTH_INTERACTIVE=true`. Enable interactive debugging mode. By default when a `RUN` command fails, earthly will display the error and exit. If the interactive mode is enabled and an error occurs, an interactive shell is presented which can be used for investigating the error interactively. Due to technical limitations, only a single interactive shell can be used on the system at any given time. @@ -305,12 +307,12 @@ Disallow usage of features that may create unrepeatable builds. These options can only be set via environment variables, and have no command line equivalent. -| Variable | Usage | -|------------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| NO_COLOR | `NO_COLOR=1` disables the use of color. | -| FORCE_COLOR | `FORCE_COLOR=1` forces the use of color. | -| EARTHLY_TARGET_PADDING | `EARTHLY_TARGET_PADDING=n` will set the column to the width of `n` characters. If a name is longer than `n`, its path will be truncated and remaining extra length will cause the column to go ragged. | -| EARTHLY_FULL_TARGET | `EARTHLY_FULL_TARGET=1` will always print the full target name, and leave the target name column ragged. | +| Variable | Usage | +|-----------------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| NO_COLOR | `NO_COLOR=1` disables the use of color. | +| FORCE_COLOR | `FORCE_COLOR=1` forces the use of color. | +| EARTH_TARGET_PADDING | `EARTH_TARGET_PADDING=n` will set the column to the width of `n` characters. If a name is longer than `n`, its path will be truncated and remaining extra length will cause the column to go ragged. | +| EARTH_FULL_TARGET | `EARTH_FULL_TARGET=1` will always print the full target name, and leave the target name column ragged. | ## earthly --version diff --git a/docs/guides/auth.md b/docs/guides/auth.md index 580d04d5c2..b7187b2538 100644 --- a/docs/guides/auth.md +++ b/docs/guides/auth.md @@ -38,7 +38,7 @@ For users who want explicit control over git authentication, the following secti Earthly uses the environment variable `SSH_AUTH_SOCK` to detect where the SSH agent socket is located and mounts that socket to the BuildKit daemon container. (As an exception, on Mac, Docker's compatibility SSH auth socket is used instead). -If you need to override the SSH agent socket, you can set the environment variable `EARTHLY_SSH_AUTH_SOCK`, or use the `--ssh-auth-sock` flag to point to an alternative SSH agent. +If you need to override the SSH agent socket, you can set the environment variable `EARTH_SSH_AUTH_SOCK`, or use the `--ssh-auth-sock` flag to point to an alternative SSH agent. In order for the SSH agent to have the right credentials available, make sure you run `ssh-add` before executing Earthly builds. diff --git a/docs/guides/best-practices.md b/docs/guides/best-practices.md index 785e4026fe..334253244b 100644 --- a/docs/guides/best-practices.md +++ b/docs/guides/best-practices.md @@ -1129,7 +1129,7 @@ A good balance is often to perform pushes on the `main` branch only, and to disa Main branch build: `earthly --ci --push +target`. PR build: `earthly --ci +target`. -The push option can also be configured via the env var `EARTHLY_PUSH`, which may be easier to manipulate in your CI of choice. +The push option can also be configured via the env var `EARTH_PUSH`, which may be easier to manipulate in your CI of choice. A more extreme case of this idea can be to use explicit maximum cache: `earthly --ci --push --remote-cache=.... --max-remote-cache +target`. The idea, again is to tradeoff performance on the `main` branch, for the benefit of faster PR builds. Whether this is actually beneficial needs to be measured on a project-by-project basis, however. diff --git a/docs/guides/build-args.md b/docs/guides/build-args.md index e5b9e44d0c..886544bc6e 100644 --- a/docs/guides/build-args.md +++ b/docs/guides/build-args.md @@ -81,7 +81,7 @@ Argument values can be set multiple ways: 1. On the command line The value can be directly specified on the command line (as shown in the previous example): - + ``` earthly +hello --HELLO=world --FOO=bar ``` @@ -89,35 +89,35 @@ Argument values can be set multiple ways: 2. From environment variables Similar to above, except that the value is an environment variable: - + ```bash export HELLO="world" export FOO="bar" earthly +hello --HELLO="$HELLO" --FOO="$FOO" ``` -3. Via the `EARTHLY_BUILD_ARGS` environment variable +3. Via the `EARTH_BUILD_ARGS` environment variable + + The value can also be set via the `EARTH_BUILD_ARGS` environment variable. - The value can also be set via the `EARTHLY_BUILD_ARGS` environment variable. - ```bash - export EARTHLY_BUILD_ARGS="HELLO=world,FOO=bar" + export EARTH_BUILD_ARGS="HELLO=world,FOO=bar" earthly +hello ``` - This may be useful if you have a set of build args that you'd like to always use and would prefer not to have to specify them on the command line every time. The `EARTHLY_BUILD_ARGS` environment variable may also be stored in your `~/.bashrc` file, or some other shell-specific startup script. + This may be useful if you have a set of build args that you'd like to always use and would prefer not to have to specify them on the command line every time. The `EARTH_BUILD_ARGS` environment variable may also be stored in your `~/.bashrc` file, or some other shell-specific startup script. 4. From an `.arg` file It is also possible to create an `.arg` file to contain the build arguments to pass to earthly. First create an `.arg` file with: - + ``` name=eggplant ``` - + Then simply run earthly: - + ```bash earthly +hello ``` diff --git a/docs/guides/secrets.md b/docs/guides/secrets.md index 9c3ee1512d..3bba52bb5c 100644 --- a/docs/guides/secrets.md +++ b/docs/guides/secrets.md @@ -54,10 +54,10 @@ This is possible in a few ways: If the value of the secret is omitted on the command line Earthly will lookup the environment variable with that name. -3. Via the environment variable `EARTHLY_SECRETS` +3. Via the environment variable `EARTH_SECRETS` ```bash - export EARTHLY_SECRETS="passwd=itsasecret" + export EARTH_SECRETS="passwd=itsasecret" earthly +hush ``` @@ -66,13 +66,13 @@ This is possible in a few ways: 4. Via the `.secret` file. Create a `.secret` file in the same directory where you plan to run `earthly` from. Its contents should be: - + ``` passwd=itsasecret ``` - + Then simply run earthly: - + ```bash earthly +hello ``` diff --git a/util/envutil/is_true.go b/util/envutil/is_true.go index 96b6da6b82..b7eb1c67f7 100644 --- a/util/envutil/is_true.go +++ b/util/envutil/is_true.go @@ -14,3 +14,28 @@ func IsTrue(k string) bool { return true } } + +// LookupEnv checks for newKey first, then falls back to oldKey. +// Returns the value and whether either key was found. +func LookupEnv(newKey, oldKey string) (string, bool) { + if v, ok := os.LookupEnv(newKey); ok { + return v, true + } + + return os.LookupEnv(oldKey) +} + +// IsTrueWithFallback checks newKey first, then oldKey, returning true +// if either is set to a truthy value. +func IsTrueWithFallback(newKey, oldKey string) bool { + if v, ok := os.LookupEnv(newKey); ok { + switch v { + case "", "0", "false", "FALSE": + return false + default: + return true + } + } + + return IsTrue(oldKey) +} diff --git a/util/gitutil/detectgit.go b/util/gitutil/detectgit.go index 0f34ec1213..832396756d 100644 --- a/util/gitutil/detectgit.go +++ b/util/gitutil/detectgit.go @@ -24,6 +24,8 @@ var ( ErrCouldNotDetectGitHash = errors.New("Could not auto-detect or parse Git hash") // ErrCouldNotDetectGitShortHash is an error returned when git short hash could not be detected. ErrCouldNotDetectGitShortHash = errors.New("Could not auto-detect or parse Git short hash") + // ErrCouldNotDetectGitContentHash is an error returned when git tree hash could not be detected. + ErrCouldNotDetectGitContentHash = errors.New("Could not auto-detect or parse Git content hash") // ErrCouldNotDetectGitBranch is an error returned when git branch could not be detected. ErrCouldNotDetectGitBranch = errors.New("Could not auto-detect or parse Git branch") // ErrCouldNotDetectGitTags is an error returned when git tags could not be detected. @@ -40,6 +42,7 @@ type GitMetadata struct { GitURL string Hash string ShortHash string + ContentHash string RelDir string AuthorName string AuthorEmail string @@ -96,6 +99,12 @@ func Metadata(ctx context.Context, dir, gitBranchOverride string) (*GitMetadata, // Keep going. } + contentHash, err := detectGitContentHash(ctx, dir) + if err != nil { + retErr = err + // Keep going. + } + branch, err := detectGitBranch(ctx, dir, gitBranchOverride) if err != nil { retErr = err @@ -160,6 +169,7 @@ func Metadata(ctx context.Context, dir, gitBranchOverride string) (*GitMetadata, GitURL: gitURL, Hash: hash, ShortHash: shortHash, + ContentHash: contentHash, BranchOverrideTagArg: gitBranchOverride != "", Branch: branch, Tags: tags, @@ -181,6 +191,7 @@ func (gm *GitMetadata) Clone() *GitMetadata { GitURL: gm.GitURL, Hash: gm.Hash, ShortHash: gm.ShortHash, + ContentHash: gm.ContentHash, BranchOverrideTagArg: gm.BranchOverrideTagArg, Branch: gm.Branch, Tags: gm.Tags, @@ -266,38 +277,34 @@ func detectGitBaseDir(ctx context.Context, dir string) (string, error) { return strings.SplitN(outStr, "\n", 2)[0], nil } -func detectGitHash(ctx context.Context, dir string) (string, error) { - cmd := exec.CommandContext(ctx, "git", "rev-parse", "HEAD") +func gitRevParse(ctx context.Context, dir string, sentinel error, args ...string) (string, error) { + revParseArgs := append([]string{"rev-parse"}, args...) + cmd := exec.CommandContext(ctx, "git", revParseArgs...) //nolint:gosec cmd.Dir = dir out, err := cmd.Output() if err != nil { - return "", errors.Wrapf(ErrCouldNotDetectGitHash, "returned error %s: %s", err.Error(), string(out)) + return "", errors.Wrapf(sentinel, "returned error %s: %s", err.Error(), string(out)) } outStr := string(out) if outStr == "" { - return "", errors.Wrapf(ErrCouldNotDetectGitHash, "no remote origin url output") + return "", errors.Wrapf(sentinel, "no output") } return strings.SplitN(outStr, "\n", 2)[0], nil } -func detectGitShortHash(ctx context.Context, dir string) (string, error) { - cmd := exec.CommandContext(ctx, "git", "rev-parse", "--short=8", "HEAD") - cmd.Dir = dir - - out, err := cmd.Output() - if err != nil { - return "", errors.Wrapf(ErrCouldNotDetectGitShortHash, "returned error %s: %s", err.Error(), string(out)) - } +func detectGitHash(ctx context.Context, dir string) (string, error) { + return gitRevParse(ctx, dir, ErrCouldNotDetectGitHash, "HEAD") +} - outStr := string(out) - if outStr == "" { - return "", errors.Wrapf(ErrCouldNotDetectGitShortHash, "no remote origin url output") - } +func detectGitShortHash(ctx context.Context, dir string) (string, error) { + return gitRevParse(ctx, dir, ErrCouldNotDetectGitShortHash, "--short=8", "HEAD") +} - return strings.SplitN(outStr, "\n", 2)[0], nil +func detectGitContentHash(ctx context.Context, dir string) (string, error) { + return gitRevParse(ctx, dir, ErrCouldNotDetectGitContentHash, "HEAD^{tree}") } func detectGitBranch(ctx context.Context, dir, gitBranchOverride string) ([]string, error) { diff --git a/util/gitutil/detectgit_test.go b/util/gitutil/detectgit_test.go index 7851b8d427..88d48eaa16 100644 --- a/util/gitutil/detectgit_test.go +++ b/util/gitutil/detectgit_test.go @@ -1,6 +1,11 @@ package gitutil import ( + "context" + "os" + "os/exec" + "path/filepath" + "strings" "testing" ) @@ -52,3 +57,61 @@ func TestParseGitRemoteURL(t *testing.T) { Equal(t, test.expectedGitURL, gitURL) } } + +func TestDetectGitContentHash(t *testing.T) { + t.Parallel() + + // Create a temp git repo with a known commit. + dir := t.TempDir() + + run := func(args ...string) string { + t.Helper() + + cmd := exec.CommandContext(context.Background(), args[0], args[1:]...) //nolint:gosec // test helper + cmd.Dir = dir + + cmd.Env = append(os.Environ(), + "GIT_AUTHOR_NAME=test", + "GIT_AUTHOR_EMAIL=test@test.com", + "GIT_COMMITTER_NAME=test", + "GIT_COMMITTER_EMAIL=test@test.com", + ) + + out, err := cmd.CombinedOutput() + if err != nil { + t.Fatalf("%v failed: %s\n%s", args, err, out) + } + + return strings.TrimSpace(string(out)) + } + + run("git", "init") + run("git", "checkout", "-b", "main") + run("git", "config", "commit.gpgsign", "false") + + // Write a file and commit. + NoError(t, os.WriteFile(filepath.Join(dir, "hello.txt"), []byte("hello\n"), 0o600)) + run("git", "add", "hello.txt") + run("git", "commit", "--no-verify", "-m", "first") + + ctx := context.Background() + + contentHash, err := detectGitContentHash(ctx, dir) + NoError(t, err) + + // Tree hash for a single file "hello.txt" containing "hello\n" is deterministic. + Equal(t, "aaa96ced2d9a1c8e72c56b253a0e2fe78393feb7", contentHash) + + // The tree hash should differ from the commit hash. + commitHash := run("git", "rev-parse", "HEAD") + True(t, contentHash != commitHash) + + // Amend the commit (different commit hash, same tree hash). + run("git", "commit", "--no-verify", "--amend", "-m", "amended") + newCommitHash := run("git", "rev-parse", "HEAD") + True(t, commitHash != newCommitHash) + + contentHash2, err := detectGitContentHash(ctx, dir) + NoError(t, err) + Equal(t, contentHash, contentHash2) // tree hash unchanged +} diff --git a/variables/builtin.go b/variables/builtin.go index 456037b171..46c7bdf4fd 100644 --- a/variables/builtin.go +++ b/variables/builtin.go @@ -33,11 +33,15 @@ func BuiltinArgs( ) *Scope { ret := NewScope() ret.Add(arg.EarthlyTarget, target.StringCanonical()) + ret.Add(arg.EarthTarget, target.StringCanonical()) ret.Add(arg.EarthlyTargetProject, target.ProjectCanonical()) + ret.Add(arg.EarthTargetProject, target.ProjectCanonical()) targetNoTag := target targetNoTag.Tag = "" ret.Add(arg.EarthlyTargetProjectNoTag, targetNoTag.ProjectCanonical()) + ret.Add(arg.EarthTargetProjectNoTag, targetNoTag.ProjectCanonical()) ret.Add(arg.EarthlyTargetName, target.Target) + ret.Add(arg.EarthTargetName, target.Target) setTargetTag(ret, target, gitMeta) @@ -52,15 +56,19 @@ func BuiltinArgs( if ftrs.WaitBlock { ret.Add(arg.EarthlyPush, strconv.FormatBool(push)) + ret.Add(arg.EarthPush, strconv.FormatBool(push)) } if ftrs.EarthlyVersionArg { ret.Add(arg.EarthlyVersion, defaultArgs.EarthlyVersion) + ret.Add(arg.EarthVersion, defaultArgs.EarthlyVersion) ret.Add(arg.EarthlyBuildSha, defaultArgs.EarthlyBuildSha) + ret.Add(arg.EarthBuildSha, defaultArgs.EarthlyBuildSha) } if ftrs.EarthlyCIArg { ret.Add(arg.EarthlyCI, strconv.FormatBool(ci)) + ret.Add(arg.EarthCI, strconv.FormatBool(ci)) } if ftrs.EarthlyLocallyArg { @@ -69,53 +77,73 @@ func BuiltinArgs( if ftrs.EarthlyCIRunnerArg { ret.Add(arg.EarthlyCIRunner, strconv.FormatBool(false)) + ret.Add(arg.EarthCIRunner, strconv.FormatBool(false)) } if gitMeta == nil { // Ensure SOURCE_DATE_EPOCH is always available ret.Add(arg.EarthlySourceDateEpoch, "0") + ret.Add(arg.EarthSourceDateEpoch, "0") + return ret } // Populate git-related built-in args ret.Add(arg.EarthlyGitHash, gitMeta.Hash) + ret.Add(arg.EarthGitHash, gitMeta.Hash) ret.Add(arg.EarthlyGitShortHash, gitMeta.ShortHash) + ret.Add(arg.EarthGitShortHash, gitMeta.ShortHash) + ret.Add(arg.EarthGitContentHash, gitMeta.ContentHash) branch := firstOrZero(gitMeta.Branch) ret.Add(arg.EarthlyGitBranch, branch) + ret.Add(arg.EarthGitBranch, branch) tag := firstOrZero(gitMeta.Tags) ret.Add(arg.EarthlyGitTag, tag) + ret.Add(arg.EarthGitTag, tag) ret.Add(arg.EarthlyGitOriginURL, gitMeta.RemoteURL) + ret.Add(arg.EarthGitOriginURL, gitMeta.RemoteURL) ret.Add(arg.EarthlyGitOriginURLScrubbed, stringutil.ScrubCredentials(gitMeta.RemoteURL)) + ret.Add(arg.EarthGitOriginURLScrubbed, stringutil.ScrubCredentials(gitMeta.RemoteURL)) ret.Add(arg.EarthlyGitProjectName, getProjectName(gitMeta.RemoteURL)) + ret.Add(arg.EarthGitProjectName, getProjectName(gitMeta.RemoteURL)) ret.Add(arg.EarthlyGitCommitTimestamp, gitMeta.CommitterTimestamp) + ret.Add(arg.EarthGitCommitTimestamp, gitMeta.CommitterTimestamp) if ftrs.GitCommitAuthorTimestamp { ret.Add(arg.EarthlyGitCommitAuthorTimestamp, gitMeta.AuthorTimestamp) + ret.Add(arg.EarthGitCommitAuthorTimestamp, gitMeta.AuthorTimestamp) } ret.Add(arg.EarthlySourceDateEpoch, max(gitMeta.CommitterTimestamp, "0")) + ret.Add(arg.EarthSourceDateEpoch, max(gitMeta.CommitterTimestamp, "0")) if ftrs.EarthlyGitAuthorArgs { ret.Add(arg.EarthlyGitAuthor, gitMeta.AuthorEmail) + ret.Add(arg.EarthGitAuthor, gitMeta.AuthorEmail) ret.Add(arg.EarthlyGitCoAuthors, strings.Join(gitMeta.CoAuthors, " ")) + ret.Add(arg.EarthGitCoAuthors, strings.Join(gitMeta.CoAuthors, " ")) } if ftrs.GitAuthorEmailNameArgs { if gitMeta.AuthorName != "" && gitMeta.AuthorEmail != "" { ret.Add(arg.EarthlyGitAuthor, fmt.Sprintf("%s <%s>", gitMeta.AuthorName, gitMeta.AuthorEmail)) + ret.Add(arg.EarthGitAuthor, fmt.Sprintf("%s <%s>", gitMeta.AuthorName, gitMeta.AuthorEmail)) } ret.Add(arg.EarthlyGitAuthorEmail, gitMeta.AuthorEmail) + ret.Add(arg.EarthGitAuthorEmail, gitMeta.AuthorEmail) ret.Add(arg.EarthlyGitAuthorName, gitMeta.AuthorName) + ret.Add(arg.EarthGitAuthorName, gitMeta.AuthorName) } if ftrs.GitRefs { ret.Add(arg.EarthlyGitRefs, strings.Join(gitMeta.Refs, " ")) + ret.Add(arg.EarthGitRefs, strings.Join(gitMeta.Refs, " ")) } return ret @@ -150,6 +178,7 @@ func setNativePlatformArgs(s *Scope, platr *platutil.Resolver) { // SetLocally sets the locally built-in arg value. func SetLocally(s *Scope, locally bool) { s.Add(arg.EarthlyLocally, strconv.FormatBool(locally)) + s.Add(arg.EarthLocally, strconv.FormatBool(locally)) } // getProjectName returns the deprecated PROJECT_NAME value. @@ -187,13 +216,17 @@ func setTargetTag(ret *Scope, target domain.Target, gitMeta *gitutil.GitMetadata if gitMeta != nil && gitMeta.BranchOverrideTagArg && len(gitMeta.Branch) > 0 { branch := gitMeta.Branch[0] ret.Add(arg.EarthlyTargetTag, branch) + ret.Add(arg.EarthTargetTag, branch) ret.Add(arg.EarthlyTargetTagDocker, llbutil.DockerTagSafe(branch)) + ret.Add(arg.EarthTargetTagDocker, llbutil.DockerTagSafe(branch)) return } ret.Add(arg.EarthlyTargetTag, target.Tag) + ret.Add(arg.EarthTargetTag, target.Tag) ret.Add(arg.EarthlyTargetTagDocker, llbutil.DockerTagSafe(target.Tag)) + ret.Add(arg.EarthTargetTagDocker, llbutil.DockerTagSafe(target.Tag)) } // firstOrZero returns the first element of a slice or the zero value if the slice is empty. diff --git a/variables/builtin_test.go b/variables/builtin_test.go index fa861d8d11..23d6b9de04 100644 --- a/variables/builtin_test.go +++ b/variables/builtin_test.go @@ -2,6 +2,11 @@ package variables import ( "testing" + + "github.com/EarthBuild/earthbuild/domain" + "github.com/EarthBuild/earthbuild/features" + "github.com/EarthBuild/earthbuild/util/gitutil" + arg "github.com/EarthBuild/earthbuild/variables/reserved" ) func TestGetProjectName(t *testing.T) { @@ -71,3 +76,31 @@ func TestGetProjectName(t *testing.T) { Equal(t, tt.safe, ans) } } + +func TestBuiltinArgsContentHash(t *testing.T) { + t.Parallel() + + ftrs := &features.Features{} + gitMeta := &gitutil.GitMetadata{ + Hash: "abc123def456abc123def456abc123def456abc1", + ShortHash: "abc123de", + ContentHash: "deadbeef01234567890abcdef01234567890abcd", + } + + scope := BuiltinArgs(domain.Target{Target: "test"}, nil, gitMeta, DefaultArgs{}, ftrs, false, false) + + val, found := scope.Get(arg.EarthGitContentHash) + Equal(t, true, found) + Equal(t, "deadbeef01234567890abcdef01234567890abcd", val) +} + +func TestBuiltinArgsContentHashNilGitMeta(t *testing.T) { + t.Parallel() + + ftrs := &features.Features{} + + scope := BuiltinArgs(domain.Target{Target: "test"}, nil, nil, DefaultArgs{}, ftrs, false, false) + + _, found := scope.Get(arg.EarthGitContentHash) + Equal(t, false, found) +} diff --git a/variables/reserved/names.go b/variables/reserved/names.go index 414a876509..ccb801d46d 100644 --- a/variables/reserved/names.go +++ b/variables/reserved/names.go @@ -1,6 +1,34 @@ package reserved const ( + EarthGitAuthor = "EARTH_GIT_AUTHOR" + EarthGitAuthorEmail = "EARTH_GIT_AUTHOR_EMAIL" + EarthGitAuthorName = "EARTH_GIT_AUTHOR_NAME" + EarthGitBranch = "EARTH_GIT_BRANCH" + EarthGitCoAuthors = "EARTH_GIT_CO_AUTHORS" + EarthGitCommitAuthorTimestamp = "EARTH_GIT_COMMIT_AUTHOR_TIMESTAMP" + EarthGitCommitTimestamp = "EARTH_GIT_COMMIT_TIMESTAMP" + EarthGitContentHash = "EARTH_GIT_CONTENT_HASH" + EarthGitHash = "EARTH_GIT_HASH" + EarthGitOriginURL = "EARTH_GIT_ORIGIN_URL" + EarthGitOriginURLScrubbed = "EARTH_GIT_ORIGIN_URL_SCRUBBED" + EarthGitProjectName = "EARTH_GIT_PROJECT_NAME" + EarthGitRefs = "EARTH_GIT_REFS" + EarthGitShortHash = "EARTH_GIT_SHORT_HASH" + EarthGitTag = "EARTH_GIT_TAG" + EarthSourceDateEpoch = "EARTH_SOURCE_DATE_EPOCH" + EarthBuildSha = "EARTH_BUILD_SHA" + EarthLocally = "EARTH_LOCALLY" + EarthPush = "EARTH_PUSH" + EarthCI = "EARTH_CI" + EarthCIRunner = "EARTH_CI_RUNNER" + EarthTarget = "EARTH_TARGET" + EarthTargetName = "EARTH_TARGET_NAME" + EarthTargetProject = "EARTH_TARGET_PROJECT" + EarthTargetProjectNoTag = "EARTH_TARGET_PROJECT_NO_TAG" + EarthTargetTag = "EARTH_TARGET_TAG" + EarthTargetTagDocker = "EARTH_TARGET_TAG_DOCKER" + EarthVersion = "EARTH_VERSION" EarthlyBuildSha = "EARTHLY_BUILD_SHA" EarthlyGitBranch = "EARTHLY_GIT_BRANCH" EarthlyGitCommitTimestamp = "EARTHLY_GIT_COMMIT_TIMESTAMP" @@ -46,6 +74,34 @@ var args map[string]struct{} func init() { args = map[string]struct{}{ + EarthGitAuthor: {}, + EarthGitAuthorEmail: {}, + EarthGitAuthorName: {}, + EarthGitBranch: {}, + EarthGitCoAuthors: {}, + EarthGitCommitAuthorTimestamp: {}, + EarthGitCommitTimestamp: {}, + EarthGitContentHash: {}, + EarthGitHash: {}, + EarthGitOriginURL: {}, + EarthGitOriginURLScrubbed: {}, + EarthGitProjectName: {}, + EarthGitRefs: {}, + EarthGitShortHash: {}, + EarthGitTag: {}, + EarthSourceDateEpoch: {}, + EarthBuildSha: {}, + EarthLocally: {}, + EarthPush: {}, + EarthCI: {}, + EarthCIRunner: {}, + EarthTarget: {}, + EarthTargetName: {}, + EarthTargetProject: {}, + EarthTargetProjectNoTag: {}, + EarthTargetTag: {}, + EarthTargetTagDocker: {}, + EarthVersion: {}, EarthlyBuildSha: {}, EarthlyGitBranch: {}, EarthlyGitCommitTimestamp: {},