Skip to content
Merged
136 changes: 31 additions & 105 deletions buildcontext/git.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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 ; " +
Expand Down Expand Up @@ -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
Expand All @@ -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

Expand All @@ -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

Expand All @@ -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

Expand All @@ -537,6 +462,7 @@ func (gr *gitResolver) resolveGitProject(
rgp = &resolvedGitProject{
hash: gitHash,
shortHash: gitShortHash,
contentHash: gitContentHash,
branches: gitBranches2,
tags: gitTags2,
committerTs: gitCommitterTs,
Expand All @@ -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,
Expand Down
8 changes: 4 additions & 4 deletions docs/caching/caching-in-earthfiles.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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" ]
Expand Down Expand Up @@ -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

Expand Down
39 changes: 23 additions & 16 deletions docs/earthfile/builtin-args.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,16 @@ 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"
```

{% endhint %}

### General args
Expand All @@ -43,26 +46,27 @@ RUN echo "The current target is $EARTHLY_TARGET"

### 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 <john@example.com>` 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 <jane@example.com Jack Smith <jack@example.com>` | |
| `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 <john@example.com>` 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 <jane@example.com Jack Smith <jack@example.com>` | |
| `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` |
Expand All @@ -81,7 +85,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
Expand All @@ -90,4 +96,5 @@ my-target:
ARG TARGETPLATFORM
RUN echo "The target platform under LOCALLY is $TARGETPLATFORM"
```

{% endhint %}
Loading
Loading