You've already forked goreleaser
mirror of
https://github.com/goreleaser/goreleaser.git
synced 2025-09-16 09:26:52 +02:00
feat: add ko support (#3653)
continuing the PR by @developer-guy - [x] should be a publisher, as it does publish the images it builds every time - [x] `Default` method does not work - [x] the `fromConfig` thing should probably be on the defaults, too - [x] wire `--skip-ko` - [x] documentation - [x] more tests - [x] use same registry as docker tests does - [ ] see if we can make the log output match goreleaser's - [ ] ?? closes #2556 closes #3490 Signed-off-by: Batuhan Apaydın <batuhan.apaydin@trendyol.com> Signed-off-by: Carlos A Becker <caarlos0@users.noreply.github.com> Co-authored-by: actions-user <actions@github.com> Co-authored-by: Jason Hall <jason@chainguard.dev> Co-authored-by: Batuhan Apaydın <batuhan.apaydin@trendyol.com>
This commit is contained in:
committed by
GitHub
parent
48f77f9ea4
commit
2450746e5c
@@ -36,6 +36,7 @@ type releaseOpts struct {
|
||||
skipAnnounce bool
|
||||
skipSBOMCataloging bool
|
||||
skipDocker bool
|
||||
skipKo bool
|
||||
skipBefore bool
|
||||
rmDist bool
|
||||
deprecated bool
|
||||
@@ -77,6 +78,7 @@ func newReleaseCmd() *releaseCmd {
|
||||
cmd.Flags().BoolVar(&root.opts.skipSign, "skip-sign", false, "Skips signing artifacts")
|
||||
cmd.Flags().BoolVar(&root.opts.skipSBOMCataloging, "skip-sbom", false, "Skips cataloging artifacts")
|
||||
cmd.Flags().BoolVar(&root.opts.skipDocker, "skip-docker", false, "Skips Docker Images/Manifests builds")
|
||||
cmd.Flags().BoolVar(&root.opts.skipKo, "skip-ko", false, "Skips Ko builds")
|
||||
cmd.Flags().BoolVar(&root.opts.skipBefore, "skip-before", false, "Skips global before hooks")
|
||||
cmd.Flags().BoolVar(&root.opts.skipValidate, "skip-validate", false, "Skips git checks")
|
||||
cmd.Flags().BoolVar(&root.opts.rmDist, "rm-dist", false, "Removes the dist folder")
|
||||
@@ -137,6 +139,7 @@ func setupReleaseContext(ctx *context.Context, options releaseOpts) {
|
||||
ctx.SkipSign = options.skipSign
|
||||
ctx.SkipSBOMCataloging = options.skipSBOMCataloging
|
||||
ctx.SkipDocker = options.skipDocker
|
||||
ctx.SkipKo = options.skipKo
|
||||
ctx.SkipBefore = options.skipBefore
|
||||
ctx.RmDist = options.rmDist
|
||||
|
||||
|
80
go.mod
80
go.mod
@@ -6,6 +6,7 @@ require (
|
||||
code.gitea.io/sdk/gitea v0.15.1
|
||||
github.com/Masterminds/semver/v3 v3.2.0
|
||||
github.com/atc0005/go-teams-notify/v2 v2.7.0
|
||||
github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20220517224237-e6f29200ae04
|
||||
github.com/caarlos0/ctrlc v1.2.0
|
||||
github.com/caarlos0/env/v6 v6.10.1
|
||||
github.com/caarlos0/go-reddit/v3 v3.0.1
|
||||
@@ -13,12 +14,16 @@ require (
|
||||
github.com/caarlos0/log v0.2.1
|
||||
github.com/charmbracelet/keygen v0.3.0
|
||||
github.com/charmbracelet/lipgloss v0.6.0
|
||||
github.com/chrismellard/docker-credential-acr-env v0.0.0-20220327082430-c57b701bfc08
|
||||
github.com/dghubble/go-twitter v0.0.0-20211115160449-93a8679adecb
|
||||
github.com/dghubble/oauth1 v0.7.2
|
||||
github.com/disgoorg/disgo v0.14.1
|
||||
github.com/disgoorg/snowflake/v2 v2.0.1
|
||||
github.com/distribution/distribution/v3 v3.0.0-20221021092657-c47a966fded8
|
||||
github.com/go-telegram-bot-api/telegram-bot-api v4.6.4+incompatible
|
||||
github.com/google/go-containerregistry v0.11.0
|
||||
github.com/google/go-github/v48 v48.2.0
|
||||
github.com/google/ko v0.12.0
|
||||
github.com/google/uuid v1.3.0
|
||||
github.com/goreleaser/fileglob v1.3.0
|
||||
github.com/goreleaser/nfpm/v2 v2.23.0
|
||||
@@ -44,6 +49,7 @@ require (
|
||||
golang.org/x/oauth2 v0.4.0
|
||||
golang.org/x/sync v0.1.0
|
||||
golang.org/x/text v0.6.0
|
||||
golang.org/x/tools v0.2.0
|
||||
gopkg.in/mail.v2 v2.3.1
|
||||
gopkg.in/yaml.v3 v3.0.1
|
||||
)
|
||||
@@ -56,6 +62,7 @@ require (
|
||||
cloud.google.com/go/kms v1.7.0 // indirect
|
||||
cloud.google.com/go/storage v1.28.0 // indirect
|
||||
github.com/AlekSi/pointer v1.2.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go v66.0.0+incompatible // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.2.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.2.0 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 // indirect
|
||||
@@ -64,8 +71,16 @@ require (
|
||||
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v0.6.1 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect
|
||||
github.com/Azure/go-autorest v14.2.0+incompatible // indirect
|
||||
github.com/Azure/go-autorest/autorest v0.11.28 // indirect
|
||||
github.com/Azure/go-autorest/autorest/adal v0.9.21 // indirect
|
||||
github.com/Azure/go-autorest/autorest/azure/auth v0.5.11 // indirect
|
||||
github.com/Azure/go-autorest/autorest/azure/cli v0.4.6 // indirect
|
||||
github.com/Azure/go-autorest/autorest/date v0.3.0 // indirect
|
||||
github.com/Azure/go-autorest/autorest/to v0.4.0 // indirect
|
||||
github.com/Azure/go-autorest/logger v0.2.1 // indirect
|
||||
github.com/Azure/go-autorest/tracing v0.6.0 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v0.7.0 // indirect
|
||||
github.com/BurntSushi/toml v1.1.0 // indirect
|
||||
github.com/Masterminds/goutils v1.1.1 // indirect
|
||||
github.com/Masterminds/semver v1.5.0 // indirect
|
||||
github.com/Masterminds/sprig v2.22.0+incompatible // indirect
|
||||
@@ -73,6 +88,8 @@ require (
|
||||
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 // indirect
|
||||
github.com/ProtonMail/go-crypto v0.0.0-20210512092938-c05353c2d58c // indirect
|
||||
github.com/acomagu/bufpipe v1.0.3 // indirect
|
||||
github.com/alessio/shellescape v1.4.1 // indirect
|
||||
github.com/asaskevich/govalidator v0.0.0-20210307081110-f21760c49a8d // indirect
|
||||
github.com/aws/aws-sdk-go v1.44.151 // indirect
|
||||
github.com/aws/aws-sdk-go-v2 v1.17.1 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.4.9 // indirect
|
||||
@@ -84,6 +101,8 @@ require (
|
||||
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.4.19 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/ini v1.3.26 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/internal/v4a v1.0.16 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ecr v1.17.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.13.5 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.9.10 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.1.20 // indirect
|
||||
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.9.19 // indirect
|
||||
@@ -95,24 +114,43 @@ require (
|
||||
github.com/aws/aws-sdk-go-v2/service/sts v1.17.5 // indirect
|
||||
github.com/aws/smithy-go v1.13.4 // indirect
|
||||
github.com/aymanbagabas/go-osc52 v1.2.1 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/blakesmith/ar v0.0.0-20190502131153-809d4375e1fb // indirect
|
||||
github.com/caarlos0/sshmarshal v0.0.0-20220308164159-9ddb9f83c6b3 // indirect
|
||||
github.com/cavaliergopher/cpio v1.0.1 // indirect
|
||||
github.com/cenkalti/backoff/v4 v4.1.3 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/containerd/continuity v0.3.0 // indirect
|
||||
github.com/containerd/stargz-snapshotter/estargz v0.12.0 // indirect
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.2 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/dghubble/sling v1.4.0 // indirect
|
||||
github.com/dimchansky/utfbom v1.1.1 // indirect
|
||||
github.com/disgoorg/json v1.0.0 // indirect
|
||||
github.com/disgoorg/log v1.2.0 // indirect
|
||||
github.com/docker/cli v20.10.14+incompatible // indirect
|
||||
github.com/docker/cli v20.10.17+incompatible // indirect
|
||||
github.com/docker/distribution v2.8.1+incompatible // indirect
|
||||
github.com/docker/docker v20.10.21+incompatible // indirect
|
||||
github.com/docker/docker-credential-helpers v0.6.4 // indirect
|
||||
github.com/docker/go-connections v0.4.0 // indirect
|
||||
github.com/docker/go-metrics v0.0.1 // indirect
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/emirpasic/gods v1.12.0 // indirect
|
||||
github.com/evanphx/json-patch/v5 v5.6.0 // indirect
|
||||
github.com/fsnotify/fsnotify v1.6.0 // indirect
|
||||
github.com/go-git/gcfg v1.5.0 // indirect
|
||||
github.com/go-git/go-billy/v5 v5.3.1 // indirect
|
||||
github.com/go-git/go-git/v5 v5.4.2 // indirect
|
||||
github.com/go-openapi/analysis v0.21.4 // indirect
|
||||
github.com/go-openapi/errors v0.20.3 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.19.5 // indirect
|
||||
github.com/go-openapi/jsonreference v0.20.0 // indirect
|
||||
github.com/go-openapi/loads v0.21.2 // indirect
|
||||
github.com/go-openapi/runtime v0.24.2 // indirect
|
||||
github.com/go-openapi/spec v0.20.7 // indirect
|
||||
github.com/go-openapi/strfmt v0.21.3 // indirect
|
||||
github.com/go-openapi/swag v0.22.3 // indirect
|
||||
github.com/go-openapi/validate v0.22.0 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang-jwt/jwt/v4 v4.4.3 // indirect
|
||||
@@ -125,22 +163,29 @@ require (
|
||||
github.com/googleapis/enterprise-certificate-proxy v0.2.0 // indirect
|
||||
github.com/googleapis/gax-go/v2 v2.7.0 // indirect
|
||||
github.com/goreleaser/chglog v0.2.2 // indirect
|
||||
github.com/gorilla/mux v1.8.0 // indirect
|
||||
github.com/gorilla/websocket v1.5.0 // indirect
|
||||
github.com/hashicorp/errwrap v1.1.0 // indirect
|
||||
github.com/hashicorp/go-cleanhttp v0.5.2 // indirect
|
||||
github.com/hashicorp/go-retryablehttp v0.7.1 // indirect
|
||||
github.com/hashicorp/go-version v1.2.1 // indirect
|
||||
github.com/hashicorp/go-version v1.6.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/huandu/xstrings v1.3.2 // indirect
|
||||
github.com/iancoleman/orderedmap v0.2.0 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.0.1 // indirect
|
||||
github.com/jbenet/go-context v0.0.0-20150711004518-d14ea06fba99 // indirect
|
||||
github.com/jmespath/go-jmespath v0.4.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/kevinburke/ssh_config v1.1.0 // indirect
|
||||
github.com/klauspost/compress v1.15.13 // indirect
|
||||
github.com/kylelemons/godebug v1.1.0 // indirect
|
||||
github.com/letsencrypt/boulder v0.0.0-20220929215747-76583552c2be // indirect
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/magiconair/properties v1.8.6 // indirect
|
||||
github.com/mailru/easyjson v0.7.7 // indirect
|
||||
github.com/mattn/go-isatty v0.0.16 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.14 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369 // indirect
|
||||
github.com/mitchellh/copystructure v1.2.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/mitchellh/reflectwalk v1.0.2 // indirect
|
||||
@@ -148,28 +193,49 @@ require (
|
||||
github.com/muesli/mango v0.1.0 // indirect
|
||||
github.com/muesli/mango-pflag v0.1.0 // indirect
|
||||
github.com/muesli/reflow v0.3.0 // indirect
|
||||
github.com/oklog/ulid v1.3.1 // indirect
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.2 // indirect
|
||||
github.com/opencontainers/image-spec v1.0.3-0.20220114050600-8b9d41f48198 // indirect
|
||||
github.com/opencontainers/runc v1.1.2 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.0.5 // indirect
|
||||
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/prometheus/client_golang v1.13.1 // indirect
|
||||
github.com/prometheus/client_model v0.3.0 // indirect
|
||||
github.com/prometheus/common v0.37.0 // indirect
|
||||
github.com/prometheus/procfs v0.8.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.2 // indirect
|
||||
github.com/russross/blackfriday/v2 v2.1.0 // indirect
|
||||
github.com/sasha-s/go-csync v0.0.0-20210812194225-61421b77c44b // indirect
|
||||
github.com/sergi/go-diff v1.2.0 // indirect
|
||||
github.com/sirupsen/logrus v1.8.1 // indirect
|
||||
github.com/sigstore/cosign v1.13.1 // indirect
|
||||
github.com/sigstore/rekor v0.12.1-0.20220915152154-4bb6f441c1b2 // indirect
|
||||
github.com/sigstore/sigstore v1.4.4 // indirect
|
||||
github.com/sirupsen/logrus v1.9.0 // indirect
|
||||
github.com/spf13/afero v1.9.2 // indirect
|
||||
github.com/spf13/cast v1.5.0 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/viper v1.13.0 // indirect
|
||||
github.com/subosito/gotenv v1.4.1 // indirect
|
||||
github.com/technoweenie/multipartstreamer v1.0.1 // indirect
|
||||
github.com/theupdateframework/go-tuf v0.5.2-0.20220930112810-3890c1e7ace4 // indirect
|
||||
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
|
||||
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80 // indirect
|
||||
github.com/vbatts/tar-split v0.11.2 // indirect
|
||||
github.com/xanzy/ssh-agent v0.3.1 // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
|
||||
gitlab.com/digitalxero/go-conventional-commit v1.0.7 // indirect
|
||||
go.mongodb.org/mongo-driver v1.10.2 // indirect
|
||||
go.opencensus.io v0.24.0 // indirect
|
||||
golang.org/x/mod v0.6.0 // indirect
|
||||
golang.org/x/net v0.5.0 // indirect
|
||||
golang.org/x/sys v0.4.0 // indirect
|
||||
golang.org/x/term v0.4.0 // indirect
|
||||
golang.org/x/time v0.3.0 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
|
||||
google.golang.org/api v0.103.0 // indirect
|
||||
@@ -178,6 +244,10 @@ require (
|
||||
google.golang.org/grpc v1.51.0 // indirect
|
||||
google.golang.org/protobuf v1.28.1 // indirect
|
||||
gopkg.in/alexcesaro/quotedprintable.v3 v3.0.0-20150716171945-2caba252f4dc // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/square/go-jose.v2 v2.6.0 // indirect
|
||||
gopkg.in/warnings.v0 v0.1.2 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
sigs.k8s.io/kind v0.14.0 // indirect
|
||||
sigs.k8s.io/yaml v1.3.0 // indirect
|
||||
)
|
||||
|
@@ -13,8 +13,6 @@ import (
|
||||
"github.com/goreleaser/goreleaser/internal/testlib"
|
||||
"github.com/goreleaser/goreleaser/pkg/config"
|
||||
"github.com/goreleaser/goreleaser/pkg/context"
|
||||
"github.com/ory/dockertest/v3"
|
||||
"github.com/ory/dockertest/v3/docker"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
@@ -28,30 +26,8 @@ const (
|
||||
func start(tb testing.TB) {
|
||||
tb.Helper()
|
||||
tb.Log("starting registries")
|
||||
startRegistry(tb, "registry", registryPort)
|
||||
startRegistry(tb, "alt_registry", altRegistryPort)
|
||||
}
|
||||
|
||||
func startRegistry(tb testing.TB, name, port string) {
|
||||
tb.Helper()
|
||||
|
||||
pool := testlib.MustDockerPool(tb)
|
||||
testlib.MustKillContainer(tb, name)
|
||||
resource, err := pool.RunWithOptions(&dockertest.RunOptions{
|
||||
Name: name,
|
||||
Repository: "registry",
|
||||
Tag: "2",
|
||||
PortBindings: map[docker.Port][]docker.PortBinding{
|
||||
docker.Port("5000/tcp"): {{HostPort: port}},
|
||||
},
|
||||
}, func(hc *docker.HostConfig) {
|
||||
hc.AutoRemove = true
|
||||
})
|
||||
require.NoError(tb, err)
|
||||
|
||||
tb.Cleanup(func() {
|
||||
require.NoError(tb, pool.Purge(resource))
|
||||
})
|
||||
testlib.StartRegistry(tb, "registry", registryPort)
|
||||
testlib.StartRegistry(tb, "alt_registry", altRegistryPort)
|
||||
}
|
||||
|
||||
// TODO: this test is too big... split in smaller tests? Mainly the manifest ones...
|
||||
|
350
internal/pipe/ko/ko.go
Normal file
350
internal/pipe/ko/ko.go
Normal file
@@ -0,0 +1,350 @@
|
||||
// Package ko implements the pipe interface with the intent of
|
||||
// building OCI compliant images with ko.
|
||||
package ko
|
||||
|
||||
import (
|
||||
stdctx "context"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"github.com/awslabs/amazon-ecr-credential-helper/ecr-login"
|
||||
"github.com/chrismellard/docker-credential-acr-env/pkg/credhelper"
|
||||
"github.com/google/go-containerregistry/pkg/authn"
|
||||
"github.com/google/go-containerregistry/pkg/authn/github"
|
||||
"github.com/google/go-containerregistry/pkg/name"
|
||||
"github.com/google/go-containerregistry/pkg/v1/google"
|
||||
"github.com/google/go-containerregistry/pkg/v1/remote"
|
||||
"github.com/google/ko/pkg/build"
|
||||
"github.com/google/ko/pkg/commands/options"
|
||||
"github.com/google/ko/pkg/publish"
|
||||
"github.com/goreleaser/goreleaser/internal/ids"
|
||||
"github.com/goreleaser/goreleaser/internal/semerrgroup"
|
||||
"github.com/goreleaser/goreleaser/internal/tmpl"
|
||||
"github.com/goreleaser/goreleaser/pkg/config"
|
||||
"github.com/goreleaser/goreleaser/pkg/context"
|
||||
"golang.org/x/tools/go/packages"
|
||||
)
|
||||
|
||||
const chainguardStatic = "cgr.dev/chainguard/static"
|
||||
|
||||
var (
|
||||
baseImages sync.Map
|
||||
amazonKeychain authn.Keychain = authn.NewKeychainFromHelper(ecr.NewECRHelper(ecr.WithLogger(io.Discard)))
|
||||
azureKeychain authn.Keychain = authn.NewKeychainFromHelper(credhelper.NewACRCredentialsHelper())
|
||||
keychain = authn.NewMultiKeychain(
|
||||
amazonKeychain,
|
||||
authn.DefaultKeychain,
|
||||
google.Keychain,
|
||||
github.Keychain,
|
||||
azureKeychain,
|
||||
)
|
||||
|
||||
errNoRepository = errors.New("ko: missing repository: please set either the repository field or a $KO_DOCKER_REPO environment variable")
|
||||
)
|
||||
|
||||
// Pipe that build OCI compliant images with ko.
|
||||
type Pipe struct{}
|
||||
|
||||
func (Pipe) String() string { return "ko" }
|
||||
func (Pipe) Skip(ctx *context.Context) bool {
|
||||
return ctx.SkipKo || len(ctx.Config.Kos) == 0
|
||||
}
|
||||
|
||||
// Default sets the Pipes defaults.
|
||||
func (Pipe) Default(ctx *context.Context) error {
|
||||
ids := ids.New("kos")
|
||||
for i := range ctx.Config.Kos {
|
||||
ko := &ctx.Config.Kos[i]
|
||||
if ko.ID == "" {
|
||||
ko.ID = ctx.Config.ProjectName
|
||||
}
|
||||
|
||||
if ko.Build == "" {
|
||||
ko.Build = ko.ID
|
||||
}
|
||||
|
||||
build, err := findBuild(ctx, *ko)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(ko.Ldflags) == 0 {
|
||||
ko.Ldflags = build.Ldflags
|
||||
}
|
||||
|
||||
if len(ko.Flags) == 0 {
|
||||
ko.Flags = build.Flags
|
||||
}
|
||||
|
||||
if len(ko.Env) == 0 {
|
||||
ko.Env = build.Env
|
||||
}
|
||||
|
||||
if ko.Main == "" {
|
||||
ko.Main = build.Main
|
||||
}
|
||||
|
||||
if ko.WorkingDir == "" {
|
||||
ko.WorkingDir = build.Dir
|
||||
}
|
||||
|
||||
if ko.BaseImage == "" {
|
||||
ko.BaseImage = chainguardStatic
|
||||
}
|
||||
|
||||
if len(ko.Platforms) == 0 {
|
||||
ko.Platforms = []string{"linux/amd64"}
|
||||
}
|
||||
|
||||
if len(ko.Tags) == 0 {
|
||||
ko.Tags = []string{"latest"}
|
||||
}
|
||||
|
||||
if ko.SBOM == "" {
|
||||
ko.SBOM = "spdx"
|
||||
}
|
||||
|
||||
if repo := ctx.Env["KO_DOCKER_REPO"]; repo != "" {
|
||||
ko.Repository = repo
|
||||
ko.RepositoryFromEnv = true
|
||||
}
|
||||
|
||||
if ko.Repository == "" {
|
||||
return errNoRepository
|
||||
}
|
||||
|
||||
ids.Inc(ko.ID)
|
||||
}
|
||||
return ids.Validate()
|
||||
}
|
||||
|
||||
// Publish executes the Pipe.
|
||||
func (Pipe) Publish(ctx *context.Context) error {
|
||||
g := semerrgroup.New(ctx.Parallelism)
|
||||
for _, ko := range ctx.Config.Kos {
|
||||
g.Go(doBuild(ctx, ko))
|
||||
}
|
||||
return g.Wait()
|
||||
}
|
||||
|
||||
type buildOptions struct {
|
||||
importPath string
|
||||
main string
|
||||
flags []string
|
||||
env []string
|
||||
imageRepo string
|
||||
fromEnv bool
|
||||
workingDir string
|
||||
platforms []string
|
||||
baseImage string
|
||||
tags []string
|
||||
sbom string
|
||||
ldflags []string
|
||||
bare bool
|
||||
preserveImportPaths bool
|
||||
baseImportPaths bool
|
||||
}
|
||||
|
||||
func (o *buildOptions) makeBuilder(ctx *context.Context) (*build.Caching, error) {
|
||||
buildOptions := []build.Option{
|
||||
build.WithConfig(map[string]build.Config{
|
||||
o.importPath: {
|
||||
Ldflags: o.ldflags,
|
||||
Flags: o.flags,
|
||||
Main: o.main,
|
||||
Env: o.env,
|
||||
},
|
||||
}),
|
||||
build.WithPlatforms(o.platforms...),
|
||||
build.WithBaseImages(func(ctx stdctx.Context, s string) (name.Reference, build.Result, error) {
|
||||
ref, err := name.ParseReference(o.baseImage)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
if cached, found := baseImages.Load(o.baseImage); found {
|
||||
return ref, cached.(build.Result), nil
|
||||
}
|
||||
|
||||
desc, err := remote.Get(
|
||||
ref,
|
||||
remote.WithAuthFromKeychain(keychain),
|
||||
)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
if desc.MediaType.IsImage() {
|
||||
img, err := desc.Image()
|
||||
baseImages.Store(o.baseImage, img)
|
||||
return ref, img, err
|
||||
}
|
||||
if desc.MediaType.IsIndex() {
|
||||
idx, err := desc.ImageIndex()
|
||||
baseImages.Store(o.baseImage, idx)
|
||||
return ref, idx, err
|
||||
}
|
||||
return nil, nil, fmt.Errorf("unexpected base image media type: %s", desc.MediaType)
|
||||
}),
|
||||
}
|
||||
switch o.sbom {
|
||||
case "spdx":
|
||||
buildOptions = append(buildOptions, build.WithSPDX("devel"))
|
||||
case "cyclonedx":
|
||||
buildOptions = append(buildOptions, build.WithCycloneDX())
|
||||
case "go.version-m":
|
||||
buildOptions = append(buildOptions, build.WithGoVersionSBOM())
|
||||
case "none":
|
||||
// don't do anything.
|
||||
default:
|
||||
return nil, fmt.Errorf("unknown sbom type: %q", o.sbom)
|
||||
}
|
||||
|
||||
b, err := build.NewGo(ctx, o.workingDir, buildOptions...)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("newGo: %w", err)
|
||||
}
|
||||
return build.NewCaching(b)
|
||||
}
|
||||
|
||||
func doBuild(ctx *context.Context, ko config.Ko) func() error {
|
||||
return func() error {
|
||||
opts, err := buildBuildOptions(ctx, ko)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
b, err := opts.makeBuilder(ctx)
|
||||
if err != nil {
|
||||
return fmt.Errorf("makeBuilder: %w", err)
|
||||
}
|
||||
r, err := b.Build(ctx, opts.importPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("build: %w", err)
|
||||
}
|
||||
|
||||
po := []publish.Option{publish.WithTags(opts.tags), publish.WithAuthFromKeychain(keychain)}
|
||||
|
||||
var repo string
|
||||
if opts.fromEnv {
|
||||
repo = opts.imageRepo
|
||||
} else {
|
||||
// image resource's `repo` takes precedence if set, and selects the
|
||||
// `--bare` namer so the image is named exactly `repo`.
|
||||
repo = opts.imageRepo
|
||||
po = append(po, publish.WithNamer(options.MakeNamer(&options.PublishOptions{
|
||||
DockerRepo: opts.imageRepo,
|
||||
Bare: opts.bare,
|
||||
PreserveImportPaths: opts.preserveImportPaths,
|
||||
BaseImportPaths: opts.baseImportPaths,
|
||||
})))
|
||||
}
|
||||
|
||||
p, err := publish.NewDefault(repo, po...)
|
||||
if err != nil {
|
||||
return fmt.Errorf("newDefault: %w", err)
|
||||
}
|
||||
defer func() { _ = p.Close() }()
|
||||
if _, err = p.Publish(ctx, r, opts.importPath); err != nil {
|
||||
return fmt.Errorf("publish: %w", err)
|
||||
}
|
||||
if err := p.Close(); err != nil {
|
||||
return fmt.Errorf("close: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func findBuild(ctx *context.Context, ko config.Ko) (config.Build, error) {
|
||||
for _, build := range ctx.Config.Builds {
|
||||
if build.ID == ko.Build {
|
||||
return build, nil
|
||||
}
|
||||
}
|
||||
return config.Build{}, fmt.Errorf("no builds with id %q", ko.Build)
|
||||
}
|
||||
|
||||
func buildBuildOptions(ctx *context.Context, cfg config.Ko) (*buildOptions, error) {
|
||||
localImportPath := cfg.Main
|
||||
|
||||
dir := filepath.Clean(cfg.WorkingDir)
|
||||
if dir == "." {
|
||||
dir = ""
|
||||
}
|
||||
|
||||
pkgs, err := packages.Load(&packages.Config{
|
||||
Mode: packages.NeedName,
|
||||
Dir: dir,
|
||||
}, localImportPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf(
|
||||
"ko: %s does not contain a valid local import path (%s) for directory (%s): %w",
|
||||
cfg.ID, localImportPath, cfg.WorkingDir, err,
|
||||
)
|
||||
}
|
||||
|
||||
if len(pkgs) != 1 {
|
||||
return nil, fmt.Errorf(
|
||||
"ko: %s results in %d local packages, only 1 is expected",
|
||||
cfg.ID, len(pkgs),
|
||||
)
|
||||
}
|
||||
|
||||
opts := &buildOptions{
|
||||
importPath: pkgs[0].PkgPath,
|
||||
workingDir: cfg.WorkingDir,
|
||||
bare: cfg.Bare,
|
||||
preserveImportPaths: cfg.PreserveImportPaths,
|
||||
baseImportPaths: cfg.BaseImportPaths,
|
||||
baseImage: cfg.BaseImage,
|
||||
platforms: cfg.Platforms,
|
||||
sbom: cfg.SBOM,
|
||||
imageRepo: cfg.Repository,
|
||||
fromEnv: cfg.RepositoryFromEnv,
|
||||
}
|
||||
|
||||
tags, err := applyTemplate(ctx, cfg.Tags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts.tags = tags
|
||||
|
||||
if len(cfg.Env) > 0 {
|
||||
env, err := applyTemplate(ctx, cfg.Env)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts.env = env
|
||||
}
|
||||
|
||||
if len(cfg.Flags) > 0 {
|
||||
flags, err := applyTemplate(ctx, cfg.Flags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts.flags = flags
|
||||
}
|
||||
|
||||
if len(cfg.Ldflags) > 0 {
|
||||
ldflags, err := applyTemplate(ctx, cfg.Ldflags)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
opts.ldflags = ldflags
|
||||
}
|
||||
return opts, nil
|
||||
}
|
||||
|
||||
func applyTemplate(ctx *context.Context, templateable []string) ([]string, error) {
|
||||
var templated []string
|
||||
for _, t := range templateable {
|
||||
tlf, err := tmpl.New(ctx).Apply(t)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
templated = append(templated, tlf)
|
||||
}
|
||||
return templated, nil
|
||||
}
|
280
internal/pipe/ko/ko_test.go
Normal file
280
internal/pipe/ko/ko_test.go
Normal file
@@ -0,0 +1,280 @@
|
||||
package ko
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
_ "github.com/distribution/distribution/v3/registry/auth/htpasswd"
|
||||
_ "github.com/distribution/distribution/v3/registry/storage/driver/inmemory"
|
||||
"github.com/goreleaser/goreleaser/internal/testlib"
|
||||
"github.com/goreleaser/goreleaser/pkg/config"
|
||||
"github.com/goreleaser/goreleaser/pkg/context"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
const (
|
||||
registryPort = "5052"
|
||||
registry = "localhost:5052/"
|
||||
)
|
||||
|
||||
func TestDefault(t *testing.T) {
|
||||
ctx := context.New(config.Project{
|
||||
Env: []string{
|
||||
"KO_DOCKER_REPO=" + registry,
|
||||
"COSIGN_REPOSITORY=" + registry,
|
||||
"LDFLAGS=foobar",
|
||||
"FLAGS=barfoo",
|
||||
"LE_ENV=test",
|
||||
},
|
||||
ProjectName: "test",
|
||||
Builds: []config.Build{
|
||||
{
|
||||
ID: "test",
|
||||
Dir: ".",
|
||||
BuildDetails: config.BuildDetails{
|
||||
Ldflags: []string{"{{.Env.LDFLAGS}}"},
|
||||
Flags: []string{"{{.Env.FLAGS}}"},
|
||||
Env: []string{"SOME_ENV={{.Env.LE_ENV}}"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Kos: []config.Ko{
|
||||
{},
|
||||
},
|
||||
})
|
||||
require.NoError(t, Pipe{}.Default(ctx))
|
||||
require.Equal(t, config.Ko{
|
||||
ID: "test",
|
||||
Build: "test",
|
||||
BaseImage: chainguardStatic,
|
||||
Repository: registry,
|
||||
RepositoryFromEnv: true,
|
||||
Platforms: []string{"linux/amd64"},
|
||||
SBOM: "spdx",
|
||||
Tags: []string{"latest"},
|
||||
WorkingDir: ".",
|
||||
Ldflags: []string{"{{.Env.LDFLAGS}}"},
|
||||
Flags: []string{"{{.Env.FLAGS}}"},
|
||||
Env: []string{"SOME_ENV={{.Env.LE_ENV}}"},
|
||||
}, ctx.Config.Kos[0])
|
||||
}
|
||||
|
||||
func TestDefaultNoImage(t *testing.T) {
|
||||
ctx := context.New(config.Project{
|
||||
ProjectName: "test",
|
||||
Builds: []config.Build{
|
||||
{
|
||||
ID: "test",
|
||||
},
|
||||
},
|
||||
Kos: []config.Ko{
|
||||
{},
|
||||
},
|
||||
})
|
||||
require.ErrorIs(t, Pipe{}.Default(ctx), errNoRepository)
|
||||
}
|
||||
|
||||
func TestDescription(t *testing.T) {
|
||||
require.NotEmpty(t, Pipe{}.String())
|
||||
}
|
||||
|
||||
func TestSkip(t *testing.T) {
|
||||
t.Run("skip ko set", func(t *testing.T) {
|
||||
ctx := context.New(config.Project{
|
||||
Kos: []config.Ko{{}},
|
||||
})
|
||||
ctx.SkipKo = true
|
||||
require.True(t, Pipe{}.Skip(ctx))
|
||||
})
|
||||
t.Run("skip no kos", func(t *testing.T) {
|
||||
ctx := context.New(config.Project{})
|
||||
require.True(t, Pipe{}.Skip(ctx))
|
||||
})
|
||||
t.Run("dont skip", func(t *testing.T) {
|
||||
ctx := context.New(config.Project{
|
||||
Kos: []config.Ko{{}},
|
||||
})
|
||||
require.False(t, Pipe{}.Skip(ctx))
|
||||
})
|
||||
}
|
||||
|
||||
func TestPublishPipeNoMatchingBuild(t *testing.T) {
|
||||
ctx := context.New(config.Project{
|
||||
Builds: []config.Build{
|
||||
{
|
||||
ID: "doesnt matter",
|
||||
},
|
||||
},
|
||||
Kos: []config.Ko{
|
||||
{
|
||||
ID: "default",
|
||||
Build: "wont match nothing",
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
require.EqualError(t, Pipe{}.Default(ctx), `no builds with id "wont match nothing"`)
|
||||
}
|
||||
|
||||
func TestPublishPipeSuccess(t *testing.T) {
|
||||
testlib.StartRegistry(t, "ko_registry", registryPort)
|
||||
|
||||
table := []struct {
|
||||
Name string
|
||||
SBOM string
|
||||
BaseImage string
|
||||
Platforms []string
|
||||
}{
|
||||
{
|
||||
Name: "sbom-spdx",
|
||||
SBOM: "spdx",
|
||||
},
|
||||
{
|
||||
Name: "sbom-none",
|
||||
SBOM: "none",
|
||||
},
|
||||
{
|
||||
Name: "sbom-cyclonedx",
|
||||
SBOM: "cyclonedx",
|
||||
},
|
||||
{
|
||||
Name: "sbom-go.version-m",
|
||||
SBOM: "go.version-m",
|
||||
},
|
||||
{
|
||||
Name: "base-image-is-not-index",
|
||||
BaseImage: "alpine:latest@sha256:c0d488a800e4127c334ad20d61d7bc21b4097540327217dfab52262adc02380c",
|
||||
},
|
||||
{
|
||||
Name: "multiple-platforms",
|
||||
Platforms: []string{"linux/amd64", "linux/arm64"},
|
||||
},
|
||||
}
|
||||
|
||||
for _, table := range table {
|
||||
t.Run(table.Name, func(t *testing.T) {
|
||||
ctx := context.New(config.Project{
|
||||
Builds: []config.Build{
|
||||
{
|
||||
ID: "foo",
|
||||
BuildDetails: config.BuildDetails{
|
||||
Ldflags: []string{"-s", "-w"},
|
||||
Flags: []string{"-tags", "netgo"},
|
||||
Env: []string{"GOCACHE=" + t.TempDir()},
|
||||
},
|
||||
},
|
||||
},
|
||||
Kos: []config.Ko{
|
||||
{
|
||||
ID: "default",
|
||||
Build: "foo",
|
||||
WorkingDir: "./testdata/app/",
|
||||
BaseImage: table.BaseImage,
|
||||
Repository: fmt.Sprintf("%s/goreleasertest", registry),
|
||||
Platforms: table.Platforms,
|
||||
Tags: []string{table.Name},
|
||||
SBOM: table.SBOM,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
require.NoError(t, Pipe{}.Default(ctx))
|
||||
require.NoError(t, Pipe{}.Publish(ctx))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestPublishPipeError(t *testing.T) {
|
||||
makeCtx := func() *context.Context {
|
||||
ctx := context.New(config.Project{
|
||||
Builds: []config.Build{
|
||||
{
|
||||
ID: "foo",
|
||||
Main: "./...",
|
||||
},
|
||||
},
|
||||
Kos: []config.Ko{
|
||||
{
|
||||
ID: "default",
|
||||
Build: "foo",
|
||||
WorkingDir: "./testdata/app/",
|
||||
Repository: "fakerepo:8080/",
|
||||
Tags: []string{"latest", "{{.Tag}}"},
|
||||
},
|
||||
},
|
||||
})
|
||||
ctx.Git.CurrentTag = "v1.0.0"
|
||||
return ctx
|
||||
}
|
||||
|
||||
t.Run("invalid base image", func(t *testing.T) {
|
||||
ctx := makeCtx()
|
||||
ctx.Config.Kos[0].BaseImage = "not a valid image hopefully"
|
||||
require.NoError(t, Pipe{}.Default(ctx))
|
||||
require.EqualError(t, Pipe{}.Publish(ctx), `build: could not parse reference: not a valid image hopefully`)
|
||||
})
|
||||
|
||||
t.Run("invalid sbom", func(t *testing.T) {
|
||||
ctx := makeCtx()
|
||||
ctx.Config.Kos[0].SBOM = "nope"
|
||||
require.NoError(t, Pipe{}.Default(ctx))
|
||||
require.EqualError(t, Pipe{}.Publish(ctx), `makeBuilder: unknown sbom type: "nope"`)
|
||||
})
|
||||
|
||||
t.Run("invalid build", func(t *testing.T) {
|
||||
ctx := makeCtx()
|
||||
ctx.Config.Kos[0].WorkingDir = t.TempDir()
|
||||
require.NoError(t, Pipe{}.Default(ctx))
|
||||
require.EqualError(t, Pipe{}.Publish(ctx), `build: exit status 1`)
|
||||
})
|
||||
|
||||
t.Run("invalid tags tmpl", func(t *testing.T) {
|
||||
ctx := makeCtx()
|
||||
ctx.Config.Kos[0].Tags = []string{"{{.Nope}}"}
|
||||
require.NoError(t, Pipe{}.Default(ctx))
|
||||
testlib.RequireTemplateError(t, Pipe{}.Publish(ctx))
|
||||
})
|
||||
|
||||
t.Run("invalid env tmpl", func(t *testing.T) {
|
||||
ctx := makeCtx()
|
||||
ctx.Config.Builds[0].Env = []string{"{{.Nope}}"}
|
||||
require.NoError(t, Pipe{}.Default(ctx))
|
||||
testlib.RequireTemplateError(t, Pipe{}.Publish(ctx))
|
||||
})
|
||||
|
||||
t.Run("invalid ldflags tmpl", func(t *testing.T) {
|
||||
ctx := makeCtx()
|
||||
ctx.Config.Builds[0].Ldflags = []string{"{{.Nope}}"}
|
||||
require.NoError(t, Pipe{}.Default(ctx))
|
||||
testlib.RequireTemplateError(t, Pipe{}.Publish(ctx))
|
||||
})
|
||||
|
||||
t.Run("invalid flags tmpl", func(t *testing.T) {
|
||||
ctx := makeCtx()
|
||||
ctx.Config.Builds[0].Flags = []string{"{{.Nope}}"}
|
||||
require.NoError(t, Pipe{}.Default(ctx))
|
||||
testlib.RequireTemplateError(t, Pipe{}.Publish(ctx))
|
||||
})
|
||||
|
||||
t.Run("publish fail", func(t *testing.T) {
|
||||
ctx := makeCtx()
|
||||
require.NoError(t, Pipe{}.Default(ctx))
|
||||
err := Pipe{}.Publish(ctx)
|
||||
require.Error(t, err)
|
||||
require.Contains(t, err.Error(), `publish: writing sbom: Get "https://fakerepo:8080/v2/": dial tcp:`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestApplyTemplate(t *testing.T) {
|
||||
t.Run("success", func(t *testing.T) {
|
||||
foo, err := applyTemplate(context.New(config.Project{
|
||||
Env: []string{"FOO=bar"},
|
||||
}), []string{"{{ .Env.FOO }}"})
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, []string{"bar"}, foo)
|
||||
})
|
||||
t.Run("error", func(t *testing.T) {
|
||||
_, err := applyTemplate(context.New(config.Project{}), []string{"{{ .Nope}}"})
|
||||
require.Error(t, err)
|
||||
})
|
||||
}
|
3
internal/pipe/ko/testdata/app/go.mod
vendored
Normal file
3
internal/pipe/ko/testdata/app/go.mod
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
module testapp
|
||||
|
||||
go 1.19
|
5
internal/pipe/ko/testdata/app/main.go
vendored
Normal file
5
internal/pipe/ko/testdata/app/main.go
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
package main
|
||||
|
||||
func main() {
|
||||
println("hello")
|
||||
}
|
@@ -14,6 +14,7 @@ import (
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/chocolatey"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/custompublishers"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/docker"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/ko"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/krew"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/milestone"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/release"
|
||||
@@ -40,6 +41,7 @@ var publishers = []Publisher{
|
||||
custompublishers.Pipe{},
|
||||
docker.Pipe{},
|
||||
docker.ManifestPipe{},
|
||||
ko.Pipe{},
|
||||
sign.DockerPipe{},
|
||||
snapcraft.Pipe{},
|
||||
// This should be one of the last steps
|
||||
|
@@ -2,8 +2,11 @@ package testlib
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"github.com/ory/dockertest/v3"
|
||||
"github.com/ory/dockertest/v3/docker"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
var (
|
||||
@@ -41,3 +44,27 @@ func MustKillContainer(f Fataler, name string) {
|
||||
type Fataler interface {
|
||||
Fatal(args ...any)
|
||||
}
|
||||
|
||||
// StartRegistry starts a registry with the given name in the given port, and
|
||||
// sets up its deletion on test.Cleanup.
|
||||
func StartRegistry(tb testing.TB, name, port string) {
|
||||
tb.Helper()
|
||||
|
||||
pool := MustDockerPool(tb)
|
||||
MustKillContainer(tb, name)
|
||||
resource, err := pool.RunWithOptions(&dockertest.RunOptions{
|
||||
Name: name,
|
||||
Repository: "registry",
|
||||
Tag: "2",
|
||||
PortBindings: map[docker.Port][]docker.PortBinding{
|
||||
docker.Port("5000/tcp"): {{HostPort: port}},
|
||||
},
|
||||
}, func(hc *docker.HostConfig) {
|
||||
hc.AutoRemove = true
|
||||
})
|
||||
require.NoError(tb, err)
|
||||
|
||||
tb.Cleanup(func() {
|
||||
require.NoError(tb, pool.Purge(resource))
|
||||
})
|
||||
}
|
||||
|
@@ -199,6 +199,26 @@ type Krew struct {
|
||||
SkipUpload string `yaml:"skip_upload,omitempty" json:"skip_upload,omitempty" jsonschema:"oneof_type=string;boolean"`
|
||||
}
|
||||
|
||||
// Ko contains the ko section
|
||||
type Ko struct {
|
||||
ID string `yaml:"id,omitempty" json:"id,omitempty"`
|
||||
Build string `yaml:"build,omitempty" json:"build,omitempty"`
|
||||
Main string `yaml:"main,omitempty" json:"main,omitempty"`
|
||||
WorkingDir string `yaml:"working_dir,omitempty" json:"working_dir,omitempty"`
|
||||
BaseImage string `yaml:"base_image,omitempty" json:"base_image,omitempty"`
|
||||
Repository string `yaml:"repository,omitempty" json:"repository,omitempty"`
|
||||
RepositoryFromEnv bool `yaml:"-" json:"-"`
|
||||
Platforms []string `yaml:"platforms,omitempty" json:"platforms,omitempty"`
|
||||
Tags []string `yaml:"tags,omitempty" json:"tags,omitempty"`
|
||||
SBOM string `yaml:"sbom,omitempty" json:"sbom,omitempty"`
|
||||
Ldflags []string `yaml:"ldflags,omitempty" json:"ldflags,omitempty"`
|
||||
Flags []string `yaml:"flags,omitempty" json:"flags,omitempty"`
|
||||
Env []string `yaml:"env,omitempty" json:"env,omitempty"`
|
||||
Bare bool `yaml:"bare,omitempty" json:"bare,omitempty"`
|
||||
PreserveImportPaths bool `yaml:"preserve_import_paths,omitempty" json:"preserve_import_paths,omitempty"`
|
||||
BaseImportPaths bool `yaml:"base_import_paths,omitempty" json:"base_import_paths,omitempty"`
|
||||
}
|
||||
|
||||
// Scoop contains the scoop.sh section.
|
||||
type Scoop struct {
|
||||
Name string `yaml:"name,omitempty" json:"name,omitempty"`
|
||||
@@ -916,6 +936,7 @@ type Project struct {
|
||||
Brews []Homebrew `yaml:"brews,omitempty" json:"brews,omitempty"`
|
||||
AURs []AUR `yaml:"aurs,omitempty" json:"aurs,omitempty"`
|
||||
Krews []Krew `yaml:"krews,omitempty" json:"krews,omitempty"`
|
||||
Kos []Ko `yaml:"kos,omitempty" json:"kos,omitempty"`
|
||||
Scoop Scoop `yaml:"scoop,omitempty" json:"scoop,omitempty"`
|
||||
Builds []Build `yaml:"builds,omitempty" json:"builds,omitempty"`
|
||||
Archives []Archive `yaml:"archives,omitempty" json:"archives,omitempty"`
|
||||
|
@@ -96,6 +96,7 @@ type Context struct {
|
||||
SkipSign bool
|
||||
SkipValidate bool
|
||||
SkipSBOMCataloging bool
|
||||
SkipKo bool
|
||||
SkipDocker bool
|
||||
SkipBefore bool
|
||||
RmDist bool
|
||||
|
@@ -16,6 +16,7 @@ import (
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/discord"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/docker"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/gomod"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/ko"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/krew"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/linkedin"
|
||||
"github.com/goreleaser/goreleaser/internal/pipe/mastodon"
|
||||
@@ -74,6 +75,7 @@ var Defaulters = []Defaulter{
|
||||
aur.Pipe{},
|
||||
brew.Pipe{},
|
||||
krew.Pipe{},
|
||||
ko.Pipe{},
|
||||
scoop.Pipe{},
|
||||
discord.Pipe{},
|
||||
reddit.Pipe{},
|
||||
|
143
www/docs/customization/ko.md
Normal file
143
www/docs/customization/ko.md
Normal file
@@ -0,0 +1,143 @@
|
||||
# Docker Images with Ko
|
||||
|
||||
> Since v1.15.
|
||||
|
||||
You can also use [ko][] to build and publish Docker container images.
|
||||
|
||||
Please notice that ko will build your binary again.
|
||||
That shouldn't increase the release times too much, as it'll use the same build
|
||||
options as the [build][] pipe when possible, so the results will probably be cached.
|
||||
|
||||
!!! warning
|
||||
Ko only runs on the publish phase, so it might be a bit hard to test — you
|
||||
might need to push to a fake repository (or a fake tag) when playing around
|
||||
with its configuration.
|
||||
|
||||
```yaml
|
||||
# .goreleaser.yaml
|
||||
kos:
|
||||
-
|
||||
# ID of this image.
|
||||
id: foo
|
||||
|
||||
# Build ID that should be used to import the build settings.
|
||||
build: build-id
|
||||
|
||||
# Main path to build.
|
||||
#
|
||||
# Defaults to the build's main.
|
||||
main: ./cmd/...
|
||||
|
||||
# Working directory used to build.
|
||||
#
|
||||
# Defaults to the build's dir.
|
||||
working_dir: .
|
||||
|
||||
# Base image to publish to use.
|
||||
#
|
||||
# Defaults to cgr.dev/chainguard/static.
|
||||
base_image: alpine
|
||||
|
||||
# Repository to push to.
|
||||
#
|
||||
# Defaults to the value of $KO_DOCKER_REPO.
|
||||
repository: ghcr.io/foo/bar
|
||||
|
||||
# Platforms to build and publish.
|
||||
#
|
||||
# Defaults to linux/amd64.
|
||||
platforms:
|
||||
- linux/amd64
|
||||
- linux/arm64
|
||||
|
||||
# Tag templates to build and push.
|
||||
#
|
||||
# Defaults to `latest`.
|
||||
tags:
|
||||
- latest
|
||||
- '{{.Tag}}'
|
||||
|
||||
# SBOM format to use.
|
||||
#
|
||||
# Defaults to spdx.
|
||||
# Valid options are: spdx, cyclonedx, go.version-m and none.
|
||||
sbom: none
|
||||
|
||||
# Ldflags to use on build.
|
||||
#
|
||||
# Defaults to the build's ldflags.
|
||||
ldflags:
|
||||
- foo
|
||||
- bar
|
||||
|
||||
# Flags to use on build.
|
||||
#
|
||||
# Defaults to the build's flags.
|
||||
flags:
|
||||
- foo
|
||||
- bar
|
||||
|
||||
# Env to use on build.
|
||||
#
|
||||
# Defaults to the build's env.
|
||||
env:
|
||||
- FOO=bar
|
||||
- SOMETHING=value
|
||||
|
||||
|
||||
# Bare uses a tag on the KO_DOCKER_REPO without anything additional.
|
||||
#
|
||||
# Defaults to false.
|
||||
bare: true
|
||||
|
||||
# Whether to preserve the full import path after the repository name.
|
||||
#
|
||||
# Defaults to false.
|
||||
preserve_import_paths: true
|
||||
|
||||
# Whether to use the base path without the MD5 hash after the repository name.
|
||||
#
|
||||
# Defaults to false.
|
||||
base_import_paths: true
|
||||
```
|
||||
|
||||
Refer to [ko's project page][ko] for more information.
|
||||
|
||||
|
||||
## Example
|
||||
|
||||
Here's a minimal example:
|
||||
|
||||
```yaml
|
||||
# .goreleaser.yml
|
||||
before:
|
||||
hooks:
|
||||
- go mod tidy
|
||||
|
||||
builds:
|
||||
- env: [ "CGO_ENABLED=1" ]
|
||||
binary: test
|
||||
goos:
|
||||
- darwin
|
||||
- linux
|
||||
goarch:
|
||||
- amd64
|
||||
- arch64
|
||||
|
||||
kos:
|
||||
- repository: ghcr.io/caarlos0/test-ko
|
||||
tags:
|
||||
- '{{.Version}}'
|
||||
- latest
|
||||
bare: true
|
||||
preserve_import_paths: false
|
||||
platforms:
|
||||
- linux/amd64
|
||||
- linux/arm64
|
||||
```
|
||||
|
||||
This will build the binaries for `linux/arm64`, `linux/amd64`, `darwin/amd64`
|
||||
and `darwin/arm64`, as well as the Docker images and manifest for Linux.
|
||||
|
||||
[ko]: https://ko.build
|
||||
[build]: /customization/build/
|
@@ -106,6 +106,7 @@ nav:
|
||||
- customization/chocolatey.md
|
||||
- customization/docker.md
|
||||
- customization/docker_manifest.md
|
||||
- customization/ko.md
|
||||
- customization/sbom.md
|
||||
- Signing:
|
||||
- Checksums and artifacts: customization/sign.md
|
||||
|
Reference in New Issue
Block a user