Skip to content

Commit

Permalink
feat: Add git common build template params
Browse files Browse the repository at this point in the history
This includes a number of template parameters supported by [goreleaser](https://goreleaser.com/customization/templates/). Specifically, the build date information and the majority of the Git params.

Fixes #493

Signed-off-by: Nathan Mittler <nmittler@aviatrix.com>
  • Loading branch information
nmittler committed May 12, 2024
1 parent bde269b commit 2cd8f0e
Show file tree
Hide file tree
Showing 9 changed files with 1,168 additions and 5 deletions.
27 changes: 22 additions & 5 deletions pkg/build/gobuild.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ import (
"strconv"
"strings"
"text/template"
"time"

"github.com/google/go-containerregistry/pkg/name"
v1 "github.com/google/go-containerregistry/pkg/v1"
Expand All @@ -40,6 +41,7 @@ import (
"github.com/google/go-containerregistry/pkg/v1/types"
"github.com/google/ko/internal/sbom"
"github.com/google/ko/pkg/caps"
"github.com/google/ko/pkg/internal/git"
specsv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/sigstore/cosign/v2/pkg/oci"
ocimutate "github.com/sigstore/cosign/v2/pkg/oci/mutate"
Expand All @@ -56,6 +58,11 @@ const (

defaultGoBin = "go" // defaults to first go binary found in PATH
goBinPathEnv = "KO_GO_PATH" // env lookup for optional relative or full go binary path

envTemplateKey = "Env"
gitTemplateKey = "Git"
dateTemplateKey = "Date"
timestampTemplateKey = "Timestamp"
)

// GetBase takes an importpath and returns a base image reference and base image (or index).
Expand Down Expand Up @@ -252,7 +259,7 @@ func getGoBinary() string {
}

func build(ctx context.Context, ip string, dir string, platform v1.Platform, config Config) (string, error) {
buildArgs, err := createBuildArgs(config)
buildArgs, err := createBuildArgs(ctx, config)
if err != nil {
return "", err
}
Expand Down Expand Up @@ -708,7 +715,7 @@ func (g *gobuild) tarKoData(ref reference, platform *v1.Platform) (*bytes.Buffer
return buf, walkRecursive(tw, root, chroot, creationTime, platform)
}

func createTemplateData() map[string]interface{} {
func createTemplateData(ctx context.Context) map[string]interface{} {
envVars := map[string]string{
"LDFLAGS": "",
}
Expand All @@ -717,8 +724,18 @@ func createTemplateData() map[string]interface{} {
envVars[kv[0]] = kv[1]
}

// Get the git information, if available.
info, err := git.GetInfo(ctx)
if err != nil {
log.Printf("%v", err)
}

date := time.Now()
return map[string]interface{}{
"Env": envVars,
envTemplateKey: envVars,
gitTemplateKey: info.TemplateValue(),
dateTemplateKey: date.Format(time.RFC3339),
timestampTemplateKey: date.UTC().Unix(),
}
}

Expand All @@ -741,10 +758,10 @@ func applyTemplating(list []string, data map[string]interface{}) ([]string, erro
return result, nil
}

func createBuildArgs(buildCfg Config) ([]string, error) {
func createBuildArgs(ctx context.Context, buildCfg Config) ([]string, error) {
var args []string

data := createTemplateData()
data := createTemplateData(ctx)

if len(buildCfg.Flags) > 0 {
flags, err := applyTemplating(buildCfg.Flags, data)
Expand Down
64 changes: 64 additions & 0 deletions pkg/internal/git/errors.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// Copyright 2024 ko Build Authors All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// MIT License
//
// Copyright (c) 2016-2022 Carlos Alexandro Becker
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package git

import (
"errors"
"fmt"
)

var (
// ErrNoTag happens if the underlying git repository doesn't contain any tags
// but no snapshot-release was requested.
ErrNoTag = errors.New("git doesn't contain any tags. Tag info will not be available")

// ErrNotRepository happens if you try to run ko against a folder
// which is not a git repository.
ErrNotRepository = errors.New("current folder is not a git repository. Git info will not be available")

// ErrNoGit happens when git is not present in PATH.
ErrNoGit = errors.New("git not present in PATH. Git info will not be available")
)

// ErrDirty happens when the repo has uncommitted/unstashed changes.
type ErrDirty struct {
status string
}

func (e ErrDirty) Error() string {
return fmt.Sprintf("git is in a dirty state\nPlease check in your pipeline what can be changing the following files:\n%v\n", e.status)
}
106 changes: 106 additions & 0 deletions pkg/internal/git/git.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
// Copyright 2024 ko Build Authors All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// MIT License
//
// Copyright (c) 2016-2022 Carlos Alexandro Becker
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.

package git

import (
"bytes"
"context"
"errors"
"os/exec"
"strings"
)

// isRepo returns true if current folder is a git repository.
func isRepo(ctx context.Context) bool {
out, err := run(ctx, "rev-parse", "--is-inside-work-tree")
return err == nil && strings.TrimSpace(out) == "true"
}

func runWithEnv(ctx context.Context, env []string, args ...string) (string, error) {
extraArgs := []string{
"-c", "log.showSignature=false",
}
args = append(extraArgs, args...)
/* #nosec */
cmd := exec.CommandContext(ctx, "git", args...)

stdout := bytes.Buffer{}
stderr := bytes.Buffer{}

cmd.Stdout = &stdout
cmd.Stderr = &stderr
cmd.Env = append(cmd.Env, env...)

err := cmd.Run()

if err != nil {
return "", errors.New(stderr.String())
}

return stdout.String(), nil
}

// run runs a git command and returns its output or errors.
func run(ctx context.Context, args ...string) (string, error) {
return runWithEnv(ctx, []string{}, args...)
}

// clean the output.
func clean(output string, err error) (string, error) {
output = strings.ReplaceAll(strings.Split(output, "\n")[0], "'", "")
if err != nil {
err = errors.New(strings.TrimSuffix(err.Error(), "\n"))
}
return output, err
}

// cleanAllLines returns all the non-empty lines of the output, cleaned up.
func cleanAllLines(output string, err error) ([]string, error) {
var result []string
for _, line := range strings.Split(output, "\n") {
l := strings.TrimSpace(strings.ReplaceAll(line, "'", ""))
if l == "" {
continue
}
result = append(result, l)
}
// TODO: maybe check for exec.ExitError only?
if err != nil {
err = errors.New(strings.TrimSuffix(err.Error(), "\n"))
}
return result, err
}
Loading

0 comments on commit 2cd8f0e

Please sign in to comment.