├── .github
├── dependabot.yml
└── workflows
│ ├── analyze.yaml
│ ├── boilerplate.yaml
│ ├── build.yaml
│ ├── donotsubmit.yaml
│ ├── e2e.yaml
│ ├── image.yaml
│ ├── kind-e2e.yaml
│ ├── modules-integration-test.yaml
│ ├── publish-site.yaml
│ ├── registries.yaml
│ ├── release.yml
│ ├── sbom.yaml
│ ├── stale.yaml
│ ├── style.yaml
│ ├── test.yaml
│ └── verify.yaml
├── .gitignore
├── .golangci.yaml
├── .goreleaser.yml
├── .ko.yaml
├── .wokeignore
├── CNAME
├── CONTRIBUTING.md
├── LICENSE
├── MAINTAINERS.md
├── README.md
├── ROADMAP.md
├── cmd
└── help
│ └── main.go
├── code-of-conduct.md
├── docs
├── CNAME
├── README.md
├── advanced
│ ├── faq.md
│ ├── go-packages.md
│ ├── lambda.md
│ ├── limitations.md
│ ├── linux-capabilities.md
│ ├── migrating-from-dockerfile.md
│ ├── root-ca-certificates.md
│ └── terraform.md
├── community.md
├── configuration.md
├── custom
│ ├── main.html
│ └── partials
│ │ └── copyright.html
├── deployment.md
├── features
│ ├── build-cache.md
│ ├── debugging.md
│ ├── k8s.md
│ ├── multi-platform.md
│ ├── sboms.md
│ └── static-assets.md
├── get-started.md
├── images
│ ├── android-icon-192x192.png
│ ├── apple-icon-180x180.png
│ ├── apple-icon.png
│ ├── cncf-dark.svg
│ ├── cncf-light.svg
│ ├── demo.png
│ ├── favicon-16x16.png
│ ├── favicon-32x32.png
│ ├── favicon-96x96.png
│ ├── ko-with-name.svg
│ ├── ko.png
│ ├── ko.svg
│ ├── ms-icon-144x144.png
│ └── og.png
├── index.md
├── install.md
└── reference
│ ├── ko.md
│ ├── ko_apply.md
│ ├── ko_build.md
│ ├── ko_create.md
│ ├── ko_delete.md
│ ├── ko_login.md
│ ├── ko_resolve.md
│ ├── ko_run.md
│ └── ko_version.md
├── go.mod
├── go.sum
├── hack
├── boilerplate
│ ├── boilerplate.go.txt
│ └── boilerplate.sh.txt
├── presubmit.sh
├── tools.go
├── update-codegen.sh
└── update-deps.sh
├── integration_test.sh
├── internal
└── sbom
│ ├── sbom.go
│ └── spdx.go
├── main.go
├── mkdocs.yml
├── pkg
├── build
│ ├── build.go
│ ├── cache.go
│ ├── config.go
│ ├── doc.go
│ ├── future.go
│ ├── future_test.go
│ ├── gobuild.go
│ ├── gobuild_test.go
│ ├── gobuilds.go
│ ├── gobuilds_test.go
│ ├── layer.go
│ ├── limit.go
│ ├── limit_test.go
│ ├── options.go
│ ├── recorder.go
│ ├── recorder_test.go
│ ├── shared.go
│ ├── shared_test.go
│ ├── strict.go
│ └── strict_test.go
├── caps
│ ├── caps.go
│ ├── caps_dd_test.go
│ ├── caps_test.go
│ ├── gen.sh
│ └── new_file_caps_test.go
├── commands
│ ├── apply.go
│ ├── build.go
│ ├── cache.go
│ ├── commands.go
│ ├── config.go
│ ├── config_test.go
│ ├── create.go
│ ├── delete.go
│ ├── options
│ │ ├── build.go
│ │ ├── build_test.go
│ │ ├── filestuff.go
│ │ ├── namer_test.go
│ │ ├── publish.go
│ │ ├── selector.go
│ │ ├── testdata
│ │ │ ├── bad-config
│ │ │ │ └── .ko.yaml
│ │ │ │ │ └── .gitignore
│ │ │ ├── config
│ │ │ │ ├── .ko.yaml
│ │ │ │ └── my-ko.yaml
│ │ │ ├── multiple-platforms
│ │ │ │ └── .ko.yaml
│ │ │ └── paths
│ │ │ │ ├── .ko.yaml
│ │ │ │ └── app
│ │ │ │ ├── cmd
│ │ │ │ └── foo
│ │ │ │ │ └── main.go
│ │ │ │ └── go.mod
│ │ └── validate.go
│ ├── publisher.go
│ ├── publisher_test.go
│ ├── resolve.go
│ ├── resolver.go
│ ├── resolver_test.go
│ ├── root.go
│ ├── run.go
│ └── version.go
├── doc.go
├── internal
│ ├── git
│ │ ├── clone.go
│ │ ├── errors.go
│ │ ├── git.go
│ │ ├── info.go
│ │ └── info_test.go
│ ├── gittesting
│ │ ├── git.go
│ │ └── git_test.go
│ └── testing
│ │ ├── daemon.go
│ │ ├── doc.go
│ │ ├── fixed.go
│ │ └── fixed_test.go
├── publish
│ ├── daemon.go
│ ├── daemon_test.go
│ ├── default.go
│ ├── default_test.go
│ ├── doc.go
│ ├── future.go
│ ├── future_test.go
│ ├── kind.go
│ ├── kind
│ │ ├── doc.go
│ │ ├── write.go
│ │ └── write_test.go
│ ├── layout.go
│ ├── layout_test.go
│ ├── multi.go
│ ├── multi_test.go
│ ├── options.go
│ ├── publish.go
│ ├── recorder.go
│ ├── recorder_test.go
│ ├── shared.go
│ ├── shared_test.go
│ ├── tarball.go
│ └── tarball_test.go
└── resolve
│ ├── doc.go
│ ├── resolve.go
│ ├── resolve_test.go
│ ├── selector.go
│ └── selector_test.go
└── test
├── build-configs
├── .ko.yaml
├── bar
│ ├── cmd
│ │ └── main.go
│ └── go.mod
├── caps.ko.yaml
├── caps
│ ├── cmd
│ │ └── main.go
│ └── go.mod
├── foo
│ ├── cmd
│ │ └── main.go
│ └── go.mod
└── toolexec
│ ├── cmd
│ └── main.go
│ └── go.mod
├── kodata
├── a
├── b
└── kenobi
├── main.go
└── test.yaml
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: "gomod"
4 | directory: "/"
5 | schedule:
6 | interval: "daily"
7 | - package-ecosystem: github-actions
8 | directory: /
9 | schedule:
10 | interval: "daily"
11 |
--------------------------------------------------------------------------------
/.github/workflows/analyze.yaml:
--------------------------------------------------------------------------------
1 | name: Analyze
2 |
3 | on:
4 | workflow_dispatch:
5 | push:
6 | branches: ['main']
7 | pull_request:
8 | branches: ['main']
9 |
10 | permissions: {}
11 |
12 | jobs:
13 | analyze:
14 | name: Analyze
15 | runs-on: ubuntu-latest
16 |
17 | permissions:
18 | security-events: write
19 |
20 | steps:
21 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
22 | with:
23 | # We must fetch at least the immediate parents so that if this is
24 | # a pull request then we can checkout the head.
25 | fetch-depth: 2
26 | persist-credentials: false
27 |
28 | - uses: github/codeql-action/init@df409f7d9260372bd5f19e5b04e83cb3c43714ae # v3.27.9
29 | with:
30 | languages: go
31 |
32 | - uses: github/codeql-action/autobuild@df409f7d9260372bd5f19e5b04e83cb3c43714ae # v3.27.9
33 |
34 | - uses: github/codeql-action/analyze@df409f7d9260372bd5f19e5b04e83cb3c43714ae # v3.27.9
35 |
--------------------------------------------------------------------------------
/.github/workflows/boilerplate.yaml:
--------------------------------------------------------------------------------
1 | name: Boilerplate
2 |
3 | on:
4 | pull_request:
5 | branches: ['main']
6 |
7 | permissions: {}
8 |
9 | jobs:
10 | check:
11 | name: Boilerplate Check
12 | runs-on: ubuntu-latest
13 |
14 | permissions:
15 | contents: read
16 |
17 | strategy:
18 | fail-fast: false # Keep running if one leg fails.
19 | matrix:
20 | extension:
21 | - go
22 | - sh
23 |
24 | # Map between extension and human-readable name.
25 | include:
26 | - extension: go
27 | language: Go
28 | - extension: sh
29 | language: Bash
30 |
31 | steps:
32 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
33 | with:
34 | persist-credentials: false
35 |
36 | - uses: chainguard-dev/actions/boilerplate@e82b4e5ae10182af72972addcb3fedf7454621c8 # main
37 | with:
38 | extension: ${{ matrix.extension }}
39 | language: ${{ matrix.language }}
40 |
--------------------------------------------------------------------------------
/.github/workflows/build.yaml:
--------------------------------------------------------------------------------
1 | name: Build
2 |
3 | on:
4 | pull_request:
5 | branches:
6 | - 'main'
7 |
8 | permissions: {}
9 |
10 | jobs:
11 | build:
12 | name: Build
13 | runs-on: ubuntu-latest
14 |
15 | steps:
16 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
17 | - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
18 | with:
19 | go-version: '1.24'
20 | check-latest: true
21 |
22 | - uses: golang/govulncheck-action@b625fbe08f3bccbe446d94fbf87fcc875a4f50ee # v1.0.4
23 | with:
24 | go-version-input: '1.24'
25 |
26 | - run: |
27 | go build ./...
28 | go test -run=^$ ./...
29 |
--------------------------------------------------------------------------------
/.github/workflows/donotsubmit.yaml:
--------------------------------------------------------------------------------
1 | name: Do Not Submit
2 |
3 | on:
4 | pull_request:
5 | branches: ['main']
6 |
7 | permissions: {}
8 |
9 | jobs:
10 | donotsubmit:
11 | name: Do Not Submit
12 | runs-on: ubuntu-latest
13 |
14 | permissions:
15 | contents: read
16 |
17 | steps:
18 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
19 | with:
20 | persist-credentials: false
21 |
22 | - uses: chainguard-dev/actions/donotsubmit@84c993eaf02da1c325854fb272a4df9184bd80fc # main
23 |
--------------------------------------------------------------------------------
/.github/workflows/image.yaml:
--------------------------------------------------------------------------------
1 | name: image
2 |
3 | on:
4 | push:
5 | branches:
6 | - 'main'
7 | workflow_dispatch:
8 |
9 | permissions: {}
10 |
11 | jobs:
12 | image:
13 | runs-on: ubuntu-latest
14 |
15 | permissions:
16 | contents: read
17 | packages: write
18 | id-token: write
19 |
20 | steps:
21 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
22 | with:
23 | persist-credentials: false
24 |
25 | - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
26 | with:
27 | go-version-file: 'go.mod'
28 | check-latest: true
29 | - uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0
30 |
31 | # Build ko from HEAD, build and push an image tagged with the commit SHA,
32 | # then keylessly sign it with cosign.
33 | - name: Publish and sign image
34 | env:
35 | KO_DOCKER_REPO: ghcr.io/${{ github.repository }}
36 | run: |
37 | go build ./
38 | echo "${{ github.token }}" | ./ko login ghcr.io --username "${{ github.actor }}" --password-stdin
39 | img=$(./ko build --bare --platform=all -t latest -t ${{ github.sha }} ./)
40 | echo "built ${img}"
41 | cosign sign ${img} --yes \
42 | -a sha=${{ github.sha }} \
43 | -a run_id=${{ github.run_id }} \
44 | -a run_attempt=${{ github.run_attempt }}
45 |
--------------------------------------------------------------------------------
/.github/workflows/kind-e2e.yaml:
--------------------------------------------------------------------------------
1 | name: KinD e2e tests
2 |
3 | on:
4 | workflow_dispatch: # Allow manual runs.
5 | pull_request:
6 | branches:
7 | - 'main'
8 |
9 | permissions: {}
10 |
11 | jobs:
12 | e2e-tests:
13 | name: e2e tests
14 | runs-on: ubuntu-latest
15 | env:
16 | # https://github.com/google/go-containerregistry/pull/125 allows insecure registry for
17 | # '*.local' hostnames. This works both for `ko` and our own tag-to-digest resolution logic,
18 | # thus allowing us to test without bypassing tag-to-digest resolution.
19 | REGISTRY_NAME: registry.local
20 | REGISTRY_PORT: 5000
21 | KO_DOCKER_REPO: registry.local:5000/ko
22 |
23 | permissions:
24 | contents: read
25 |
26 | steps:
27 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
28 | with:
29 | persist-credentials: false
30 |
31 | - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
32 | with:
33 | go-version-file: 'go.mod'
34 | check-latest: true
35 |
36 | - name: Install ko
37 | run: go install ./
38 |
39 | - name: Setup Cluster
40 | uses: chainguard-dev/actions/setup-kind@29fb6e979a0b3efc79748a17e8cec08d0594cbfd # main
41 | with:
42 | k8s-version: v1.28.x
43 | registry-authority: ${{ env.REGISTRY_NAME }}:${{ env.REGISTRY_PORT }}
44 |
45 | - name: Install Cosign
46 | uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0
47 |
48 | - name: Run Smoke Test
49 | run: |
50 | # Test with kind load
51 | KO_DOCKER_REPO=kind.local ko apply --platform=all -f ./test
52 | kubectl wait --timeout=10s --for=condition=Ready pod/kodata
53 | kubectl delete pod kodata
54 |
55 | # Test with registry
56 | ko apply --platform=all -f ./test
57 | kubectl wait --timeout=60s --for=condition=Ready pod/kodata
58 | kubectl delete pod kodata
59 |
60 | # Test ko run with kind load
61 | # This tests that --labels are passed to kubectl, and -wait is passed to the test binary.
62 | KO_DOCKER_REPO=kind.local ko run ./test -- --labels=foo=bar -- -wait=false
63 |
64 | - name: Check SBOM
65 | run: |
66 | set -o pipefail
67 |
68 | echo '::group:: SBOM'
69 | cosign download sbom $(ko build ./test)
70 | echo "${SBOM}"
71 | echo '::endgroup::'
72 |
73 | - name: Collect diagnostics and upload
74 | if: ${{ failure() }}
75 | uses: chainguard-dev/actions/kind-diag@29fb6e979a0b3efc79748a17e8cec08d0594cbfd # main
76 |
--------------------------------------------------------------------------------
/.github/workflows/modules-integration-test.yaml:
--------------------------------------------------------------------------------
1 | name: Integration Test
2 |
3 | on:
4 | pull_request:
5 | branches:
6 | - 'main'
7 |
8 | permissions: {}
9 |
10 | jobs:
11 | test:
12 | name: Module Tests
13 | runs-on: 'ubuntu-latest'
14 |
15 | permissions:
16 | contents: read
17 |
18 | steps:
19 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
20 | with:
21 | persist-credentials: false
22 |
23 | - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
24 | with:
25 | go-version: '1.24'
26 | check-latest: true
27 |
28 | - env:
29 | GOPATH: does not matter
30 | run: ./integration_test.sh
31 |
--------------------------------------------------------------------------------
/.github/workflows/publish-site.yaml:
--------------------------------------------------------------------------------
1 | name: publish
2 | on:
3 | workflow_dispatch:
4 | push:
5 | branches:
6 | - 'main'
7 |
8 | permissions: {}
9 |
10 | jobs:
11 | publish:
12 | runs-on: ubuntu-latest
13 |
14 | permissions:
15 | contents: write
16 | pages: write
17 |
18 | steps:
19 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
20 | - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0
21 | with:
22 | python-version: 3.x
23 | - run: pip install mkdocs-material mkdocs-redirects
24 | - run: mkdocs gh-deploy --force
25 |
--------------------------------------------------------------------------------
/.github/workflows/registries.yaml:
--------------------------------------------------------------------------------
1 | name: Push to registries
2 |
3 | on:
4 | push:
5 | branches:
6 | - 'main'
7 |
8 | workflow_dispatch: # Allow manual runs.
9 |
10 | permissions: {}
11 |
12 | jobs:
13 | quay:
14 | name: Push to quay.io
15 | runs-on: ubuntu-latest
16 |
17 | permissions:
18 | contents: read
19 |
20 | steps:
21 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
22 | with:
23 | persist-credentials: false
24 |
25 | - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
26 | with:
27 | go-version-file: 'go.mod'
28 | check-latest: true
29 |
30 | - env:
31 | QUAY_USERNAME: ko-testing+test
32 | QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }}
33 | KO_DOCKER_REPO: quay.io/ko-testing/test
34 | run: |
35 | echo ${QUAY_PASSWORD} | go run ./ login --username=${QUAY_USERNAME} --password-stdin quay.io
36 | go run ./ build --platform=all ./test/ --sbom=none --bare
37 |
38 | dockerhub:
39 | name: Push to dockerhub
40 | runs-on: ubuntu-latest
41 |
42 | permissions:
43 | contents: read
44 |
45 | steps:
46 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
47 | with:
48 | persist-credentials: false
49 |
50 | - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
51 | with:
52 | go-version-file: 'go.mod'
53 | check-latest: true
54 |
55 | - env:
56 | DOCKERHUB_USERNAME: kotesting
57 | DOCKERHUB_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }}
58 | KO_DOCKER_REPO: kotesting/test
59 | run: |
60 | echo ${DOCKERHUB_PASSWORD} | go run ./ login --username=${DOCKERHUB_USERNAME} --password-stdin index.docker.io
61 | go run ./ build --platform=all ./test/ --bare
62 |
63 | ecr:
64 | name: Push to ECR
65 | runs-on: ubuntu-latest
66 | env:
67 | # This is an AWS account that Chainguard provides to enable
68 | # go-containerregistry and ko to test ECR support.
69 | AWS_ACCOUNT: 479305788615
70 | AWS_REGION: us-west-2
71 | REPOSITORY: ko-ecr-e2e-testing
72 |
73 | permissions:
74 | # This lets us clone the repo
75 | contents: read
76 | # This lets us mint identity tokens for federation with AWS.
77 | id-token: write
78 |
79 | steps:
80 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
81 | with:
82 | persist-credentials: false
83 |
84 | - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
85 | with:
86 | go-version-file: 'go.mod'
87 | check-latest: true
88 |
89 | - name: Install ko
90 | run: go install .
91 |
92 | - name: Configure AWS Credentials
93 | uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2
94 | with:
95 | role-to-assume: arn:aws:iam::${{ env.AWS_ACCOUNT }}:role/federated-ecr-readwrite
96 | aws-region: ${{ env.AWS_REGION }}
97 |
98 | - name: Test ko build
99 | run: |
100 | export KO_DOCKER_REPO=${{ env.AWS_ACCOUNT }}.dkr.ecr.${{ env.AWS_REGION }}.amazonaws.com/${{ env.REPOSITORY }}
101 |
102 | ko build --bare ./test
103 |
--------------------------------------------------------------------------------
/.github/workflows/sbom.yaml:
--------------------------------------------------------------------------------
1 | name: Validate SBOMs
2 |
3 | on:
4 | pull_request:
5 | branches:
6 | - 'main'
7 |
8 | env:
9 | SPDX_TOOLS_VERSION: 1.1.0
10 |
11 | permissions: {}
12 |
13 | jobs:
14 | spdx:
15 | name: Validate SPDX SBOM
16 | runs-on: ubuntu-latest
17 |
18 | permissions:
19 | contents: read
20 |
21 | env:
22 | KO_DOCKER_REPO: localhost:1338
23 |
24 | steps:
25 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
26 | with:
27 | persist-credentials: false
28 |
29 | - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
30 | with:
31 | go-version-file: 'go.mod'
32 | check-latest: true
33 |
34 | - uses: chainguard-dev/actions/setup-registry@main
35 | - uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0
36 |
37 | - name: Install SPDX Tools
38 | run: |
39 | wget https://github.com/spdx/tools-java/releases/download/v${SPDX_TOOLS_VERSION}/tools-java-${SPDX_TOOLS_VERSION}.zip
40 | unzip tools-java-${SPDX_TOOLS_VERSION}.zip
41 |
42 | - name: Generate and Validate
43 | run: |
44 | cosign download sbom $(go run ./ build) | tee spdx.json
45 | java -jar ./tools-java-${SPDX_TOOLS_VERSION}-jar-with-dependencies.jar Verify spdx.json
46 |
47 | - uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
48 | if: ${{ always() }}
49 | with:
50 | name: spdx.json
51 | path: spdx.json
52 |
53 | spdx-multi-arch:
54 | name: Validate SPDX multi-arch SBOM
55 | runs-on: ubuntu-latest
56 |
57 | env:
58 | KO_DOCKER_REPO: localhost:1338
59 |
60 | permissions:
61 | contents: read
62 |
63 | steps:
64 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
65 | with:
66 | persist-credentials: false
67 |
68 | - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
69 | with:
70 | go-version-file: 'go.mod'
71 | check-latest: true
72 | - uses: chainguard-dev/actions/setup-registry@main
73 | - uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0
74 |
75 | - name: Install SPDX Tools
76 | run: |
77 | wget https://github.com/spdx/tools-java/releases/download/v${SPDX_TOOLS_VERSION}/tools-java-${SPDX_TOOLS_VERSION}.zip
78 | unzip tools-java-${SPDX_TOOLS_VERSION}.zip
79 |
80 | - name: Generate and Validate
81 | run: |
82 | img=$(go run ./ build --platform=linux/amd64,linux/arm64)
83 | cosign download sbom $img | tee spdx-multi-arch.json
84 |
85 | java -jar ./tools-java-${SPDX_TOOLS_VERSION}-jar-with-dependencies.jar Verify spdx-multi-arch.json
86 |
87 | - uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0
88 | if: ${{ always() }}
89 | with:
90 | name: spdx-multi-arch.json
91 | path: spdx-multi-arch.json
92 |
--------------------------------------------------------------------------------
/.github/workflows/stale.yaml:
--------------------------------------------------------------------------------
1 | name: 'Close stale'
2 |
3 | on:
4 | schedule:
5 | - cron: '0 1 * * *'
6 |
7 | permissions: {}
8 |
9 | jobs:
10 | stale:
11 | runs-on: 'ubuntu-latest'
12 |
13 | permissions:
14 | issues: write
15 | pull-requests: write
16 |
17 | steps:
18 | - uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0
19 | with:
20 | repo-token: ${{ secrets.GITHUB_TOKEN }}
21 |
22 | stale-issue-message: |-
23 | This issue is stale because it has been open for 90 days with no
24 | activity. It will automatically close after 30 more days of
25 | inactivity. Keep fresh with the 'lifecycle/frozen' label.
26 | stale-issue-label: 'lifecycle/stale'
27 | exempt-issue-labels: 'lifecycle/frozen'
28 |
29 | stale-pr-message: |-
30 | This Pull Request is stale because it has been open for 90 days with
31 | no activity. It will automatically close after 30 more days of
32 | inactivity. Keep fresh with the 'lifecycle/frozen' label.
33 | stale-pr-label: 'lifecycle/stale'
34 | exempt-pr-labels: 'lifecycle/frozen'
35 |
36 | days-before-stale: 90
37 | days-before-close: 30
38 |
--------------------------------------------------------------------------------
/.github/workflows/style.yaml:
--------------------------------------------------------------------------------
1 | name: Code Style
2 |
3 | on:
4 | pull_request:
5 | branches:
6 | - 'main'
7 |
8 | permissions: {}
9 |
10 | jobs:
11 | gofmt:
12 | name: check gofmt
13 | runs-on: ubuntu-latest
14 |
15 | permissions:
16 | contents: read
17 |
18 | steps:
19 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
20 | with:
21 | persist-credentials: false
22 |
23 | - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
24 | with:
25 | go-version-file: 'go.mod'
26 | check-latest: true
27 |
28 | - uses: chainguard-dev/actions/gofmt@d886686603afb809f7ef9b734b333e20b7ce5cda
29 | with:
30 | args: -s
31 |
32 | goimports:
33 | name: check goimports
34 | runs-on: ubuntu-latest
35 |
36 | permissions:
37 | contents: read
38 |
39 | steps:
40 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
41 | with:
42 | persist-credentials: false
43 |
44 | - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
45 | with:
46 | go-version-file: 'go.mod'
47 | check-latest: true
48 |
49 | - uses: chainguard-dev/actions/goimports@d886686603afb809f7ef9b734b333e20b7ce5cda
50 |
51 | lint:
52 | name: Lint
53 | runs-on: ubuntu-latest
54 |
55 | permissions:
56 | contents: read
57 |
58 | steps:
59 | - name: Check out code
60 | uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
61 | with:
62 | persist-credentials: false
63 |
64 | - name: Set up Go
65 | uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
66 | with:
67 | go-version-file: 'go.mod'
68 | check-latest: true
69 |
70 | - uses: chainguard-dev/actions/trailing-space@d886686603afb809f7ef9b734b333e20b7ce5cda
71 | if: ${{ always() }}
72 |
73 | - uses: chainguard-dev/actions/eof-newline@d886686603afb809f7ef9b734b333e20b7ce5cda
74 | if: ${{ always() }}
75 |
76 | - uses: reviewdog/action-misspell@18ffb61effb93b47e332f185216be7e49592e7e1 # v1.26.1
77 | if: ${{ always() }}
78 | with:
79 | github_token: ${{ secrets.GITHUB_TOKEN }}
80 | fail_level: warning
81 | locale: "US"
82 | exclude: |
83 | ./.golangci.yaml
84 |
85 | - uses: get-woke/woke-action-reviewdog@d71fd0115146a01c3181439ce714e21a69d75e31 # v0
86 | if: ${{ always() }}
87 | with:
88 | github-token: ${{ secrets.GITHUB_TOKEN }}
89 | reporter: github-pr-check
90 | level: error
91 | fail-on-error: true
92 |
--------------------------------------------------------------------------------
/.github/workflows/test.yaml:
--------------------------------------------------------------------------------
1 | name: Test
2 |
3 | on:
4 | push:
5 | branches:
6 | - 'main'
7 | pull_request:
8 | branches:
9 | - 'main'
10 |
11 | permissions: {}
12 |
13 | jobs:
14 |
15 | test:
16 | name: Unit Tests
17 | runs-on: ubuntu-latest
18 |
19 | permissions:
20 | contents: read
21 |
22 | steps:
23 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
24 | with:
25 | persist-credentials: false
26 |
27 | - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
28 | with:
29 | go-version-file: 'go.mod'
30 | check-latest: true
31 |
32 | - run: go test -coverprofile=coverage.txt -covermode=atomic -race ./...
33 |
--------------------------------------------------------------------------------
/.github/workflows/verify.yaml:
--------------------------------------------------------------------------------
1 | name: Verify
2 |
3 | on:
4 | pull_request:
5 | branches:
6 | - 'main'
7 |
8 | permissions: {}
9 |
10 | jobs:
11 | verify:
12 | name: Verify Codegen
13 | runs-on: ubuntu-latest
14 |
15 | permissions:
16 | contents: read
17 |
18 | steps:
19 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
20 | with:
21 | persist-credentials: false
22 |
23 | - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
24 | with:
25 | go-version-file: 'go.mod'
26 | check-latest: true
27 |
28 | - name: Verify
29 | run: ./hack/presubmit.sh
30 |
31 | golangci:
32 | name: lint
33 | runs-on: ubuntu-latest
34 |
35 | permissions:
36 | contents: read
37 |
38 | steps:
39 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
40 | with:
41 | persist-credentials: false
42 |
43 | - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0
44 | with:
45 | go-version-file: 'go.mod'
46 | check-latest: true
47 |
48 | - name: golangci-lint
49 | uses: golangci/golangci-lint-action@1481404843c368bc19ca9406f87d6e0fc97bdcfd # v7.0.0
50 | with:
51 | version: v2.0
52 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Ignore GoLand (IntelliJ) files.
2 | .idea/
3 |
4 | ko
5 |
6 | .DS_Store
7 |
8 | /vendor/
9 |
--------------------------------------------------------------------------------
/.golangci.yaml:
--------------------------------------------------------------------------------
1 | version: "2"
2 | linters:
3 | enable:
4 | - asciicheck
5 | - errorlint
6 | - gosec
7 | - importas
8 | - misspell
9 | - prealloc
10 | - revive
11 | - staticcheck
12 | - tparallel
13 | - unconvert
14 | - unparam
15 | - whitespace
16 | disable:
17 | - depguard
18 | - errcheck
19 | settings:
20 | gosec:
21 | excludes:
22 | - G115
23 | exclusions:
24 | generated: lax
25 | presets:
26 | - comments
27 | - common-false-positives
28 | - legacy
29 | - std-error-handling
30 | rules:
31 | - linters:
32 | - gosec
33 | path: test
34 | paths:
35 | - third_party$
36 | - builtin$
37 | - examples$
38 | formatters:
39 | enable:
40 | - gofmt
41 | - goimports
42 | exclusions:
43 | generated: lax
44 | paths:
45 | - third_party$
46 | - builtin$
47 | - examples$
48 |
--------------------------------------------------------------------------------
/.goreleaser.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 |
3 | before:
4 | hooks:
5 | - go mod tidy
6 | - /bin/bash -c 'if [ -n "$(git --no-pager diff --exit-code go.mod go.sum)" ]; then exit 1; fi'
7 |
8 | builds:
9 | - id: binary
10 | main: ./main.go
11 | env:
12 | - CGO_ENABLED=0
13 | flags:
14 | - -trimpath
15 | ldflags:
16 | - "-s -w -X github.com/google/ko/pkg/commands.Version={{.Version}}"
17 | goos:
18 | - windows
19 | - linux
20 | - darwin
21 | goarch:
22 | - amd64
23 | - arm64
24 | - s390x
25 | - 386
26 | - mips64le
27 | - ppc64le
28 | - riscv64
29 |
30 | kos:
31 | - id: ko-image
32 | build: binary
33 | main: .
34 | base_image: golang:latest
35 | ldflags:
36 | - "-s -w -X github.com/google/ko/pkg/commands.Version={{.Version}}"
37 | platforms:
38 | - all
39 | tags:
40 | - '{{ .Tag }}'
41 | - '{{ .FullCommit }}'
42 | - latest
43 | sbom: spdx
44 | bare: true
45 | preserve_import_paths: false
46 | base_import_paths: false
47 |
48 | archives:
49 | - id: with-version
50 | name_template: >-
51 | {{ .ProjectName }}_
52 | {{- .Version }}_
53 | {{- title .Os }}_
54 | {{- if eq .Arch "amd64" }}x86_64
55 | {{- else if eq .Arch "386" }}i386
56 | {{- else }}{{ .Arch }}{{ end }}
57 | - id: without-version
58 | name_template: >-
59 | {{ .ProjectName }}_
60 | {{- title .Os }}_
61 | {{- if eq .Arch "amd64" }}x86_64
62 | {{- else if eq .Arch "386" }}i386
63 | {{- else }}{{ .Arch }}{{ end }}
64 |
65 | checksum:
66 | name_template: 'checksums.txt'
67 |
68 | snapshot:
69 | version_template: "{{ .Tag }}-next"
70 |
71 | changelog:
72 | sort: asc
73 | use: github
74 | filters:
75 | exclude:
76 | - '^docs:'
77 | - '^test:'
78 |
--------------------------------------------------------------------------------
/.ko.yaml:
--------------------------------------------------------------------------------
1 | baseImageOverrides:
2 | github.com/google/ko: golang:latest
3 |
4 | builds:
5 | - id: ko
6 | ldflags:
7 | - "{{ .Env.LDFLAGS }}"
8 |
--------------------------------------------------------------------------------
/.wokeignore:
--------------------------------------------------------------------------------
1 | # Uses some Cobra methods
2 | pkg/commands/*
3 |
--------------------------------------------------------------------------------
/CNAME:
--------------------------------------------------------------------------------
1 | ko.build
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # How to Contribute to ko
2 |
3 | We'd love to accept your patches and contributions to this project. There are
4 | just a few small guidelines you need to follow.
5 |
6 | ## Code reviews
7 |
8 | All submissions, including submissions by project members, require review. We
9 | use GitHub pull requests for this purpose. Consult
10 | [GitHub Help](https://help.github.com/articles/about-pull-requests/) for more
11 | information on using pull requests.
12 |
13 | ## Testing
14 |
15 | Ensure the following passes:
16 | ```
17 | ./hack/presubmit.sh
18 | ```
19 | and commit any resultant changes to `go.mod` and `go.sum`. To update any docs
20 | after client changes, run:
21 |
22 | ```
23 | ./hack/update-codegen.sh
24 | ```
25 |
--------------------------------------------------------------------------------
/MAINTAINERS.md:
--------------------------------------------------------------------------------
1 | # Maintainers
2 |
3 | This page lists all active members of the maintainers for the `ko` project and any subprojects.
4 |
5 | - Jon Johnson (@jonjohnsonjr)
6 | - Matt Moore (@mattmoor)
7 | - Jason Hall (@imjasonh)
8 |
9 | New projects and new maintainers can be added or removed with the consensus approval of the current maintainers.
10 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # `ko`: Easy Go Containers
2 |
3 | [](https://github.com/ko-build/ko/actions?query=workflow%3ABuild)
4 | [](https://godoc.org/github.com/google/ko)
5 | [](https://goreportcard.com/report/ko-build/ko)
6 | [](https://slsa.dev/images/gh-badge-level3.svg)
7 |
8 |
9 |
10 | ---
11 |
12 | > 🎉 Google has applied for `ko` to join the Cloud Native Computing Foundation as a Sandbox project! Learn more [here](https://opensource.googleblog.com/2022/10/ko-applies-to-become-a-cncf-sandbox-project.html)!
13 |
14 | `ko` is a simple, fast container image builder for Go applications.
15 |
16 | It's ideal for use cases where your image contains a single Go application
17 | without any/many dependencies on the OS base image (e.g., no cgo, no OS package
18 | dependencies).
19 |
20 | `ko` builds images by effectively executing `go build` on your local machine,
21 | and as such doesn't require `docker` to be installed. This can make it a good
22 | fit for lightweight CI/CD use cases.
23 |
24 | `ko` makes [multi-platform builds](https://ko.build/features/multi-platform/) easy, produces [SBOMs](https://ko.build/features/sboms/) by default, and includes support for simple YAML templating which makes it a powerful tool for [Kubernetes applications](https://ko.build/features/k8s/).
25 |
26 | # [Install `ko`](https://ko.build/install/) and [get started](https://ko.build/get-started/)!
27 |
28 | ### Acknowledgements
29 |
30 | This work is based heavily on experience from having built the [Docker](https://github.com/bazelbuild/rules_docker) and [Kubernetes](https://github.com/bazelbuild/rules_k8s) support for [Bazel](https://bazel.build).
31 | That work was presented [here](https://www.youtube.com/watch?v=RS1aiQqgUTA).
32 |
33 | ### Discuss
34 |
35 | Questions? Comments? Ideas?
36 | Come discuss `ko` with us in the `#ko-build` channel on the [Kubernetes Slack](https://slack.k8s.io)!
37 | See you there!
38 |
39 | ### Community Meetings
40 |
41 | You can find all the necessary details about the community meetings in this [page](https://ko.build/community).
42 |
--------------------------------------------------------------------------------
/ROADMAP.md:
--------------------------------------------------------------------------------
1 | # `ko` Project Roadmap
2 |
3 | _Last updated October 2022_
4 |
5 | - Foster a community of contributors and users
6 | - give talks, do outreach, expand the pool of contributors
7 | - identify projects that could benefit from using `ko`, and help onboard them
8 | - publish case studies from successful migrations
9 |
10 | - Integrate [sigstore](https://sigstore.dev) for built artifacts
11 | - attach signed SBOMs
12 | - attach signed provenance attestations
13 | - support the OCI referrers API and [fallback tag scheme](https://github.com/opencontainers/distribution-spec/blob/main/spec.md#referrers-tag-schema)
14 | - integrate with CI workload identity (e.g., GitHub OIDC) to keylessly sign artifacts
15 |
16 | - Faster builds
17 | - identify unnecessary work and avoid it when possible
18 |
19 | - Ecosystem integrations
20 | - support Terraform provider, and potentially Pulumi and CDK, others
21 | - provide working examples of these integrations
22 |
--------------------------------------------------------------------------------
/cmd/help/main.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package main
16 |
17 | import (
18 | "fmt"
19 | "os"
20 |
21 | "github.com/google/ko/pkg/commands"
22 | "github.com/spf13/cobra"
23 | "github.com/spf13/cobra/doc"
24 | )
25 |
26 | var dir string
27 | var root = &cobra.Command{
28 | Use: "gendoc",
29 | Short: "Generate ko's help docs",
30 | Args: cobra.NoArgs,
31 | RunE: func(*cobra.Command, []string) error {
32 | return doc.GenMarkdownTree(commands.Root, dir)
33 | },
34 | }
35 |
36 | func init() {
37 | root.Flags().StringVarP(&dir, "dir", "d", ".", "Path to directory in which to generate docs")
38 | }
39 |
40 | func main() {
41 | if err := root.Execute(); err != nil {
42 | fmt.Println(err)
43 | os.Exit(1)
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/docs/CNAME:
--------------------------------------------------------------------------------
1 | ko.build
2 |
--------------------------------------------------------------------------------
/docs/README.md:
--------------------------------------------------------------------------------
1 | # Docs for https://ko.build
2 |
3 | ## Development
4 |
5 | Update `.md` files to update content.
6 |
7 | Update `mkdocs.yml` to update sidebar headers and ordering.
8 |
9 | To run locally:
10 |
11 | - [install `mkdocs` and `mkdocs-material`](https://squidfunk.github.io/mkdocs-material/getting-started/) and run `mkdocs serve`, or
12 | - `docker run --rm -it -p 8000:8000 -v ${PWD}:/docs squidfunk/mkdocs-material`
13 | - on an M1 Mac, use `ghcr.io/afritzler/mkdocs-material` instead.
14 |
15 | This will start a local server on localhost:8000 that autoupdates as you make changes.
16 |
17 | ## Deployment
18 |
19 | When PRs are merged, the site will be rebuilt and published automatically.
20 |
21 | ### Credits
22 |
23 | The site is powered by [mkdocs-material](https://squidfunk.github.io/mkdocs-material). The code and theme are released under the MIT license.
24 |
25 | Content is licensed [CC-BY](https://creativecommons.org/licenses/by/4.0/).
26 |
27 |
--------------------------------------------------------------------------------
/docs/advanced/go-packages.md:
--------------------------------------------------------------------------------
1 | # Go Packages
2 |
3 | `ko`'s functionality can be consumed as a library in a Go application.
4 |
5 | To build an image, use [`pkg/build`](https://pkg.go.dev/github.com/google/ko/pkg/build), and publish it with [`pkg/publish`](https://pkg.go.dev/github.com/google/ko/pkg/publish).
6 |
7 | This is a minimal example of using the packages together, to implement the core subset of `ko`'s functionality:
8 |
9 | ```go
10 | package main
11 |
12 | import (
13 | "context"
14 | "fmt"
15 | "log"
16 |
17 | "github.com/google/go-containerregistry/pkg/authn"
18 | "github.com/google/go-containerregistry/pkg/name"
19 | "github.com/google/go-containerregistry/pkg/v1/remote"
20 | "github.com/google/ko/pkg/build"
21 | "github.com/google/ko/pkg/publish"
22 | )
23 |
24 | const (
25 | baseImage = "cgr.dev/chainguard/static:latest"
26 | targetRepo = "example.registry/my-repo"
27 | importpath = "github.com/my-org/miniko"
28 | commitSHA = "deadbeef"
29 | )
30 |
31 | func main() {
32 | ctx := context.Background()
33 |
34 | b, err := build.NewGo(ctx, ".",
35 | build.WithPlatforms("linux/amd64"), // only build for these platforms.
36 | build.WithBaseImages(func(ctx context.Context, _ string) (name.Reference, build.Result, error) {
37 | ref := name.MustParseReference(baseImage)
38 | base, err := remote.Index(ref, remote.WithContext(ctx))
39 | return ref, base, err
40 | }))
41 | if err != nil {
42 | log.Fatalf("NewGo: %v", err)
43 | }
44 | r, err := b.Build(ctx, importpath)
45 | if err != nil {
46 | log.Fatalf("Build: %v", err)
47 | }
48 |
49 | p, err := publish.NewDefault(targetRepo, // publish to example.registry/my-repo
50 | publish.WithTags([]string{commitSHA}), // tag with :deadbeef
51 | publish.WithAuthFromKeychain(authn.DefaultKeychain)) // use credentials from ~/.docker/config.json
52 | if err != nil {
53 | log.Fatalf("NewDefault: %v", err)
54 | }
55 | ref, err := p.Publish(ctx, r, importpath)
56 | if err != nil {
57 | log.Fatalf("Publish: %v", err)
58 | }
59 | fmt.Println(ref.String())
60 | }
61 | ```
62 |
--------------------------------------------------------------------------------
/docs/advanced/lambda.md:
--------------------------------------------------------------------------------
1 | # AWS Lambda
2 |
3 | `ko` can build images that can be deployed as AWS Lambda functions, using [Lambda's container support](https://docs.aws.amazon.com/lambda/latest/dg/images-create.html).
4 |
5 | For best results, use the [Go runtime interface client](https://docs.aws.amazon.com/lambda/latest/dg/go-image.html#go-image-clients) provided by the [`lambda` package](https://pkg.go.dev/github.com/aws/aws-lambda-go/lambda).
6 |
7 | For example:
8 |
9 | ```go
10 | package main
11 |
12 | import (
13 | "context"
14 | "fmt"
15 | "github.com/aws/aws-lambda-go/lambda"
16 | )
17 |
18 | type Event struct {
19 | Name string `json:"name"`
20 | // TODO: add other request fields here.
21 | }
22 |
23 | func main() {
24 | lambda.Start(func(ctx context.Context, event Event) (string, error) {
25 | return fmt.Sprintf("Hello %s!", event.Name), nil
26 | })
27 | }
28 | ```
29 |
30 | See AWS's [documentation](https://docs.aws.amazon.com/lambda/latest/dg/golang-handler.html) for more information on writing Lambda functions in Go.
31 |
32 | To deploy to Lambda, you must push to AWS Elastic Container Registry (ECR):
33 |
34 | ```sh
35 | KO_DOCKER_REPO=[account-id].dkr.ecr.[region].amazonaws.com/my-repo
36 | image=$(ko build ./cmd/app)
37 | ```
38 |
39 | Then, create a Lambda function using the image in ECR:
40 |
41 | ```sh
42 | aws lambda create-function \
43 | --function-name hello-world \
44 | --package-type Image \
45 | --code ImageUri=${image} \
46 | --role arn:aws:iam::[account-id]:role/lambda-ex
47 | ```
48 |
49 | See AWS's [documentation](https://docs.aws.amazon.com/lambda/latest/dg/go-image.html) for more information on deploying Lambda functions using Go container images, including how to configure push access to ECR, and how to configure the IAM role for the function.
50 |
51 | The base image that `ko` uses by default supports both x86 and Graviton2 architectures.
52 |
53 | You can also use the [`ko` Terraform provider](./terraform.md) to build and deploy Lambda functions as part of your IaC workflow, using the [`aws_lambda_function` resource](https://registry.terraform.io/providers/hashicorp/aws/latest/docs/resources/lambda_function.html). See the [provider example](https://github.com/ko-build/terraform-provider-ko/tree/main/provider-examples/lambda) to get started.
54 |
--------------------------------------------------------------------------------
/docs/advanced/limitations.md:
--------------------------------------------------------------------------------
1 | # Limitations
2 |
3 | `ko` works best when your application has no dependencies on the underlying image.
4 |
5 | This means `ko` is ideal when you don't require [cgo](https://pkg.go.dev/cmd/cgo), and builds are executed with `CGO_ENABLED=0` by default.
6 |
7 | To install other OS packages, make those available in your [configured base image](../../configuration).
8 |
9 | `ko` only supports Go applications.
10 | For a similar tool targeting Java applications, try [Jib](https://github.com/GoogleContainerTools/jib).
11 | For other languages, try [apko](https://github.com/chainguard-dev/apko) and [melange](https://github.com/chainguard-dev/melange).
12 |
13 |
--------------------------------------------------------------------------------
/docs/advanced/linux-capabilities.md:
--------------------------------------------------------------------------------
1 | # Linux Capabilities
2 |
3 | In Linux, capabilities are a way to selectively grant privileges to a running process.
4 |
5 | Docker provides `--cap-add` and `--cap-drop`
6 | [run options](https://docs.docker.com/engine/containers/run/#runtime-privilege-and-linux-capabilities)
7 | to tweak container capabilities, e.g:
8 |
9 | ```
10 | docker run --cap-add bpf hello-world
11 | ```
12 |
13 | If container runs as a non-root user,
14 | capabilities are narrowed by intersecting with *file* capabilities of the
15 | application binary. When building images with a Dockerfile, one
16 | typically uses `setcap` tool to modify file capabilities, e.g:
17 | `setcap FILE bpf=ep`.
18 |
19 | To set file capabilities with `ko`, specify `linux_capabilities`
20 | in builds configuration section in your `.ko.yaml`. Use `setcap` syntax:
21 |
22 | ```yaml
23 | builds:
24 | - id: caps
25 | linux_capabilities: bpf=ep
26 | ```
27 | ## Alternative spelling
28 |
29 | ```yaml
30 | builds:
31 | - id: caps
32 | linux_capabilities:
33 | - cap1
34 | - cap2
35 | - cap3
36 | ```
37 |
38 | A list of capability names is equivalent to `cap1,cap2,cap3=p`.
39 |
40 | ## Improving UX in capability-reliant apps
41 |
42 | A capability can be *permitted* (`=p`), or both *permitted* and *effective* (`=ep`).
43 | Effective capabilities are used for permission checks.
44 | A program can promote permitted capability to effective when needed.
45 |
46 | ```yaml
47 | builds:
48 | - id: caps
49 | linux_capabilities: bpf,perfmon,net_admin=ep
50 | ```
51 |
52 | Initially, `=ep` might look like a good idea.
53 | There's no need to explicitly promote *permitted* capabilities.
54 | Application takes advantage of *effective* capabilities right away.
55 |
56 | There is a catch though.
57 |
58 | ```
59 | $ docker run --cap-add bpf ko.local/caps.test-4b8f7bca75c467b3d2803e1c087a3287
60 | exec /ko-app/caps.test: operation not permitted
61 | ```
62 |
63 | When run options request fewer capabilities than specified in file capabilities,
64 | container fails to start. It is hard to tell what went wrong since
65 | `operation not permitted` is a generic error. (Docker is unlikely to improve diagnostics
66 | for this failure case since the check is implemented in Linux kernel.)
67 |
68 |
69 | We suggest to use `=p` instead. This option puts application in charge of verifying and
70 | promoting permitted capabilities to effective. It can produce much better diagnostics:
71 |
72 | ```
73 | $ docker run --cap-add bpf ko.local/caps.test-4b8f7bca75c467b3d2803e1c087a3287
74 | current capabilities: cap_bpf=p
75 | activating capabilities: cap_net_admin,cap_perfmon,cap_bpf=ep: operation not permitted
76 | ```
77 |
78 | [Sample code](https://go.dev/play/p/uPMzyotkNHg).
79 |
--------------------------------------------------------------------------------
/docs/advanced/migrating-from-dockerfile.md:
--------------------------------------------------------------------------------
1 | # Migrating from Dockerfile
2 |
3 | If your `Dockerfile` looks like either of the examples in the [official tutorial for writing a Dockerfile to containerize a Go application](https://docs.docker.com/language/golang/build-images/), you can easily migrate to use `ko` instead.
4 |
5 | Let's review the best practice multi-stage Dockerfile in that tutorial first:
6 |
7 | ```Dockerfile
8 | ## Build
9 | FROM golang:1.16-buster AS build
10 |
11 | WORKDIR /app
12 |
13 | COPY go.mod ./
14 | COPY go.sum ./
15 | RUN go mod download
16 |
17 | COPY *.go ./
18 |
19 | RUN go build -o /docker-gs-ping
20 |
21 | ## Deploy
22 | FROM gcr.io/distroless/base-debian10
23 |
24 | WORKDIR /
25 |
26 | COPY --from=build /docker-gs-ping /docker-gs-ping
27 |
28 | EXPOSE 8080
29 |
30 | USER nonroot:nonroot
31 |
32 | ENTRYPOINT ["/docker-gs-ping"]
33 | ```
34 |
35 | This `Dockerfile`:
36 |
37 | 1. pulls the `golang:1.16` image
38 | 1. `COPY`s your local source into the container environment (`COPY`ing `go.mod` and `go.sum` first and running `go mod download`, to cache dependencies in the container environment)
39 | 1. `RUN`s `go build` on your source, inside the container, to produce an executable
40 | 1. `COPY`s the executable built in the previous step into a new image, on top of a minimal [distroless](https://github.com/GoogleContainerTools/distroless) base image.
41 |
42 | The result is a Go application built on a minimal base image, with an optimally cached build sequence.
43 |
44 | After running `docker build` on this `Dockerfile`, don't forget to push that image to the registry so you can deploy it.
45 |
46 | ---
47 |
48 | ## Migrating to `ko`
49 |
50 | If your Go source is laid out as described in the tutorial, and you've [installed](../../install) and [set up your environment](../../get-started), you can simply run `ko build ./` to build and push the container image to your registry.
51 |
52 | You're done. You can delete your `Dockerfile` and uninstall `docker`.
53 |
54 | `ko` takes advantage of your local [Go build cache](../../features/build-cache) without needing to be told to, and it sets the `ENTRYPOINT` and uses a nonroot distroless base image by default.
55 |
56 | To build a multi-arch image, simply add `--platform=all`.
57 | Compare this to the [equivalent Docker instructions](https://docs.docker.com/desktop/multi-arch/).
58 |
59 |
--------------------------------------------------------------------------------
/docs/advanced/terraform.md:
--------------------------------------------------------------------------------
1 | # Terraform Provider
2 |
3 | In addition to the CLI, `ko`'s functionality is also available as a Terraform provider.
4 |
5 | This allows `ko` to be integrated with your Infrastructure-as-Code (IaC) workflows, and makes building your code a seamless part of your deployment process.
6 |
7 | Using the Terraform provider is as simple as adding a `ko_build` resource to your Terraform configuration:
8 |
9 | ```hcl
10 | // Require the `ko-build/ko` provider.
11 | terraform {
12 | required_providers {
13 | ko = { source = "ko-build/ko" }
14 | }
15 | }
16 |
17 | // Configure the provider to push to your repo.
18 | provider "ko" {
19 | repo = "example.registry/my-repo" // equivalent to KO_DOCKER_REPO
20 | }
21 |
22 | // Build your code.
23 | resource "ko_build" "app" {
24 | importpath = "github.com/example/repo/cmd/app"
25 | }
26 |
27 | // TODO: use the `ko_build.app` resource elsewhere in your Terraform configuration.
28 |
29 | // Report the build image's digest.
30 | output "image" {
31 | value = ko_build.app.image_ref
32 | }
33 | ```
34 |
35 | See the [`ko-build/ko` provider on the Terraform Registry](https://registry.terraform.io/providers/ko-build/ko/latest) for more information, and the [GitHub repo](https://github.com/ko-build/terraform-provider-ko) for more examples.
36 |
--------------------------------------------------------------------------------
/docs/community.md:
--------------------------------------------------------------------------------
1 | # Community
2 |
3 | ## Meetings
4 |
5 | We have a bi-weekly community meeting on [Wednesdays at 1:00 PM US Eastern time, 10:00 AM US Western time](https://dateful.com/eventlink/2763257725). The main goal of these meetings is that we want to hear from you! We want to know what you're using `ko` for, what you'd like to see in `ko`, how we can make `ko` better for you. With any remaining time we can go through open issues and PRs.
6 |
7 | We have a [meeting agenda](https://ko.build/agenda) you can use to propose topics for discussion/ideas. You can also just show up and we'll figure out what to talk about.
8 |
9 | ## Slack
10 |
11 | Come discuss `ko` with us in the `#ko-build` channel on the [Kubernetes Slack](https://ko.build/slack)!
12 | See you there!
13 |
--------------------------------------------------------------------------------
/docs/custom/main.html:
--------------------------------------------------------------------------------
1 | {% extends "base.html" %}
2 |
3 | {% block site_meta %}
4 | {{ super() }}
5 | {% if page and page.meta and page.meta.ko_meta %}
6 |
7 |
8 |
9 | {% endif %}
10 | {% endblock %}
11 |
12 |
--------------------------------------------------------------------------------
/docs/custom/partials/copyright.html:
--------------------------------------------------------------------------------
1 | The Linux Foundation® (TLF) has registered trademarks and uses trademarks. For a list of TLF trademarks, see Trademark Usage
2 |
--------------------------------------------------------------------------------
/docs/deployment.md:
--------------------------------------------------------------------------------
1 | # Deployment
2 |
3 | _See [Kubernetes Integration](../features/k8s) for information about deploying to Kubernetes._
4 |
5 | Because the output of `ko build` is an image reference, you can easily pass it to other tools that expect to take an image reference.
6 |
7 | ### [`docker run`](https://docs.docker.com/engine/reference/run/)
8 |
9 | To run the container locally:
10 |
11 | ```plaintext
12 | docker run -p 8080:8080 $(ko build ./cmd/app)
13 | ```
14 |
15 | ---
16 |
17 | ### [Google Cloud Run](https://cloud.google.com/run)
18 |
19 | ```plaintext
20 | gcloud run deploy --image=$(ko build ./cmd/app)
21 | ```
22 |
23 | > 💡 **Note:** The image must be pushed to [Google Container Registry](https://cloud.google.com/container-registry) or [Artifact Registry](https://cloud.google.com/artifact-registry).
24 |
25 | ---
26 |
27 | ### [fly.io](https://fly.io)
28 |
29 | ```plaintext
30 | flyctl launch --image=$(ko build ./cmd/app)
31 | ```
32 |
33 | > 💡 **Note:** The image must be pushed to Fly.io's container registry at `registry.fly.io`, or if not, the image must be publicly available. When pushing to `registry.fly.io`, you must first log in with [`flyctl auth docker`](https://fly.io/docs/flyctl/auth-docker/).
34 |
35 | ---
36 |
37 | ### [AWS Lambda](https://aws.amazon.com/lambda/)
38 |
39 | ```plaintext
40 | aws lambda update-function-code \
41 | --function-name=my-function-name \
42 | --image-uri=$(ko build ./cmd/app)
43 | ```
44 |
45 | > 💡 **Note:** The image must be pushed to [ECR](https://aws.amazon.com/ecr/), based on the AWS provided base image, and use the [`aws-lambda-go`](https://github.com/aws/aws-lambda-go) framework.
46 | See [official docs](https://docs.aws.amazon.com/lambda/latest/dg/go-image.html) for more information.
47 |
48 | ---
49 |
50 | ### [Azure Container Apps](https://azure.microsoft.com/services/container-apps/)
51 |
52 | ```plaintext
53 | az containerapp update \
54 | --name my-container-app
55 | --resource-group my-resource-group
56 | --image $(ko build ./cmd/app)
57 | ```
58 |
59 | > 💡 **Note:** The image must be pushed to [ACR](https://azure.microsoft.com/services/container-registry/) or other registry service.
60 | See [official docs](https://docs.microsoft.com/azure/container-apps/) for more information.
61 |
62 |
--------------------------------------------------------------------------------
/docs/features/build-cache.md:
--------------------------------------------------------------------------------
1 | # Build Cache
2 |
3 | Because `ko` just runs `go build` in your normal development environment, it automatically reuses your [`go build` cache](https://pkg.go.dev/cmd/go#hdr-Build_and_test_caching) from previous builds, making iterative development faster.
4 |
5 | `ko` also avoids pushing blobs to the remote image registry if they're already present, making pushes faster.
6 |
7 | You can make `ko` even faster by setting the `KOCACHE` environment variable.
8 | This tells `ko` to store a local mapping between the `go build` inputs to the image layer that they produce, so `go build` can be skipped entirely if the layer is already present in the image registry.
9 |
10 |
--------------------------------------------------------------------------------
/docs/features/debugging.md:
--------------------------------------------------------------------------------
1 | # Debugging
2 |
3 | Sometimes it's challenging to track down the cause of unexpected behavior in an app. Because `ko` makes it simple to make tweaks to your app and immediately rebuild your image, it's possible to iteratively explore various aspects of your app, such as by adding log lines that print variable values.
4 |
5 | But to help you solve the problem _as fast as possible_, `ko` supports debugging your Go app with [delve](https://github.com/go-delve/delve).
6 |
7 | To use this feature, just add the `--debug` flag to your `ko build` command. This adjusts how the image is built:
8 |
9 | - It installs `delve` in the image (in addition to your own app).
10 | - It sets the image's `ENTRYPOINT` to a `delve exec ...` command that runs the Go app in debug-mode, listening on port `40000` for a debugger client.
11 | - It ensures your compiled Go app includes debug symbols needed to enable debugging.
12 |
13 | **Note:** This feature is geared toward development workflows. It **should not** be used in production.
14 |
15 | ### How it works
16 |
17 | Build the image using the debug feature.
18 |
19 | ```plaintext
20 | ko build . --debug
21 | ```
22 |
23 | Run the container, ensuring that the debug port (`40000`) is exposed to allow clients to connect to it.
24 |
25 | ```plaintext
26 | docker run -p 40000:40000
27 | ```
28 |
29 | This sets up your app to be waiting to run the command you've specified. All that's needed now is to connect your debugger client to the running container!
30 |
--------------------------------------------------------------------------------
/docs/features/k8s.md:
--------------------------------------------------------------------------------
1 | # Kubernetes Integration
2 |
3 | You _could_ stop at just building and pushing images.
4 |
5 | But, because building images is so _easy_ with `ko`, and because building with
6 | `ko` only requires a string importpath to identify the image, we can integrate
7 | this with YAML generation to make Kubernetes use cases much simpler.
8 |
9 | ## YAML Changes
10 |
11 | Traditionally, you might have a Kubernetes deployment, defined in a YAML file,
12 | that runs an image:
13 |
14 | ```yaml
15 | apiVersion: apps/v1
16 | kind: Deployment
17 | metadata:
18 | name: my-deployment
19 | spec:
20 | selector:
21 | matchLabels:
22 | app: my-app
23 | template:
24 | metadata:
25 | labels:
26 | app: my-app
27 | spec:
28 | containers:
29 | - name: my-app
30 | image: registry.example.com/my-app:v1.2.3
31 | ```
32 |
33 | ...which you apply to your cluster with `kubectl apply`:
34 |
35 | ```plaintext
36 | kubectl apply -f deployment.yaml
37 | ```
38 |
39 | With `ko`, you can instead reference your Go binary by its importpath, prefixed
40 | with `ko://`:
41 |
42 | ```yaml
43 | ...
44 | spec:
45 | containers:
46 | - name: my-app
47 | image: ko://github.com/my-user/my-repo/cmd/app
48 | ```
49 |
50 | ## `ko resolve`
51 |
52 | With this small change, running `ko resolve -f deployment.yaml` will instruct
53 | `ko` to:
54 |
55 | 1. scan the YAML file(s) for values with the `ko://` prefix,
56 | 2. for each unique `ko://`-prefixed string, execute `ko build ` to
57 | build and push an image,
58 | 3. replace `ko://`-prefixed string(s) in the input YAML with the fully-specified
59 | image reference of the built image(s), as above.
60 | 4. Print the resulting resolved YAML to stdout.
61 |
62 | The result can be redirected to a file, to distribute to others:
63 |
64 | ```plaintext
65 | ko resolve -f config/ > release.yaml
66 | ```
67 |
68 | Taken together, `ko resolve` aims to make packaging, pushing, and referencing
69 | container images an invisible implementation detail of your Kubernetes
70 | deployment, and let you focus on writing code in Go.
71 |
72 | ## `ko apply`
73 |
74 | To apply the resulting resolved YAML config, you can redirect the output of
75 | `ko resolve` to `kubectl apply`:
76 |
77 | ```plaintext
78 | ko resolve -f config/ | kubectl apply -f -
79 | ```
80 |
81 | Since this is a relatively common use case, the same functionality is available
82 | using `ko apply`:
83 |
84 | ```plaintext
85 | ko apply -f config/
86 | ```
87 |
88 | Also, any flags passed after `--` are passed to `kubectl apply` directly, for example to specify context and kubeconfig:
89 | ```
90 | ko apply -f config -- --context=foo --kubeconfig=cfg.yaml
91 | ```
92 |
93 | **NB:** This requires that `kubectl` is available.
94 |
95 | ## `ko delete`
96 |
97 | To teardown resources applied using `ko apply`, you can run `ko delete`:
98 |
99 | ```plaintext
100 | ko delete -f config/
101 | ```
102 |
103 | This is purely a convenient alias for `kubectl delete`, and doesn't perform any
104 | builds, or delete any previously built images.
105 |
106 |
--------------------------------------------------------------------------------
/docs/features/multi-platform.md:
--------------------------------------------------------------------------------
1 | # Multi-Platform Images
2 |
3 | Because Go supports cross-compilation to other CPU architectures and operating systems, `ko` excels at producing multi-platform images.
4 |
5 | To build and push an image for all platforms supported by the configured base image, simply add `--platform=all`.
6 | This will instruct `ko` to look up all the supported platforms in the base image, execute `GOOS= GOARCH= GOARM= go build` for each platform, and produce a manifest list containing an image for each platform.
7 |
8 | You can also select specific platforms, for example, `--platform=linux/amd64,linux/arm64`.
9 |
10 | `ko` also has experimental support for building for Windows images.
11 | See [FAQ](../../advanced/faq#can-i-build-windows-containers).
12 |
13 |
--------------------------------------------------------------------------------
/docs/features/sboms.md:
--------------------------------------------------------------------------------
1 | # SBOMs
2 |
3 | A [Software Bill of Materials (SBOM)](https://en.wikipedia.org/wiki/Software_bill_of_materials) is a list of software components that a software artifact depends on.
4 | Having a list of dependencies can be helpful in determining whether any vulnerable components were used to build the software artifact.
5 |
6 | **From v0.9+, `ko` generates and uploads an SBOM for every image it produces by default.**
7 |
8 | ko will generate an SBOM in the [SPDX](https://spdx.dev/) format by default. To disable SBOM generation, pass `--sbom=none`.
9 |
10 | These SBOMs can be downloaded using the [`cosign download sbom`](https://github.com/sigstore/cosign/blob/main/doc/cosign_download_sbom.md) command.
11 |
12 |
--------------------------------------------------------------------------------
/docs/features/static-assets.md:
--------------------------------------------------------------------------------
1 | # Static Assets
2 |
3 | `ko` can also bundle static assets into the images it produces.
4 |
5 | By convention, any contents of a directory named `/kodata/` will be
6 | bundled into the image, and the path where it's available in the image will be
7 | identified by the environment variable `KO_DATA_PATH`.
8 |
9 | As an example, you can bundle and serve static contents in your image:
10 |
11 | ```
12 | cmd/
13 | app/
14 | main.go
15 | kodata/
16 | favicon.ico
17 | index.html
18 | ```
19 |
20 | Then, in your `main.go`:
21 |
22 | ```go
23 | func main() {
24 | http.Handle("/", http.FileServer(http.Dir(os.Getenv("KO_DATA_PATH"))))
25 | log.Fatal(http.ListenAndServe(":8080", nil))
26 | }
27 | ```
28 |
29 | You can simulate `ko`'s behavior outside of the container image by setting the
30 | `KO_DATA_PATH` environment variable yourself with `KO_DATA_PATH=cmd/app/kodata/ go run ./cmd/app`.
31 |
32 | > 💡 **Tip:** Symlinks in `kodata` are followed and included as well. For example,
33 | you can include Git commit information in your image with `ln -s -r .git/HEAD ./cmd/app/kodata/`
34 |
35 | Also note that `http.FileServer` will not serve the `Last-Modified` header
36 | (or validate `If-Modified-Since` request headers) because `ko` does not embed
37 | timestamps by default.
38 |
39 | This can be supported by manually setting the `KO_DATA_DATE_EPOCH` environment
40 | variable during build ([See FAQ](../../advanced/faq#why-are-my-images-all-created-in-1970)).
41 |
42 |
--------------------------------------------------------------------------------
/docs/get-started.md:
--------------------------------------------------------------------------------
1 | # Get Started
2 |
3 | ## Setup
4 |
5 | First, [install `ko`](../install).
6 |
7 | ### Authenticate
8 |
9 | `ko` depends on the authentication configured in your Docker config (typically `~/.docker/config.json`).
10 |
11 | ✨ **If you can push an image with `docker push`, you are already authenticated for `ko`!** ✨
12 |
13 | Since `ko` doesn't require `docker`, `ko login` also provides a surface for logging in to a container image registry with a username and password, similar to [`docker login`](https://docs.docker.com/engine/reference/commandline/login/).
14 |
15 | Additionally, even if auth is not configured in the Docker config, `ko` includes built-in support for authenticating to the following container registries using credentials configured in the environment:
16 |
17 | - Google Container Registry and Artifact Registry, using [Application Default Credentials](https://cloud.google.com/docs/authentication/production) or auth configured in `gcloud`.
18 | - Amazon Elastic Container Registry, using [AWS credentials](https://github.com/awslabs/amazon-ecr-credential-helper/#aws-credentials)
19 | - Azure Container Registry, using [environment variables](https://github.com/chrismellard/docker-credential-acr-env/)
20 | - GitHub Container Registry, using the `GITHUB_TOKEN` environment variable
21 |
22 | ### Choose Destination
23 |
24 | `ko` depends on an environment variable, `KO_DOCKER_REPO`, to identify where it should push images that it builds. Typically this will be a remote registry, e.g.:
25 |
26 | - `KO_DOCKER_REPO=gcr.io/my-project`, or
27 | - `KO_DOCKER_REPO=ghcr.io/my-org/my-repo`, or
28 | - `KO_DOCKER_REPO=my-dockerhub-user`
29 |
30 | ## Build an Image
31 |
32 | `ko build ./cmd/app` builds and pushes a container image, and prints the resulting image digest to stdout.
33 |
34 | In this example, `./cmd/app` must be a `package main` that defines `func main()`.
35 |
36 | ```plaintext
37 | $ ko build ./cmd/app
38 | ...
39 | registry.example.com/my-project/app-099ba5bcefdead87f92606265fb99ac0@sha256:6e398316742b7aa4a93161dce4a23bc5c545700b862b43347b941000b112ec3e
40 | ```
41 |
42 | > 💡 **Note**: Prior to v0.10, the command was called `ko publish` -- this is equivalent to `ko build`, and both commands will work and do the same thing.
43 |
44 | The executable binary that was built from `./cmd/app` is available in the image at `/ko-app/app` -- the binary name matches the base import path name -- and that binary is the image's entrypoint.
45 |
46 |
--------------------------------------------------------------------------------
/docs/images/android-icon-192x192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ko-build/ko/c47112d64ca597d8499920a0bbd2d37652143d2a/docs/images/android-icon-192x192.png
--------------------------------------------------------------------------------
/docs/images/apple-icon-180x180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ko-build/ko/c47112d64ca597d8499920a0bbd2d37652143d2a/docs/images/apple-icon-180x180.png
--------------------------------------------------------------------------------
/docs/images/apple-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ko-build/ko/c47112d64ca597d8499920a0bbd2d37652143d2a/docs/images/apple-icon.png
--------------------------------------------------------------------------------
/docs/images/demo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ko-build/ko/c47112d64ca597d8499920a0bbd2d37652143d2a/docs/images/demo.png
--------------------------------------------------------------------------------
/docs/images/favicon-16x16.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ko-build/ko/c47112d64ca597d8499920a0bbd2d37652143d2a/docs/images/favicon-16x16.png
--------------------------------------------------------------------------------
/docs/images/favicon-32x32.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ko-build/ko/c47112d64ca597d8499920a0bbd2d37652143d2a/docs/images/favicon-32x32.png
--------------------------------------------------------------------------------
/docs/images/favicon-96x96.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ko-build/ko/c47112d64ca597d8499920a0bbd2d37652143d2a/docs/images/favicon-96x96.png
--------------------------------------------------------------------------------
/docs/images/ko.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ko-build/ko/c47112d64ca597d8499920a0bbd2d37652143d2a/docs/images/ko.png
--------------------------------------------------------------------------------
/docs/images/ms-icon-144x144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ko-build/ko/c47112d64ca597d8499920a0bbd2d37652143d2a/docs/images/ms-icon-144x144.png
--------------------------------------------------------------------------------
/docs/images/og.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ko-build/ko/c47112d64ca597d8499920a0bbd2d37652143d2a/docs/images/og.png
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | ko_meta: true
3 | ---
4 |
5 | # Introduction
6 |
7 | `ko` makes building Go container images easy, fast, and secure by default.
8 |
9 | 
10 |
11 | `ko` is a simple, fast container image builder for Go applications.
12 |
13 | It's ideal for use cases where your image contains a single Go application without many dependencies on the OS base image (e.g., no cgo, no OS package dependencies).
14 |
15 | `ko` builds images by executing `go build` on your local machine, and as such doesn't require `docker` to be installed.
16 | This can make it a good fit for lightweight CI/CD use cases.
17 |
18 | `ko` makes [multi-platform builds](https://ko.build/features/multi-platform/) easy, produces [SBOMs](https://ko.build/features/sboms/) by default, and includes support for simple YAML templating which makes it a powerful tool for [Kubernetes applications](https://ko.build/features/k8s/).
19 |
20 | ---
21 |
22 | > 🏃 [Install `ko`](./install) and [get started](./get-started)!
23 |
24 | ---
25 |
26 | `ko` is used and loved by these open source projects:
27 |
28 | - [Knative](https://knative.dev)
29 | - [Tekton](https://tekton.dev)
30 | - [Karpenter](https://karpenter.sh)
31 | - [Kyverno](https://kyverno.io)
32 | - [Sigstore](https://sigstore.dev)
33 | - [Shipwright](https://shipwright.io)
34 | - [Capsule](https://capsule.clastix.io/)
35 | - [CloudScript](https://cloudscript.com.br/)
36 | - [Kamaji](https://kamaji.clastix.io/)
37 |
38 | [_Add your project here!_](https://github.com/ko-build/ko/edit/main/docs/index.md)
39 |
40 | ---
41 |
42 | `ko` is a Cloud Native Computing Foundation Sandbox project.
43 |
44 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/docs/install.md:
--------------------------------------------------------------------------------
1 | # Installation
2 |
3 | ### Install from [GitHub Releases](https://github.com/ko-build/ko/releases)
4 |
5 | ```
6 | $ VERSION=TODO # choose the latest version (without v prefix)
7 | $ OS=Linux # or Darwin
8 | $ ARCH=x86_64 # or arm64, i386, s390x
9 | ```
10 |
11 | We generate [SLSA3 provenance](https://slsa.dev) using the OpenSSF's [slsa-framework/slsa-github-generator](https://github.com/slsa-framework/slsa-github-generator). To verify our release, install the verification tool from [slsa-framework/slsa-verifier#installation](https://github.com/slsa-framework/slsa-verifier#installation) and verify as follows:
12 |
13 |
14 | ```shell
15 | $ curl -sSfL "https://github.com/ko-build/ko/releases/download/v${VERSION}/ko_${VERSION}_${OS}_${ARCH}.tar.gz" > ko.tar.gz
16 | $ curl -sSfL https://github.com/ko-build/ko/releases/download/v${VERSION}/multiple.intoto.jsonl > multiple.intoto.jsonl
17 | $ slsa-verifier verify-artifact --provenance-path multiple.intoto.jsonl --source-uri github.com/ko-build/ko --source-tag "v${VERSION}" ko.tar.gz
18 | Verified signature against tlog entry index 24413745 at URL: https://rekor.sigstore.dev/api/v1/log/entries/24296fb24b8ad77ab97a5263b5fa8f35789618348a39358b1f9470b0c31045effbbe5e23e77a5836
19 | Verified build using builder "https://github.com/slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@refs/tags/v1.7.0" at commit 200db7243f02b5c0303e21d8ab8e3b4ad3a229d0
20 | Verifying artifact /Users/batuhanapaydin/workspace/ko/ko.tar.gz: PASSED
21 |
22 | PASSED: Verified SLSA provenance
23 | ```
24 |
25 | ```shell
26 | $ tar xzf ko.tar.gz ko
27 | $ chmod +x ./ko
28 | ```
29 |
30 | ### Install using [Homebrew](https://brew.sh)
31 |
32 | ```plaintext
33 | brew install ko
34 | ```
35 |
36 | ### Install using [MacPorts](https://www.macports.org)
37 |
38 | ```plaintext
39 | sudo port install ko
40 | ```
41 |
42 | More info [here](https://ports.macports.org/port/ko/)
43 |
44 | ### Install on Windows using [Scoop](https://scoop.sh)
45 |
46 | ```plaintext
47 | scoop install ko
48 | ```
49 |
50 | ### Install on [Alpine Linux](https://www.alpinelinux.org)
51 |
52 | Installation on Alpine requires using the [`testing` repository](https://wiki.alpinelinux.org/wiki/Enable_Community_Repository#Using_testing_repositories)
53 |
54 | ```
55 | echo https://dl-cdn.alpinelinux.org/alpine/edge/testing/ >> /etc/apk/repositories
56 | apk update
57 | apk add ko
58 | ```
59 |
60 | ### Build and Install from source
61 |
62 | With Go 1.16+, build and install the latest released version:
63 |
64 | ```plaintext
65 | go install github.com/google/ko@latest
66 | ```
67 |
68 | ### Setup on GitHub Actions
69 |
70 | You can use the [setup-ko](https://github.com/ko-build/setup-ko) action to install ko and setup auth to [GitHub Container Registry](https://github.com/features/packages) in a GitHub Action workflow:
71 |
72 | ```plaintext
73 | steps:
74 | - uses: ko-build/setup-ko@v0.6
75 | ```
76 |
--------------------------------------------------------------------------------
/docs/reference/ko.md:
--------------------------------------------------------------------------------
1 | ## ko
2 |
3 | Rapidly iterate with Go, Containers, and Kubernetes.
4 |
5 | ```
6 | ko [flags]
7 | ```
8 |
9 | ### Options
10 |
11 | ```
12 | -h, --help help for ko
13 | -v, --verbose Enable debug logs
14 | ```
15 |
16 | ### SEE ALSO
17 |
18 | * [ko apply](ko_apply.md) - Apply the input files with image references resolved to built/pushed image digests.
19 | * [ko build](ko_build.md) - Build and publish container images from the given importpaths.
20 | * [ko create](ko_create.md) - Create the input files with image references resolved to built/pushed image digests.
21 | * [ko delete](ko_delete.md) - See "kubectl help delete" for detailed usage.
22 | * [ko login](ko_login.md) - Log in to a registry
23 | * [ko resolve](ko_resolve.md) - Print the input files with image references resolved to built/pushed image digests.
24 | * [ko run](ko_run.md) - A variant of `kubectl run` that containerizes IMPORTPATH first.
25 | * [ko version](ko_version.md) - Print ko version.
26 |
27 |
--------------------------------------------------------------------------------
/docs/reference/ko_delete.md:
--------------------------------------------------------------------------------
1 | ## ko delete
2 |
3 | See "kubectl help delete" for detailed usage.
4 |
5 | ```
6 | ko delete [flags]
7 | ```
8 |
9 | ### Options
10 |
11 | ```
12 | -h, --help help for delete
13 | ```
14 |
15 | ### Options inherited from parent commands
16 |
17 | ```
18 | -v, --verbose Enable debug logs
19 | ```
20 |
21 | ### SEE ALSO
22 |
23 | * [ko](ko.md) - Rapidly iterate with Go, Containers, and Kubernetes.
24 |
25 |
--------------------------------------------------------------------------------
/docs/reference/ko_login.md:
--------------------------------------------------------------------------------
1 | ## ko login
2 |
3 | Log in to a registry
4 |
5 | ```
6 | ko login [OPTIONS] [SERVER] [flags]
7 | ```
8 |
9 | ### Examples
10 |
11 | ```
12 | # Log in to reg.example.com
13 | ko login reg.example.com -u AzureDiamond -p hunter2
14 | ```
15 |
16 | ### Options
17 |
18 | ```
19 | -h, --help help for login
20 | -p, --password string Password
21 | --password-stdin Take the password from stdin
22 | -u, --username string Username
23 | ```
24 |
25 | ### Options inherited from parent commands
26 |
27 | ```
28 | -v, --verbose Enable debug logs
29 | ```
30 |
31 | ### SEE ALSO
32 |
33 | * [ko](ko.md) - Rapidly iterate with Go, Containers, and Kubernetes.
34 |
35 |
--------------------------------------------------------------------------------
/docs/reference/ko_run.md:
--------------------------------------------------------------------------------
1 | ## ko run
2 |
3 | A variant of `kubectl run` that containerizes IMPORTPATH first.
4 |
5 | ### Synopsis
6 |
7 | This sub-command combines "ko build" and "kubectl run" to support containerizing and running Go binaries on Kubernetes in a single command.
8 |
9 | ```
10 | ko run IMPORTPATH [flags]
11 | ```
12 |
13 | ### Examples
14 |
15 | ```
16 |
17 | # Publish the image and run it on Kubernetes as:
18 | # ${KO_DOCKER_REPO}/-
19 | # When KO_DOCKER_REPO is ko.local, it is the same as if
20 | # --local and --preserve-import-paths were passed.
21 | ko run github.com/foo/bar/cmd/baz
22 |
23 | # This supports relative import paths as well.
24 | ko run ./cmd/baz
25 |
26 | # You can also supply args and flags to the command.
27 | ko run ./cmd/baz -- -v arg1 arg2 --yes
28 | ```
29 |
30 | ### Options
31 |
32 | ```
33 | --bare Whether to just use KO_DOCKER_REPO without additional context (may not work properly with --tags).
34 | -B, --base-import-paths Whether to use the base path without MD5 hash after KO_DOCKER_REPO (may not work properly with --tags).
35 | --debug Include Delve debugger into image and wrap around ko-app. This debugger will listen to port 40000.
36 | --disable-optimizations Disable optimizations when building Go code. Useful when you want to interactively debug the created container.
37 | -h, --help help for run
38 | --image-annotation strings Which annotations (key=value[,key=value]) to add to the OCI manifest.
39 | --image-label strings Which labels (key=value[,key=value]) to add to the image.
40 | --image-refs string Path to file where a list of the published image references will be written.
41 | --image-user string The default user the image should be run as.
42 | --insecure-registry Whether to skip TLS verification on the registry
43 | -j, --jobs int The maximum number of concurrent builds (default GOMAXPROCS)
44 | -L, --local Load into images to local docker daemon.
45 | --oci-layout-path string Path to save the OCI image layout of the built images
46 | --platform strings Which platform to use when pulling a multi-platform base. Format: all | [/[/]][,platform]*
47 | -P, --preserve-import-paths Whether to preserve the full import path after KO_DOCKER_REPO.
48 | --push Push images to KO_DOCKER_REPO (default true)
49 | --sbom string The SBOM media type to use (none will disable SBOM synthesis and upload). (default "spdx")
50 | --sbom-dir string Path to file where the SBOM will be written.
51 | --tag-only Include tags but not digests in resolved image references. Useful when digests are not preserved when images are repopulated.
52 | -t, --tags strings Which tags to use for the produced image instead of the default 'latest' tag (may not work properly with --base-import-paths or --bare). (default [latest])
53 | --tarball string File to save images tarballs
54 | ```
55 |
56 | ### Options inherited from parent commands
57 |
58 | ```
59 | -v, --verbose Enable debug logs
60 | ```
61 |
62 | ### SEE ALSO
63 |
64 | * [ko](ko.md) - Rapidly iterate with Go, Containers, and Kubernetes.
65 |
66 |
--------------------------------------------------------------------------------
/docs/reference/ko_version.md:
--------------------------------------------------------------------------------
1 | ## ko version
2 |
3 | Print ko version.
4 |
5 | ```
6 | ko version [flags]
7 | ```
8 |
9 | ### Options
10 |
11 | ```
12 | -h, --help help for version
13 | ```
14 |
15 | ### Options inherited from parent commands
16 |
17 | ```
18 | -v, --verbose Enable debug logs
19 | ```
20 |
21 | ### SEE ALSO
22 |
23 | * [ko](ko.md) - Rapidly iterate with Go, Containers, and Kubernetes.
24 |
25 |
--------------------------------------------------------------------------------
/hack/boilerplate/boilerplate.go.txt:
--------------------------------------------------------------------------------
1 | // Copyright 2023 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
--------------------------------------------------------------------------------
/hack/boilerplate/boilerplate.sh.txt:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Copyright 2023 ko Build Authors All Rights Reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
--------------------------------------------------------------------------------
/hack/presubmit.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Copyright 2021 ko Build Authors All Rights Reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | set -o errexit
18 | set -o nounset
19 | set -o pipefail
20 |
21 | PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
22 |
23 | pushd "${PROJECT_ROOT}"
24 | trap popd EXIT
25 |
26 | # Verify that all source files are correctly formatted.
27 | find . -name "*.go" -exec gofmt -d -e -l {} +
28 |
29 | # Verify that generated Markdown docs are up-to-date.
30 | tmpdir=$(mktemp -d)
31 | go run cmd/help/main.go --dir "$tmpdir"
32 | diff -Naur -I '###### Auto generated' "$tmpdir" docs/reference/
33 |
--------------------------------------------------------------------------------
/hack/tools.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | //go:build tools
16 | // +build tools
17 |
18 | package hack
19 |
20 | import (
21 | _ "github.com/go-training/helloworld"
22 | )
23 |
--------------------------------------------------------------------------------
/hack/update-codegen.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Copyright 2021 ko Build Authors All Rights Reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | set -o errexit
18 | set -o nounset
19 | set -o pipefail
20 |
21 | PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
22 |
23 | pushd ${PROJECT_ROOT}
24 | trap popd EXIT
25 |
26 | go mod tidy
27 |
28 | go run $PROJECT_ROOT/cmd/help/main.go --dir=$PROJECT_ROOT/docs/reference/
29 |
--------------------------------------------------------------------------------
/hack/update-deps.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Copyright 2018 ko Build Authors All Rights Reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | set -o errexit
18 | set -o nounset
19 | set -o pipefail
20 |
21 | PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
22 |
23 | pushd ${PROJECT_ROOT}
24 | trap popd EXIT
25 |
26 | go mod tidy
27 |
--------------------------------------------------------------------------------
/integration_test.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 | set -o errexit
3 | set -o nounset
4 | set -o pipefail
5 |
6 | ROOT_DIR=$(dirname "$0")
7 |
8 | pushd "$ROOT_DIR"
9 |
10 | ROOT_DIR="$(pwd)"
11 |
12 | echo "Moving GOPATH into /tmp/ to test modules behavior."
13 | export GOPATH="${GOPATH:-$(go env GOPATH)}"
14 | export ORIGINAL_GOPATH="$GOPATH"
15 | GOPATH="$(mktemp -d)"
16 | export GOPATH
17 |
18 | export CGO_ENABLED=0
19 |
20 | GOARCH="${GOARCH:-$(go env GOARCH)}"
21 |
22 | pushd "$GOPATH" || exit 1
23 |
24 | echo "Copying ko to temp gopath."
25 | mkdir -p "$GOPATH/src/github.com/google/ko"
26 | cp -r "$ROOT_DIR/"* "$GOPATH/src/github.com/google/ko/"
27 |
28 | pushd "$GOPATH/src/github.com/google/ko" || exit 1
29 |
30 | echo "Building ko"
31 |
32 | RESULT="$(go build .)"
33 |
34 | echo "Beginning scenarios."
35 |
36 | FILTER="[^ ]local[^ ]*"
37 |
38 | echo "1. Test should create an image that outputs 'Hello World'."
39 | RESULT="$(./ko build --local --platform="linux/$GOARCH" "$GOPATH/src/github.com/google/ko/test" | grep "$FILTER" | xargs -I% docker run %)"
40 | if [[ "$RESULT" != *"Hello there"* ]]; then
41 | echo "Test FAILED. Saw $RESULT" && exit 1
42 | else
43 | echo "Test PASSED"
44 | fi
45 |
46 | echo "2. Test knative 'KO_FLAGS' variable is ignored."
47 | # https://github.com/ko-build/ko/issues/1317
48 | RESULT="$(KO_FLAGS="--platform=badvalue" ./ko build --local --platform="linux/$GOARCH" "$GOPATH/src/github.com/google/ko/test" | grep "$FILTER" | xargs -I% docker run %)"
49 | if [[ "$RESULT" != *"Hello there"* ]]; then
50 | echo "Test FAILED. Saw $RESULT" && exit 1
51 | else
52 | echo "Test PASSED"
53 | fi
54 |
55 | echo "3. Linux capabilities."
56 | pushd test/build-configs || exit 1
57 | # run as non-root user with net_bind_service cap granted
58 | docker_run_opts="--user 1 --cap-add=net_bind_service"
59 | RESULT="$(../../ko build --local --platform="linux/$GOARCH" ./caps/cmd | grep "$FILTER" | xargs -I% docker run $docker_run_opts %)"
60 | if [[ "$RESULT" != "No capabilities" ]]; then
61 | echo "Test FAILED. Saw '$RESULT' but expected 'No capabilities'. Docker 'cap-add' must have no effect unless matching capabilities are granted to the file." && exit 1
62 | fi
63 | # build with a different config requesting net_bind_service file capability
64 | RESULT_WITH_FILE_CAPS="$(KO_CONFIG_PATH=caps.ko.yaml ../../ko build --local --platform="linux/$GOARCH" ./caps/cmd | grep "$FILTER" | xargs -I% docker run $docker_run_opts %)"
65 | if [[ "$RESULT_WITH_FILE_CAPS" != "Has capabilities"* ]]; then
66 | echo "Test FAILED. Saw '$RESULT_WITH_FILE_CAPS' but expected 'Has capabilities'. Docker 'cap-add' must work when matching capabilities are granted to the file." && exit 1
67 | else
68 | echo "Test PASSED"
69 | fi
70 | popd || exit 1
71 |
72 | popd || exit 1
73 | popd || exit 1
74 |
75 | export GOPATH="$ORIGINAL_GOPATH"
76 |
--------------------------------------------------------------------------------
/internal/sbom/sbom.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package sbom
16 |
17 | import (
18 | "bufio"
19 | "bytes"
20 | "fmt"
21 | "runtime/debug"
22 | "strings"
23 | "unicode"
24 | )
25 |
26 | func modulePackageName(mod *debug.Module) string {
27 | return fmt.Sprintf("SPDXRef-Package-%s-%s",
28 | strings.ReplaceAll(mod.Path, "/", "."),
29 | mod.Version)
30 | }
31 |
32 | func goRef(mod *debug.Module) string {
33 | path := mod.Path
34 | // Try to lowercase the first 2 path elements to comply with spec
35 | // https://github.com/package-url/purl-spec/blob/master/PURL-TYPES.rst#golang
36 | p := strings.Split(path, "/")
37 | if len(p) > 2 {
38 | path = strings.Join(
39 | append(
40 | []string{strings.ToLower(p[0]), strings.ToLower(p[1])},
41 | p[2:]...,
42 | ), "/",
43 | )
44 | }
45 | return fmt.Sprintf("pkg:golang/%s@%s?type=module", path, mod.Version)
46 | }
47 |
48 | // massageGoVersionM massages the output of `go version -m` into a form that
49 | // can be consumed by ParseBuildInfo.
50 | //
51 | // `go version -m` adds a line at the beginning of its output, and tabs at the
52 | // beginning of every line, that ParseBuildInfo doesn't like.
53 | func massageGoVersionM(b []byte) ([]byte, error) {
54 | var out bytes.Buffer
55 | scanner := bufio.NewScanner(bytes.NewReader(b))
56 | if !scanner.Scan() {
57 | // Input was malformed, and doesn't contain any newlines (it
58 | // may even be empty). This seems to happen on Windows
59 | // (https://github.com/ko-build/ko/issues/535) and in unit tests.
60 | // Just proceed with an empty output for now, and SBOMs will be empty.
61 | // TODO: This should be an error.
62 | return nil, nil
63 | }
64 | if err := scanner.Err(); err != nil {
65 | return nil, fmt.Errorf("malformed input: %w", err)
66 | }
67 | for scanner.Scan() {
68 | // NOTE: debug.ParseBuildInfo relies on trailing tabs.
69 | line := strings.TrimLeftFunc(scanner.Text(), unicode.IsSpace)
70 | fmt.Fprintln(&out, line)
71 | }
72 | if err := scanner.Err(); err != nil {
73 | return nil, err
74 | }
75 | return out.Bytes(), nil
76 | }
77 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | //go:generate go run ./cmd/help/main.go -d docs/reference/
16 | package main
17 |
18 | import (
19 | "context"
20 | "os"
21 | "os/signal"
22 |
23 | "github.com/google/ko/pkg/commands"
24 | )
25 |
26 | func main() {
27 | ctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)
28 | defer stop()
29 | if err := commands.Root.ExecuteContext(ctx); err != nil {
30 | os.Exit(1)
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/mkdocs.yml:
--------------------------------------------------------------------------------
1 | site_name: 'ko: Easy Go Containers'
2 | site_url: https://ko.build
3 | repo_url: https://github.com/ko-build/ko
4 | edit_uri: edit/main/docs/
5 |
6 | theme:
7 | name: material
8 | logo: images/favicon-96x96.png
9 | favicon: images/favicon-96x96.png
10 | custom_dir: docs/custom/
11 | palette:
12 | # Palette toggle for automatic mode
13 | - media: "(prefers-color-scheme)"
14 | toggle:
15 | icon: material/brightness-auto
16 | name: Switch to light mode
17 |
18 | # Palette toggle for light mode
19 | - media: "(prefers-color-scheme: light)"
20 | primary: light blue
21 | toggle:
22 | icon: material/brightness-7
23 | name: Switch to dark mode
24 |
25 | # Palette toggle for dark mode
26 | - media: "(prefers-color-scheme: dark)"
27 | scheme: slate
28 | primary: light blue
29 | toggle:
30 | icon: material/brightness-4
31 | name: Switch to system preference
32 |
33 | nav:
34 | - index.md
35 | - install.md
36 | - get-started.md
37 | - configuration.md
38 | - deployment.md
39 | - community.md
40 | - Features:
41 | - features/multi-platform.md
42 | - features/sboms.md
43 | - features/k8s.md
44 | - features/static-assets.md
45 | - features/build-cache.md
46 | - features/debugging.md
47 | - Advanced:
48 | - advanced/go-packages.md
49 | - advanced/limitations.md
50 | - advanced/migrating-from-dockerfile.md
51 | - advanced/faq.md
52 | - advanced/terraform.md
53 | - advanced/lambda.md
54 | - advanced/linux-capabilities.md
55 | - advanced/root-ca-certificates.md
56 | - CLI Reference:
57 | - 'ko': reference/ko.md
58 | - 'ko apply': reference/ko_apply.md
59 | - 'ko build': reference/ko_build.md
60 | - 'ko create': reference/ko_create.md
61 | - 'ko delete': reference/ko_delete.md
62 | - 'ko login': reference/ko_login.md
63 | - 'ko resolve': reference/ko_resolve.md
64 | - 'ko run': reference/ko_run.md
65 | - 'ko version': reference/ko_version.md
66 | - Releases: "https://github.com/ko-build/ko/releases"
67 |
68 | plugins:
69 | - search
70 | - redirects:
71 | redirect_maps:
72 | 'repo.md': 'https://github.com/ko-build/ko'
73 | 'issues.md': 'https://github.com/ko-build/ko/issues'
74 | 'prs.md': 'https://github.com/ko-build/ko/pulls'
75 | 'releases.md': 'https://github.com/ko-build/ko/releases'
76 | 'godoc.md': 'https://pkg.go.dev/github.com/google/ko'
77 | 'terraform.md': 'https://github.com/ko-build/terraform-provider-ko'
78 | 'action.md': 'https://github.com/ko-build/setup-ko'
79 | 'slack.md': 'https://kubernetes.slack.com/archives/C01T7DTP65S'
80 | 'agenda.md': 'https://docs.google.com/document/d/1eQ67Qxwf1tkTv0yU_dw9bIRnlwJZz-5GXCRVOhqbgvU/edit'
81 | 'meet.md': 'meet.google.com/xvn-dzzk-wur'
82 |
83 | markdown_extensions:
84 | - pymdownx.highlight:
85 | anchor_linenums: true
86 | line_spans: __span
87 | pygments_lang_class: true
88 | - pymdownx.inlinehilite
89 | - pymdownx.snippets
90 | - pymdownx.superfences
91 |
--------------------------------------------------------------------------------
/pkg/build/build.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package build
16 |
17 | import (
18 | "context"
19 |
20 | v1 "github.com/google/go-containerregistry/pkg/v1"
21 | "github.com/google/go-containerregistry/pkg/v1/types"
22 | )
23 |
24 | // Interface abstracts different methods for turning a supported importpath
25 | // reference into a v1.Image.
26 | type Interface interface {
27 | // QualifyImport turns relative importpath references into complete importpaths.
28 | // It also adds the ko scheme prefix if necessary.
29 | // E.g., "github.com/ko-build/ko/test" => "ko://github.com/ko-build/ko/test"
30 | // and "./test" => "ko://github.com/ko-build/ko/test"
31 | QualifyImport(string) (string, error)
32 |
33 | // IsSupportedReference determines whether the given reference is to an
34 | // importpath reference that Ko supports building, returning an error
35 | // if it is not.
36 | // TODO(mattmoor): Verify that some base repo: foo.io/bar can be suffixed with this reference and parsed.
37 | IsSupportedReference(string) error
38 |
39 | // Build turns the given importpath reference into a v1.Image containing the Go binary
40 | // (or a set of images as a v1.ImageIndex).
41 | Build(context.Context, string) (Result, error)
42 | }
43 |
44 | // Result represents the product of a Build.
45 | // This is generally one of:
46 | // - v1.Image (or oci.SignedImage), or
47 | // - v1.ImageIndex (or oci.SignedImageIndex)
48 | type Result interface {
49 | MediaType() (types.MediaType, error)
50 | Size() (int64, error)
51 | Digest() (v1.Hash, error)
52 | RawManifest() ([]byte, error)
53 | }
54 |
55 | // Assert that Image and ImageIndex implement Result.
56 | var _ Result = (v1.Image)(nil)
57 | var _ Result = (v1.ImageIndex)(nil)
58 |
--------------------------------------------------------------------------------
/pkg/build/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | // Package build defines methods for building a v1.Image reference from a
16 | // Go binary reference.
17 | package build
18 |
--------------------------------------------------------------------------------
/pkg/build/future.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package build
16 |
17 | import (
18 | "sync"
19 | )
20 |
21 | func newFuture(work func() (Result, error)) *future {
22 | // Create a channel on which to send the result.
23 | ch := make(chan *result)
24 | // Initiate the actual work, sending its result
25 | // along the above channel.
26 | go func() {
27 | img, err := work()
28 | ch <- &result{img: img, err: err}
29 | }()
30 | // Return a future for the above work. Callers should
31 | // call .Get() on this result (as many times as needed).
32 | // One of these calls will receive the result, store it,
33 | // and close the channel so that the rest of the callers
34 | // can consume it.
35 | return &future{
36 | promise: ch,
37 | }
38 | }
39 |
40 | type result struct {
41 | img Result
42 | err error
43 | }
44 |
45 | type future struct {
46 | m sync.RWMutex
47 |
48 | result *result
49 | promise chan *result
50 | }
51 |
52 | // Get blocks on the result of the future.
53 | func (f *future) Get() (Result, error) {
54 | // Block on the promise of a result until we get one.
55 | result, ok := <-f.promise
56 | if ok {
57 | func() {
58 | f.m.Lock()
59 | defer f.m.Unlock()
60 | // If we got the result, then store it so that
61 | // others may access it.
62 | f.result = result
63 | // Close the promise channel so that others
64 | // are signaled that the result is available.
65 | close(f.promise)
66 | }()
67 | }
68 |
69 | f.m.RLock()
70 | defer f.m.RUnlock()
71 |
72 | return f.result.img, f.result.err
73 | }
74 |
--------------------------------------------------------------------------------
/pkg/build/future_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package build
16 |
17 | import (
18 | "testing"
19 |
20 | "github.com/google/go-containerregistry/pkg/v1/random"
21 | )
22 |
23 | func makeImage() (Result, error) {
24 | return random.Index(256, 8, 1)
25 | }
26 |
27 | func digest(t *testing.T, img Result) string {
28 | d, err := img.Digest()
29 | if err != nil {
30 | t.Fatalf("Digest() = %v", err)
31 | }
32 | return d.String()
33 | }
34 |
35 | func TestSameFutureSameImage(t *testing.T) {
36 | f := newFuture(makeImage)
37 |
38 | i1, err := f.Get()
39 | if err != nil {
40 | t.Errorf("Get() = %v", err)
41 | }
42 | d1 := digest(t, i1)
43 |
44 | i2, err := f.Get()
45 | if err != nil {
46 | t.Errorf("Get() = %v", err)
47 | }
48 | d2 := digest(t, i2)
49 |
50 | if d1 != d2 {
51 | t.Errorf("Got different digests %s and %s", d1, d2)
52 | }
53 | }
54 |
55 | func TestDiffFutureDiffImage(t *testing.T) {
56 | f1 := newFuture(makeImage)
57 | f2 := newFuture(makeImage)
58 |
59 | i1, err := f1.Get()
60 | if err != nil {
61 | t.Errorf("Get() = %v", err)
62 | }
63 | d1 := digest(t, i1)
64 |
65 | i2, err := f2.Get()
66 | if err != nil {
67 | t.Errorf("Get() = %v", err)
68 | }
69 | d2 := digest(t, i2)
70 |
71 | if d1 == d2 {
72 | t.Errorf("Got same digest %s, wanted different", d1)
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/pkg/build/layer.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package build
16 |
17 | import (
18 | "io"
19 | "sync"
20 |
21 | v1 "github.com/google/go-containerregistry/pkg/v1"
22 | "github.com/google/go-containerregistry/pkg/v1/types"
23 | )
24 |
25 | type lazyLayer struct {
26 | diffid v1.Hash
27 | desc v1.Descriptor
28 |
29 | sync.Once
30 | buildLayer func() (v1.Layer, error)
31 | layer v1.Layer
32 | err error
33 | }
34 |
35 | // All this info is cached by previous builds.
36 | func (l *lazyLayer) Digest() (v1.Hash, error) {
37 | return l.desc.Digest, nil
38 | }
39 |
40 | func (l *lazyLayer) DiffID() (v1.Hash, error) {
41 | return l.diffid, nil
42 | }
43 |
44 | func (l *lazyLayer) Size() (int64, error) {
45 | return l.desc.Size, nil
46 | }
47 |
48 | func (l *lazyLayer) MediaType() (types.MediaType, error) {
49 | return l.desc.MediaType, nil
50 | }
51 |
52 | // This is only called if the registry doesn't have this blob already.
53 | func (l *lazyLayer) Compressed() (io.ReadCloser, error) {
54 | layer, err := l.compute()
55 | if err != nil {
56 | return nil, err
57 | }
58 | return layer.Compressed()
59 | }
60 |
61 | // This should never actually be called but we need it to impl v1.Layer.
62 | func (l *lazyLayer) Uncompressed() (io.ReadCloser, error) {
63 | layer, err := l.compute()
64 | if err != nil {
65 | return nil, err
66 | }
67 | return layer.Uncompressed()
68 | }
69 |
70 | func (l *lazyLayer) compute() (v1.Layer, error) {
71 | l.Do(func() {
72 | l.layer, l.err = l.buildLayer()
73 | })
74 | return l.layer, l.err
75 | }
76 |
--------------------------------------------------------------------------------
/pkg/build/limit.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package build
16 |
17 | import (
18 | "context"
19 |
20 | "golang.org/x/sync/semaphore"
21 | )
22 |
23 | // Limiter composes with another Interface to limit the number of concurrent builds.
24 | type Limiter struct {
25 | Builder Interface
26 | semaphore *semaphore.Weighted
27 | }
28 |
29 | // Limiter implements Interface
30 | var _ Interface = (*Recorder)(nil)
31 |
32 | // QualifyImport implements Interface
33 | func (l *Limiter) QualifyImport(ip string) (string, error) {
34 | return l.Builder.QualifyImport(ip)
35 | }
36 |
37 | // IsSupportedReference implements Interface
38 | func (l *Limiter) IsSupportedReference(ip string) error {
39 | return l.Builder.IsSupportedReference(ip)
40 | }
41 |
42 | // Build implements Interface
43 | func (l *Limiter) Build(ctx context.Context, ip string) (Result, error) {
44 | if err := l.semaphore.Acquire(ctx, 1); err != nil {
45 | return nil, err
46 | }
47 | defer l.semaphore.Release(1)
48 |
49 | return l.Builder.Build(ctx, ip)
50 | }
51 |
52 | // NewLimiter returns a new builder that only allows n concurrent builds of b.
53 | //
54 | // Deprecated: Obsoleted by WithJobs option.
55 | func NewLimiter(b Interface, n int) *Limiter {
56 | return &Limiter{
57 | Builder: b,
58 | semaphore: semaphore.NewWeighted(int64(n)),
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/pkg/build/limit_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package build
16 |
17 | import (
18 | "context"
19 | "testing"
20 | "time"
21 |
22 | "golang.org/x/sync/errgroup"
23 | )
24 |
25 | type sleeper struct{}
26 |
27 | var _ Interface = (*sleeper)(nil)
28 |
29 | // QualifyImport implements Interface
30 | func (*sleeper) QualifyImport(ip string) (string, error) {
31 | return ip, nil
32 | }
33 |
34 | // IsSupportedReference implements Interface
35 | func (*sleeper) IsSupportedReference(_ string) error {
36 | return nil
37 | }
38 |
39 | // Build implements Interface
40 | func (*sleeper) Build(_ context.Context, _ string) (Result, error) {
41 | time.Sleep(50 * time.Millisecond)
42 | return nil, nil
43 | }
44 |
45 | func TestLimiter(t *testing.T) {
46 | b := NewLimiter(&sleeper{}, 2)
47 |
48 | start := time.Now()
49 | g, _ := errgroup.WithContext(context.TODO())
50 | for i := 0; i <= 10; i++ {
51 | g.Go(func() error {
52 | _, _ = b.Build(context.Background(), "whatever")
53 | return nil
54 | })
55 | }
56 | g.Wait()
57 |
58 | // 50 ms * 10 builds / 2 concurrency = ~250ms
59 | if time.Now().Before(start.Add(250 * time.Millisecond)) {
60 | t.Fatal("Too many builds")
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/pkg/build/recorder.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package build
16 |
17 | import (
18 | "context"
19 | "sync"
20 | )
21 |
22 | // Recorder composes with another Interface to record the built import paths.
23 | type Recorder struct {
24 | m sync.Mutex
25 | ImportPaths []string
26 | Builder Interface
27 | }
28 |
29 | // Recorder implements Interface
30 | var _ Interface = (*Recorder)(nil)
31 |
32 | // QualifyImport implements Interface
33 | func (r *Recorder) QualifyImport(ip string) (string, error) {
34 | return r.Builder.QualifyImport(ip)
35 | }
36 |
37 | // IsSupportedReference implements Interface
38 | func (r *Recorder) IsSupportedReference(ip string) error {
39 | return r.Builder.IsSupportedReference(ip)
40 | }
41 |
42 | // Build implements Interface
43 | func (r *Recorder) Build(ctx context.Context, ip string) (Result, error) {
44 | func() {
45 | r.m.Lock()
46 | defer r.m.Unlock()
47 | r.ImportPaths = append(r.ImportPaths, ip)
48 | }()
49 | return r.Builder.Build(ctx, ip)
50 | }
51 |
--------------------------------------------------------------------------------
/pkg/build/recorder_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package build
16 |
17 | import (
18 | "context"
19 | "testing"
20 |
21 | "github.com/google/go-cmp/cmp"
22 | )
23 |
24 | type fake struct {
25 | isr func(string) error
26 | b func(string) (Result, error)
27 | }
28 |
29 | var _ Interface = (*fake)(nil)
30 |
31 | // QualifyImport implements Interface
32 | func (r *fake) QualifyImport(ip string) (string, error) { return ip, nil }
33 |
34 | // IsSupportedReference implements Interface
35 | func (r *fake) IsSupportedReference(ip string) error { return r.isr(ip) }
36 |
37 | // Build implements Interface
38 | func (r *fake) Build(_ context.Context, ip string) (Result, error) { return r.b(ip) }
39 |
40 | func TestISRPassThrough(t *testing.T) {
41 | tests := []struct {
42 | name string
43 | input string
44 | }{{
45 | name: "empty string",
46 | }, {
47 | name: "non-empty string",
48 | input: "asdf asdf asdf",
49 | }}
50 |
51 | for _, test := range tests {
52 | t.Run(test.name, func(t *testing.T) {
53 | called := false
54 | inner := &fake{
55 | isr: func(ip string) error {
56 | called = true
57 | if ip != test.input {
58 | t.Errorf("ISR = %v, wanted %v", ip, test.input)
59 | }
60 | return nil
61 | },
62 | }
63 | rec := &Recorder{
64 | Builder: inner,
65 | }
66 | rec.IsSupportedReference(test.input)
67 | if !called {
68 | t.Error("IsSupportedReference wasn't called, wanted called")
69 | }
70 | })
71 | }
72 | }
73 |
74 | func TestBuildRecording(t *testing.T) {
75 | tests := []struct {
76 | name string
77 | inputs []string
78 | }{{
79 | name: "no calls",
80 | }, {
81 | name: "one call",
82 | inputs: []string{
83 | "github.com/foo/bar",
84 | },
85 | }, {
86 | name: "two calls",
87 | inputs: []string{
88 | "github.com/foo/bar",
89 | "github.com/foo/baz",
90 | },
91 | }, {
92 | name: "duplicates",
93 | inputs: []string{
94 | "github.com/foo/bar",
95 | "github.com/foo/baz",
96 | "github.com/foo/bar",
97 | "github.com/foo/baz",
98 | },
99 | }}
100 |
101 | for _, test := range tests {
102 | t.Run(test.name, func(t *testing.T) {
103 | inner := &fake{
104 | b: func(_ string) (Result, error) {
105 | return nil, nil
106 | },
107 | }
108 | rec := &Recorder{
109 | Builder: inner,
110 | }
111 | for _, in := range test.inputs {
112 | rec.Build(context.Background(), in)
113 | }
114 | if diff := cmp.Diff(test.inputs, rec.ImportPaths); diff != "" {
115 | t.Errorf("Build (-want, +got): %s", diff)
116 | }
117 | })
118 | }
119 | }
120 |
--------------------------------------------------------------------------------
/pkg/build/shared.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package build
16 |
17 | import (
18 | "context"
19 | "sync"
20 | )
21 |
22 | // Caching wraps a builder implementation in a layer that shares build results
23 | // for the same inputs using a simple "future" implementation. Cached results
24 | // may be invalidated by calling Invalidate with the same input passed to Build.
25 | type Caching struct {
26 | inner Interface
27 |
28 | m sync.Mutex
29 | results map[string]*future
30 | }
31 |
32 | // Caching implements Interface
33 | var _ Interface = (*Caching)(nil)
34 |
35 | // NewCaching wraps the provided build.Interface in an implementation that
36 | // shares build results for a given path until the result has been invalidated.
37 | func NewCaching(inner Interface) (*Caching, error) {
38 | return &Caching{
39 | inner: inner,
40 | results: make(map[string]*future),
41 | }, nil
42 | }
43 |
44 | // Build implements Interface
45 | func (c *Caching) Build(ctx context.Context, ip string) (Result, error) {
46 | f := func() *future {
47 | // Lock the map of futures.
48 | c.m.Lock()
49 | defer c.m.Unlock()
50 |
51 | // If a future for "ip" exists, then return it.
52 | f, ok := c.results[ip]
53 | if ok {
54 | return f
55 | }
56 | // Otherwise create and record a future for a Build of "ip".
57 | f = newFuture(func() (Result, error) {
58 | return c.inner.Build(ctx, ip)
59 | })
60 | c.results[ip] = f
61 | return f
62 | }()
63 |
64 | return f.Get()
65 | }
66 |
67 | // QualifyImport implements Interface
68 | func (c *Caching) QualifyImport(ip string) (string, error) {
69 | return c.inner.QualifyImport(ip)
70 | }
71 |
72 | // IsSupportedReference implements Interface
73 | func (c *Caching) IsSupportedReference(ip string) error {
74 | return c.inner.IsSupportedReference(ip)
75 | }
76 |
77 | // Invalidate removes an import path's cached results.
78 | func (c *Caching) Invalidate(ip string) {
79 | c.m.Lock()
80 | defer c.m.Unlock()
81 |
82 | delete(c.results, ip)
83 | }
84 |
--------------------------------------------------------------------------------
/pkg/build/shared_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package build
16 |
17 | import (
18 | "context"
19 | "testing"
20 | "time"
21 |
22 | "github.com/google/go-containerregistry/pkg/v1/random"
23 | )
24 |
25 | type slowbuild struct {
26 | sleep time.Duration
27 | }
28 |
29 | // slowbuild implements Interface
30 | var _ Interface = (*slowbuild)(nil)
31 |
32 | func (sb *slowbuild) QualifyImport(ip string) (string, error) { return ip, nil }
33 |
34 | func (sb *slowbuild) IsSupportedReference(string) error { return nil }
35 |
36 | func (sb *slowbuild) Build(context.Context, string) (Result, error) {
37 | time.Sleep(sb.sleep)
38 | return random.Index(256, 8, 3)
39 | }
40 |
41 | func TestCaching(t *testing.T) {
42 | duration := 100 * time.Millisecond
43 | ip := "foo"
44 |
45 | sb := &slowbuild{duration}
46 | cb, _ := NewCaching(sb)
47 |
48 | if err := cb.IsSupportedReference(ip); err != nil {
49 | t.Errorf("ISR(%q) = (%v), wanted nil", err, ip)
50 | }
51 |
52 | previousDigest := "not-a-digest"
53 | // Each iteration, we test that the first build is slow and subsequent
54 | // builds are fast and return the same image. Then we invalidate the
55 | // cache and iterate.
56 | for idx := 0; idx < 3; idx++ {
57 | start := time.Now()
58 | img1, err := cb.Build(context.Background(), ip)
59 | if err != nil {
60 | t.Errorf("Build() = %v", err)
61 | }
62 | end := time.Now()
63 |
64 | elapsed := end.Sub(start)
65 | if elapsed < duration {
66 | t.Errorf("Elapsed time %v, wanted >= %s", elapsed, duration)
67 | }
68 | d1 := digest(t, img1)
69 |
70 | if d1 == previousDigest {
71 | t.Errorf("Got same digest as previous iteration, wanted different: %v", d1)
72 | }
73 | previousDigest = d1
74 |
75 | start = time.Now()
76 | img2, err := cb.Build(context.Background(), ip)
77 | if err != nil {
78 | t.Errorf("Build() = %v", err)
79 | }
80 | end = time.Now()
81 |
82 | elapsed = end.Sub(start)
83 | if elapsed >= duration {
84 | t.Errorf("Elapsed time %v, wanted < %s", elapsed, duration)
85 | }
86 | d2 := digest(t, img2)
87 |
88 | if d1 != d2 {
89 | t.Error("Got different images, wanted same")
90 | }
91 |
92 | cb.Invalidate(ip)
93 | }
94 | }
95 |
--------------------------------------------------------------------------------
/pkg/build/strict.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package build
16 |
17 | import "strings"
18 |
19 | // StrictScheme is a prefix that can be placed on import paths that users
20 | // think MUST be supported references.
21 | const StrictScheme = "ko://"
22 |
23 | type reference struct {
24 | strict bool
25 | path string
26 | }
27 |
28 | func newRef(s string) reference {
29 | return reference{
30 | strict: strings.HasPrefix(s, StrictScheme),
31 | path: strings.TrimPrefix(s, StrictScheme),
32 | }
33 | }
34 |
35 | func (r reference) IsStrict() bool {
36 | return r.strict
37 | }
38 |
39 | func (r reference) Path() string {
40 | return r.path
41 | }
42 |
43 | func (r reference) String() string {
44 | if r.IsStrict() {
45 | return StrictScheme + r.Path()
46 | }
47 | return r.Path()
48 | }
49 |
--------------------------------------------------------------------------------
/pkg/build/strict_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package build
16 |
17 | import "testing"
18 |
19 | func TestStrictReference(t *testing.T) {
20 | tests := []struct {
21 | name string
22 | input string
23 | strict bool
24 | path string
25 | }{{
26 | name: "loose",
27 | input: "github.com/foo/bar",
28 | strict: false,
29 | path: "github.com/foo/bar",
30 | }, {
31 | name: "strict",
32 | input: "ko://github.com/foo/bar",
33 | strict: true,
34 | path: "github.com/foo/bar",
35 | }}
36 |
37 | for _, test := range tests {
38 | t.Run(test.name, func(t *testing.T) {
39 | ref := newRef(test.input)
40 | if got, want := ref.IsStrict(), test.strict; got != want {
41 | t.Errorf("got: %v, want: %v", got, want)
42 | }
43 | if got, want := ref.Path(), test.path; got != want {
44 | t.Errorf("got: %v, want: %v", got, want)
45 | }
46 | if got, want := ref.String(), test.input; got != want {
47 | t.Errorf("got: %v, want: %v", got, want)
48 | }
49 | })
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/pkg/caps/caps_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package caps
16 |
17 | import (
18 | "encoding/base64"
19 | "fmt"
20 | "testing"
21 | )
22 |
23 | func TestParse(t *testing.T) {
24 | tests := []struct {
25 | arg string
26 | res Mask
27 | mustFail bool
28 | }{
29 | {arg: "chown", res: 1},
30 | {arg: "cap_chown", res: 1},
31 | {arg: "cAp_cHoWn", res: 1},
32 | {arg: "unknown", mustFail: true},
33 | {arg: "63", res: 1 << 63},
34 | {arg: "64", mustFail: true},
35 | {arg: "all", res: allKnownCaps()},
36 | }
37 | for _, tc := range tests {
38 | t.Run(tc.arg, func(t *testing.T) {
39 | mask, err := Parse(tc.arg)
40 | if err == nil && tc.mustFail {
41 | t.Fatal("invalid input accepted")
42 | }
43 | if err != nil && !tc.mustFail {
44 | t.Fatal(err)
45 | }
46 | if mask != tc.res {
47 | t.Fatalf("unexpected result: %x", mask)
48 | }
49 | })
50 | }
51 | }
52 |
53 | //go:generate ./gen.sh
54 |
55 | type ddTest struct {
56 | permitted, inheritable string
57 | effective bool
58 | res string
59 | }
60 |
61 | func TestDd(t *testing.T) {
62 | for _, test := range ddTests {
63 | label := fmt.Sprintf("%s,%s,%v", test.permitted, test.inheritable, test.effective)
64 | t.Run(label, func(t *testing.T) {
65 | var permitted, inheritable Mask
66 | var flags Flags
67 |
68 | if test.permitted != "" {
69 | mask, err := Parse(test.permitted)
70 | if err != nil {
71 | t.Fatal(err)
72 | }
73 | permitted = mask
74 | }
75 |
76 | if test.inheritable != "" {
77 | mask, err := Parse(test.inheritable)
78 | if err != nil {
79 | t.Fatal(err)
80 | }
81 | inheritable = mask
82 | }
83 |
84 | if test.effective {
85 | flags = FlagEffective
86 | }
87 |
88 | res, err := XattrBytes(permitted, inheritable, flags)
89 | if err != nil {
90 | t.Fatal(err)
91 | }
92 |
93 | resBase64 := make([]byte, base64.StdEncoding.EncodedLen(len(res)))
94 | base64.StdEncoding.Encode(resBase64, res)
95 | if string(resBase64) != test.res {
96 | t.Fatalf("expected %s, result %s", test.res, resBase64)
97 | }
98 | })
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/pkg/caps/gen.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | # Copyright 2024 ko Build Authors All Rights Reserved.
4 | #
5 | # Licensed under the Apache License, Version 2.0 (the "License");
6 | # you may not use this file except in compliance with the License.
7 | # You may obtain a copy of the License at
8 | #
9 | # http://www.apache.org/licenses/LICENSE-2.0
10 | #
11 | # Unless required by applicable law or agreed to in writing, software
12 | # distributed under the License is distributed on an "AS IS" BASIS,
13 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | # See the License for the specific language governing permissions and
15 | # limitations under the License.
16 |
17 | # This script assigns different capabilities to files and captures
18 | # resulting xattr blobs for testing (generates caps_dd_test.go).
19 | #
20 | # It has to be run on a reasonably recent Linux to ensure that the full
21 | # set of capabilities is supported. Setting capabilities requires
22 | # privileges; the script assumes paswordless sudo is available.
23 |
24 | set -o errexit
25 | set -o nounset
26 | set -o pipefail
27 | shopt -s inherit_errexit
28 |
29 | # capblob CAP_STRING
30 | # Obtain base64-encoded value of the underlying xattr that implemens
31 | # specified capabilities, setcap syntax.
32 | # Example: capblob cap_chown=eip
33 | capblob() {
34 | f=$(mktemp)
35 | sudo -n setcap $1 $f
36 | getfattr -n security.capability --absolute-names --only-values $f | base64
37 | rm $f
38 | }
39 |
40 | (
41 | license=$(sed -e '/^$/,$d' caps.go)
42 |
43 | echo "// Generated file, do not edit."
44 | echo ""
45 | echo "$license"
46 | echo ""
47 | echo "package caps"
48 | echo "var ddTests = []ddTest{"
49 |
50 | res=$(capblob cap_chown=p)
51 | echo "{permitted: \"chown\", inheritable: \"\", effective: false, res: \"$res\"},"
52 |
53 | res=$(capblob cap_chown=ep)
54 | echo "{permitted: \"chown\", inheritable: \"\", effective: true, res: \"$res\"},"
55 |
56 | res=$(capblob cap_chown=i)
57 | echo "{permitted: \"\", inheritable: \"chown\", effective: false, res: \"$res\"},"
58 |
59 | CAPS="chown dac_override dac_read_search fowner fsetid kill setgid setuid
60 | setpcap linux_immutable net_bind_service net_broadcast net_admin net_raw ipc_lock ipc_owner
61 | sys_module sys_rawio sys_chroot sys_ptrace sys_pacct sys_admin sys_boot sys_nice
62 | sys_resource sys_time sys_tty_config mknod lease audit_write audit_control setfcap
63 | mac_override mac_admin syslog wake_alarm block_suspend audit_read perfmon bpf
64 | checkpoint_restore"
65 | for cap in $CAPS; do
66 | res=$(capblob cap_$cap=eip)
67 | echo "{permitted: \"$cap\", inheritable: \"$cap\", effective: true, res: \"$res\"},"
68 | done
69 |
70 | echo "}"
71 | ) > caps_dd_test.go
72 |
73 | gofmt -w -s ./caps_dd_test.go
74 |
--------------------------------------------------------------------------------
/pkg/caps/new_file_caps_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package caps
16 |
17 | import (
18 | "reflect"
19 | "strings"
20 | "testing"
21 | )
22 |
23 | func TestNewFileCaps(t *testing.T) {
24 | tests := []struct {
25 | args []string
26 | res *FileCaps
27 | mustFail bool
28 | }{
29 | {},
30 | {
31 | args: []string{"chown", "dac_override", "dac_read_search"},
32 | res: &FileCaps{permitted: 7},
33 | },
34 | {
35 | args: []string{"chown,dac_override,dac_read_search=p"},
36 | res: &FileCaps{permitted: 7},
37 | },
38 | {
39 | args: []string{"chown,dac_override,dac_read_search=i"},
40 | res: &FileCaps{inheritable: 7},
41 | },
42 | {
43 | args: []string{"chown,dac_override,dac_read_search=e"},
44 | },
45 | {
46 | args: []string{"chown,dac_override,dac_read_search=pe"},
47 | res: &FileCaps{permitted: 7, flags: FlagEffective},
48 | },
49 | {
50 | args: []string{"=pe"},
51 | res: &FileCaps{permitted: allKnownCaps(), flags: FlagEffective},
52 | },
53 | {
54 | args: []string{"chown=ie", "chown=p"},
55 | res: &FileCaps{permitted: 1},
56 | },
57 | {
58 | args: []string{"chown=ie", "chown="},
59 | },
60 | {
61 | args: []string{"chown=ie", "chown+p"},
62 | res: &FileCaps{permitted: 1, inheritable: 1, flags: FlagEffective},
63 | },
64 | {
65 | args: []string{"chown=pie", "dac_override,chown-p"},
66 | res: &FileCaps{inheritable: 1, flags: FlagEffective},
67 | },
68 | {args: []string{"chown,=pie"}, mustFail: true},
69 | {args: []string{"-pie"}, mustFail: true},
70 | {args: []string{"+pie"}, mustFail: true},
71 | {args: []string{"="}},
72 | }
73 | for _, tc := range tests {
74 | label := strings.Join(tc.args, ":")
75 | t.Run(label, func(t *testing.T) {
76 | res, err := NewFileCaps(tc.args...)
77 | if tc.mustFail && err == nil {
78 | t.Fatal("didn't fail")
79 | }
80 | if !tc.mustFail && err != nil {
81 | t.Fatalf("unexpectedly failed: %v", err)
82 | }
83 | if !reflect.DeepEqual(res, tc.res) {
84 | t.Fatalf("got %v expected %v", res, tc.res)
85 | }
86 | })
87 | }
88 | }
89 |
--------------------------------------------------------------------------------
/pkg/commands/commands.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package commands
16 |
17 | import (
18 | "os/exec"
19 |
20 | "github.com/spf13/cobra"
21 | )
22 |
23 | // AddKubeCommands augments our CLI surface with a passthru delete command, and an apply
24 | // command that realizes the promise of ko, as outlined here:
25 | //
26 | // https://github.com/google/go-containerregistry/issues/80
27 | func AddKubeCommands(topLevel *cobra.Command) {
28 | addDelete(topLevel)
29 | addVersion(topLevel)
30 | addCreate(topLevel)
31 | addApply(topLevel)
32 | addResolve(topLevel)
33 | addBuild(topLevel)
34 | addRun(topLevel)
35 | }
36 |
37 | // check if kubectl is installed
38 | func isKubectlAvailable() bool {
39 | if _, err := exec.LookPath("kubectl"); err != nil {
40 | return false
41 | }
42 | return true
43 | }
44 |
--------------------------------------------------------------------------------
/pkg/commands/config_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package commands
16 |
17 | import (
18 | "context"
19 | "fmt"
20 | "testing"
21 |
22 | "github.com/google/go-containerregistry/pkg/crane"
23 |
24 | "github.com/google/ko/pkg/commands/options"
25 | )
26 |
27 | func TestOverrideDefaultBaseImageUsingBuildOption(t *testing.T) {
28 | namespace := "base"
29 | s, err := registryServerWithImage(namespace)
30 | if err != nil {
31 | t.Fatalf("could not create test registry server: %v", err)
32 | }
33 | defer s.Close()
34 | baseImage := fmt.Sprintf("%s/%s", s.Listener.Addr().String(), namespace)
35 | wantDigest, err := crane.Digest(baseImage)
36 | if err != nil {
37 | t.Fatalf("crane.Digest(%s): %v", baseImage, err)
38 | }
39 | wantImage := fmt.Sprintf("%s@%s", baseImage, wantDigest)
40 | bo := &options.BuildOptions{
41 | BaseImage: wantImage,
42 | Platforms: []string{"all"},
43 | }
44 |
45 | baseFn := getBaseImage(bo)
46 | _, res, err := baseFn(context.Background(), "ko://example.com/helloworld")
47 | if err != nil {
48 | t.Fatalf("getBaseImage(): %v", err)
49 | }
50 |
51 | digest, err := res.Digest()
52 | if err != nil {
53 | t.Fatalf("res.Digest(): %v", err)
54 | }
55 | gotDigest := digest.String()
56 | if gotDigest != wantDigest {
57 | t.Errorf("got digest %s, wanted %s", gotDigest, wantDigest)
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/pkg/commands/delete.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package commands
16 |
17 | import (
18 | "errors"
19 | "os"
20 | "os/exec"
21 |
22 | "github.com/spf13/cobra"
23 | )
24 |
25 | // runCmd is suitable for use with cobra.Command's Run field.
26 | type runCmd func(*cobra.Command, []string) error
27 |
28 | // passthru returns a runCmd that simply passes our CLI arguments
29 | // through to a binary named command.
30 | func passthru(command string) runCmd {
31 | return func(cmd *cobra.Command, _ []string) error {
32 | if !isKubectlAvailable() {
33 | return errors.New("error: kubectl is not available. kubectl must be installed to use ko delete")
34 | }
35 | ctx := cmd.Context()
36 |
37 | // Start building a command line invocation by passing
38 | // through our arguments to command's CLI.
39 | //nolint:gosec // We actively want to pass arguments through, so this is fine.
40 | ecmd := exec.CommandContext(ctx, command, os.Args[1:]...)
41 |
42 | // Pass through our environment
43 | ecmd.Env = os.Environ()
44 | // Pass through our stdfoo
45 | ecmd.Stderr = os.Stderr
46 | ecmd.Stdout = os.Stdout
47 | ecmd.Stdin = os.Stdin
48 |
49 | // Run it.
50 | return ecmd.Run()
51 | }
52 | }
53 |
54 | // addDelete augments our CLI surface with publish.
55 | func addDelete(topLevel *cobra.Command) {
56 | topLevel.AddCommand(&cobra.Command{
57 | Use: "delete",
58 | Short: `See "kubectl help delete" for detailed usage.`,
59 | RunE: passthru("kubectl"),
60 | // We ignore unknown flags to avoid importing everything Go exposes
61 | // from our commands.
62 | FParseErrWhitelist: cobra.FParseErrWhitelist{
63 | UnknownFlags: true,
64 | },
65 | })
66 | }
67 |
--------------------------------------------------------------------------------
/pkg/commands/options/filestuff.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package options
16 |
17 | import (
18 | "log"
19 | "os"
20 | "path/filepath"
21 |
22 | "github.com/spf13/cobra"
23 | )
24 |
25 | // FilenameOptions is from pkg/kubectl.
26 | type FilenameOptions struct {
27 | Filenames []string
28 | Recursive bool
29 | }
30 |
31 | func AddFileArg(cmd *cobra.Command, fo *FilenameOptions) {
32 | // From pkg/kubectl
33 | cmd.Flags().StringSliceVarP(&fo.Filenames, "filename", "f", fo.Filenames,
34 | "Filename, directory, or URL to files to use to create the resource")
35 | cmd.Flags().BoolVarP(&fo.Recursive, "recursive", "R", fo.Recursive,
36 | "Process the directory used in -f, --filename recursively. Useful when you want to manage related manifests organized within the same directory.")
37 | }
38 |
39 | // Based heavily on pkg/kubectl
40 | func EnumerateFiles(fo *FilenameOptions) chan string {
41 | files := make(chan string)
42 | go func() {
43 | // When we're done enumerating files, close the channel
44 | defer close(files)
45 | for _, paths := range fo.Filenames {
46 | // Just pass through '-' as it is indicative of stdin.
47 | if paths == "-" {
48 | files <- paths
49 | continue
50 | }
51 | // For each of the "filenames" we are passed (file or directory) start a
52 | // "Walk" to enumerate all of the contained files recursively.
53 | err := filepath.Walk(paths, func(path string, fi os.FileInfo, err error) error {
54 | if err != nil {
55 | return err
56 | }
57 |
58 | // If this is a directory, skip it if it isn't the current directory we are
59 | // processing (unless we are in recursive mode). If we decide to process
60 | // the directory, and we're in watch mode, then we set up a watch on the
61 | // directory.
62 | if fi.IsDir() {
63 | if path != paths && !fo.Recursive {
64 | return filepath.SkipDir
65 | }
66 | // We don't stream back directories, we just decide to skip them, or not.
67 | return nil
68 | }
69 |
70 | // Don't check extension if the filepath was passed explicitly
71 | if path != paths {
72 | switch filepath.Ext(path) {
73 | case ".json", ".yaml":
74 | // Process these.
75 | default:
76 | return nil
77 | }
78 | }
79 |
80 | files <- path
81 | return nil
82 | })
83 | if err != nil {
84 | log.Fatalf("Error enumerating files: %v", err)
85 | }
86 | }
87 | }()
88 | return files
89 | }
90 |
--------------------------------------------------------------------------------
/pkg/commands/options/namer_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package options_test
16 |
17 | import (
18 | "path"
19 | "testing"
20 |
21 | "github.com/google/ko/pkg/commands/options"
22 | )
23 |
24 | func TestMakeNamer(t *testing.T) {
25 | foreachTestCaseMakeNamer(func(tc testMakeNamerCase) {
26 | t.Run(tc.name, func(t *testing.T) {
27 | namer := options.MakeNamer(&tc.opts)
28 | got := namer("registry.example.org/foo/bar", "example.org/sample/cmd/example")
29 |
30 | if got != tc.want {
31 | t.Errorf("got image name %s, wanted %s", got, tc.want)
32 | }
33 | })
34 | })
35 | }
36 |
37 | func foreachTestCaseMakeNamer(fn func(tc testMakeNamerCase)) {
38 | for _, namerCase := range testMakeNamerCases() {
39 | fn(namerCase)
40 | }
41 | }
42 |
43 | func testMakeNamerCases() []testMakeNamerCase {
44 | return []testMakeNamerCase{{
45 | name: "defaults",
46 | want: "registry.example.org/foo/bar/example-51d74b7127c5f7495a338df33ecdeb19",
47 | }, {
48 | name: "with preserve import paths",
49 | want: "registry.example.org/foo/bar/example.org/sample/cmd/example",
50 | opts: options.PublishOptions{PreserveImportPaths: true},
51 | }, {
52 | name: "with base import paths",
53 | want: "registry.example.org/foo/bar/example",
54 | opts: options.PublishOptions{BaseImportPaths: true},
55 | }, {
56 | name: "with bare",
57 | want: "registry.example.org/foo/bar",
58 | opts: options.PublishOptions{Bare: true},
59 | }, {
60 | name: "with custom namer",
61 | want: "registry.example.org/foo/bar-example",
62 | opts: options.PublishOptions{ImageNamer: func(base string, importpath string) string {
63 | return base + "-" + path.Base(importpath)
64 | }},
65 | }}
66 | }
67 |
68 | type testMakeNamerCase struct {
69 | name string
70 | opts options.PublishOptions
71 | want string
72 | }
73 |
--------------------------------------------------------------------------------
/pkg/commands/options/selector.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package options
16 |
17 | import (
18 | "github.com/spf13/cobra"
19 | )
20 |
21 | // SelectorOptions allows selecting objects from the input manifests by label
22 | type SelectorOptions struct {
23 | Selector string
24 | }
25 |
26 | func AddSelectorArg(cmd *cobra.Command, so *SelectorOptions) {
27 | cmd.Flags().StringVarP(&so.Selector, "selector", "l", "",
28 | "Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)")
29 | }
30 |
--------------------------------------------------------------------------------
/pkg/commands/options/testdata/bad-config/.ko.yaml/.gitignore:
--------------------------------------------------------------------------------
1 | .# We just want to have a practically empty directory.
2 | *
3 | !.gitignore
4 |
--------------------------------------------------------------------------------
/pkg/commands/options/testdata/config/.ko.yaml:
--------------------------------------------------------------------------------
1 | defaultBaseImage: alpine
2 | defaultPlatforms: all
3 | defaultEnv: FOO=bar
4 | defaultFlags:
5 | - -tags
6 | - netgo
7 | defaultLdflags:
8 | - -s -w
9 |
--------------------------------------------------------------------------------
/pkg/commands/options/testdata/config/my-ko.yaml:
--------------------------------------------------------------------------------
1 | defaultBaseImage: wow
2 |
--------------------------------------------------------------------------------
/pkg/commands/options/testdata/multiple-platforms/.ko.yaml:
--------------------------------------------------------------------------------
1 | defaultBaseImage: alpine
2 | defaultPlatforms:
3 | - linux/arm64
4 | - linux/amd64
5 |
--------------------------------------------------------------------------------
/pkg/commands/options/testdata/paths/.ko.yaml:
--------------------------------------------------------------------------------
1 | builds:
2 | - id: app-with-main-package-in-different-directory-to-go-mod-and-ko-yaml
3 | dir: ./app
4 | main: ./cmd/foo
5 |
--------------------------------------------------------------------------------
/pkg/commands/options/testdata/paths/app/cmd/foo/main.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package main
16 |
17 | import "fmt"
18 |
19 | func main() {
20 | fmt.Println("cmd/foo")
21 | }
22 |
--------------------------------------------------------------------------------
/pkg/commands/options/testdata/paths/app/go.mod:
--------------------------------------------------------------------------------
1 | module example.com/testapp
2 |
3 | go 1.15
4 |
--------------------------------------------------------------------------------
/pkg/commands/options/validate.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package options
16 |
17 | import (
18 | "errors"
19 | "log"
20 | "strings"
21 | )
22 |
23 | const bareBaseFlagsWarning = `WARNING!
24 | -----------------------------------------------------------------
25 | Both --base-import-paths and --bare were set.
26 |
27 | --base-import-paths will take precedence and ignore --bare flag.
28 |
29 | In a future release this will be an error.
30 | -----------------------------------------------------------------
31 | `
32 |
33 | const localFlagsWarning = `WARNING!
34 | -----------------------------------------------------------------
35 | The --local flag is set and KO_DOCKER_REPO is set to ko.local
36 |
37 | You can choose either one to build a local image.
38 |
39 | The --local flag might be deprecated in the future.
40 | -----------------------------------------------------------------
41 | `
42 |
43 | func Validate(po *PublishOptions, bo *BuildOptions) error {
44 | po.Jobs = bo.ConcurrentBuilds
45 | if po.Bare && po.BaseImportPaths {
46 | log.Print(bareBaseFlagsWarning)
47 | // TODO: return error when we decided to make this an error, for now it is a warning
48 | }
49 |
50 | if po.Local && strings.Contains(po.DockerRepo, "ko.local") {
51 | log.Print(localFlagsWarning)
52 | }
53 |
54 | if len(bo.Platforms) > 1 {
55 | for _, platform := range bo.Platforms {
56 | if platform == "all" {
57 | return errors.New("all or specific platforms should be used")
58 | }
59 | }
60 | }
61 |
62 | return nil
63 | }
64 |
--------------------------------------------------------------------------------
/pkg/commands/publisher.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package commands
16 |
17 | import (
18 | "context"
19 | "fmt"
20 |
21 | "github.com/google/go-containerregistry/pkg/name"
22 | "github.com/google/ko/pkg/build"
23 | "github.com/google/ko/pkg/publish"
24 | )
25 |
26 | // PublishImages publishes images
27 | func PublishImages(ctx context.Context, importpaths []string, pub publish.Interface, b build.Interface) (map[string]name.Reference, error) {
28 | return publishImages(ctx, importpaths, pub, b)
29 | }
30 |
31 | func publishImages(ctx context.Context, importpaths []string, pub publish.Interface, b build.Interface) (map[string]name.Reference, error) {
32 | imgs := make(map[string]name.Reference)
33 | for _, importpath := range importpaths {
34 | importpath, err := b.QualifyImport(importpath)
35 | if err != nil {
36 | return nil, err
37 | }
38 | if err := b.IsSupportedReference(importpath); err != nil {
39 | return nil, fmt.Errorf("importpath %q is not supported: %w", importpath, err)
40 | }
41 |
42 | img, err := b.Build(ctx, importpath)
43 | if err != nil {
44 | return nil, fmt.Errorf("error building %q: %w", importpath, err)
45 | }
46 | ref, err := pub.Publish(ctx, img, importpath)
47 | if err != nil {
48 | return nil, fmt.Errorf("error publishing %s: %w", importpath, err)
49 | }
50 | imgs[importpath] = ref
51 | }
52 | return imgs, nil
53 | }
54 |
--------------------------------------------------------------------------------
/pkg/commands/resolve.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package commands
16 |
17 | import (
18 | "fmt"
19 | "os"
20 |
21 | "github.com/google/ko/pkg/commands/options"
22 | "github.com/spf13/cobra"
23 | )
24 |
25 | // addResolve augments our CLI surface with resolve.
26 | func addResolve(topLevel *cobra.Command) {
27 | po := &options.PublishOptions{}
28 | fo := &options.FilenameOptions{}
29 | so := &options.SelectorOptions{}
30 | bo := &options.BuildOptions{}
31 |
32 | resolve := &cobra.Command{
33 | Use: "resolve -f FILENAME",
34 | Short: "Print the input files with image references resolved to built/pushed image digests.",
35 | Long: `This sub-command finds import path references within the provided files, builds them into Go binaries, containerizes them, publishes them, and prints the resulting yaml.`,
36 | Example: `
37 | # Build and publish import path references to a Docker
38 | # Registry as:
39 | # ${KO_DOCKER_REPO}/-
40 | # When KO_DOCKER_REPO is ko.local, it is the same as if
41 | # --local and --preserve-import-paths were passed.
42 | ko resolve -f config/
43 |
44 | # Build and publish import path references to a Docker
45 | # Registry preserving import path names as:
46 | # ${KO_DOCKER_REPO}/
47 | # When KO_DOCKER_REPO is ko.local, it is the same as if
48 | # --local was passed.
49 | ko resolve --preserve-import-paths -f config/
50 |
51 | # Build and publish import path references to a Docker
52 | # daemon as:
53 | # ko.local/
54 | # This always preserves import paths.
55 | ko resolve --local -f config/`,
56 | Args: cobra.NoArgs,
57 | RunE: func(cmd *cobra.Command, _ []string) error {
58 | if err := options.Validate(po, bo); err != nil {
59 | return fmt.Errorf("validating options: %w", err)
60 | }
61 |
62 | ctx := cmd.Context()
63 |
64 | bo.InsecureRegistry = po.InsecureRegistry
65 | builder, err := makeBuilder(ctx, bo)
66 | if err != nil {
67 | return fmt.Errorf("error creating builder: %w", err)
68 | }
69 | publisher, err := makePublisher(po)
70 | if err != nil {
71 | return fmt.Errorf("error creating publisher: %w", err)
72 | }
73 | defer publisher.Close()
74 | return ResolveFilesToWriter(ctx, builder, publisher, fo, so, os.Stdout)
75 | },
76 | }
77 | options.AddPublishArg(resolve, po)
78 | options.AddFileArg(resolve, fo)
79 | options.AddSelectorArg(resolve, so)
80 | options.AddBuildOptions(resolve, bo)
81 | topLevel.AddCommand(resolve)
82 | }
83 |
--------------------------------------------------------------------------------
/pkg/commands/root.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package commands
16 |
17 | import (
18 | "os"
19 |
20 | cranecmd "github.com/google/go-containerregistry/cmd/crane/cmd"
21 | "github.com/google/go-containerregistry/pkg/logs"
22 | "github.com/spf13/cobra"
23 | "go.uber.org/automaxprocs/maxprocs"
24 | )
25 |
26 | var Root = New()
27 |
28 | func New() *cobra.Command {
29 | var verbose bool
30 | root := &cobra.Command{
31 | Use: "ko",
32 | Short: "Rapidly iterate with Go, Containers, and Kubernetes.",
33 | SilenceUsage: true, // Don't show usage on errors
34 | DisableAutoGenTag: true,
35 | PersistentPreRun: func(_ *cobra.Command, _ []string) {
36 | if verbose {
37 | logs.Warn.SetOutput(os.Stderr)
38 | logs.Debug.SetOutput(os.Stderr)
39 | }
40 | logs.Progress.SetOutput(os.Stderr)
41 |
42 | maxprocs.Set(maxprocs.Logger(logs.Debug.Printf))
43 | },
44 | Run: func(cmd *cobra.Command, _ []string) {
45 | cmd.Help()
46 | },
47 | }
48 | root.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "Enable debug logs")
49 |
50 | AddKubeCommands(root)
51 |
52 | // Also add the auth group from crane to facilitate logging into a
53 | // registry.
54 | authCmd := cranecmd.NewCmdAuth(nil, "ko", "auth")
55 | // That was a mistake, but just set it to Hidden so we don't break people.
56 | authCmd.Hidden = true
57 | root.AddCommand(authCmd)
58 |
59 | // Just add a `ko login` command:
60 | root.AddCommand(cranecmd.NewCmdAuthLogin("ko"))
61 | return root
62 | }
63 |
--------------------------------------------------------------------------------
/pkg/commands/version.go:
--------------------------------------------------------------------------------
1 | // Copyright 2019 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package commands
16 |
17 | import (
18 | "fmt"
19 | "runtime/debug"
20 |
21 | "github.com/spf13/cobra"
22 | )
23 |
24 | // Version is provided by govvv at compile-time
25 | var Version string
26 |
27 | // addVersion augments our CLI surface with version.
28 | func addVersion(topLevel *cobra.Command) {
29 | topLevel.AddCommand(&cobra.Command{
30 | Use: "version",
31 | Short: `Print ko version.`,
32 | Run: func(_ *cobra.Command, _ []string) {
33 | v := version()
34 | if v == "" {
35 | fmt.Println("could not determine build information")
36 | } else {
37 | fmt.Println(v)
38 | }
39 | },
40 | })
41 | }
42 |
43 | func version() string {
44 | if Version == "" {
45 | i, ok := debug.ReadBuildInfo()
46 | if !ok {
47 | return ""
48 | }
49 | Version = i.Main.Version
50 | }
51 | return Version
52 | }
53 |
--------------------------------------------------------------------------------
/pkg/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | // Package ko holds libraries used to implement the ko CLI.
16 | package ko
17 |
--------------------------------------------------------------------------------
/pkg/internal/git/clone.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | // MIT License
16 | //
17 | // Copyright (c) 2016-2022 Carlos Alexandro Becker
18 | //
19 | // Permission is hereby granted, free of charge, to any person obtaining a copy
20 | // of this software and associated documentation files (the "Software"), to deal
21 | // in the Software without restriction, including without limitation the rights
22 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23 | // copies of the Software, and to permit persons to whom the Software is
24 | // furnished to do so, subject to the following conditions:
25 | //
26 | // The above copyright notice and this permission notice shall be included in all
27 | // copies or substantial portions of the Software.
28 | //
29 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35 | // SOFTWARE.
36 |
37 | package git
38 |
39 | import (
40 | "context"
41 | "fmt"
42 | "os/exec"
43 | )
44 |
45 | // Clone the git repository from the repoURL to the specified dir.
46 | func Clone(ctx context.Context, dir string, repoURL string) error {
47 | rc := runConfig{
48 | dir: dir,
49 | args: []string{"clone", "--depth", "1", repoURL, "."},
50 | }
51 |
52 | cmd := exec.CommandContext(ctx, "git", "clone", repoURL, dir)
53 | cmd.Dir = dir
54 |
55 | _, err := run(ctx, rc)
56 | if err != nil {
57 | return fmt.Errorf("running git clone: %w", err)
58 | }
59 |
60 | return nil
61 | }
62 |
--------------------------------------------------------------------------------
/pkg/internal/git/errors.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | // MIT License
16 | //
17 | // Copyright (c) 2016-2022 Carlos Alexandro Becker
18 | //
19 | // Permission is hereby granted, free of charge, to any person obtaining a copy
20 | // of this software and associated documentation files (the "Software"), to deal
21 | // in the Software without restriction, including without limitation the rights
22 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23 | // copies of the Software, and to permit persons to whom the Software is
24 | // furnished to do so, subject to the following conditions:
25 | //
26 | // The above copyright notice and this permission notice shall be included in all
27 | // copies or substantial portions of the Software.
28 | //
29 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35 | // SOFTWARE.
36 |
37 | package git
38 |
39 | import (
40 | "errors"
41 | "fmt"
42 | )
43 |
44 | var (
45 | // ErrNoTag happens if the underlying git repository doesn't contain any tags
46 | // but no snapshot-release was requested.
47 | ErrNoTag = errors.New("git doesn't contain any tags. Tag info will not be available")
48 |
49 | // ErrNotRepository happens if you try to run ko against a folder
50 | // which is not a git repository.
51 | ErrNotRepository = errors.New("current folder is not a git repository. Git info will not be available")
52 |
53 | // ErrNoGit happens when git is not present in PATH.
54 | ErrNoGit = errors.New("git not present in PATH. Git info will not be available")
55 | )
56 |
57 | // ErrDirty happens when the repo has uncommitted/unstashed changes.
58 | type ErrDirty struct {
59 | status string
60 | }
61 |
62 | func (e ErrDirty) Error() string {
63 | 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)
64 | }
65 |
--------------------------------------------------------------------------------
/pkg/internal/gittesting/git_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | // MIT License
16 | //
17 | // Copyright (c) 2016-2022 Carlos Alexandro Becker
18 | //
19 | // Permission is hereby granted, free of charge, to any person obtaining a copy
20 | // of this software and associated documentation files (the "Software"), to deal
21 | // in the Software without restriction, including without limitation the rights
22 | // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
23 | // copies of the Software, and to permit persons to whom the Software is
24 | // furnished to do so, subject to the following conditions:
25 | //
26 | // The above copyright notice and this permission notice shall be included in all
27 | // copies or substantial portions of the Software.
28 | //
29 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30 | // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31 | // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32 | // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33 | // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34 | // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35 | // SOFTWARE.
36 |
37 | package gittesting
38 |
39 | import "testing"
40 |
41 | func TestGit(t *testing.T) {
42 | dir := t.TempDir()
43 | GitInit(t, dir)
44 | GitAdd(t, dir)
45 | GitCommit(t, dir, "commit1")
46 | GitRemoteAdd(t, dir, "git@github.com:goreleaser/nope.git")
47 | GitTag(t, dir, "v1.0.0")
48 | }
49 |
--------------------------------------------------------------------------------
/pkg/internal/testing/daemon.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package testing
16 |
17 | import (
18 | "context"
19 | "io"
20 | "strings"
21 |
22 | "github.com/docker/docker/api/types/image"
23 | "github.com/docker/docker/client"
24 | "github.com/google/go-containerregistry/pkg/v1/daemon"
25 | )
26 |
27 | type MockDaemon struct {
28 | daemon.Client
29 | Tags []string
30 |
31 | inspectErr error
32 | inspectResp image.InspectResponse
33 | inspectBody []byte
34 | }
35 |
36 | func (m *MockDaemon) NegotiateAPIVersion(context.Context) {}
37 | func (m *MockDaemon) ImageLoad(context.Context, io.Reader, ...client.ImageLoadOption) (image.LoadResponse, error) {
38 | return image.LoadResponse{
39 | Body: io.NopCloser(strings.NewReader("Loaded")),
40 | }, nil
41 | }
42 |
43 | func (m *MockDaemon) ImageTag(_ context.Context, _ string, tag string) error {
44 | if m.Tags == nil {
45 | m.Tags = []string{}
46 | }
47 | m.Tags = append(m.Tags, tag)
48 | return nil
49 | }
50 |
51 | func (m *MockDaemon) ImageInspectWithRaw(_ context.Context, _ string) (image.InspectResponse, []byte, error) {
52 | return m.inspectResp, m.inspectBody, m.inspectErr
53 | }
54 |
--------------------------------------------------------------------------------
/pkg/internal/testing/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | // Package testing holds a variety test doubles to help with testing
16 | package testing
17 |
--------------------------------------------------------------------------------
/pkg/internal/testing/fixed.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package testing
16 |
17 | import (
18 | "context"
19 | "errors"
20 | "fmt"
21 | "strings"
22 |
23 | "github.com/google/go-containerregistry/pkg/name"
24 | v1 "github.com/google/go-containerregistry/pkg/v1"
25 | "github.com/google/ko/pkg/build"
26 | "github.com/google/ko/pkg/publish"
27 | )
28 |
29 | type fixedBuild struct {
30 | entries map[string]build.Result
31 | }
32 |
33 | // NewFixedBuild returns a build.Interface implementation that simply resolves
34 | // particular references to fixed v1.Image objects
35 | func NewFixedBuild(entries map[string]build.Result) build.Interface {
36 | return &fixedBuild{entries}
37 | }
38 |
39 | // QualifyImport implements build.Interface
40 | func (f *fixedBuild) QualifyImport(ip string) (string, error) {
41 | return ip, nil
42 | }
43 |
44 | // IsSupportedReference implements build.Interface
45 | func (f *fixedBuild) IsSupportedReference(s string) error {
46 | s = strings.TrimPrefix(s, build.StrictScheme)
47 | if _, ok := f.entries[s]; !ok {
48 | return errors.New("importpath is not supported")
49 | }
50 | return nil
51 | }
52 |
53 | // Build implements build.Interface
54 | func (f *fixedBuild) Build(_ context.Context, s string) (build.Result, error) {
55 | s = strings.TrimPrefix(s, build.StrictScheme)
56 | if img, ok := f.entries[s]; ok {
57 | return img, nil
58 | }
59 | return nil, fmt.Errorf("unsupported reference: %q", s)
60 | }
61 |
62 | type fixedPublish struct {
63 | base name.Repository
64 | entries map[string]v1.Hash
65 | }
66 |
67 | // NewFixedPublish returns a publish.Interface implementation that simply
68 | // resolves particular references to fixed name.Digest references.
69 | func NewFixedPublish(base name.Repository, entries map[string]v1.Hash) publish.Interface {
70 | return &fixedPublish{base, entries}
71 | }
72 |
73 | // Publish implements publish.Interface
74 | func (f *fixedPublish) Publish(_ context.Context, _ build.Result, s string) (name.Reference, error) {
75 | s = strings.TrimPrefix(s, build.StrictScheme)
76 | h, ok := f.entries[s]
77 | if !ok {
78 | return nil, fmt.Errorf("unsupported importpath: %q", s)
79 | }
80 | d, err := name.NewDigest(fmt.Sprintf("%s/%s@%s", f.base, s, h))
81 | if err != nil {
82 | return nil, err
83 | }
84 | return &d, nil
85 | }
86 |
87 | func (f *fixedPublish) Close() error {
88 | return nil
89 | }
90 |
91 | func ComputeDigest(base name.Repository, ref string, h v1.Hash) string {
92 | d, err := name.NewDigest(fmt.Sprintf("%s/%s@%s", base, ref, h))
93 | if err != nil {
94 | panic(err)
95 | }
96 | return d.String()
97 | }
98 |
--------------------------------------------------------------------------------
/pkg/internal/testing/fixed_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package testing
16 |
17 | import (
18 | "context"
19 | "testing"
20 |
21 | "github.com/google/go-containerregistry/pkg/name"
22 | v1 "github.com/google/go-containerregistry/pkg/v1"
23 | "github.com/google/go-containerregistry/pkg/v1/random"
24 | "github.com/google/ko/pkg/build"
25 | )
26 |
27 | func TestFixedPublish(t *testing.T) {
28 | hex1 := "deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
29 | hex2 := "baadf00dbaadf00dbaadf00dbaadf00dbaadf00dbaadf00dbaadf00dbaadf00d"
30 | fixedBaseRepo, _ := name.NewRepository("gcr.io/asdf")
31 | f := NewFixedPublish(fixedBaseRepo, map[string]v1.Hash{
32 | "foo": {
33 | Algorithm: "sha256",
34 | Hex: hex1,
35 | },
36 | "bar": {
37 | Algorithm: "sha256",
38 | Hex: hex2,
39 | },
40 | })
41 |
42 | fooDigest, err := f.Publish(context.Background(), nil, "foo")
43 | if err != nil {
44 | t.Errorf("Publish(foo) = %v", err)
45 | }
46 | if got, want := fooDigest.String(), "gcr.io/asdf/foo@sha256:"+hex1; got != want {
47 | t.Errorf("Publish(foo) = %q, want %q", got, want)
48 | }
49 |
50 | barDigest, err := f.Publish(context.Background(), nil, "bar")
51 | if err != nil {
52 | t.Errorf("Publish(bar) = %v", err)
53 | }
54 | if got, want := barDigest.String(), "gcr.io/asdf/bar@sha256:"+hex2; got != want {
55 | t.Errorf("Publish(bar) = %q, want %q", got, want)
56 | }
57 |
58 | d, err := f.Publish(context.Background(), nil, "baz")
59 | if err == nil {
60 | t.Errorf("Publish(baz) = %v, want error", d)
61 | }
62 | }
63 |
64 | func TestFixedBuild(t *testing.T) {
65 | testImage, _ := random.Image(1024, 5)
66 | f := NewFixedBuild(map[string]build.Result{
67 | "asdf": testImage,
68 | })
69 |
70 | if got := f.IsSupportedReference("asdf"); got != nil {
71 | t.Errorf("IsSupportedReference(asdf) = (%v), want nil", got)
72 | }
73 | if got, err := f.Build(context.Background(), "asdf"); err != nil {
74 | t.Errorf("Build(asdf) = %v, want %v", err, testImage)
75 | } else if got != testImage {
76 | t.Errorf("Build(asdf) = %v, want %v", got, testImage)
77 | }
78 |
79 | if got := f.IsSupportedReference("blah"); got == nil {
80 | t.Error("IsSupportedReference(blah) = nil, want error")
81 | }
82 | if got, err := f.Build(context.Background(), "blah"); err == nil {
83 | t.Errorf("Build(blah) = %v, want error", got)
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/pkg/publish/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | // Package publish defines methods for publishing a v1.Image reference and
16 | // returning the published digest for embedding back into a Kubernetes yaml.
17 | package publish
18 |
--------------------------------------------------------------------------------
/pkg/publish/future.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package publish
16 |
17 | import (
18 | "sync"
19 |
20 | "github.com/google/go-containerregistry/pkg/name"
21 | )
22 |
23 | func newFuture(work func() (name.Reference, error)) *future {
24 | // Create a channel on which to send the result.
25 | ch := make(chan *result)
26 | // Initiate the actual work, sending its result
27 | // along the above channel.
28 | go func() {
29 | ref, err := work()
30 | ch <- &result{ref: ref, err: err}
31 | }()
32 | // Return a future for the above work. Callers should
33 | // call .Get() on this result (as many times as needed).
34 | // One of these calls will receive the result, store it,
35 | // and close the channel so that the rest of the callers
36 | // can consume it.
37 | return &future{
38 | promise: ch,
39 | }
40 | }
41 |
42 | type result struct {
43 | ref name.Reference
44 | err error
45 | }
46 |
47 | type future struct {
48 | m sync.RWMutex
49 |
50 | result *result
51 | promise chan *result
52 | }
53 |
54 | // Get blocks on the result of the future.
55 | func (f *future) Get() (name.Reference, error) {
56 | // Block on the promise of a result until we get one.
57 | result, ok := <-f.promise
58 | if ok {
59 | func() {
60 | f.m.Lock()
61 | defer f.m.Unlock()
62 | // If we got the result, then store it so that
63 | // others may access it.
64 | f.result = result
65 | // Close the promise channel so that others
66 | // are signaled that the result is available.
67 | close(f.promise)
68 | }()
69 | }
70 |
71 | f.m.RLock()
72 | defer f.m.RUnlock()
73 |
74 | return f.result.ref, f.result.err
75 | }
76 |
--------------------------------------------------------------------------------
/pkg/publish/future_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package publish
16 |
17 | import (
18 | "fmt"
19 | "testing"
20 |
21 | "github.com/google/go-containerregistry/pkg/name"
22 | "github.com/google/go-containerregistry/pkg/v1/random"
23 | )
24 |
25 | func makeRef() (name.Reference, error) {
26 | img, err := random.Image(256, 8)
27 | if err != nil {
28 | return nil, err
29 | }
30 | d, err := img.Digest()
31 | if err != nil {
32 | return nil, err
33 | }
34 | return name.NewDigest(fmt.Sprintf("gcr.io/foo/bar@%s", d))
35 | }
36 |
37 | func TestSameFutureSameReference(t *testing.T) {
38 | f := newFuture(makeRef)
39 |
40 | ref1, err := f.Get()
41 | if err != nil {
42 | t.Errorf("Get() = %v", err)
43 | }
44 | d1 := ref1.String()
45 |
46 | ref2, err := f.Get()
47 | if err != nil {
48 | t.Errorf("Get() = %v", err)
49 | }
50 | d2 := ref2.String()
51 |
52 | if d1 != d2 {
53 | t.Errorf("Got different digests %s and %s", d1, d2)
54 | }
55 | }
56 |
57 | func TestDiffFutureDiffReference(t *testing.T) {
58 | f1 := newFuture(makeRef)
59 | f2 := newFuture(makeRef)
60 |
61 | ref1, err := f1.Get()
62 | if err != nil {
63 | t.Errorf("Get() = %v", err)
64 | }
65 | d1 := ref1.String()
66 |
67 | ref2, err := f2.Get()
68 | if err != nil {
69 | t.Errorf("Get() = %v", err)
70 | }
71 | d2 := ref2.String()
72 |
73 | if d1 == d2 {
74 | t.Errorf("Got same digest %s, wanted different", d1)
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/pkg/publish/kind/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | // Package kind defines methods for publishing images into kind nodes.
16 | package kind
17 |
--------------------------------------------------------------------------------
/pkg/publish/layout.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package publish
16 |
17 | import (
18 | "context"
19 | "fmt"
20 | "log"
21 |
22 | "github.com/google/go-containerregistry/pkg/name"
23 | v1 "github.com/google/go-containerregistry/pkg/v1"
24 | "github.com/google/go-containerregistry/pkg/v1/empty"
25 | "github.com/google/go-containerregistry/pkg/v1/layout"
26 | "github.com/google/go-containerregistry/pkg/v1/types"
27 | "github.com/google/ko/pkg/build"
28 | )
29 |
30 | type LayoutPublisher struct {
31 | p string
32 | }
33 |
34 | // NewLayout returns a new publish.Interface that saves images to an OCI Image Layout.
35 | func NewLayout(p string) Interface {
36 | return &LayoutPublisher{p}
37 | }
38 |
39 | func (l *LayoutPublisher) writeResult(br build.Result) (layout.Path, error) {
40 | p, err := layout.FromPath(l.p)
41 | if err != nil {
42 | p, err = layout.Write(l.p, empty.Index)
43 | if err != nil {
44 | return "", err
45 | }
46 | }
47 |
48 | mt, err := br.MediaType()
49 | if err != nil {
50 | return "", err
51 | }
52 |
53 | switch mt {
54 | case types.OCIImageIndex, types.DockerManifestList:
55 | idx, ok := br.(v1.ImageIndex)
56 | if !ok {
57 | return "", fmt.Errorf("failed to interpret result as index: %v", br)
58 | }
59 | if err := p.AppendIndex(idx); err != nil {
60 | return "", err
61 | }
62 | return p, nil
63 | case types.OCIManifestSchema1, types.DockerManifestSchema2:
64 | img, ok := br.(v1.Image)
65 | if !ok {
66 | return "", fmt.Errorf("failed to interpret result as image: %v", br)
67 | }
68 | if err := p.AppendImage(img); err != nil {
69 | return "", err
70 | }
71 | return p, nil
72 | default:
73 | return "", fmt.Errorf("result image media type: %s", mt)
74 | }
75 | }
76 |
77 | // Publish implements publish.Interface.
78 | func (l *LayoutPublisher) Publish(_ context.Context, br build.Result, s string) (name.Reference, error) {
79 | log.Printf("Saving %v", s)
80 | p, err := l.writeResult(br)
81 | if err != nil {
82 | return nil, err
83 | }
84 | log.Printf("Saved %v", s)
85 |
86 | h, err := br.Digest()
87 | if err != nil {
88 | return nil, err
89 | }
90 |
91 | dig, err := name.NewDigest(fmt.Sprintf("%s@%s", p, h))
92 | if err != nil {
93 | return nil, err
94 | }
95 |
96 | return dig, nil
97 | }
98 |
99 | func (l *LayoutPublisher) Close() error {
100 | return nil
101 | }
102 |
--------------------------------------------------------------------------------
/pkg/publish/layout_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package publish
16 |
17 | import (
18 | "context"
19 | "os"
20 | "strings"
21 | "testing"
22 |
23 | "github.com/google/go-containerregistry/pkg/v1/random"
24 | )
25 |
26 | func TestLayout(t *testing.T) {
27 | img, err := random.Image(1024, 1)
28 | if err != nil {
29 | t.Fatalf("random.Image() = %v", err)
30 | }
31 | importpath := "github.com/Google/go-containerregistry/cmd/crane"
32 |
33 | tmp, err := os.MkdirTemp("/tmp", "ko")
34 | if err != nil {
35 | t.Fatal(err)
36 | }
37 | defer os.RemoveAll(tmp)
38 |
39 | lp := NewLayout(tmp)
40 | if d, err := lp.Publish(context.Background(), img, importpath); err != nil {
41 | t.Errorf("Publish() = %v", err)
42 | } else if !strings.HasPrefix(d.String(), tmp) {
43 | t.Errorf("Publish() = %v, wanted prefix %v", d, tmp)
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/pkg/publish/multi.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package publish
16 |
17 | import (
18 | "context"
19 | "errors"
20 |
21 | "github.com/google/go-containerregistry/pkg/name"
22 | "github.com/google/ko/pkg/build"
23 | )
24 |
25 | // MultiPublisher creates a publisher that publishes to all
26 | // the provided publishers, similar to the Unix tee(1) command.
27 | //
28 | // When calling Publish, the name.Reference returned will be the return value
29 | // of the last publisher passed to MultiPublisher (last one wins).
30 | func MultiPublisher(publishers ...Interface) Interface {
31 | return &multiPublisher{publishers}
32 | }
33 |
34 | type multiPublisher struct {
35 | publishers []Interface
36 | }
37 |
38 | // Publish implements publish.Interface.
39 | func (p *multiPublisher) Publish(ctx context.Context, br build.Result, s string) (ref name.Reference, err error) {
40 | if len(p.publishers) == 0 {
41 | return nil, errors.New("MultiPublisher configured with zero publishers")
42 | }
43 |
44 | for _, pub := range p.publishers {
45 | ref, err = pub.Publish(ctx, br, s)
46 | if err != nil {
47 | return
48 | }
49 | }
50 | return
51 | }
52 |
53 | func (p *multiPublisher) Close() (err error) {
54 | for _, pub := range p.publishers {
55 | if perr := pub.Close(); perr != nil {
56 | err = perr
57 | }
58 | }
59 | return
60 | }
61 |
--------------------------------------------------------------------------------
/pkg/publish/multi_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package publish_test
16 |
17 | import (
18 | "context"
19 | "fmt"
20 | "os"
21 | "testing"
22 |
23 | "github.com/google/go-containerregistry/pkg/v1/random"
24 | "github.com/google/ko/pkg/publish"
25 | )
26 |
27 | func TestMulti(t *testing.T) {
28 | img, err := random.Image(1024, 1)
29 | if err != nil {
30 | t.Fatalf("random.Image() = %v", err)
31 | }
32 | base := "blah"
33 | repoName := fmt.Sprintf("%s/%s", "example.com", base)
34 | importpath := "github.com/Google/go-containerregistry/cmd/crane"
35 |
36 | fp, err := os.CreateTemp("", "")
37 | if err != nil {
38 | t.Fatal(err)
39 | }
40 | defer fp.Close()
41 | defer os.Remove(fp.Name())
42 |
43 | tp := publish.NewTarball(fp.Name(), repoName, md5Hash, []string{})
44 |
45 | tmp, err := os.MkdirTemp("/tmp", "ko")
46 | if err != nil {
47 | t.Fatal(err)
48 | }
49 | defer os.RemoveAll(tmp)
50 |
51 | lp := publish.NewLayout(tmp)
52 |
53 | p := publish.MultiPublisher(lp, tp)
54 | if _, err := p.Publish(context.Background(), img, importpath); err != nil {
55 | t.Errorf("Publish() = %v", err)
56 | }
57 |
58 | if err := p.Close(); err != nil {
59 | t.Errorf("Close() = %v", err)
60 | }
61 | }
62 |
63 | func TestMulti_Zero(t *testing.T) {
64 | img, err := random.Image(1024, 1)
65 | if err != nil {
66 | t.Fatalf("random.Image() = %v", err)
67 | }
68 |
69 | p := publish.MultiPublisher() // No publishers.
70 | if _, err := p.Publish(context.Background(), img, "foo"); err == nil {
71 | t.Errorf("Publish() got nil error")
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/pkg/publish/options.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package publish
16 |
17 | import (
18 | "crypto/tls"
19 | "net/http"
20 |
21 | "github.com/google/go-containerregistry/pkg/authn"
22 | )
23 |
24 | type staticKeychain struct {
25 | auth authn.Authenticator
26 | }
27 |
28 | func (s staticKeychain) Resolve(authn.Resource) (authn.Authenticator, error) {
29 | return s.auth, nil
30 | }
31 |
32 | // WithTransport is a functional option for overriding the default transport
33 | // on a default publisher.
34 | func WithTransport(t http.RoundTripper) Option {
35 | return func(i *defaultOpener) error {
36 | i.t = t
37 | return nil
38 | }
39 | }
40 |
41 | // WithUserAgent is a functional option for overriding the User-Agent
42 | // on a default publisher.
43 | func WithUserAgent(ua string) Option {
44 | return func(i *defaultOpener) error {
45 | i.userAgent = ua
46 | return nil
47 | }
48 | }
49 |
50 | // WithAuth is a functional option for overriding the default authenticator
51 | // on a default publisher.
52 | func WithAuth(a authn.Authenticator) Option {
53 | return func(i *defaultOpener) error {
54 | i.keychain = staticKeychain{a}
55 | return nil
56 | }
57 | }
58 |
59 | // WithAuthFromKeychain is a functional option for overriding the default
60 | // authenticator on a default publisher using an authn.Keychain
61 | func WithAuthFromKeychain(keys authn.Keychain) Option {
62 | return func(i *defaultOpener) error {
63 | i.keychain = keys
64 | return nil
65 | }
66 | }
67 |
68 | // WithNamer is a functional option for overriding the image naming behavior
69 | // in our default publisher.
70 | func WithNamer(n Namer) Option {
71 | return func(i *defaultOpener) error {
72 | i.namer = n
73 | return nil
74 | }
75 | }
76 |
77 | // WithTags is a functional option for overriding the image tags
78 | func WithTags(tags []string) Option {
79 | return func(i *defaultOpener) error {
80 | i.tags = tags
81 | return nil
82 | }
83 | }
84 |
85 | // WithTagOnly is a functional option for resolving images into tag-only references
86 | func WithTagOnly(tagOnly bool) Option {
87 | return func(i *defaultOpener) error {
88 | i.tagOnly = tagOnly
89 | return nil
90 | }
91 | }
92 |
93 | func Insecure(b bool) Option {
94 | return func(i *defaultOpener) error {
95 | i.insecure = b
96 | t, ok := i.t.(*http.Transport)
97 | if !ok {
98 | return nil
99 | }
100 | t = t.Clone()
101 | if t.TLSClientConfig == nil {
102 | t.TLSClientConfig = &tls.Config{} //nolint: gosec
103 | }
104 | t.TLSClientConfig.InsecureSkipVerify = b //nolint: gosec
105 | i.t = t
106 |
107 | return nil
108 | }
109 | }
110 |
111 | // WithJobs limits the number of concurrent pushes.
112 | func WithJobs(jobs int) Option {
113 | return func(i *defaultOpener) error {
114 | i.jobs = jobs
115 | return nil
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/pkg/publish/publish.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package publish
16 |
17 | import (
18 | "context"
19 |
20 | "github.com/google/go-containerregistry/pkg/name"
21 | "github.com/google/ko/pkg/build"
22 | )
23 |
24 | // Interface abstracts different methods for publishing images.
25 | type Interface interface {
26 | // Publish uploads the given build.Result to a registry incorporating the
27 | // provided string into the image's repository name. Returns the digest
28 | // of the published image.
29 | Publish(context.Context, build.Result, string) (name.Reference, error)
30 |
31 | // Close exists for the tarball implementation so we can
32 | // do the whole thing in one write.
33 | Close() error
34 | }
35 |
--------------------------------------------------------------------------------
/pkg/publish/recorder.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package publish
16 |
17 | import (
18 | "context"
19 | "io"
20 | "os"
21 | "strings"
22 |
23 | "github.com/google/go-containerregistry/pkg/name"
24 | v1 "github.com/google/go-containerregistry/pkg/v1"
25 | "github.com/google/ko/pkg/build"
26 | "github.com/sigstore/cosign/v2/pkg/oci"
27 | "github.com/sigstore/cosign/v2/pkg/oci/walk"
28 | )
29 |
30 | // recorder wraps a publisher implementation in a layer that recordes the published
31 | // references to a file.
32 | type recorder struct {
33 | inner Interface
34 | fileName string
35 | wc io.Writer
36 | }
37 |
38 | // recorder implements Interface
39 | var _ Interface = (*recorder)(nil)
40 |
41 | // NewRecorder wraps the provided publish.Interface in an implementation that
42 | // records publish results to a file.
43 | func NewRecorder(inner Interface, name string) (Interface, error) {
44 | return &recorder{
45 | inner: inner,
46 | fileName: name,
47 | }, nil
48 | }
49 |
50 | // Publish implements Interface
51 | func (r *recorder) Publish(ctx context.Context, br build.Result, ref string) (name.Reference, error) {
52 | result, err := r.inner.Publish(ctx, br, ref)
53 | if err != nil {
54 | return nil, err
55 | }
56 |
57 | references := make([]string, 0, 20 /* just try to avoid resizing*/)
58 | switch t := br.(type) {
59 | case oci.SignedImageIndex:
60 | if err := walk.SignedEntity(ctx, t, func(_ context.Context, se oci.SignedEntity) error {
61 | // Both of the SignedEntity types implement Digest()
62 | h, err := se.(interface{ Digest() (v1.Hash, error) }).Digest()
63 | if err != nil {
64 | return err
65 | }
66 | references = append(references, result.Context().Digest(h.String()).String())
67 | return nil
68 | }); err != nil {
69 | return nil, err
70 | }
71 | default:
72 | references = append(references, result.String())
73 | }
74 |
75 | if r.wc == nil {
76 | f, err := os.OpenFile(r.fileName, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
77 | if err != nil {
78 | return nil, err
79 | }
80 | r.wc = f
81 | }
82 |
83 | if _, err := r.wc.Write([]byte(strings.Join(references, "\n") + "\n")); err != nil {
84 | return nil, err
85 | }
86 | return result, nil
87 | }
88 |
89 | // Close implements Interface
90 | func (r *recorder) Close() error {
91 | if err := r.inner.Close(); err != nil {
92 | return err
93 | }
94 | if c, ok := r.wc.(io.Closer); ok {
95 | return c.Close()
96 | }
97 | return nil
98 | }
99 |
--------------------------------------------------------------------------------
/pkg/publish/recorder_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package publish
16 |
17 | import (
18 | "context"
19 | "os"
20 | "path"
21 | "strings"
22 | "testing"
23 |
24 | "github.com/google/go-containerregistry/pkg/name"
25 | "github.com/google/go-containerregistry/pkg/v1/random"
26 | "github.com/google/ko/pkg/build"
27 | "github.com/sigstore/cosign/v2/pkg/oci/signed"
28 | )
29 |
30 | type cbPublish struct {
31 | cb func(context.Context, build.Result, string) (name.Reference, error)
32 | }
33 |
34 | var _ Interface = (*cbPublish)(nil)
35 |
36 | func (sp *cbPublish) Publish(ctx context.Context, br build.Result, ref string) (name.Reference, error) {
37 | return sp.cb(ctx, br, ref)
38 | }
39 |
40 | func (sp *cbPublish) Close() error {
41 | return nil
42 | }
43 |
44 | func TestRecorder(t *testing.T) {
45 | repo := name.MustParseReference("docker.io/ubuntu:latest")
46 | inner := &cbPublish{cb: func(_ context.Context, b build.Result, _ string) (name.Reference, error) {
47 | h, err := b.Digest()
48 | if err != nil {
49 | return nil, err
50 | }
51 | return repo.Context().Digest(h.String()), nil
52 | }}
53 |
54 | dir := t.TempDir()
55 | file := path.Join(dir, "testfile")
56 |
57 | recorder, err := NewRecorder(inner, file)
58 | if err != nil {
59 | t.Fatalf("NewRecorder() = %v", err)
60 | }
61 |
62 | img, err := random.Image(3, 3)
63 | if err != nil {
64 | t.Fatalf("random.Image() = %v", err)
65 | }
66 | si := signed.Image(img)
67 |
68 | index, err := random.Index(3, 3, 2)
69 | if err != nil {
70 | t.Fatalf("random.Image() = %v", err)
71 | }
72 | sii := signed.ImageIndex(index)
73 |
74 | if _, err := recorder.Publish(context.Background(), si, ""); err != nil {
75 | t.Errorf("recorder.Publish() = %v", err)
76 | }
77 | if _, err := recorder.Publish(context.Background(), si, ""); err != nil {
78 | t.Errorf("recorder.Publish() = %v", err)
79 | }
80 | if _, err := recorder.Publish(context.Background(), sii, ""); err != nil {
81 | t.Errorf("recorder.Publish() = %v", err)
82 | }
83 | if err := recorder.Close(); err != nil {
84 | t.Errorf("recorder.Close() = %v", err)
85 | }
86 |
87 | buf, err := os.ReadFile(file)
88 | if err != nil {
89 | t.Fatalf("os.ReadFile() = %v", err)
90 | }
91 | refs := strings.Split(strings.TrimSpace(string(buf)), "\n")
92 |
93 | if want, got := len(refs), 5; got != want {
94 | t.Errorf("len(refs) = %d, wanted %d", got, want)
95 | }
96 |
97 | for _, s := range refs {
98 | ref, err := name.ParseReference(s)
99 | if err != nil {
100 | t.Fatalf("name.ParseReference() = %v", err)
101 | }
102 | // Don't compare the digests, they are random.
103 | if want, got := repo.Context().String(), ref.Context().String(); want != got {
104 | t.Errorf("reference repo = %v, wanted %v", got, want)
105 | }
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/pkg/publish/shared.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package publish
16 |
17 | import (
18 | "context"
19 | "sync"
20 |
21 | "github.com/google/go-containerregistry/pkg/name"
22 | "github.com/google/ko/pkg/build"
23 | )
24 |
25 | // caching wraps a publisher implementation in a layer that shares publish results
26 | // for the same inputs using a simple "future" implementation.
27 | type caching struct {
28 | inner Interface
29 |
30 | m sync.Mutex
31 | results map[string]*entry
32 | }
33 |
34 | // entry holds the last image published and the result of publishing it for a
35 | // particular reference.
36 | type entry struct {
37 | br build.Result
38 | f *future
39 | }
40 |
41 | // caching implements Interface
42 | var _ Interface = (*caching)(nil)
43 |
44 | // NewCaching wraps the provided publish.Interface in an implementation that
45 | // shares publish results for a given path until the passed image object changes.
46 | func NewCaching(inner Interface) (Interface, error) {
47 | return &caching{
48 | inner: inner,
49 | results: make(map[string]*entry),
50 | }, nil
51 | }
52 |
53 | // Publish implements Interface
54 | func (c *caching) Publish(ctx context.Context, br build.Result, ref string) (name.Reference, error) {
55 | f := func() *future {
56 | // Lock the map of futures.
57 | c.m.Lock()
58 | defer c.m.Unlock()
59 |
60 | // If a future for "ref" exists, then return it.
61 | ent, ok := c.results[ref]
62 | if ok {
63 | // If the image matches, then return the same future.
64 | if ent.br == br {
65 | return ent.f
66 | }
67 | }
68 | // Otherwise create and record a future for publishing "br" to "ref".
69 | f := newFuture(func() (name.Reference, error) {
70 | return c.inner.Publish(ctx, br, ref)
71 | })
72 | c.results[ref] = &entry{br: br, f: f}
73 | return f
74 | }()
75 |
76 | return f.Get()
77 | }
78 |
79 | func (c *caching) Close() error {
80 | return c.inner.Close()
81 | }
82 |
--------------------------------------------------------------------------------
/pkg/publish/shared_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package publish
16 |
17 | import (
18 | "context"
19 | "testing"
20 | "time"
21 |
22 | "github.com/google/go-containerregistry/pkg/name"
23 | "github.com/google/go-containerregistry/pkg/v1/random"
24 | "github.com/google/ko/pkg/build"
25 | )
26 |
27 | type slowpublish struct {
28 | sleep time.Duration
29 | }
30 |
31 | // slowpublish implements Interface
32 | var _ Interface = (*slowpublish)(nil)
33 |
34 | func (sp *slowpublish) Publish(context.Context, build.Result, string) (name.Reference, error) {
35 | time.Sleep(sp.sleep)
36 | return makeRef()
37 | }
38 |
39 | func (sp *slowpublish) Close() error {
40 | return nil
41 | }
42 |
43 | func TestCaching(t *testing.T) {
44 | duration := 100 * time.Millisecond
45 | ref := "foo"
46 |
47 | sp := &slowpublish{duration}
48 | cb, _ := NewCaching(sp)
49 |
50 | previousDigest := "not-a-digest"
51 | // Each iteration, we test that the first publish is slow and subsequent
52 | // publishs are fast and return the same reference. For each of these
53 | // iterations we use a new random image, which should invalidate the
54 | // cached reference from previous iterations.
55 | for idx := 0; idx < 3; idx++ {
56 | img, _ := random.Index(256, 8, 1)
57 |
58 | start := time.Now()
59 | ref1, err := cb.Publish(context.Background(), img, ref)
60 | if err != nil {
61 | t.Errorf("Publish() = %v", err)
62 | }
63 | end := time.Now()
64 |
65 | elapsed := end.Sub(start)
66 | if elapsed < duration {
67 | t.Errorf("Elapsed time %v, wanted >= %s", elapsed, duration)
68 | }
69 | d1 := ref1.String()
70 |
71 | if d1 == previousDigest {
72 | t.Errorf("Got same digest as previous iteration, wanted different: %v", d1)
73 | }
74 | previousDigest = d1
75 |
76 | start = time.Now()
77 | ref2, err := cb.Publish(context.Background(), img, ref)
78 | if err != nil {
79 | t.Errorf("Publish() = %v", err)
80 | }
81 | end = time.Now()
82 |
83 | elapsed = end.Sub(start)
84 | if elapsed >= duration {
85 | t.Errorf("Elapsed time %v, wanted < %s", elapsed, duration)
86 | }
87 | d2 := ref2.String()
88 |
89 | if d1 != d2 {
90 | t.Error("Got different references, wanted same")
91 | }
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/pkg/publish/tarball.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package publish
16 |
17 | import (
18 | "context"
19 | "fmt"
20 | "log"
21 | "strings"
22 |
23 | "github.com/google/go-containerregistry/pkg/name"
24 | v1 "github.com/google/go-containerregistry/pkg/v1"
25 | "github.com/google/go-containerregistry/pkg/v1/tarball"
26 | "github.com/google/ko/pkg/build"
27 | )
28 |
29 | type tar struct {
30 | file string
31 | base string
32 | namer Namer
33 | tags []string
34 | refs map[name.Reference]v1.Image
35 | }
36 |
37 | // NewTarball returns a new publish.Interface that saves images to a tarball.
38 | func NewTarball(file, base string, namer Namer, tags []string) Interface {
39 | return &tar{
40 | file: file,
41 | base: base,
42 | namer: namer,
43 | tags: tags,
44 | refs: make(map[name.Reference]v1.Image),
45 | }
46 | }
47 |
48 | // Publish implements publish.Interface.
49 | func (t *tar) Publish(_ context.Context, br build.Result, s string) (name.Reference, error) {
50 | s = strings.TrimPrefix(s, build.StrictScheme)
51 | // https://github.com/google/go-containerregistry/issues/212
52 | s = strings.ToLower(s)
53 |
54 | // There's no way to write an index to a tarball, so attempt to downcast it to an image.
55 | img, ok := br.(v1.Image)
56 | if !ok {
57 | return nil, fmt.Errorf("failed to interpret %s result as image: %v", s, br)
58 | }
59 |
60 | for _, tagName := range t.tags {
61 | tag, err := name.ParseReference(fmt.Sprintf("%s:%s", t.namer(t.base, s), tagName))
62 | if err != nil {
63 | return nil, err
64 | }
65 | t.refs[tag] = img
66 | }
67 |
68 | h, err := img.Digest()
69 | if err != nil {
70 | return nil, err
71 | }
72 |
73 | if len(t.tags) == 0 {
74 | ref, err := name.ParseReference(fmt.Sprintf("%s@%s", t.namer(t.base, s), h))
75 | if err != nil {
76 | return nil, err
77 | }
78 | t.refs[ref] = img
79 | }
80 |
81 | ref := fmt.Sprintf("%s@%s", t.namer(t.base, s), h)
82 | if len(t.tags) == 1 && t.tags[0] != latestTag {
83 | // If a single tag is explicitly set (not latest), then this
84 | // is probably a release, so include the tag in the reference.
85 | ref = fmt.Sprintf("%s:%s@%s", t.namer(t.base, s), t.tags[0], h)
86 | }
87 | dig, err := name.NewDigest(ref)
88 | if err != nil {
89 | return nil, err
90 | }
91 |
92 | return &dig, nil
93 | }
94 |
95 | func (t *tar) Close() error {
96 | log.Printf("Saving %v", t.file)
97 | if err := tarball.MultiRefWriteToFile(t.file, t.refs); err != nil {
98 | // Bad practice, but we log this here because right now we just defer the Close.
99 | log.Printf("failed to save %q: %v", t.file, err)
100 | return err
101 | }
102 | log.Printf("Saved %v", t.file)
103 | return nil
104 | }
105 |
--------------------------------------------------------------------------------
/pkg/publish/tarball_test.go:
--------------------------------------------------------------------------------
1 | // Copyright 2020 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package publish_test
16 |
17 | import (
18 | "context"
19 | "fmt"
20 | "os"
21 | "strings"
22 | "testing"
23 |
24 | "github.com/google/go-containerregistry/pkg/name"
25 | "github.com/google/go-containerregistry/pkg/v1/random"
26 | "github.com/google/ko/pkg/publish"
27 | )
28 |
29 | func TestTarball(t *testing.T) {
30 | img, err := random.Image(1024, 1)
31 | if err != nil {
32 | t.Fatalf("random.Image() = %v", err)
33 | }
34 | base := "blah"
35 | importpath := "github.com/Google/go-containerregistry/cmd/crane"
36 |
37 | fp, err := os.CreateTemp("", "")
38 | if err != nil {
39 | t.Fatal(err)
40 | }
41 | defer fp.Close()
42 | defer os.Remove(fp.Name())
43 |
44 | expectedRepo := md5Hash(base, strings.ToLower(importpath))
45 |
46 | tag, err := name.NewTag(fmt.Sprintf("%s/%s:latest", "example.com", expectedRepo))
47 | if err != nil {
48 | t.Fatalf("NewTag() = %v", err)
49 | }
50 |
51 | repoName := fmt.Sprintf("%s/%s", "example.com", base)
52 | tagss := [][]string{{
53 | // no tags
54 | }, {
55 | // one tag
56 | "v0.1.0",
57 | }, {
58 | // multiple tags
59 | "latest",
60 | "debug",
61 | }}
62 | for _, tags := range tagss {
63 | tp := publish.NewTarball(fp.Name(), repoName, md5Hash, tags)
64 | if d, err := tp.Publish(context.Background(), img, importpath); err != nil {
65 | t.Errorf("Publish() = %v", err)
66 | } else if !strings.HasPrefix(d.String(), tag.Repository.String()) {
67 | t.Errorf("Publish() = %v, wanted prefix %v", d, tag.Repository)
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/pkg/resolve/doc.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | // Package resolve defines logic for resolving K8s yaml inputs to ko.
16 | package resolve
17 |
--------------------------------------------------------------------------------
/pkg/resolve/resolve.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package resolve
16 |
17 | import (
18 | "context"
19 | "fmt"
20 | "strings"
21 | "sync"
22 |
23 | "github.com/dprotaso/go-yit"
24 | "github.com/google/ko/pkg/build"
25 | "github.com/google/ko/pkg/publish"
26 | "golang.org/x/sync/errgroup"
27 | "gopkg.in/yaml.v3"
28 | )
29 |
30 | // ImageReferences resolves supported references to images within the input yaml
31 | // to published image digests.
32 | //
33 | // If a reference can be built and pushed, its yaml.Node will be mutated.
34 | func ImageReferences(ctx context.Context, docs []*yaml.Node, builder build.Interface, publisher publish.Interface) error {
35 | // First, walk the input objects and collect a list of supported references
36 | refs := make(map[string][]*yaml.Node)
37 |
38 | for _, doc := range docs {
39 | it := refsFromDoc(doc)
40 |
41 | for node, ok := it(); ok; node, ok = it() {
42 | ref := strings.TrimSpace(node.Value)
43 |
44 | if err := builder.IsSupportedReference(ref); err != nil {
45 | return fmt.Errorf("found strict reference but %s is not a valid import path: %w", ref, err)
46 | }
47 |
48 | refs[ref] = append(refs[ref], node)
49 | }
50 | }
51 |
52 | // Next, perform parallel builds for each of the supported references.
53 | var sm sync.Map
54 | var errg errgroup.Group
55 | for ref := range refs {
56 | ref := ref
57 | errg.Go(func() error {
58 | img, err := builder.Build(ctx, ref)
59 | if err != nil {
60 | return err
61 | }
62 | digest, err := publisher.Publish(ctx, img, ref)
63 | if err != nil {
64 | return err
65 | }
66 | sm.Store(ref, digest.String())
67 | return nil
68 | })
69 | }
70 | if err := errg.Wait(); err != nil {
71 | return err
72 | }
73 |
74 | // Walk the tags and update them with their digest.
75 | for ref, nodes := range refs {
76 | digest, ok := sm.Load(ref)
77 |
78 | if !ok {
79 | return fmt.Errorf("resolved reference to %q not found", ref)
80 | }
81 |
82 | for _, node := range nodes {
83 | node.Value = digest.(string)
84 | }
85 | }
86 |
87 | return nil
88 | }
89 |
90 | func refsFromDoc(doc *yaml.Node) yit.Iterator {
91 | it := yit.FromNode(doc).
92 | RecurseNodes().
93 | Filter(yit.StringValue)
94 |
95 | return it.Filter(yit.WithPrefix(build.StrictScheme))
96 | }
97 |
--------------------------------------------------------------------------------
/test/build-configs/.ko.yaml:
--------------------------------------------------------------------------------
1 | # Copyright 2021 ko Build Authors All Rights Reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | builds:
16 | - id: foo-app
17 | dir: ./foo
18 | main: ./cmd
19 | flags: -v -v # build.Config parser must handle shorthand syntax
20 | - id: bar-app
21 | dir: ./bar
22 | main: ./cmd
23 | - id: toolexec
24 | dir: ./toolexec
25 | main: ./cmd
26 | flags:
27 | - -toolexec
28 | - go
29 | - id: caps-app
30 | dir: ./caps
31 | main: ./cmd
32 |
--------------------------------------------------------------------------------
/test/build-configs/bar/cmd/main.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package main
16 |
17 | import "fmt"
18 |
19 | func main() {
20 | fmt.Println("bar")
21 | }
22 |
--------------------------------------------------------------------------------
/test/build-configs/bar/go.mod:
--------------------------------------------------------------------------------
1 | // Copyright 2021 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | module example.com/bar
16 |
17 | go 1.16
18 |
--------------------------------------------------------------------------------
/test/build-configs/caps.ko.yaml:
--------------------------------------------------------------------------------
1 | # Copyright 2024 ko Build Authors All Rights Reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | builds:
16 | - id: caps-app-with-caps
17 | dir: ./caps
18 | main: ./cmd
19 | linux_capabilities: net_bind_service chown
20 |
--------------------------------------------------------------------------------
/test/build-configs/caps/cmd/main.go:
--------------------------------------------------------------------------------
1 | // Copyright 2024 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package main
16 |
17 | import (
18 | "fmt"
19 | "io/ioutil"
20 | "os"
21 | "strconv"
22 | "strings"
23 | )
24 |
25 | func permittedCaps() (uint64, error) {
26 | data, err := ioutil.ReadFile("/proc/self/status")
27 | if err != nil {
28 | return 0, err
29 | }
30 | const prefix = "CapPrm:"
31 | for _, line := range strings.Split(string(data), "\n") {
32 | if strings.HasPrefix(line, prefix) {
33 | return strconv.ParseUint(strings.TrimSpace(line[len(prefix):]), 16, 64)
34 | }
35 | }
36 | return 0, fmt.Errorf("didn't find %#v in /proc/self/status", prefix)
37 | }
38 |
39 | func main() {
40 | caps, err := permittedCaps()
41 | if err != nil {
42 | fmt.Println(err)
43 | os.Exit(1)
44 | }
45 | if caps == 0 {
46 | fmt.Println("No capabilities")
47 | } else {
48 | fmt.Printf("Has capabilities (%x)\n", caps)
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/test/build-configs/caps/go.mod:
--------------------------------------------------------------------------------
1 | // Copyright 2024 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | module example.com/caps
16 |
17 | go 1.16
18 |
--------------------------------------------------------------------------------
/test/build-configs/foo/cmd/main.go:
--------------------------------------------------------------------------------
1 | // Copyright 2021 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package main
16 |
17 | import "fmt"
18 |
19 | func main() {
20 | fmt.Println("foo")
21 | }
22 |
--------------------------------------------------------------------------------
/test/build-configs/foo/go.mod:
--------------------------------------------------------------------------------
1 | // Copyright 2021 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | module example.com/foo
16 |
17 | go 1.16
18 |
--------------------------------------------------------------------------------
/test/build-configs/toolexec/cmd/main.go:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package main
16 |
17 | import "fmt"
18 |
19 | func main() {
20 | fmt.Println("toolexec")
21 | }
22 |
--------------------------------------------------------------------------------
/test/build-configs/toolexec/go.mod:
--------------------------------------------------------------------------------
1 | // Copyright 2022 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | module example.com/toolexec
16 |
17 | go 1.16
18 |
--------------------------------------------------------------------------------
/test/kodata/a:
--------------------------------------------------------------------------------
1 | hello
2 |
--------------------------------------------------------------------------------
/test/kodata/b:
--------------------------------------------------------------------------------
1 | a
--------------------------------------------------------------------------------
/test/kodata/kenobi:
--------------------------------------------------------------------------------
1 | Hello there
2 |
--------------------------------------------------------------------------------
/test/main.go:
--------------------------------------------------------------------------------
1 | // Copyright 2018 ko Build Authors All Rights Reserved.
2 | //
3 | // Licensed under the Apache License, Version 2.0 (the "License");
4 | // you may not use this file except in compliance with the License.
5 | // You may obtain a copy of the License at
6 | //
7 | // http://www.apache.org/licenses/LICENSE-2.0
8 | //
9 | // Unless required by applicable law or agreed to in writing, software
10 | // distributed under the License is distributed on an "AS IS" BASIS,
11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | // See the License for the specific language governing permissions and
13 | // limitations under the License.
14 |
15 | package main
16 |
17 | import (
18 | "flag"
19 | "fmt"
20 | "log"
21 | "os"
22 | "os/signal"
23 | "path/filepath"
24 | "runtime"
25 | "syscall"
26 | "time"
27 |
28 | // Give this an interesting import
29 | _ "github.com/google/go-containerregistry/pkg/registry"
30 | )
31 |
32 | var (
33 | f = flag.String("f", "kenobi", "File in kodata to print")
34 | wait = flag.Bool("wait", false, "Whether to wait for SIGTERM")
35 | )
36 |
37 | // This is defined so we can test build-time variable setting using ldflags.
38 | var version = "default"
39 |
40 | func main() {
41 | flag.Parse()
42 |
43 | log.Println("version =", version)
44 |
45 | if runtime.GOOS == "windows" {
46 | // Go seems to not load location data from Windows, so timezone
47 | // conversion fails unless tzdata is embedded in the Go program
48 | // with the go build tag `timetzdata`. Since we want to test
49 | // loading tzdata provided by the base image below, we'll just
50 | // skip that for Windows here.
51 | // See https://github.com/ko-build/ko/issues/739
52 | log.Println("skipping timezone conversion on Windows")
53 | } else {
54 | // Exercise timezone conversions, which demonstrates tzdata is provided
55 | // by the base image.
56 | now := time.Now()
57 | loc, _ := time.LoadLocation("UTC")
58 | fmt.Printf("UTC Time: %s\n", now.In(loc))
59 | loc, _ = time.LoadLocation("America/New_York")
60 | fmt.Printf("New York Time: %s\n", now.In(loc))
61 | }
62 |
63 | dp := os.Getenv("KO_DATA_PATH")
64 | file := filepath.Join(dp, *f)
65 | bytes, err := os.ReadFile(file)
66 | if err != nil {
67 | log.Fatalf("Error reading %q: %v", file, err)
68 | }
69 | fmt.Println(string(bytes))
70 |
71 | // Cause the pod to "hang" to allow us to check for a readiness state.
72 | if *wait {
73 | sigs := make(chan os.Signal, 1)
74 | signal.Notify(sigs, syscall.SIGTERM)
75 | <-sigs
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/test/test.yaml:
--------------------------------------------------------------------------------
1 | # Copyright 2018 ko Build Authors All Rights Reserved.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # http://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 | apiVersion: v1
15 | kind: Pod
16 | metadata:
17 | name: kodata
18 | spec:
19 | containers:
20 | - name: obiwan
21 | image: ko://github.com/google/ko/test
22 | args:
23 | - --wait=true
24 | restartPolicy: Never
25 |
--------------------------------------------------------------------------------