Skip to content

Commit

Permalink
allow automatically versioning images using git
Browse files Browse the repository at this point in the history
  • Loading branch information
dskiff committed Apr 27, 2024
1 parent 65e20a5 commit 84a10cc
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 12 deletions.
3 changes: 1 addition & 2 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,9 @@ jobs:
- name: Publish (latest)
run: |
tko build \
--annotations "org.opencontainers.image.revision=${{ github.sha }}" \
--annotations "org.opencontainers.image.version=dev-${{ github.sha }}" \
--target-repo "ghcr.io/dskiff/tko:latest" \
--entrypoint "/bin/bash" \
--auto-version-annotation=git \
"./dist/tko_linux_amd64_v1"
env:
TKO_BASE_REF: debian:bookworm-slim@sha256:155280b00ee0133250f7159b567a07d7cd03b1645714c3a7458b2287b0ca83cb
Expand Down
6 changes: 2 additions & 4 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,7 @@ jobs:
- name: Publish (bin)
run: |
tko build \
--annotations "org.opencontainers.image.revision=${{ github.sha }}" \
--annotations "org.opencontainers.image.version=${{ github.ref_name }}" \
--auto-version-annotation=git \
--base-ref "scratch" \
--target-repo "ghcr.io/dskiff/tko:bin" \
"./dist/tko_linux_amd64_v1"
Expand All @@ -48,8 +47,7 @@ jobs:
- name: Publish (version)
run: |
tko build \
--annotations "org.opencontainers.image.revision=${{ github.sha }}" \
--annotations "org.opencontainers.image.version=${{ github.ref_name }}" \
--auto-version-annotation=git \
--target-repo "ghcr.io/dskiff/tko:${{ github.ref_name }}" \
--entrypoint "/bin/bash" \
"./dist/tko_linux_amd64_v1"
Expand Down
52 changes: 46 additions & 6 deletions pkg/cmd/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"fmt"
"log"
"os"
"regexp"

"gopkg.in/yaml.v3"

Expand All @@ -27,9 +28,10 @@ type BuildCmd struct {
TargetRepo string `short:"t" help:"Target repository" env:"TKO_TARGET_REPO" required:"true"`
TargetType string `short:"T" help:"Target type" env:"TKO_TARGET_TYPE" default:"REMOTE" enum:"REMOTE,LOCAL_DAEMON,LOCAL_FILE"`

Author string `help:"Author of the build" env:"TKO_AUTHOR" default:"github.com/dskiff/tko"`
DefaultAnnotations map[string]string `short:"A" help:"Default annotations to apply to the image" env:"TKO_DEFAULT_ANNOTATIONS" default:"" mapsep:"," sep:"="`
Annotations map[string]string `short:"a" help:"Additional annotations to apply to the image. Can override default-annotations." env:"TKO_ANNOTATIONS" default:"" mapsep:"," sep:"="`
Author string `help:"Author of the build" env:"TKO_AUTHOR" default:"github.com/dskiff/tko"`
DefaultAnnotations map[string]string `short:"A" help:"Default annotations to apply to the image" env:"TKO_DEFAULT_ANNOTATIONS" default:"" mapsep:"," sep:"="`
Annotations map[string]string `short:"a" help:"Additional annotations to apply to the image. Can override default-annotations." env:"TKO_ANNOTATIONS" default:"" mapsep:"," sep:"="`
AutoVersionAnnotation string `help:"Automatically version annotations" env:"TKO_AUTO_VERSION_ANNOTATION" default:"none" enum:"git,none"`

RegistryUser string `help:"Registry user. Used for target registry url. You can use standard docker config for more complex auth." env:"TKO_REGISTRY_USER"`
RegistryPass string `help:"Registry password. Used for target registry url. You can use standard docker config for more complex auth." env:"TKO_REGISTRY_PASS"`
Expand All @@ -39,6 +41,8 @@ type BuildCmd struct {
}

func (b *BuildCmd) Run(cliCtx *CliCtx) error {
log.Printf("tko %s (%s) built on %s\n", cliCtx.TkoBuildVersion, cliCtx.TkoBuildCommit, cliCtx.TkoBuildDate)

targetType, err := build.ParseTargetType(b.TargetType)
if err != nil {
return err
Expand All @@ -65,8 +69,45 @@ func (b *BuildCmd) Run(cliCtx *CliCtx) error {
}
keychain := authn.NewMultiKeychain(keychains...)

// Annotations would ideally be merged by kong, but this works too
annotations := make(map[string]string)
if b.AutoVersionAnnotation == "git" {
gitInfo, err := getGitInfo(b.SourcePath)
if err != nil {
return fmt.Errorf("failed to get git info: %w", err)
}

gitInfoStr, err := yaml.Marshal(gitInfo)
if err != nil {
return fmt.Errorf("failed to marshal git info: %w", err)
}
log.Print("Found git info:", "\n"+string(gitInfoStr))

if len(gitInfo.Tag) > 1 {
return fmt.Errorf("multiple tags found for commit %s: %v", gitInfo.CommitHash, gitInfo.Tag)
}

revision := gitInfo.CommitHash
if gitInfo.Dirty {
revision += "-dirty"
}

gitVersion := "snapshot-" + gitInfo.CommitHash
if len(gitInfo.Tag) == 1 {
gitVersion = gitInfo.Tag[0]
isValid := regexp.MustCompile(`^\d+\.\d+\.\d+$`).MatchString(gitVersion)
if !isValid {
return fmt.Errorf("tag %s is not a valid version", gitVersion)
}
}
if gitInfo.Dirty {
gitVersion += "-dirty"
}

annotations["org.opencontainers.image.revision"] = revision
annotations["org.opencontainers.image.version"] = gitVersion
}

// Annotations would ideally be merged by kong, but this works too
for k, v := range b.DefaultAnnotations {
annotations[k] = v
}
Expand Down Expand Up @@ -97,8 +138,7 @@ func (b *BuildCmd) Run(cliCtx *CliCtx) error {
return err
}

log.Printf("tko %s (%s) built on %s\n", cliCtx.TkoBuildVersion, cliCtx.TkoBuildCommit, cliCtx.TkoBuildDate)
log.Println("Build configuration:", "\n"+string(out))
log.Print("Build configuration:", "\n"+string(out))

// Enable go-containerregistry logging
logs.Warn.SetOutput(os.Stderr)
Expand Down
58 changes: 58 additions & 0 deletions pkg/cmd/git.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package cmd

import (
"fmt"
"os/exec"
"strings"
)

type GitInfo struct {
Dirty bool
CommitHash string
Tag []string
}

func getGitInfo(path string) (*GitInfo, error) {
output, err := run(path, "git", "rev-parse", "HEAD")
if err != nil {
return nil, fmt.Errorf("failed to get HEAD sha: %w", err)
}
sha := strings.TrimSpace(string(output))

output, err = run(path, "git", "status", "--porcelain")
if err != nil {
return nil, fmt.Errorf("failed to check if git is dirty: %w", err)
}
dirty := len(strings.TrimSpace(string(output))) > 0

output, err = run(path, "git", "tag", "--points-at", sha)
if err != nil {
return nil, fmt.Errorf("failed to get tags: %w", err)
}
tags := strings.Split(string(output), "\n")
for i := range tags {
tags[i] = strings.TrimSpace(tags[i])
}

// trim empty string at the end
if tags[len(tags)-1] == "" {
tags = tags[:len(tags)-1]
}

return &GitInfo{
Dirty: dirty,
CommitHash: sha,
Tag: tags,
}, nil
}

func run(path string, args ...string) (string, error) {
cmd := exec.Command(args[0], args[1:]...)
cmd.Dir = path

output, err := cmd.CombinedOutput()
if err != nil {
return "", fmt.Errorf("failed to run %s: %w", strings.Join(args, " "), err)
}
return string(output), nil
}

0 comments on commit 84a10cc

Please sign in to comment.