[
  {
    "path": ".github/dependabot.yml",
    "content": "version: 2\nupdates:\n  - package-ecosystem: \"gomod\"\n    directory: \"/\"\n    schedule:\n      interval: \"daily\"\n  - package-ecosystem: github-actions\n    directory: /\n    schedule:\n      interval: \"daily\"\n"
  },
  {
    "path": ".github/workflows/analyze.yaml",
    "content": "name: Analyze\n\non:\n  workflow_dispatch:\n  push:\n    branches: ['main']\n  pull_request:\n    branches: ['main']\n\npermissions: {}\n\njobs:\n  analyze:\n    name: Analyze\n    runs-on: ubuntu-latest\n\n    permissions:\n      security-events: write\n\n    steps:\n    - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n      with:\n        # We must fetch at least the immediate parents so that if this is\n        # a pull request then we can checkout the head.\n        fetch-depth: 2\n        persist-credentials: false\n\n    - uses: github/codeql-action/init@df409f7d9260372bd5f19e5b04e83cb3c43714ae # v3.27.9\n      with:\n        languages: go\n\n    - uses: github/codeql-action/autobuild@df409f7d9260372bd5f19e5b04e83cb3c43714ae # v3.27.9\n\n    - uses: github/codeql-action/analyze@df409f7d9260372bd5f19e5b04e83cb3c43714ae # v3.27.9\n"
  },
  {
    "path": ".github/workflows/boilerplate.yaml",
    "content": "name: Boilerplate\n\non:\n  pull_request:\n    branches: ['main']\n\npermissions: {}\n\njobs:\n  check:\n    name: Boilerplate Check\n    runs-on: ubuntu-latest\n\n    permissions:\n      contents: read\n\n    strategy:\n      fail-fast: false # Keep running if one leg fails.\n      matrix:\n        extension:\n        - go\n        - sh\n\n        # Map between extension and human-readable name.\n        include:\n        - extension: go\n          language: Go\n        - extension: sh\n          language: Bash\n\n    steps:\n      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n        with:\n          persist-credentials: false\n\n      - uses: chainguard-dev/actions/boilerplate@e82b4e5ae10182af72972addcb3fedf7454621c8 # main\n        with:\n          extension: ${{ matrix.extension }}\n          language: ${{ matrix.language }}\n"
  },
  {
    "path": ".github/workflows/build.yaml",
    "content": "name: Build\n\non:\n  pull_request:\n    branches:\n      - \"main\"\n\npermissions: {}\n\njobs:\n  build:\n    name: Build\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n      - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0\n        with:\n          go-version: \"1.24\"\n          check-latest: true\n\n      - uses: golang/govulncheck-action@b625fbe08f3bccbe446d94fbf87fcc875a4f50ee # v1.0.4\n        with:\n          go-version-input: \"1.25\"\n\n      - run: |\n          go build ./...\n          go test -run=^$ ./...\n"
  },
  {
    "path": ".github/workflows/donotsubmit.yaml",
    "content": "name: Do Not Submit\n\non:\n  pull_request:\n    branches: ['main']\n\npermissions: {}\n\njobs:\n  donotsubmit:\n    name: Do Not Submit\n    runs-on: ubuntu-latest\n\n    permissions:\n      contents: read\n\n    steps:\n    - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n      with:\n        persist-credentials: false\n\n    - uses: chainguard-dev/actions/donotsubmit@84c993eaf02da1c325854fb272a4df9184bd80fc # main\n"
  },
  {
    "path": ".github/workflows/e2e.yaml",
    "content": "name: Basic e2e test\n\non:\n  pull_request:\n    branches:\n      - 'main'\n\npermissions: {}\n\njobs:\n  e2e:\n    strategy:\n      fail-fast: false\n      matrix:\n        platform:\n        - ubuntu-latest\n        - windows-latest\n\n    name: e2e ${{ matrix.platform }}\n    runs-on: ${{ matrix.platform }}\n\n    permissions:\n      contents: read\n\n    steps:\n    - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n      with:\n        persist-credentials: false\n\n    - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0\n      with:\n        go-version-file: 'go.mod'\n        check-latest: true\n\n    - name: Build and run ko container\n      env:\n        KO_DOCKER_REPO: ko.local\n      shell: bash\n      run: |\n        set -o errexit\n        set -o nounset\n        set -o pipefail\n        set -x\n\n        # Check that building without push prints the tag (and sha)\n        KO_DOCKER_REPO=\"\" go run ./ build --push=false ./test | grep \":latest@sha256:\"\n        KO_DOCKER_REPO=\"\" go run ./ build --push=false -t test ./test | grep \":test@sha256:\"\n        KO_DOCKER_REPO=\"\" go run ./ build --push=false -t test --tag-only ./test | grep \":test$\"\n\n        # Check that using sbom-dir works.\n        KO_DOCKER_REPO=\"\"  go run ./ build -t test --push=false --sbom-dir ./sbom-data ./test\n        jq . ./sbom-data/test-linux-amd64.spdx.json\n\n        # Check that using sbom-dir works for multi-arch\n        KO_DOCKER_REPO=\"\"  go run ./ build --platform=linux/amd64,linux/arm64 -t test --push=false --sbom-dir ./sbom-data2 ./test\n        jq . ./sbom-data2/test-index.spdx.json\n        jq . ./sbom-data2/test-linux-amd64.spdx.json\n        jq . ./sbom-data2/test-linux-arm64.spdx.json\n\n        export PLATFORM=$(go env GOOS)/$(go env GOARCH)\n\n        if [[ \"${{ matrix.platform }}\" == \"windows-latest\" ]]; then\n          OSVERSION=\"10.0.20348\"\n          PLATFORM=${PLATFORM}:${OSVERSION}\n          export KO_DEFAULTBASEIMAGE=mcr.microsoft.com/windows/nanoserver:ltsc2022\n        else\n          # Explicitly test multiple platform builds (a subset of what's in the base!)\n          export PLATFORM=${PLATFORM},linux/arm64\n        fi\n\n        echo platform is ${PLATFORM}\n        # Build and run the ko binary, which should be runnable.\n        docker run $(go run ./ build ./ --platform=${PLATFORM} --preserve-import-paths) version\n\n        # Build and run the test/ binary, which should log \"Hello there\" served from KO_DATA_PATH\n        testimg=$(go run ./ build ./test --platform=${PLATFORM} --preserve-import-paths)\n        docker run ${testimg} --wait=false 2>&1 | tee >(grep \"Hello there\") # Log all output too.\n\n        # Check that symlinks in kodata are chased.\n        # Skip this test on Windows.\n        if [[ \"$RUNNER_OS\" == \"Linux\" ]]; then\n          docker run ${testimg} --wait=false -f b\n        fi\n\n        # Check that using ldflags to set variables works.\n        cat > .ko.yaml << EOF\n        builds:\n        - id: test\n          main: ./test/\n          ldflags:\n          - \"-X main.version=${{ github.sha }}\"\n        EOF\n        docker run $(go run ./ build ./test/ --platform=${PLATFORM}) --wait=false 2>&1 | grep \"${{ github.sha }}\"\n\n        # TODO: check why it is failing when building for windows\n        if [[ \"${{ matrix.platform }}\" != \"windows-latest\" ]]; then\n          # Check that --debug adds dlv to the image, and that dlv is runnable.\n          docker run --entrypoint=\"dlv\" $(go run ./ build ./test/ --platform=${PLATFORM} --debug) version | grep \"Delve Debugger\"\n        fi\n"
  },
  {
    "path": ".github/workflows/image.yaml",
    "content": "name: image\n\non:\n  push:\n    branches:\n      - 'main'\n  workflow_dispatch:\n\npermissions: {}\n\njobs:\n  image:\n    runs-on: ubuntu-latest\n\n    permissions:\n      contents: read\n      packages: write\n      id-token: write\n\n    steps:\n      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n        with:\n          persist-credentials: false\n\n      - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0\n        with:\n          go-version-file: 'go.mod'\n          check-latest: true\n      - uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0\n\n      # Build ko from HEAD, build and push an image tagged with the commit SHA,\n      # then keylessly sign it with cosign.\n      - name: Publish and sign image\n        env:\n          KO_DOCKER_REPO: ghcr.io/${{ github.repository }}\n        run: |\n          go build ./\n          echo \"${{ github.token }}\" | ./ko login ghcr.io --username \"${{ github.actor }}\" --password-stdin\n          img=$(./ko build --bare --platform=all -t latest -t ${{ github.sha }} ./)\n          echo \"built ${img}\"\n          cosign sign ${img} --yes \\\n              -a sha=${{ github.sha }} \\\n              -a run_id=${{ github.run_id }} \\\n              -a run_attempt=${{ github.run_attempt }}\n"
  },
  {
    "path": ".github/workflows/kind-e2e.yaml",
    "content": "name: KinD e2e tests\n\non:\n  workflow_dispatch:  # Allow manual runs.\n  pull_request:\n    branches:\n      - 'main'\n\npermissions: {}\n\njobs:\n  e2e-tests:\n    name: e2e tests\n    runs-on: ubuntu-latest\n    env:\n      # https://github.com/google/go-containerregistry/pull/125 allows insecure registry for\n      # '*.local' hostnames. This works both for `ko` and our own tag-to-digest resolution logic,\n      # thus allowing us to test without bypassing tag-to-digest resolution.\n      REGISTRY_NAME: registry.local\n      REGISTRY_PORT: 5000\n      KO_DOCKER_REPO: registry.local:5000/ko\n\n    permissions:\n      contents: read\n\n    steps:\n    - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n      with:\n        persist-credentials: false\n\n    - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0\n      with:\n        go-version-file: 'go.mod'\n        check-latest: true\n\n    - name: Install ko\n      run: go install ./\n\n    - name: Setup Cluster\n      uses: chainguard-dev/actions/setup-kind@29fb6e979a0b3efc79748a17e8cec08d0594cbfd # main\n      with:\n        k8s-version: v1.28.x\n        registry-authority: ${{ env.REGISTRY_NAME }}:${{ env.REGISTRY_PORT }}\n\n    - name: Install Cosign\n      uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0\n\n    - name: Run Smoke Test\n      run: |\n        # Test with kind load\n        KO_DOCKER_REPO=kind.local ko apply --platform=all -f ./test\n        kubectl wait --timeout=10s --for=condition=Ready pod/kodata\n        kubectl delete pod kodata\n\n        # Test with registry\n        ko apply --platform=all -f ./test\n        kubectl wait --timeout=60s --for=condition=Ready pod/kodata\n        kubectl delete pod kodata\n\n        # Test ko run with kind load\n        # This tests that --labels are passed to kubectl, and -wait is passed to the test binary.\n        KO_DOCKER_REPO=kind.local ko run ./test -- --labels=foo=bar -- -wait=false\n\n    - name: Check SBOM\n      run: |\n        set -o pipefail\n\n        echo '::group:: SBOM'\n        cosign download sbom $(ko build ./test)\n        echo \"${SBOM}\"\n        echo '::endgroup::'\n\n    - name: Collect diagnostics and upload\n      if: ${{ failure() }}\n      uses: chainguard-dev/actions/kind-diag@29fb6e979a0b3efc79748a17e8cec08d0594cbfd # main\n"
  },
  {
    "path": ".github/workflows/modules-integration-test.yaml",
    "content": "name: Integration Test\n\non:\n  pull_request:\n    branches:\n      - 'main'\n\npermissions: {}\n\njobs:\n  test:\n    name: Module Tests\n    runs-on: 'ubuntu-latest'\n\n    permissions:\n      contents: read\n\n    steps:\n      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n        with:\n          persist-credentials: false\n\n      - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0\n        with:\n          go-version: '1.24'\n          check-latest: true\n\n      - env:\n          GOPATH: does not matter\n        run: ./integration_test.sh\n"
  },
  {
    "path": ".github/workflows/publish-site.yaml",
    "content": "name: publish\non:\n  workflow_dispatch:\n  push:\n    branches:\n      - 'main'\n\npermissions: {}\n\njobs:\n  publish:\n    runs-on: ubuntu-latest\n\n    permissions:\n      contents: write\n      pages: write\n\n    steps:\n      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n      - uses: actions/setup-python@0b93645e9fea7318ecaed2b359559ac225c90a2b # v5.3.0\n        with:\n          python-version: 3.x\n      - run: pip install mkdocs-material mkdocs-redirects\n      - run: mkdocs gh-deploy --force\n"
  },
  {
    "path": ".github/workflows/registries.yaml",
    "content": "name: Push to registries\n\non:\n  push:\n    branches:\n      - 'main'\n\n  workflow_dispatch:  # Allow manual runs.\n\npermissions: {}\n\njobs:\n  quay:\n    name: Push to quay.io\n    runs-on: ubuntu-latest\n\n    permissions:\n      contents: read\n\n    steps:\n    - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n      with:\n        persist-credentials: false\n\n    - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0\n      with:\n        go-version-file: 'go.mod'\n        check-latest: true\n\n    - env:\n        QUAY_USERNAME: ko-testing+test\n        QUAY_PASSWORD: ${{ secrets.QUAY_PASSWORD }}\n        KO_DOCKER_REPO: quay.io/ko-testing/test\n      run: |\n        echo ${QUAY_PASSWORD} | go run ./ login --username=${QUAY_USERNAME} --password-stdin quay.io\n        go run ./ build --platform=all ./test/ --sbom=none --bare\n\n  dockerhub:\n    name: Push to dockerhub\n    runs-on: ubuntu-latest\n\n    permissions:\n      contents: read\n\n    steps:\n    - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n      with:\n        persist-credentials: false\n\n    - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0\n      with:\n        go-version-file: 'go.mod'\n        check-latest: true\n\n    - env:\n        DOCKERHUB_USERNAME: kotesting\n        DOCKERHUB_PASSWORD: ${{ secrets.DOCKERHUB_PASSWORD }}\n        KO_DOCKER_REPO: kotesting/test\n      run: |\n        echo ${DOCKERHUB_PASSWORD} | go run ./ login --username=${DOCKERHUB_USERNAME} --password-stdin index.docker.io\n        go run ./ build --platform=all ./test/ --bare\n\n  ecr:\n    name: Push to ECR\n    runs-on: ubuntu-latest\n    env:\n      # This is an AWS account that Chainguard provides to enable\n      # go-containerregistry and ko to test ECR support.\n      AWS_ACCOUNT: 479305788615\n      AWS_REGION: us-west-2\n      REPOSITORY: ko-ecr-e2e-testing\n\n    permissions:\n      # This lets us clone the repo\n      contents: read\n      # This lets us mint identity tokens for federation with AWS.\n      id-token: write\n\n    steps:\n      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n        with:\n          persist-credentials: false\n\n      - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0\n        with:\n          go-version-file: 'go.mod'\n          check-latest: true\n\n      - name: Install ko\n        run: go install .\n\n      - name: Configure AWS Credentials\n        uses: aws-actions/configure-aws-credentials@e3dd6a429d7300a6a4c196c26e071d42e0343502 # v4.0.2\n        with:\n          role-to-assume: arn:aws:iam::${{ env.AWS_ACCOUNT }}:role/federated-ecr-readwrite\n          aws-region: ${{ env.AWS_REGION }}\n\n      - name: Test ko build\n        run: |\n          export KO_DOCKER_REPO=${{ env.AWS_ACCOUNT }}.dkr.ecr.${{ env.AWS_REGION }}.amazonaws.com/${{ env.REPOSITORY }}\n\n          ko build --bare ./test\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: goreleaser\n\non:\n  push:\n    tags:\n      - '*'\n\npermissions: {}\n\njobs:\n  goreleaser:\n    outputs:\n      hashes: ${{ steps.hash.outputs.hashes }}\n      tag_name: ${{ steps.tag.outputs.tag_name }}\n\n    permissions:\n      packages: write\n      id-token: write\n      contents: write\n\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n\n      - run: git fetch --prune --unshallow\n\n      - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0\n        with:\n          go-version-file: 'go.mod'\n          check-latest: true\n\n      # This installs the current latest release. We have to manually bump this.\n      - uses: ko-build/setup-ko@3aebd0597dc1e9d1a26bcfdb7cbeb19c131d3037 # v0.7\n        with:\n          version: v0.18.0 # DO NOT REMOVE THIS OR IT WILL CREATE A CYCLE.\n\n      - uses: imjasonh/setup-crane@31b88efe9de28ae0ffa220711af4b60be9435f6e # v0.4\n\n      - uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0\n\n      - name: Set tag output\n        id: tag\n        run: echo \"tag_name=${GITHUB_REF#refs/*/}\" >> \"$GITHUB_OUTPUT\"\n\n      - uses: goreleaser/goreleaser-action@9ed2f89a662bf1735a48bc8557fd212fa902bebf # v6.1.0\n        id: run-goreleaser\n        with:\n          version: latest\n          args: release --clean\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: sign ko-image\n        run: |\n          digest=$(crane digest \"${REGISTRY}\":\"${GIT_TAG}\")\n          cosign sign --yes \\\n              -a GIT_HASH=\"${GIT_HASH}\" \\\n              -a GIT_TAG=\"${GIT_TAG}\" \\\n              -a RUN_ID=\"${RUN_ID}\" \\\n              -a RUN_ATTEMPT=\"${RUN_ATTEMPT}\" \\\n              \"${REGISTRY}@${digest}\"\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          GIT_HASH: ${{ github.sha }}\n          GIT_TAG: ${{ steps.tag.outputs.tag_name }}\n          RUN_ATTEMPT: ${{ github.run_attempt }}\n          RUN_ID: ${{ github.run_id }}\n          REGISTRY: \"ghcr.io/${{ github.repository }}\"\n\n      - name: Generate subject\n        id: hash\n        env:\n          ARTIFACTS: \"${{ steps.run-goreleaser.outputs.artifacts }}\"\n        run: |\n          set -euo pipefail\n\n          checksum_file=$(echo \"$ARTIFACTS\" | jq -r '.[] | select (.type==\"Checksum\") | .path')\n          echo \"hashes=$(cat $checksum_file | base64 -w0)\" >> \"$GITHUB_OUTPUT\"\n\n  provenance:\n    needs:\n      - goreleaser\n\n    permissions:\n      actions: read   # To read the workflow path.\n      id-token: write # To sign the provenance.\n      contents: write # To add assets to a release.\n\n    uses: slsa-framework/slsa-github-generator/.github/workflows/generator_generic_slsa3.yml@v2.0.0\n    with:\n      base64-subjects: \"${{ needs.goreleaser.outputs.hashes }}\"\n      upload-assets: true\n      upload-tag-name: \"${{ needs.goreleaser.outputs.tag_name }}\"\n\n  verification:\n    needs:\n      - goreleaser\n      - provenance\n\n    runs-on: ubuntu-latest\n    permissions: read-all\n\n    steps:\n      # Note: this will be replaced with the GHA in the future.\n      - name: Install the verifier\n        env:\n          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        run: |\n          set -euo pipefail\n\n          gh -R slsa-framework/slsa-verifier release download v1.3.2 -p \"slsa-verifier-linux-amd64\"\n          chmod ug+x slsa-verifier-linux-amd64\n          # Note: see https://github.com/slsa-framework/slsa-verifier/blob/main/SHA256SUM.md\n          COMPUTED_HASH=$(sha256sum slsa-verifier-linux-amd64 | cut -d ' ' -f1)\n          EXPECTED_HASH=\"b1d6c9bbce6274e253f0be33158cacd7fb894c5ebd643f14a911bfe55574f4c0\"\n          if [[ \"$EXPECTED_HASH\" != \"$COMPUTED_HASH\" ]];then\n              echo \"error: expected $EXPECTED_HASH, computed $COMPUTED_HASH\"\n              exit 1\n          fi\n\n      - name: Download assets\n        env:\n          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          PROVENANCE: \"${{ needs.provenance.outputs.provenance-name }}\"\n        run: |\n          set -euo pipefail\n\n          gh -R \"$GITHUB_REPOSITORY\" release download \"$GITHUB_REF_NAME\" -p \"*.tar.gz\"\n          gh -R \"$GITHUB_REPOSITORY\" release download \"$GITHUB_REF_NAME\" -p \"$PROVENANCE\"\n\n      - name: Verify assets\n        env:\n          CHECKSUMS: ${{ needs.goreleaser.outputs.hashes }}\n          PROVENANCE: \"${{ needs.provenance.outputs.provenance-name }}\"\n        run: |\n          set -euo pipefail\n\n          checksums=$(echo \"$CHECKSUMS\" | base64 -d)\n          while read -r line; do\n              fn=$(echo $line | cut -d ' ' -f2)\n\n              echo \"Verifying $fn\"\n              ./slsa-verifier-linux-amd64 -artifact-path \"$fn\" \\\n                                      -provenance \"$PROVENANCE\" \\\n                                      -source \"github.com/$GITHUB_REPOSITORY\" \\\n                                      -tag \"$GITHUB_REF_NAME\"\n\n          done <<<\"$checksums\"\n"
  },
  {
    "path": ".github/workflows/sbom.yaml",
    "content": "name: Validate SBOMs\n\non:\n  pull_request:\n    branches:\n      - 'main'\n\nenv:\n  SPDX_TOOLS_VERSION: 1.1.0\n\npermissions: {}\n\njobs:\n  spdx:\n    name: Validate SPDX SBOM\n    runs-on: ubuntu-latest\n\n    permissions:\n      contents: read\n\n    env:\n      KO_DOCKER_REPO: localhost:1338\n\n    steps:\n      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n        with:\n          persist-credentials: false\n\n      - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0\n        with:\n          go-version-file: 'go.mod'\n          check-latest: true\n\n      - uses: chainguard-dev/actions/setup-registry@main\n      - uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0\n\n      - name: Install SPDX Tools\n        run: |\n          wget https://github.com/spdx/tools-java/releases/download/v${SPDX_TOOLS_VERSION}/tools-java-${SPDX_TOOLS_VERSION}.zip\n          unzip tools-java-${SPDX_TOOLS_VERSION}.zip\n\n      - name: Generate and Validate\n        run: |\n          cosign download sbom $(go run ./ build) | tee spdx.json\n          java -jar ./tools-java-${SPDX_TOOLS_VERSION}-jar-with-dependencies.jar Verify spdx.json\n\n      - uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0\n        if: ${{ always() }}\n        with:\n          name: spdx.json\n          path: spdx.json\n\n  spdx-multi-arch:\n    name: Validate SPDX multi-arch SBOM\n    runs-on: ubuntu-latest\n\n    env:\n      KO_DOCKER_REPO: localhost:1338\n\n    permissions:\n      contents: read\n\n    steps:\n      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n        with:\n          persist-credentials: false\n\n      - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0\n        with:\n          go-version-file: 'go.mod'\n          check-latest: true\n      - uses: chainguard-dev/actions/setup-registry@main\n      - uses: sigstore/cosign-installer@dc72c7d5c4d10cd6bcb8cf6e3fd625a9e5e537da # v3.7.0\n\n      - name: Install SPDX Tools\n        run: |\n          wget https://github.com/spdx/tools-java/releases/download/v${SPDX_TOOLS_VERSION}/tools-java-${SPDX_TOOLS_VERSION}.zip\n          unzip tools-java-${SPDX_TOOLS_VERSION}.zip\n\n      - name: Generate and Validate\n        run: |\n          img=$(go run ./ build --platform=linux/amd64,linux/arm64)\n          cosign download sbom $img | tee spdx-multi-arch.json\n\n          java -jar ./tools-java-${SPDX_TOOLS_VERSION}-jar-with-dependencies.jar Verify spdx-multi-arch.json\n\n      - uses: actions/upload-artifact@6f51ac03b9356f520e9adb1b1b7802705f340c2b # v4.5.0\n        if: ${{ always() }}\n        with:\n          name: spdx-multi-arch.json\n          path: spdx-multi-arch.json\n"
  },
  {
    "path": ".github/workflows/stale.yaml",
    "content": "name: 'Close stale'\n\non:\n  schedule:\n  - cron: '0 1 * * *'\n\npermissions: {}\n\njobs:\n  stale:\n    runs-on: 'ubuntu-latest'\n\n    permissions:\n      issues: write\n      pull-requests: write\n\n    steps:\n    - uses: actions/stale@28ca1036281a5e5922ead5184a1bbf96e5fc984e # v9.0.0\n      with:\n        repo-token: ${{ secrets.GITHUB_TOKEN }}\n\n        stale-issue-message: |-\n          This issue is stale because it has been open for 90 days with no\n          activity. It will automatically close after 30 more days of\n          inactivity. Keep fresh with the 'lifecycle/frozen' label.\n        stale-issue-label: 'lifecycle/stale'\n        exempt-issue-labels: 'lifecycle/frozen'\n\n        stale-pr-message: |-\n          This Pull Request is stale because it has been open for 90 days with\n          no activity. It will automatically close after 30 more days of\n          inactivity. Keep fresh with the 'lifecycle/frozen' label.\n        stale-pr-label: 'lifecycle/stale'\n        exempt-pr-labels: 'lifecycle/frozen'\n\n        days-before-stale: 90\n        days-before-close: 30\n"
  },
  {
    "path": ".github/workflows/style.yaml",
    "content": "name: Code Style\n\non:\n  pull_request:\n    branches:\n      - 'main'\n\npermissions: {}\n\njobs:\n  gofmt:\n    name: check gofmt\n    runs-on: ubuntu-latest\n\n    permissions:\n      contents: read\n\n    steps:\n      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n        with:\n          persist-credentials: false\n\n      - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0\n        with:\n          go-version-file: 'go.mod'\n          check-latest: true\n\n      - uses: chainguard-dev/actions/gofmt@d886686603afb809f7ef9b734b333e20b7ce5cda\n        with:\n          args: -s\n\n  goimports:\n    name: check goimports\n    runs-on: ubuntu-latest\n\n    permissions:\n      contents: read\n\n    steps:\n      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n        with:\n          persist-credentials: false\n\n      - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0\n        with:\n          go-version-file: 'go.mod'\n          check-latest: true\n\n      - uses: chainguard-dev/actions/goimports@d886686603afb809f7ef9b734b333e20b7ce5cda\n\n  lint:\n    name: Lint\n    runs-on: ubuntu-latest\n\n    permissions:\n      contents: read\n\n    steps:\n      - name: Check out code\n        uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n        with:\n          persist-credentials: false\n\n      - name: Set up Go\n        uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0\n        with:\n          go-version-file: 'go.mod'\n          check-latest: true\n\n      - uses: chainguard-dev/actions/trailing-space@d886686603afb809f7ef9b734b333e20b7ce5cda\n        if: ${{ always() }}\n\n      - uses: chainguard-dev/actions/eof-newline@d886686603afb809f7ef9b734b333e20b7ce5cda\n        if: ${{ always() }}\n\n      - uses: reviewdog/action-misspell@18ffb61effb93b47e332f185216be7e49592e7e1 # v1.26.1\n        if: ${{ always() }}\n        with:\n          github_token: ${{ secrets.GITHUB_TOKEN }}\n          fail_level: warning\n          locale: \"US\"\n          exclude: |\n            ./.golangci.yaml\n\n      - uses: get-woke/woke-action-reviewdog@d71fd0115146a01c3181439ce714e21a69d75e31 # v0\n        if: ${{ always() }}\n        with:\n          github-token: ${{ secrets.GITHUB_TOKEN }}\n          reporter: github-pr-check\n          level: error\n          fail-on-error: true\n"
  },
  {
    "path": ".github/workflows/test.yaml",
    "content": "name: Test\n\non:\n  push:\n    branches:\n      - 'main'\n  pull_request:\n    branches:\n      - 'main'\n\npermissions: {}\n\njobs:\n\n  test:\n    name: Unit Tests\n    runs-on: ubuntu-latest\n\n    permissions:\n      contents: read\n\n    steps:\n      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n        with:\n          persist-credentials: false\n\n      - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0\n        with:\n          go-version-file: 'go.mod'\n          check-latest: true\n\n      - run: go test -coverprofile=coverage.txt -covermode=atomic -race ./...\n"
  },
  {
    "path": ".github/workflows/verify.yaml",
    "content": "name: Verify\n\non:\n  pull_request:\n    branches:\n      - \"main\"\n\npermissions: {}\n\njobs:\n  verify:\n    name: Verify Codegen\n    runs-on: ubuntu-latest\n\n    permissions:\n      contents: read\n\n    steps:\n      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n        with:\n          persist-credentials: false\n\n      - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0\n        with:\n          go-version-file: \"go.mod\"\n          check-latest: true\n\n      - name: Verify\n        run: ./hack/presubmit.sh\n\n  golangci:\n    name: lint\n    runs-on: ubuntu-latest\n\n    permissions:\n      contents: read\n\n    steps:\n      - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2\n        with:\n          persist-credentials: false\n\n      - uses: actions/setup-go@3041bf56c941b39c61721a86cd11f3bb1338122a # v5.2.0\n        with:\n          go-version-file: \"go.mod\"\n          check-latest: true\n\n      - name: golangci-lint\n        uses: golangci/golangci-lint-action@1e7e51e771db61008b38414a730f564565cf7c20 # v9.4.0\n        with:\n          version: v2.7.2\n"
  },
  {
    "path": ".gitignore",
    "content": "# Ignore GoLand (IntelliJ) files.\n.idea/\n\nko\n\n.DS_Store\n\n/vendor/\n"
  },
  {
    "path": ".golangci.yaml",
    "content": "version: \"2\"\nlinters:\n  enable:\n    - asciicheck\n    - errorlint\n    - gosec\n    - importas\n    - misspell\n    - prealloc\n    - revive\n    - staticcheck\n    - tparallel\n    - unconvert\n    - unparam\n    - whitespace\n  disable:\n    - depguard\n    - errcheck\n  settings:\n    gosec:\n      excludes:\n        - G115\n  exclusions:\n    generated: lax\n    presets:\n      - comments\n      - common-false-positives\n      - legacy\n      - std-error-handling\n    rules:\n      - linters:\n          - gosec\n        path: test\n    paths:\n      - third_party$\n      - builtin$\n      - examples$\nformatters:\n  enable:\n    - gofmt\n    - goimports\n  exclusions:\n    generated: lax\n    paths:\n      - third_party$\n      - builtin$\n      - examples$\n"
  },
  {
    "path": ".goreleaser.yml",
    "content": "version: 2\n\nbefore:\n  hooks:\n    - go mod tidy\n    - /bin/bash -c 'if [ -n \"$(git --no-pager diff --exit-code go.mod go.sum)\" ]; then exit 1; fi'\n\nbuilds:\n  - id: binary\n    main: ./main.go\n    env:\n      - CGO_ENABLED=0\n    flags:\n      - -trimpath\n    ldflags:\n      - \"-s -w -X github.com/google/ko/pkg/commands.Version={{.Version}}\"\n    goos:\n      - windows\n      - linux\n      - darwin\n    goarch:\n      - amd64\n      - arm64\n      - s390x\n      - 386\n      - mips64le\n      - ppc64le\n      - riscv64\n\nkos:\n  - id: ko-image\n    build: binary\n    main: .\n    base_image: golang:latest\n    ldflags:\n      - \"-s -w -X github.com/google/ko/pkg/commands.Version={{.Version}}\"\n    platforms:\n      - all\n    tags:\n      - '{{ .Tag }}'\n      - '{{ .FullCommit }}'\n      - latest\n    sbom: spdx\n    bare: true\n    preserve_import_paths: false\n    base_import_paths: false\n\narchives:\n  - id: with-version\n    name_template: >-\n      {{ .ProjectName }}_\n      {{- .Version }}_\n      {{- title .Os }}_\n      {{- if eq .Arch \"amd64\" }}x86_64\n      {{- else if eq .Arch \"386\" }}i386\n      {{- else }}{{ .Arch }}{{ end }}\n  - id: without-version\n    name_template: >-\n      {{ .ProjectName }}_\n      {{- title .Os }}_\n      {{- if eq .Arch \"amd64\" }}x86_64\n      {{- else if eq .Arch \"386\" }}i386\n      {{- else }}{{ .Arch }}{{ end }}\n\nchecksum:\n  name_template: 'checksums.txt'\n\nsnapshot:\n  version_template: \"{{ .Tag }}-next\"\n\nchangelog:\n  sort: asc\n  use: github\n  filters:\n    exclude:\n      - '^docs:'\n      - '^test:'\n"
  },
  {
    "path": ".ko.yaml",
    "content": "baseImageOverrides:\n  github.com/google/ko: golang:latest\n\nbuilds:\n  - id: ko\n    ldflags:\n      - \"{{ .Env.LDFLAGS }}\"\n"
  },
  {
    "path": ".wokeignore",
    "content": "# Uses some Cobra methods\npkg/commands/*\n"
  },
  {
    "path": "CNAME",
    "content": "ko.build"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "# How to Contribute to ko\n\nWe'd love to accept your patches and contributions to this project. There are\njust a few small guidelines you need to follow.\n\n## Code reviews\n\nAll submissions, including submissions by project members, require review. We\nuse GitHub pull requests for this purpose. Consult\n[GitHub Help](https://help.github.com/articles/about-pull-requests/) for more\ninformation on using pull requests.\n\n## Testing\n\nEnsure the following passes:\n```\n./hack/presubmit.sh\n```\nand commit any resultant changes to `go.mod` and `go.sum`. To update any docs\nafter client changes, run:\n\n```\n./hack/update-codegen.sh\n```\n"
  },
  {
    "path": "LICENSE",
    "content": "\n                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License."
  },
  {
    "path": "MAINTAINERS.md",
    "content": "# Maintainers\n\nThis page lists all active members of the maintainers for the `ko` project and any subprojects.\n\n- Jon Johnson (@jonjohnsonjr)\n- Matt Moore (@mattmoor)\n- Jason Hall (@imjasonh)\n\nNew projects and new maintainers can be added or removed with the consensus approval of the current maintainers.\n"
  },
  {
    "path": "README.md",
    "content": "# `ko`: Easy Go Containers\n\n[![GitHub Actions Build Status](https://github.com/ko-build/ko/workflows/Build/badge.svg)](https://github.com/ko-build/ko/actions?query=workflow%3ABuild)\n[![GoDoc](https://godoc.org/github.com/google/ko?status.svg)](https://godoc.org/github.com/google/ko)\n[![Go Report Card](https://goreportcard.com/badge/ko-build/ko)](https://goreportcard.com/report/ko-build/ko)\n[![SLSA 3](https://slsa.dev/images/gh-badge-level3.svg)](https://slsa.dev/images/gh-badge-level3.svg)\n\n<img src=\"./docs/images/demo.png\" width=\"100%\">\n\n---\n\n> 🎉 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)!\n\n`ko` is a simple, fast container image builder for Go applications.\n\nIt's ideal for use cases where your image contains a single Go application\nwithout any/many dependencies on the OS base image (e.g., no cgo, no OS package\ndependencies).\n\n`ko` builds images by effectively executing `go build` on your local machine,\nand as such doesn't require `docker` to be installed. This can make it a good\nfit for lightweight CI/CD use cases.\n\n`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/).\n\n# [Install `ko`](https://ko.build/install/) and [get started](https://ko.build/get-started/)!\n\n### Acknowledgements\n\nThis 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).\nThat work was presented [here](https://www.youtube.com/watch?v=RS1aiQqgUTA).\n\n### Discuss\n\nQuestions? Comments? Ideas?\nCome discuss `ko` with us in the `#ko-build` channel on the [Kubernetes Slack](https://slack.k8s.io)!\nSee you there!\n\n### Community Meetings\n\nYou can find all the necessary details about the community meetings in this [page](https://ko.build/community).\n"
  },
  {
    "path": "ROADMAP.md",
    "content": "# `ko` Project Roadmap\n\n_Last updated October 2022_\n\n- Foster a community of contributors and users\n  - give talks, do outreach, expand the pool of contributors\n  - identify projects that could benefit from using `ko`, and help onboard them\n  - publish case studies from successful migrations\n\n- Integrate [sigstore](https://sigstore.dev) for built artifacts\n  - attach signed SBOMs\n  - attach signed provenance attestations\n  - support the OCI referrers API and [fallback tag scheme](https://github.com/opencontainers/distribution-spec/blob/main/spec.md#referrers-tag-schema)\n  - integrate with CI workload identity (e.g., GitHub OIDC) to keylessly sign artifacts\n\n- Faster builds\n  - identify unnecessary work and avoid it when possible\n\n- Ecosystem integrations\n  - support Terraform provider, and potentially Pulumi and CDK, others\n  - provide working examples of these integrations\n"
  },
  {
    "path": "cmd/help/main.go",
    "content": "// Copyright 2021 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/google/ko/pkg/commands\"\n\t\"github.com/spf13/cobra\"\n\t\"github.com/spf13/cobra/doc\"\n)\n\nvar dir string\nvar root = &cobra.Command{\n\tUse:   \"gendoc\",\n\tShort: \"Generate ko's help docs\",\n\tArgs:  cobra.NoArgs,\n\tRunE: func(*cobra.Command, []string) error {\n\t\treturn doc.GenMarkdownTree(commands.Root, dir)\n\t},\n}\n\nfunc init() {\n\troot.Flags().StringVarP(&dir, \"dir\", \"d\", \".\", \"Path to directory in which to generate docs\")\n}\n\nfunc main() {\n\tif err := root.Execute(); err != nil {\n\t\tfmt.Println(err)\n\t\tos.Exit(1)\n\t}\n}\n"
  },
  {
    "path": "code-of-conduct.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nIn the interest of fostering an open and welcoming environment, we as\ncontributors and maintainers pledge to making participation in our project and\nour community a harassment-free experience for everyone, regardless of age, body\nsize, disability, ethnicity, gender identity and expression, level of\nexperience, education, socio-economic status, nationality, personal appearance,\nrace, religion, or sexual identity and orientation.\n\n## Our Standards\n\nExamples of behavior that contributes to creating a positive environment\ninclude:\n\n- Using welcoming and inclusive language\n- Being respectful of differing viewpoints and experiences\n- Gracefully accepting constructive criticism\n- Focusing on what is best for the community\n- Showing empathy towards other community members\n\nExamples of unacceptable behavior by participants include:\n\n- The use of sexualized language or imagery and unwelcome sexual attention or\n  advances\n- Trolling, insulting/derogatory comments, and personal or political attacks\n- Public or private harassment\n- Publishing others' private information, such as a physical or electronic\n  address, without explicit permission\n- Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Our Responsibilities\n\nProject maintainers are responsible for clarifying the standards of acceptable\nbehavior and are expected to take appropriate and fair corrective action in\nresponse to any instances of unacceptable behavior.\n\nProject maintainers have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, or to ban temporarily or permanently any\ncontributor for other behaviors that they deem inappropriate, threatening,\noffensive, or harmful.\n\n## Scope\n\nThis Code of Conduct applies both within project spaces and in public spaces\nwhen an individual is representing the project or its community. Examples of\nrepresenting a project or community include using an official project e-mail\naddress, posting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event. Representation of a project may be\nfurther defined and clarified by project maintainers.\n\n## Enforcement\n\n<!-- TODO: Need a real CoC contact list. -->\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported by contacting the project team. All complaints will be reviewed and\ninvestigated and will result in a response that is deemed necessary and\nappropriate to the circumstances. The project team is obligated to maintain\nconfidentiality with regard to the reporter of an incident. Further details of\nspecific enforcement policies may be posted separately.\n\nProject maintainers who do not follow or enforce the Code of Conduct in good\nfaith may face temporary or permanent repercussions as determined by other\nmembers of the project's leadership.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 1.4, available at\nhttps://www.contributor-covenant.org/version/1/4/code-of-conduct.html\n\n[homepage]: https://www.contributor-covenant.org\n"
  },
  {
    "path": "docs/CNAME",
    "content": "ko.build\n"
  },
  {
    "path": "docs/README.md",
    "content": "# Docs for https://ko.build\n\n## Development\n\nUpdate `.md` files to update content.\n\nUpdate `mkdocs.yml` to update sidebar headers and ordering.\n\nTo run locally:\n\n- [install `mkdocs` and `mkdocs-material`](https://squidfunk.github.io/mkdocs-material/getting-started/) and run `mkdocs serve`, or\n- `docker run --rm -it -p 8000:8000 -v ${PWD}:/docs squidfunk/mkdocs-material`\n  - on an M1 Mac, use `ghcr.io/afritzler/mkdocs-material` instead.\n\nThis will start a local server on localhost:8000 that autoupdates as you make changes.\n\n## Deployment\n\nWhen PRs are merged, the site will be rebuilt and published automatically.\n\n### Credits\n\nThe site is powered by [mkdocs-material](https://squidfunk.github.io/mkdocs-material). The code and theme are released under the MIT license.\n\nContent is licensed [CC-BY](https://creativecommons.org/licenses/by/4.0/).\n\n"
  },
  {
    "path": "docs/advanced/faq.md",
    "content": "# Frequently Asked Questions\n\n## How can I set `ldflags`?\n\n[Using -ldflags](https://blog.cloudflare.com/setting-go-variables-at-compile-time/) is a common way to embed version info in go binaries (In fact, we do this for `ko`!).\nUnfortunately, because `ko` wraps `go build`, it's not possible to use this flag directly; however, you can use the `GOFLAGS` environment variable instead:\n\n```sh\nGOFLAGS=\"-ldflags=-X=main.version=1.2.3\" ko build .\n```\n\nCurrently, there is a limitation that does not allow to set multiple arguments in `ldflags` using `GOFLAGS`.\nUsing `-ldflags` multiple times also does not work.\nIn this use case, it works best to use the [`builds` section](./../configuration.md) in the `.ko.yaml` file.\n\n## Why are my images all created in 1970?\n\nIn order to support [reproducible builds](https://reproducible-builds.org), `ko` doesn't embed timestamps in the images it produces by default.\n\nHowever, `ko` does respect the [`SOURCE_DATE_EPOCH`](https://reproducible-builds.org/docs/source-date-epoch/) environment variable, which will set the container image's timestamp accordingly.\n\nSimilarly, the `KO_DATA_DATE_EPOCH` environment variable can be used to set the _modtime_ timestamp of the files in `KO_DATA_PATH`.\n\nFor example, you can set the container image's timestamp to the current timestamp by executing:\n\n```sh\nexport SOURCE_DATE_EPOCH=$(date +%s)\n```\n\nor set the timestamp of the files in `KO_DATA_PATH` to the latest git commit's timestamp with:\n\n```sh\nexport KO_DATA_DATE_EPOCH=$(git log -1 --format='%ct')\n```\n\n## Can I build Windows containers?\n\nYes, but support for Windows containers is new, experimental, and tenuous. Be prepared to file bugs. 🐛\n\nThe default base image does not provide a Windows image.\nYou can try out building a Windows container image by [setting the base image](./../configuration.md) to a Windows base image and building with `--platform=windows/amd64` or `--platform=all`:\n\nFor example, to build a Windows container image, update your `.ko.yaml` to set the base image:\n\n```plaintext\ndefaultBaseImage: mcr.microsoft.com/windows/nanoserver:ltsc2022\n```\n\nAnd build for `windows/amd64`.\n\n```sh\nko build ./ --platform=windows/amd64\n```\n\n### Known issues 🐛\n\n- Symlinks in `kodata` are ignored when building Windows images; only regular files and directories will be included in the Windows image.\n\n## Does `ko` support autocompletion?\n\nYes! `ko completion` generates a Bash/Zsh/Fish/PowerShell completion script.\nYou can get how to load it from help document.\n\n```sh\nko completion [bash|zsh|fish|powershell] --help\n```\n\nOr, you can source it directly:\n\n```bash\nsource <(ko completion)\n```\n\n## Does `ko` work with [Kustomize](https://kustomize.io/)?\n\nYes! `ko resolve -f -` will read and process input from stdin, so you can have `ko` easily process the output of the `kustomize` command.\n\n```sh\nkustomize build config | ko resolve -f -\n```\n\n## Does `ko` integrate with other build and development tools?\n\nOh, you betcha. Here's a partial list:\n\n- `ko` support in [Skaffold](https://skaffold.dev/docs/pipeline-stages/builders/ko/)\n- `ko` support for [goreleaser](https://goreleaser.com/customization/ko/)\n- `ko` task in the [Tekton catalog](https://github.com/tektoncd/catalog/tree/main/task/ko/)\n- `ko` support in [Carvel's `kbld`](https://carvel.dev/kbld/docs/latest/config/#ko)\n- `ko` extension for [Tilt](https://github.com/tilt-dev/tilt-extensions/tree/master/ko)\n\n## Does `ko` work with [OpenShift Internal Registry](https://access.redhat.com/documentation/en-us/openshift_container_platform/4.11/html/registry/registry-overview#registry-integrated-openshift-registry_registry-overview)?\n\nYes! Follow these steps:\n\n1. [Connect to your OpenShift installation](https://docs.openshift.com/container-platform/latest/cli_reference/openshift_cli/getting-started-cli.html#cli-logging-in_cli-developer-commands)\n1. [Expose the OpenShift Internal Registry](https://docs.openshift.com/container-platform/latest/registry/securing-exposing-registry.html) so you can push to it:\n1. Export your token to `$HOME/.docker/config.json`:\n\n```sh\noc registry login --to=$HOME/.docker/config.json\n```\n\n1. Create a namespace where you will push your images, i.e: `ko-images`\n1. Execute this command to set `KO_DOCKER_REPO` to publish images to the internal registry.\n\n```sh\nexport KO_DOCKER_REPO=$(oc registry info --public)/ko-images\n```\n\n"
  },
  {
    "path": "docs/advanced/go-packages.md",
    "content": "# Go Packages\n\n`ko`'s functionality can be consumed as a library in a Go application.\n\nTo 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).\n\nThis is a minimal example of using the packages together, to implement the core subset of `ko`'s functionality:\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\n\t\"github.com/google/go-containerregistry/pkg/authn\"\n\t\"github.com/google/go-containerregistry/pkg/name\"\n\t\"github.com/google/go-containerregistry/pkg/v1/remote\"\n\t\"github.com/google/ko/pkg/build\"\n\t\"github.com/google/ko/pkg/publish\"\n)\n\nconst (\n\tbaseImage  = \"cgr.dev/chainguard/static:latest\"\n\ttargetRepo = \"example.registry/my-repo\"\n\timportpath = \"github.com/my-org/miniko\"\n\tcommitSHA  = \"deadbeef\"\n)\n\nfunc main() {\n\tctx := context.Background()\n\n\tb, err := build.NewGo(ctx, \".\",\n\t\tbuild.WithPlatforms(\"linux/amd64\"), // only build for these platforms.\n\t\tbuild.WithBaseImages(func(ctx context.Context, _ string) (name.Reference, build.Result, error) {\n\t\t\tref := name.MustParseReference(baseImage)\n\t\t\tbase, err := remote.Index(ref, remote.WithContext(ctx))\n\t\t\treturn ref, base, err\n\t\t}))\n\tif err != nil {\n\t\tlog.Fatalf(\"NewGo: %v\", err)\n\t}\n\tr, err := b.Build(ctx, importpath)\n\tif err != nil {\n\t\tlog.Fatalf(\"Build: %v\", err)\n\t}\n\n\tp, err := publish.NewDefault(targetRepo,                 // publish to example.registry/my-repo\n\t\tpublish.WithTags([]string{commitSHA}),               // tag with :deadbeef\n\t\tpublish.WithAuthFromKeychain(authn.DefaultKeychain)) // use credentials from ~/.docker/config.json\n\tif err != nil {\n\t\tlog.Fatalf(\"NewDefault: %v\", err)\n\t}\n\tref, err := p.Publish(ctx, r, importpath)\n\tif err != nil {\n\t\tlog.Fatalf(\"Publish: %v\", err)\n\t}\n\tfmt.Println(ref.String())\n}\n```\n"
  },
  {
    "path": "docs/advanced/lambda.md",
    "content": "# AWS Lambda\n\n`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).\n\nFor 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).\n\nFor example:\n\n```go\npackage main\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"github.com/aws/aws-lambda-go/lambda\"\n)\n\ntype Event struct {\n\tName string `json:\"name\"`\n\t// TODO: add other request fields here.\n}\n\nfunc main() {\n\tlambda.Start(func(ctx context.Context, event Event) (string, error) {\n\t\treturn fmt.Sprintf(\"Hello %s!\", event.Name), nil\n\t})\n}\n```\n\nSee AWS's [documentation](https://docs.aws.amazon.com/lambda/latest/dg/golang-handler.html) for more information on writing Lambda functions in Go.\n\nTo deploy to Lambda, you must push to AWS Elastic Container Registry (ECR):\n\n```sh\nKO_DOCKER_REPO=[account-id].dkr.ecr.[region].amazonaws.com/my-repo\nimage=$(ko build ./cmd/app)\n```\n\nThen, create a Lambda function using the image in ECR:\n\n```sh\naws lambda create-function \\\n  --function-name hello-world \\\n  --package-type Image \\\n  --code ImageUri=${image} \\\n  --role arn:aws:iam::[account-id]:role/lambda-ex\n```\n\nSee 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.\n\nThe base image that `ko` uses by default supports both x86 and Graviton2 architectures.\n\nYou 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.\n"
  },
  {
    "path": "docs/advanced/limitations.md",
    "content": "# Limitations\n\n`ko` works best when your application has no dependencies on the underlying image.\n\nThis 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.\n\nTo install other OS packages, make those available in your [configured base image](../../configuration).\n\n`ko` only supports Go applications.\nFor a similar tool targeting Java applications, try [Jib](https://github.com/GoogleContainerTools/jib).\nFor other languages, try [apko](https://github.com/chainguard-dev/apko) and [melange](https://github.com/chainguard-dev/melange).\n\n"
  },
  {
    "path": "docs/advanced/linux-capabilities.md",
    "content": "# Linux Capabilities\n\nIn Linux, capabilities are a way to selectively grant privileges to a running process.\n\nDocker provides `--cap-add` and `--cap-drop`\n[run options](https://docs.docker.com/engine/containers/run/#runtime-privilege-and-linux-capabilities)\nto tweak container capabilities, e.g:\n\n```\ndocker run --cap-add bpf hello-world\n```\n\nIf container runs as a non-root user,\ncapabilities are narrowed by intersecting with *file* capabilities of the\napplication binary. When building images with a Dockerfile, one\ntypically uses `setcap` tool to modify file capabilities, e.g:\n`setcap FILE bpf=ep`.\n\nTo set file capabilities with `ko`, specify `linux_capabilities`\nin builds configuration section in your `.ko.yaml`. Use `setcap` syntax:\n\n```yaml\nbuilds:\n- id: caps\n  linux_capabilities: bpf=ep\n```\n## Alternative spelling\n\n```yaml\nbuilds:\n- id: caps\n  linux_capabilities:\n  - cap1\n  - cap2\n  - cap3\n```\n\nA list of capability names is equivalent to `cap1,cap2,cap3=p`.\n\n## Improving UX in capability-reliant apps\n\nA capability can be *permitted* (`=p`), or both *permitted* and *effective* (`=ep`).\nEffective capabilities are used for permission checks.\nA program can promote permitted capability to effective when needed.\n\n```yaml\nbuilds:\n- id: caps\n  linux_capabilities: bpf,perfmon,net_admin=ep\n```\n\nInitially, `=ep` might look like a good idea.\nThere's no need to explicitly promote *permitted* capabilities.\nApplication takes advantage of *effective* capabilities right away.\n\nThere is a catch though.\n\n```\n$ docker run --cap-add bpf ko.local/caps.test-4b8f7bca75c467b3d2803e1c087a3287\nexec /ko-app/caps.test: operation not permitted\n```\n\nWhen run options request fewer capabilities than specified in file capabilities,\ncontainer fails to start. It is hard to tell what went wrong since\n`operation not permitted` is a generic error. (Docker is unlikely to improve diagnostics\nfor this failure case since the check is implemented in Linux kernel.)\n\n\nWe suggest to use `=p` instead. This option puts application in charge of verifying and\npromoting permitted capabilities to effective. It can produce much better diagnostics:\n\n```\n$ docker run --cap-add bpf ko.local/caps.test-4b8f7bca75c467b3d2803e1c087a3287\ncurrent capabilities: cap_bpf=p\nactivating capabilities: cap_net_admin,cap_perfmon,cap_bpf=ep: operation not permitted\n```\n\n[Sample code](https://go.dev/play/p/uPMzyotkNHg).\n"
  },
  {
    "path": "docs/advanced/migrating-from-dockerfile.md",
    "content": "# Migrating from Dockerfile\n\nIf 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.\n\nLet's review the best practice multi-stage Dockerfile in that tutorial first:\n\n```Dockerfile\n## Build\nFROM golang:1.16-buster AS build\n\nWORKDIR /app\n\nCOPY go.mod ./\nCOPY go.sum ./\nRUN go mod download\n\nCOPY *.go ./\n\nRUN go build -o /docker-gs-ping\n\n## Deploy\nFROM gcr.io/distroless/base-debian10\n\nWORKDIR /\n\nCOPY --from=build /docker-gs-ping /docker-gs-ping\n\nEXPOSE 8080\n\nUSER nonroot:nonroot\n\nENTRYPOINT [\"/docker-gs-ping\"]\n```\n\nThis `Dockerfile`:\n\n1. pulls the `golang:1.16` image\n1. `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)\n1. `RUN`s `go build` on your source, inside the container, to produce an executable\n1. `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.\n\nThe result is a Go application built on a minimal base image, with an optimally cached build sequence.\n\nAfter running `docker build` on this `Dockerfile`, don't forget to push that image to the registry so you can deploy it.\n\n---\n\n## Migrating to `ko`\n\nIf 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.\n\nYou're done. You can delete your `Dockerfile` and uninstall `docker`.\n\n`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.\n\nTo build a multi-arch image, simply add `--platform=all`.\nCompare this to the [equivalent Docker instructions](https://docs.docker.com/desktop/multi-arch/).\n\n"
  },
  {
    "path": "docs/advanced/root-ca-certificates.md",
    "content": "# Root CA Certificates\n\nTo install a [root certificate](https://en.wikipedia.org/wiki/Root_certificate) into your container built using `ko`, you can use one of the following methods.\n\n## incert\n[`incert`](https://github.com/chainguard-dev/incert) allows you to append CA certificates to an image and push the modified image to a specified registry.\n\n`incert` can be run after `ko build` to build your Go application container image with custom root CA certificates.\n\n### Example\n1. Build and push your Go application container image using `ko build`\n```sh\nKO_DOCKER_REPO=mycompany/myimage:latest ko build .\n```\n\n2. Append the built image with your custom CA certificate(s) using `incert`\n```sh\nincert -image-url=mycompany/myimage:latest -ca-certs-file=/path/to/cacerts.pem -dest-image-url=myregistry/myimage:latest\n```\n\n## Custom Base Image\n\nNew root certificates can be [installed into a custom image](https://stackoverflow.com/questions/42292444/how-do-i-add-a-ca-root-certificate-inside-a-docker-image) using standard OS packages. Then, this custom image can be used [to override the base image for `ko`](https://ko.build/configuration/#overriding-base-images). Once the Go application container image is built using `ko` with the custom base image, the root certificates installed on the base image will be trusted by the Go application.\n\n### Example\n\n1. Make a custom container image with your new root certificates\n```dockerfile\n# Dockerfile\nFROM alpine\n\nRUN apk update\nRUN apk add ca-certificates\n\nADD new-root-ca.crt /usr/local/share/ca-certificates/new-root-ca.crt\nRUN chmod 644 /usr/local/share/ca-certificates/new-root-ca.crt\nRUN update-ca-certificates\n```\n\n2. Build and push the custom container image to a container registry\n```sh\ndocker build . -t docker.io/ko-build/image-with-new-root-certs\ndocker push docker.io/ko-build/image-with-new-root-certs\n```\n\n3. Configure `ko` to [override the default base image](https://ko.build/configuration/#overriding-base-images) with the custom image\n```yaml\n# .ko.yaml\ndefaultBaseImage: docker.io/ko-build/image-with-new-root-certs\n```\n\n    **OR**\n```sh\nexport KO_DEFAULTBASEIMAGE=docker.io/ko-build/image-with-new-root-certs\n```\n\n4. Build the Go app container image with `ko`\n```sh\nko build .\n```\n\n## Static Assets\nAlternatively, root certificates can be installed into the Go application container image using a combination of [`ko` static assets](https://ko.build/features/static-assets/) and [overriding the default system location for SSL certificates](https://pkg.go.dev/crypto/x509#SystemCertPool).\n\nUsing `ko`'s support for static assets, root certificates can be stored in the `<importpath>/kodata` directory (either checked into the repository, or injected dynamically by a CI pipeline). After running `ko build`, the certificate files are then bundled into the built image at the path `$KO_DATA_PATH`.\n\nTo enable the Go application to trust the bundled certificate(s), the container runtime or orchestrator (Docker, Kubernetes, etc) must set the environment variable `SSL_CERT_DIR` to the same value as `KO_DATA_PATH`. Go [uses `SSL_CERT_DIR` to determine the directory to check for SSL certificate files](https://go.dev/src/crypto/x509/root_unix.go). Once this variable is set, the Go application will trust the bundled root certificates in `$KO_DATA_PATH`.\n\n### Example\n\n1. Copy the root certificate(s) to the `<importpath>/kodata/` directory\n```sh\n# $(pwd) assumed to be at <importpath> for this example\nmkdir -p kodata\ncp $CERT_FILE_DIR/*.crt kodata/\n```\n\n2. Build the Go application container image\n```sh\nKO_DOCKER_REPO=docker.io/ko-build/static-assets-certs ko build .\n```\n\n3. Run the Go application container image with `SSL_CERT_DIR` equal to `/var/run/ko` (the default value for `$KO_DATA_PATH`)\n```sh\ndocker run -e SSL_CERT_DIR=/var/run/ko docker.io/ko-build/static-assets-certs\n```\n\nA functional client-server example for this can be seen [here](https://github.com/kosamson/ko-private-ca-test).\n"
  },
  {
    "path": "docs/advanced/terraform.md",
    "content": "# Terraform Provider\n\nIn addition to the CLI, `ko`'s functionality is also available as a Terraform provider.\n\nThis allows `ko` to be integrated with your Infrastructure-as-Code (IaC) workflows, and makes building your code a seamless part of your deployment process.\n\nUsing the Terraform provider is as simple as adding a `ko_build` resource to your Terraform configuration:\n\n```hcl\n// Require the `ko-build/ko` provider.\nterraform {\n  required_providers {\n    ko = { source = \"ko-build/ko\" }\n  }\n}\n\n// Configure the provider to push to your repo.\nprovider \"ko\" {\n  repo = \"example.registry/my-repo\" // equivalent to KO_DOCKER_REPO\n}\n\n// Build your code.\nresource \"ko_build\" \"app\" {\n  importpath = \"github.com/example/repo/cmd/app\"\n}\n\n// TODO: use the `ko_build.app` resource elsewhere in your Terraform configuration.\n\n// Report the build image's digest.\noutput \"image\" {\n  value = ko_build.app.image_ref\n}\n```\n\nSee 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.\n"
  },
  {
    "path": "docs/community.md",
    "content": "# Community\n\n## Meetings\n\nWe 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.\n\nWe 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.\n\n## Slack\n\nCome discuss `ko` with us in the `#ko-build` channel on the [Kubernetes Slack](https://ko.build/slack)!\nSee you there!\n"
  },
  {
    "path": "docs/configuration.md",
    "content": "# Configuration\n\n## Basic Configuration\n\nAside from certain environment variables (see [below](#environment-variables-advanced)) like `KO_DOCKER_REPO`, you can\nconfigure `ko`'s behavior using a `.ko.yaml` file. The location of this file can be overridden with `KO_CONFIG_PATH`.\n\n### Overriding Base Images\n\nBy default, `ko` bases images on `cgr.dev/chainguard/static`. This is a\nsmall image that provides the bare necessities to run your Go binary.\n\nYou can override this base image in two ways:\n\n1. To override the base image for all images `ko` builds, add this line to your\n   `.ko.yaml` file:\n\n```yaml\ndefaultBaseImage: registry.example.com/base/image\n```\n\nYou can also use the `KO_DEFAULTBASEIMAGE` environment variable to set the default base image, which overrides the YAML configuration:\n\n```shell\nKO_DEFAULTBASEIMAGE=registry.example.com/base/image ko build .\n```\n\n2. To override the base image for certain importpaths:\n\n```yaml\nbaseImageOverrides:\n  github.com/my-user/my-repo/cmd/app: registry.example.com/base/for/app\n  github.com/my-user/my-repo/cmd/foo: registry.example.com/base/for/foo\n```\n\n### Overriding Go build settings\n\nBy default, `ko` builds the binary with no additional build flags other than\n`-trimpath`. You can replace the default build\narguments by providing build flags and ldflags using a\n[GoReleaser](https://github.com/goreleaser/goreleaser) influenced `builds`\nconfiguration section in your `.ko.yaml`.\n\n```yaml\nbuilds:\n- id: foo\n  dir: .  # default is .\n  main: ./foobar/foo\n  env:\n  - GOPRIVATE=git.internal.example.com,source.developers.google.com\n  flags:\n  - -tags\n  - netgo\n  ldflags:\n  - -s -w\n  - -extldflags \"-static\"\n  - -X main.version={{.Env.VERSION}}\n- id: bar\n  dir: ./bar\n  main: .  # default is .\n  env:\n  - GOCACHE=/workspace/.gocache\n  ldflags:\n  - -s\n  - -w\n```\n\nIf your repository contains multiple modules (multiple `go.mod` files in\ndifferent directories), use the `dir` field to specify the directory where\n`ko` should run `go build`.\n\n`ko` picks the entry from `builds` based on the import path you request. The\nimport path is matched against the result of joining `dir` and `main`.\n\nThe paths specified in `dir` and `main` are relative to the working directory\nof the `ko` process.\n\nThe `ldflags` default value is `[]`.\n\n### Templating support\n\nThe `ko` builds supports templating of `flags` and `ldflags`, similar to the\n[GoReleaser `builds` section](https://goreleaser.com/customization/build/).\n\nThe table below lists the supported template parameters.\n\n| Template param        | Description                                              |\n|-----------------------|----------------------------------------------------------|\n| `Env`                 | Map of environment variables used for the build          |\n| `GoEnv`               | Map of `go env` environment variables used for the build |\n| `Date`                | The UTC build date in RFC 3339 format                    |\n| `Timestamp`           | The UTC build date as Unix epoc seconds                  |\n| `Git.Branch`          | The current git branch                                   |\n| `Git.Tag`             | The current git tag                                      |\n| `Git.ShortCommit`     | The git commit short hash                                |\n| `Git.FullCommit`      | The git commit full hash                                 |\n| `Git.CommitDate`      | The UTC commit date in RFC 3339 format                   |\n| `Git.CommitTimestamp` | The UTC commit date in Unix format                       |\n| `Git.IsDirty`         | Whether or not current git state is dirty                |\n| `Git.IsClean`         | Whether or not current git state is clean.               |\n| `Git.TreeState`       | Either `clean` or `dirty`                                |\n\n### Setting default platforms\n\nBy default, `ko` builds images based on the platform it runs on. If your target platform differs from your build platform you can specify the build platform:\n\n**As a parameter**\nSee [Multi-Platform Images](./features/multi-platform.md).\n\n**In .ko.yaml**\nAdd this to your `.ko.yaml` file:\n\n```yaml\ndefaultPlatforms:\n- linux/arm64\n- linux/amd64\n```\n\nYou can also use the `KO_DEFAULTPLATFORMS` environment variable to set the default platforms, which overrides the YAML configuration:\n\n```shell\nKO_DEFAULTPLATFORMS=linux/arm64,linux/amd64\n```\n\n### Setting build environment variables\n\nBy default, `ko` builds use the ambient environment from the system (i.e. `os.Environ()`).\nThese values can be overridden for your build.\n\n```yaml\ndefaultEnv:\n- FOO=foo\nbuilds:\n- id: foo\n  dir: .\n  main: ./foobar/foo\n  env:\n  - FOO=bar\n- id: bar     # Will use defaultEnv.\n  dir: ./bar\n  main: .\n```\n\nFor a given build, the environment variables are merged in the following order:\n\n- System `os.Environ` (lowest precedence)\n- Build variables: `build.env` if specified, otherwise `defaultEnv` (highest precedence)\n\n### Setting build flags and ldflags\n\nYou can specify both `flags` and `ldflags` globally as well as per-build.\n\n```yaml\ndefaultFlags:\n- -v\ndefaultLdflags:\n- -s\nbuilds:\n- id: foo\n  dir: .\n  main: ./foobar/foo\n  flags:\n  - -trimpath\n  ldflags:\n  - -w\n- id: bar     # Will use defaultFlags and defaultLdflags.\n  dir: ./bar\n  main: .\n```\n\nThe values for a `build` will be used if specified, otherwise their respective defaults will be used.\nBoth default and per-build values may use [template parameters](#templating-support).\n\n### Environment Variables (advanced)\n\nFor ease of use, backward compatibility and advanced use cases, `ko` supports the following environment variables to\ninfluence the build process.\n\n| Variable         | Default Value                              | Description                                                                                                                                                                                                                      |\n|------------------|--------------------------------------------|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|\n| `KO_DOCKER_REPO` | (not set)                                  | Container repository where to push images built with `ko` (required)                                                                                                                                                             |\n| `KO_GO_PATH`     | `go`                                       | `go` binary to use for builds, relative or absolute path, otherwise looked up via $PATH (optional)                                                                                                                               |\n| `KO_CONFIG_PATH` | `./.ko.yaml`                               | Path to `ko` configuration file (optional)                                                                                                                                                                                       |\n| `KOCACHE`        | (not set)                                  | 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 (optional).             |\n\n## Naming Images\n\n`ko` provides a few different strategies for naming the image it pushes, to\nworkaround certain registry limitations and user preferences:\n\nGiven `KO_DOCKER_REPO=registry.example.com/repo`, by default,\n`ko build ./cmd/app` will produce an image named like\n`registry.example.com/repo/app-<md5>`, which includes the MD5 hash of the full\nimport path, to avoid collisions.\n\n- `--preserve-import-paths` (`-P`) will include the entire importpath:\n  `registry.example.com/repo/github.com/my-user/my-repo/cmd/app`\n- `--base-import-paths` (`-B`) will omit the MD5 portion:\n  `registry.example.com/repo/app`\n- `--bare` will only include the `KO_DOCKER_REPO`: `registry.example.com/repo`\n\n## Local Publishing Options\n\n`ko` is normally used to publish images to container image registries,\nidentified by `KO_DOCKER_REPO`.\n\n`ko` can also load images to a local Docker daemon, if available, by setting\n`KO_DOCKER_REPO=ko.local`, or by passing the `--local` (`-L`) flag.\n\nLocal images can be used as a base image for other `ko` images:\n\n```yaml\ndefaultBaseImage: ko.local/example/base/image\n```\n\n`ko` can also load images into a local [KinD](https://kind.sigs.k8s.io)\ncluster, if available, by setting `KO_DOCKER_REPO=kind.local`. By default this\nloads into the default KinD cluster name (`kind`). To load into another KinD\ncluster, set `KIND_CLUSTER_NAME=my-other-cluster`.\n\n"
  },
  {
    "path": "docs/custom/main.html",
    "content": "{% extends \"base.html\" %}\n\n{% block site_meta %}\n    {{ super() }}\n    {% if page and page.meta and page.meta.ko_meta %}\n    <meta charset=utf-8>\n    <meta name=go-import content=\"ko.build git https://github.com/ko-build/ko\">\n    <meta name=go-source content=\"ko.build git https://github.com/ko-build/ko https://github.com/ko-build/ko/tree/main{/dir} https://github.com/ko-build/ko/blob/main{/dir}/{file}#L{line}\">\n    {% endif %}\n{% endblock %}\n\n"
  },
  {
    "path": "docs/custom/partials/copyright.html",
    "content": "<small>The Linux Foundation® (TLF) has registered trademarks and uses trademarks. For a list of TLF trademarks, see <a href=\"https://www.linuxfoundation.org/legal/trademark-usage\" window=\"_blank\">Trademark Usage</a></small>\n"
  },
  {
    "path": "docs/deployment.md",
    "content": "# Deployment\n\n_See [Kubernetes Integration](../features/k8s) for information about deploying to Kubernetes._\n\nBecause the output of `ko build` is an image reference, you can easily pass it to other tools that expect to take an image reference.\n\n### [`docker run`](https://docs.docker.com/engine/reference/run/)\n\nTo run the container locally:\n\n```plaintext\ndocker run -p 8080:8080 $(ko build ./cmd/app)\n```\n\n---\n\n### [Google Cloud Run](https://cloud.google.com/run)\n\n```plaintext\ngcloud run deploy --image=$(ko build ./cmd/app)\n```\n\n> 💡 **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).\n\n---\n\n###  [fly.io](https://fly.io)\n\n```plaintext\nflyctl launch --image=$(ko build ./cmd/app)\n```\n\n> 💡 **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/).\n\n---\n\n### [AWS Lambda](https://aws.amazon.com/lambda/)\n\n```plaintext\naws lambda update-function-code \\\n  --function-name=my-function-name \\\n  --image-uri=$(ko build ./cmd/app)\n```\n\n> 💡 **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.\nSee [official docs](https://docs.aws.amazon.com/lambda/latest/dg/go-image.html) for more information.\n\n---\n\n### [Azure Container Apps](https://azure.microsoft.com/services/container-apps/)\n\n```plaintext\naz containerapp update \\\n  --name my-container-app\n  --resource-group my-resource-group\n  --image $(ko build ./cmd/app)\n```\n\n> 💡 **Note:** The image must be pushed to [ACR](https://azure.microsoft.com/services/container-registry/) or other registry service.\nSee [official docs](https://docs.microsoft.com/azure/container-apps/) for more information.\n\n"
  },
  {
    "path": "docs/features/build-cache.md",
    "content": "# Build Cache\n\nBecause `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.\n\n`ko` also avoids pushing blobs to the remote image registry if they're already present, making pushes faster.\n\nYou can make `ko` even faster by setting the `KOCACHE` environment variable.\nThis 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.\n\n"
  },
  {
    "path": "docs/features/debugging.md",
    "content": "# Debugging\n\nSometimes 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.\n\nBut to help you solve the problem _as fast as possible_, `ko` supports debugging your Go app with [delve](https://github.com/go-delve/delve).\n\nTo use this feature, just add the `--debug` flag to your `ko build` command. This adjusts how the image is built:\n\n- It installs `delve` in the image (in addition to your own app).\n- 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.\n- It ensures your compiled Go app includes debug symbols needed to enable debugging.\n\n**Note:** This feature is geared toward development workflows. It **should not** be used in production.\n\n### How it works\n\nBuild the image using the debug feature.\n\n```plaintext\nko build . --debug\n```\n\nRun the container, ensuring that the debug port (`40000`) is exposed to allow clients to connect to it.\n\n```plaintext\ndocker run -p 40000:40000 <img>\n```\n\nThis 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!\n"
  },
  {
    "path": "docs/features/k8s.md",
    "content": "# Kubernetes Integration\n\nYou _could_ stop at just building and pushing images.\n\nBut, because building images is so _easy_ with `ko`, and because building with\n`ko` only requires a string importpath to identify the image, we can integrate\nthis with YAML generation to make Kubernetes use cases much simpler.\n\n## YAML Changes\n\nTraditionally, you might have a Kubernetes deployment, defined in a YAML file,\nthat runs an image:\n\n```yaml\napiVersion: apps/v1\nkind: Deployment\nmetadata:\n  name: my-deployment\nspec:\n  selector:\n    matchLabels:\n      app: my-app\n  template:\n    metadata:\n      labels:\n        app: my-app\n    spec:\n      containers:\n      - name: my-app\n        image: registry.example.com/my-app:v1.2.3\n```\n\n...which you apply to your cluster with `kubectl apply`:\n\n```plaintext\nkubectl apply -f deployment.yaml\n```\n\nWith `ko`, you can instead reference your Go binary by its importpath, prefixed\nwith `ko://`:\n\n```yaml\n    ...\n    spec:\n      containers:\n      - name: my-app\n        image: ko://github.com/my-user/my-repo/cmd/app\n```\n\n## `ko resolve`\n\nWith this small change, running `ko resolve -f deployment.yaml` will instruct\n`ko` to:\n\n1. scan the YAML file(s) for values with the `ko://` prefix,\n2. for each unique `ko://`-prefixed string, execute `ko build <importpath>` to\n   build and push an image,\n3. replace `ko://`-prefixed string(s) in the input YAML with the fully-specified\n   image reference of the built image(s), as above.\n4. Print the resulting resolved YAML to stdout.\n\nThe result can be redirected to a file, to distribute to others:\n\n```plaintext\nko resolve -f config/ > release.yaml\n```\n\nTaken together, `ko resolve` aims to make packaging, pushing, and referencing\ncontainer images an invisible implementation detail of your Kubernetes\ndeployment, and let you focus on writing code in Go.\n\n## `ko apply`\n\nTo apply the resulting resolved YAML config, you can redirect the output of\n`ko resolve` to `kubectl apply`:\n\n```plaintext\nko resolve -f config/ | kubectl apply -f -\n```\n\nSince this is a relatively common use case, the same functionality is available\nusing `ko apply`:\n\n```plaintext\nko apply -f config/\n```\n\nAlso, any flags passed after `--` are passed to `kubectl apply` directly, for example to specify context and kubeconfig:\n```\nko apply -f config -- --context=foo --kubeconfig=cfg.yaml\n```\n\n**NB:** This requires that `kubectl` is available.\n\n## `ko delete`\n\nTo teardown resources applied using `ko apply`, you can run `ko delete`:\n\n```plaintext\nko delete -f config/\n```\n\nThis is purely a convenient alias for `kubectl delete`, and doesn't perform any\nbuilds, or delete any previously built images.\n\n"
  },
  {
    "path": "docs/features/multi-platform.md",
    "content": "# Multi-Platform Images\n\nBecause Go supports cross-compilation to other CPU architectures and operating systems, `ko` excels at producing multi-platform images.\n\nTo build and push an image for all platforms supported by the configured base image, simply add `--platform=all`.\nThis will instruct `ko` to look up all the supported platforms in the base image, execute `GOOS=<os> GOARCH=<arch> GOARM=<variant> go build` for each platform, and produce a manifest list containing an image for each platform.\n\nYou can also select specific platforms, for example, `--platform=linux/amd64,linux/arm64`.\n\n`ko` also has experimental support for building for Windows images.\nSee [FAQ](../../advanced/faq#can-i-build-windows-containers).\n\n"
  },
  {
    "path": "docs/features/sboms.md",
    "content": "# SBOMs\n\nA [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.\nHaving a list of dependencies can be helpful in determining whether any vulnerable components were used to build the software artifact.\n\n**From v0.9+, `ko` generates and uploads an SBOM for every image it produces by default.**\n\nko will generate an SBOM in the [SPDX](https://spdx.dev/) format by default. To disable SBOM generation, pass `--sbom=none`.\n\nThese SBOMs can be downloaded using the [`cosign download sbom`](https://github.com/sigstore/cosign/blob/main/doc/cosign_download_sbom.md) command.\n\n"
  },
  {
    "path": "docs/features/static-assets.md",
    "content": "# Static Assets\n\n`ko` can also bundle static assets into the images it produces.\n\nBy convention, any contents of a directory named `<importpath>/kodata/` will be\nbundled into the image, and the path where it's available in the image will be\nidentified by the environment variable `KO_DATA_PATH`.\n\nAs an example, you can bundle and serve static contents in your image:\n\n```\ncmd/\n  app/\n    main.go\n    kodata/\n      favicon.ico\n      index.html\n```\n\nThen, in your `main.go`:\n\n```go\nfunc main() {\n    http.Handle(\"/\", http.FileServer(http.Dir(os.Getenv(\"KO_DATA_PATH\"))))\n    log.Fatal(http.ListenAndServe(\":8080\", nil))\n}\n```\n\nYou can simulate `ko`'s behavior outside of the container image by setting the\n`KO_DATA_PATH` environment variable yourself with `KO_DATA_PATH=cmd/app/kodata/ go run ./cmd/app`.\n\n> 💡 **Tip:** Symlinks in `kodata` are followed and included as well. For example,\nyou can include Git commit information in your image with `ln -s -r .git/HEAD ./cmd/app/kodata/`\n\nAlso note that `http.FileServer` will not serve the `Last-Modified` header\n(or validate `If-Modified-Since` request headers) because `ko` does not embed\ntimestamps by default.\n\nThis can be supported by manually setting the `KO_DATA_DATE_EPOCH` environment\nvariable during build ([See FAQ](../../advanced/faq#why-are-my-images-all-created-in-1970)).\n\n"
  },
  {
    "path": "docs/get-started.md",
    "content": "# Get Started\n\n## Setup\n\nFirst, [install `ko`](../install).\n\n### Authenticate\n\n`ko` depends on the authentication configured in your Docker config (typically `~/.docker/config.json`).\n\n✨ **If you can push an image with `docker push`, you are already authenticated for `ko`!** ✨\n\nSince `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/).\n\nAdditionally, 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:\n\n- Google Container Registry and Artifact Registry, using [Application Default Credentials](https://cloud.google.com/docs/authentication/production) or auth configured in `gcloud`.\n- Amazon Elastic Container Registry, using [AWS credentials](https://github.com/awslabs/amazon-ecr-credential-helper/#aws-credentials)\n- Azure Container Registry, using [environment variables](https://github.com/chrismellard/docker-credential-acr-env/)\n- GitHub Container Registry, using the `GITHUB_TOKEN` environment variable\n\n### Choose Destination\n\n`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.:\n\n- `KO_DOCKER_REPO=gcr.io/my-project`, or\n- `KO_DOCKER_REPO=ghcr.io/my-org/my-repo`, or\n- `KO_DOCKER_REPO=my-dockerhub-user`\n\n## Build an Image\n\n`ko build ./cmd/app` builds and pushes a container image, and prints the resulting image digest to stdout.\n\nIn this example, `./cmd/app` must be a `package main` that defines `func main()`.\n\n```plaintext\n$ ko build ./cmd/app\n...\nregistry.example.com/my-project/app-099ba5bcefdead87f92606265fb99ac0@sha256:6e398316742b7aa4a93161dce4a23bc5c545700b862b43347b941000b112ec3e\n```\n\n> 💡 **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.\n\nThe 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.\n\n"
  },
  {
    "path": "docs/index.md",
    "content": "---\nko_meta: true\n---\n\n# Introduction\n\n`ko` makes building Go container images easy, fast, and secure by default.\n\n![Demo of ko build](./images/demo.png)\n\n`ko` is a simple, fast container image builder for Go applications.\n\nIt'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).\n\n`ko` builds images by executing `go build` on your local machine, and as such doesn't require `docker` to be installed.\nThis can make it a good fit for lightweight CI/CD use cases.\n\n`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/).\n\n---\n\n> 🏃 [Install `ko`](./install) and [get started](./get-started)!\n\n---\n\n`ko` is used and loved by these open source projects:\n\n- [Knative](https://knative.dev)\n- [Tekton](https://tekton.dev)\n- [Karpenter](https://karpenter.sh)\n- [Kyverno](https://kyverno.io)\n- [Sigstore](https://sigstore.dev)\n- [Shipwright](https://shipwright.io)\n- [Capsule](https://capsule.clastix.io/)\n- [CloudScript](https://cloudscript.com.br/)\n- [Kamaji](https://kamaji.clastix.io/)\n\n[_Add your project here!_](https://github.com/ko-build/ko/edit/main/docs/index.md)\n\n---\n\n`ko` is a Cloud Native Computing Foundation Sandbox project.\n\n<a href=\"https://cncf.io\">\n    <img width=300 src=\"./images/cncf-light.svg#only-light\" alt=\"CNCF logo\" />\n    <img width=300 src=\"./images/cncf-dark.svg#only-dark\" alt=\"CNCF logo\" />\n</a>\n"
  },
  {
    "path": "docs/install.md",
    "content": "# Installation\n\n### Install from [GitHub Releases](https://github.com/ko-build/ko/releases)\n\n```\n$ VERSION=TODO # choose the latest version (without v prefix)\n$ OS=Linux     # or Darwin\n$ ARCH=x86_64  # or arm64, i386, s390x\n```\n\nWe 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:\n\n\n```shell\n$ curl -sSfL \"https://github.com/ko-build/ko/releases/download/v${VERSION}/ko_${VERSION}_${OS}_${ARCH}.tar.gz\" > ko.tar.gz\n$ curl -sSfL https://github.com/ko-build/ko/releases/download/v${VERSION}/multiple.intoto.jsonl > multiple.intoto.jsonl\n$ slsa-verifier verify-artifact --provenance-path multiple.intoto.jsonl --source-uri github.com/ko-build/ko --source-tag \"v${VERSION}\" ko.tar.gz\nVerified signature against tlog entry index 24413745 at URL: https://rekor.sigstore.dev/api/v1/log/entries/24296fb24b8ad77ab97a5263b5fa8f35789618348a39358b1f9470b0c31045effbbe5e23e77a5836\nVerified 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\nVerifying artifact /Users/batuhanapaydin/workspace/ko/ko.tar.gz: PASSED\n\nPASSED: Verified SLSA provenance\n```\n\n```shell\n$ tar xzf ko.tar.gz ko\n$ chmod +x ./ko\n```\n\n### Install using [Homebrew](https://brew.sh)\n\n```plaintext\nbrew install ko\n```\n\n### Install using [MacPorts](https://www.macports.org)\n\n```plaintext\nsudo port install ko\n```\n\nMore info [here](https://ports.macports.org/port/ko/)\n\n### Install on Windows using [Scoop](https://scoop.sh)\n\n```plaintext\nscoop install ko\n```\n\n### Install on [Alpine Linux](https://www.alpinelinux.org)\n\nInstallation on Alpine requires using the [`testing` repository](https://wiki.alpinelinux.org/wiki/Enable_Community_Repository#Using_testing_repositories)\n\n```\necho https://dl-cdn.alpinelinux.org/alpine/edge/testing/ >> /etc/apk/repositories\napk update\napk add ko\n```\n\n### Build and Install from source\n\nWith Go 1.16+, build and install the latest released version:\n\n```plaintext\ngo install github.com/google/ko@latest\n```\n\n### Setup on GitHub Actions\n\nYou 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:\n\n```plaintext\nsteps:\n- uses: ko-build/setup-ko@v0.6\n```\n"
  },
  {
    "path": "docs/reference/ko.md",
    "content": "## ko\n\nRapidly iterate with Go, Containers, and Kubernetes.\n\n```\nko [flags]\n```\n\n### Options\n\n```\n  -h, --help      help for ko\n  -v, --verbose   Enable debug logs\n```\n\n### SEE ALSO\n\n* [ko apply](ko_apply.md)\t - Apply the input files with image references resolved to built/pushed image digests.\n* [ko build](ko_build.md)\t - Build and publish container images from the given importpaths.\n* [ko create](ko_create.md)\t - Create the input files with image references resolved to built/pushed image digests.\n* [ko delete](ko_delete.md)\t - See \"kubectl help delete\" for detailed usage.\n* [ko login](ko_login.md)\t - Log in to a registry\n* [ko resolve](ko_resolve.md)\t - Print the input files with image references resolved to built/pushed image digests.\n* [ko run](ko_run.md)\t - A variant of `kubectl run` that containerizes IMPORTPATH first.\n* [ko version](ko_version.md)\t - Print ko version.\n\n"
  },
  {
    "path": "docs/reference/ko_apply.md",
    "content": "## ko apply\n\nApply the input files with image references resolved to built/pushed image digests.\n\n### Synopsis\n\nThis sub-command finds import path references within the provided files, builds them into Go binaries, containerizes them, publishes them, and then feeds the resulting yaml into \"kubectl apply\".\n\n```\nko apply -f FILENAME [flags]\n```\n\n### Examples\n\n```\n\n  # Build and publish import path references to a Docker\n  # Registry as:\n  #   ${KO_DOCKER_REPO}/<package name>-<hash of import path>\n  # Then, feed the resulting yaml into \"kubectl apply\".\n  # When KO_DOCKER_REPO is ko.local, it is the same as if\n  # --local was passed.\n  ko apply -f config/\n\n  # Build and publish import path references to a Docker\n  # Registry preserving import path names as:\n  #   ${KO_DOCKER_REPO}/<import path>\n  # Then, feed the resulting yaml into \"kubectl apply\".\n  ko apply --preserve-import-paths -f config/\n\n  # Build and publish import path references to a Docker\n  # daemon as:\n  #   ko.local/<import path>\n  # Then, feed the resulting yaml into \"kubectl apply\".\n  ko apply --local -f config/\n\n  # Apply from stdin:\n  cat config.yaml | ko apply -f -\n\n  # Any flags passed after '--' are passed to 'kubectl apply' directly:\n  ko apply -f config -- --namespace=foo --kubeconfig=cfg.yaml\n\n```\n\n### Options\n\n```\n      --bare                       Whether to just use KO_DOCKER_REPO without additional context (may not work properly with --tags).\n  -B, --base-import-paths          Whether to use the base path without MD5 hash after KO_DOCKER_REPO (may not work properly with --tags).\n      --debug                      Include Delve debugger into image and wrap around ko-app. This debugger will listen to port 40000.\n      --disable-optimizations      Disable optimizations when building Go code. Useful when you want to interactively debug the created container.\n  -f, --filename strings           Filename, directory, or URL to files to use to create the resource\n  -h, --help                       help for apply\n      --image-annotation strings   Which annotations (key=value[,key=value]) to add to the OCI manifest.\n      --image-label strings        Which labels (key=value[,key=value]) to add to the image.\n      --image-refs string          Path to file where a list of the published image references will be written.\n      --image-user string          The default user the image should be run as.\n      --insecure-registry          Whether to skip TLS verification on the registry\n  -j, --jobs int                   The maximum number of concurrent builds (default GOMAXPROCS)\n  -L, --local                      Load into images to local docker daemon.\n      --oci-layout-path string     Path to save the OCI image layout of the built images\n      --platform strings           Which platform to use when pulling a multi-platform base. Format: all | <os>[/<arch>[/<variant>]][,platform]*\n  -P, --preserve-import-paths      Whether to preserve the full import path after KO_DOCKER_REPO.\n      --push                       Push images to KO_DOCKER_REPO (default true)\n  -R, --recursive                  Process the directory used in -f, --filename recursively. Useful when you want to manage related manifests organized within the same directory.\n      --sbom string                The SBOM media type to use (none will disable SBOM synthesis and upload). (default \"spdx\")\n      --sbom-dir string            Path to file where the SBOM will be written.\n  -l, --selector string            Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)\n      --tag-only                   Include tags but not digests in resolved image references. Useful when digests are not preserved when images are repopulated.\n  -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])\n      --tarball string             File to save images tarballs\n```\n\n### Options inherited from parent commands\n\n```\n  -v, --verbose   Enable debug logs\n```\n\n### SEE ALSO\n\n* [ko](ko.md)\t - Rapidly iterate with Go, Containers, and Kubernetes.\n\n"
  },
  {
    "path": "docs/reference/ko_build.md",
    "content": "## ko build\n\nBuild and publish container images from the given importpaths.\n\n### Synopsis\n\nThis sub-command builds the provided import paths into Go binaries, containerizes them, and publishes them.\n\n```\nko build IMPORTPATH... [flags]\n```\n\n### Examples\n\n```\n\n  # Build and publish import path references to a Docker Registry as:\n  #   ${KO_DOCKER_REPO}/<package name>-<hash of import path>\n  # When KO_DOCKER_REPO is ko.local, it is the same as if --local and\n  # --preserve-import-paths were passed.\n  # If the import path is not provided, the current working directory is the\n  # default.\n  ko build github.com/foo/bar/cmd/baz github.com/foo/bar/cmd/blah\n\n  # Build and publish a relative import path as:\n  #   ${KO_DOCKER_REPO}/<package name>-<hash of import path>\n  # When KO_DOCKER_REPO is ko.local, it is the same as if --local and\n  # --preserve-import-paths were passed.\n  ko build ./cmd/blah\n\n  # Build and publish a relative import path as:\n  #   ${KO_DOCKER_REPO}/<import path>\n  # When KO_DOCKER_REPO is ko.local, it is the same as if --local was passed.\n  ko build --preserve-import-paths ./cmd/blah\n\n  # Build and publish import path references to a Docker daemon as:\n  #   ko.local/<import path>\n  # This always preserves import paths.\n  ko build --local github.com/foo/bar/cmd/baz github.com/foo/bar/cmd/blah\n```\n\n### Options\n\n```\n      --bare                       Whether to just use KO_DOCKER_REPO without additional context (may not work properly with --tags).\n  -B, --base-import-paths          Whether to use the base path without MD5 hash after KO_DOCKER_REPO (may not work properly with --tags).\n      --debug                      Include Delve debugger into image and wrap around ko-app. This debugger will listen to port 40000.\n      --disable-optimizations      Disable optimizations when building Go code. Useful when you want to interactively debug the created container.\n  -h, --help                       help for build\n      --image-annotation strings   Which annotations (key=value[,key=value]) to add to the OCI manifest.\n      --image-label strings        Which labels (key=value[,key=value]) to add to the image.\n      --image-refs string          Path to file where a list of the published image references will be written.\n      --image-user string          The default user the image should be run as.\n      --insecure-registry          Whether to skip TLS verification on the registry\n  -j, --jobs int                   The maximum number of concurrent builds (default GOMAXPROCS)\n  -L, --local                      Load into images to local docker daemon.\n      --oci-layout-path string     Path to save the OCI image layout of the built images\n      --platform strings           Which platform to use when pulling a multi-platform base. Format: all | <os>[/<arch>[/<variant>]][,platform]*\n  -P, --preserve-import-paths      Whether to preserve the full import path after KO_DOCKER_REPO.\n      --push                       Push images to KO_DOCKER_REPO (default true)\n      --sbom string                The SBOM media type to use (none will disable SBOM synthesis and upload). (default \"spdx\")\n      --sbom-dir string            Path to file where the SBOM will be written.\n      --tag-only                   Include tags but not digests in resolved image references. Useful when digests are not preserved when images are repopulated.\n  -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])\n      --tarball string             File to save images tarballs\n```\n\n### Options inherited from parent commands\n\n```\n  -v, --verbose   Enable debug logs\n```\n\n### SEE ALSO\n\n* [ko](ko.md)\t - Rapidly iterate with Go, Containers, and Kubernetes.\n\n"
  },
  {
    "path": "docs/reference/ko_create.md",
    "content": "## ko create\n\nCreate the input files with image references resolved to built/pushed image digests.\n\n### Synopsis\n\nThis sub-command finds import path references within the provided files, builds them into Go binaries, containerizes them, publishes them, and then feeds the resulting yaml into \"kubectl create\".\n\n```\nko create -f FILENAME [flags]\n```\n\n### Examples\n\n```\n\n  # Build and publish import path references to a Docker\n  # Registry as:\n  #   ${KO_DOCKER_REPO}/<package name>-<hash of import path>\n  # Then, feed the resulting yaml into \"kubectl create\".\n  # When KO_DOCKER_REPO is ko.local, it is the same as if\n  # --local was passed.\n  ko create -f config/\n\n  # Build and publish import path references to a Docker\n  # Registry preserving import path names as:\n  #   ${KO_DOCKER_REPO}/<import path>\n  # Then, feed the resulting yaml into \"kubectl create\".\n  ko create --preserve-import-paths -f config/\n\n  # Build and publish import path references to a Docker\n  # daemon as:\n  #   ko.local/<import path>\n  # Then, feed the resulting yaml into \"kubectl create\".\n  ko create --local -f config/\n\n  # Create from stdin:\n  cat config.yaml | ko create -f -\n\n  # Any flags passed after '--' are passed to 'kubectl apply' directly:\n  ko apply -f config -- --namespace=foo --kubeconfig=cfg.yaml\n\n```\n\n### Options\n\n```\n      --bare                       Whether to just use KO_DOCKER_REPO without additional context (may not work properly with --tags).\n  -B, --base-import-paths          Whether to use the base path without MD5 hash after KO_DOCKER_REPO (may not work properly with --tags).\n      --debug                      Include Delve debugger into image and wrap around ko-app. This debugger will listen to port 40000.\n      --disable-optimizations      Disable optimizations when building Go code. Useful when you want to interactively debug the created container.\n  -f, --filename strings           Filename, directory, or URL to files to use to create the resource\n  -h, --help                       help for create\n      --image-annotation strings   Which annotations (key=value[,key=value]) to add to the OCI manifest.\n      --image-label strings        Which labels (key=value[,key=value]) to add to the image.\n      --image-refs string          Path to file where a list of the published image references will be written.\n      --image-user string          The default user the image should be run as.\n      --insecure-registry          Whether to skip TLS verification on the registry\n  -j, --jobs int                   The maximum number of concurrent builds (default GOMAXPROCS)\n  -L, --local                      Load into images to local docker daemon.\n      --oci-layout-path string     Path to save the OCI image layout of the built images\n      --platform strings           Which platform to use when pulling a multi-platform base. Format: all | <os>[/<arch>[/<variant>]][,platform]*\n  -P, --preserve-import-paths      Whether to preserve the full import path after KO_DOCKER_REPO.\n      --push                       Push images to KO_DOCKER_REPO (default true)\n  -R, --recursive                  Process the directory used in -f, --filename recursively. Useful when you want to manage related manifests organized within the same directory.\n      --sbom string                The SBOM media type to use (none will disable SBOM synthesis and upload). (default \"spdx\")\n      --sbom-dir string            Path to file where the SBOM will be written.\n  -l, --selector string            Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)\n      --tag-only                   Include tags but not digests in resolved image references. Useful when digests are not preserved when images are repopulated.\n  -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])\n      --tarball string             File to save images tarballs\n```\n\n### Options inherited from parent commands\n\n```\n  -v, --verbose   Enable debug logs\n```\n\n### SEE ALSO\n\n* [ko](ko.md)\t - Rapidly iterate with Go, Containers, and Kubernetes.\n\n"
  },
  {
    "path": "docs/reference/ko_delete.md",
    "content": "## ko delete\n\nSee \"kubectl help delete\" for detailed usage.\n\n```\nko delete [flags]\n```\n\n### Options\n\n```\n  -h, --help   help for delete\n```\n\n### Options inherited from parent commands\n\n```\n  -v, --verbose   Enable debug logs\n```\n\n### SEE ALSO\n\n* [ko](ko.md)\t - Rapidly iterate with Go, Containers, and Kubernetes.\n\n"
  },
  {
    "path": "docs/reference/ko_login.md",
    "content": "## ko login\n\nLog in to a registry\n\n```\nko login [OPTIONS] [SERVER] [flags]\n```\n\n### Examples\n\n```\n  # Log in to reg.example.com\n  ko login reg.example.com -u AzureDiamond -p hunter2\n```\n\n### Options\n\n```\n  -h, --help              help for login\n  -p, --password string   Password\n      --password-stdin    Take the password from stdin\n  -u, --username string   Username\n```\n\n### Options inherited from parent commands\n\n```\n  -v, --verbose   Enable debug logs\n```\n\n### SEE ALSO\n\n* [ko](ko.md)\t - Rapidly iterate with Go, Containers, and Kubernetes.\n\n"
  },
  {
    "path": "docs/reference/ko_resolve.md",
    "content": "## ko resolve\n\nPrint the input files with image references resolved to built/pushed image digests.\n\n### Synopsis\n\nThis sub-command finds import path references within the provided files, builds them into Go binaries, containerizes them, publishes them, and prints the resulting yaml.\n\n```\nko resolve -f FILENAME [flags]\n```\n\n### Examples\n\n```\n\n  # Build and publish import path references to a Docker\n  # Registry as:\n  #   ${KO_DOCKER_REPO}/<package name>-<hash of import path>\n  # When KO_DOCKER_REPO is ko.local, it is the same as if\n  # --local and --preserve-import-paths were passed.\n  ko resolve -f config/\n\n  # Build and publish import path references to a Docker\n  # Registry preserving import path names as:\n  #   ${KO_DOCKER_REPO}/<import path>\n  # When KO_DOCKER_REPO is ko.local, it is the same as if\n  # --local was passed.\n  ko resolve --preserve-import-paths -f config/\n\n  # Build and publish import path references to a Docker\n  # daemon as:\n  #   ko.local/<import path>\n  # This always preserves import paths.\n  ko resolve --local -f config/\n```\n\n### Options\n\n```\n      --bare                       Whether to just use KO_DOCKER_REPO without additional context (may not work properly with --tags).\n  -B, --base-import-paths          Whether to use the base path without MD5 hash after KO_DOCKER_REPO (may not work properly with --tags).\n      --debug                      Include Delve debugger into image and wrap around ko-app. This debugger will listen to port 40000.\n      --disable-optimizations      Disable optimizations when building Go code. Useful when you want to interactively debug the created container.\n  -f, --filename strings           Filename, directory, or URL to files to use to create the resource\n  -h, --help                       help for resolve\n      --image-annotation strings   Which annotations (key=value[,key=value]) to add to the OCI manifest.\n      --image-label strings        Which labels (key=value[,key=value]) to add to the image.\n      --image-refs string          Path to file where a list of the published image references will be written.\n      --image-user string          The default user the image should be run as.\n      --insecure-registry          Whether to skip TLS verification on the registry\n  -j, --jobs int                   The maximum number of concurrent builds (default GOMAXPROCS)\n  -L, --local                      Load into images to local docker daemon.\n      --oci-layout-path string     Path to save the OCI image layout of the built images\n      --platform strings           Which platform to use when pulling a multi-platform base. Format: all | <os>[/<arch>[/<variant>]][,platform]*\n  -P, --preserve-import-paths      Whether to preserve the full import path after KO_DOCKER_REPO.\n      --push                       Push images to KO_DOCKER_REPO (default true)\n  -R, --recursive                  Process the directory used in -f, --filename recursively. Useful when you want to manage related manifests organized within the same directory.\n      --sbom string                The SBOM media type to use (none will disable SBOM synthesis and upload). (default \"spdx\")\n      --sbom-dir string            Path to file where the SBOM will be written.\n  -l, --selector string            Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)\n      --tag-only                   Include tags but not digests in resolved image references. Useful when digests are not preserved when images are repopulated.\n  -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])\n      --tarball string             File to save images tarballs\n```\n\n### Options inherited from parent commands\n\n```\n  -v, --verbose   Enable debug logs\n```\n\n### SEE ALSO\n\n* [ko](ko.md)\t - Rapidly iterate with Go, Containers, and Kubernetes.\n\n"
  },
  {
    "path": "docs/reference/ko_run.md",
    "content": "## ko run\n\nA variant of `kubectl run` that containerizes IMPORTPATH first.\n\n### Synopsis\n\nThis sub-command combines \"ko build\" and \"kubectl run\" to support containerizing and running Go binaries on Kubernetes in a single command.\n\n```\nko run IMPORTPATH [flags]\n```\n\n### Examples\n\n```\n\n  # Publish the image and run it on Kubernetes as:\n  #   ${KO_DOCKER_REPO}/<package name>-<hash of import path>\n  # When KO_DOCKER_REPO is ko.local, it is the same as if\n  # --local and --preserve-import-paths were passed.\n  ko run github.com/foo/bar/cmd/baz\n\n  # This supports relative import paths as well.\n  ko run ./cmd/baz\n\n  # You can also supply args and flags to the command.\n  ko run ./cmd/baz -- -v arg1 arg2 --yes\n```\n\n### Options\n\n```\n      --bare                       Whether to just use KO_DOCKER_REPO without additional context (may not work properly with --tags).\n  -B, --base-import-paths          Whether to use the base path without MD5 hash after KO_DOCKER_REPO (may not work properly with --tags).\n      --debug                      Include Delve debugger into image and wrap around ko-app. This debugger will listen to port 40000.\n      --disable-optimizations      Disable optimizations when building Go code. Useful when you want to interactively debug the created container.\n  -h, --help                       help for run\n      --image-annotation strings   Which annotations (key=value[,key=value]) to add to the OCI manifest.\n      --image-label strings        Which labels (key=value[,key=value]) to add to the image.\n      --image-refs string          Path to file where a list of the published image references will be written.\n      --image-user string          The default user the image should be run as.\n      --insecure-registry          Whether to skip TLS verification on the registry\n  -j, --jobs int                   The maximum number of concurrent builds (default GOMAXPROCS)\n  -L, --local                      Load into images to local docker daemon.\n      --oci-layout-path string     Path to save the OCI image layout of the built images\n      --platform strings           Which platform to use when pulling a multi-platform base. Format: all | <os>[/<arch>[/<variant>]][,platform]*\n  -P, --preserve-import-paths      Whether to preserve the full import path after KO_DOCKER_REPO.\n      --push                       Push images to KO_DOCKER_REPO (default true)\n      --sbom string                The SBOM media type to use (none will disable SBOM synthesis and upload). (default \"spdx\")\n      --sbom-dir string            Path to file where the SBOM will be written.\n      --tag-only                   Include tags but not digests in resolved image references. Useful when digests are not preserved when images are repopulated.\n  -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])\n      --tarball string             File to save images tarballs\n```\n\n### Options inherited from parent commands\n\n```\n  -v, --verbose   Enable debug logs\n```\n\n### SEE ALSO\n\n* [ko](ko.md)\t - Rapidly iterate with Go, Containers, and Kubernetes.\n\n"
  },
  {
    "path": "docs/reference/ko_version.md",
    "content": "## ko version\n\nPrint ko version.\n\n```\nko version [flags]\n```\n\n### Options\n\n```\n  -h, --help   help for version\n```\n\n### Options inherited from parent commands\n\n```\n  -v, --verbose   Enable debug logs\n```\n\n### SEE ALSO\n\n* [ko](ko.md)\t - Rapidly iterate with Go, Containers, and Kubernetes.\n\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/google/ko\n\ngo 1.25.7\n\nrequire (\n\tgithub.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.12.0\n\tgithub.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589\n\tgithub.com/dprotaso/go-yit v0.0.0-20260209000607-dfb86291624d\n\tgithub.com/go-training/helloworld v0.0.0-20200225145412-ba5f4379d78b\n\tgithub.com/go-viper/mapstructure/v2 v2.5.0\n\tgithub.com/google/go-cmp v0.7.0\n\tgithub.com/google/go-containerregistry v0.21.3\n\tgithub.com/moby/moby/api v1.54.0\n\tgithub.com/moby/moby/client v0.3.0\n\tgithub.com/opencontainers/image-spec v1.1.1\n\tgithub.com/sigstore/cosign/v3 v3.0.5\n\tgithub.com/spf13/cobra v1.10.2\n\tgithub.com/spf13/viper v1.21.0\n\tgithub.com/stretchr/testify v1.11.1\n\tgo.uber.org/automaxprocs v1.6.0\n\tgo.yaml.in/yaml/v4 v4.0.0-rc.4\n\tgolang.org/x/sync v0.20.0\n\tgolang.org/x/tools v0.43.0\n\tk8s.io/apimachinery v0.35.3\n\tsigs.k8s.io/kind v0.31.0\n)\n\nrequire (\n\tal.essio.dev/pkg/shellescape v1.6.0 // indirect\n\tcloud.google.com/go/compute/metadata v0.9.0 // indirect\n\tgithub.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect\n\tgithub.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect\n\tgithub.com/Azure/go-autorest v14.2.0+incompatible // indirect\n\tgithub.com/Azure/go-autorest/autorest v0.11.30 // indirect\n\tgithub.com/Azure/go-autorest/autorest/adal v0.9.24 // indirect\n\tgithub.com/Azure/go-autorest/autorest/azure/auth v0.5.13 // indirect\n\tgithub.com/Azure/go-autorest/autorest/azure/cli v0.4.7 // indirect\n\tgithub.com/Azure/go-autorest/autorest/date v0.3.1 // indirect\n\tgithub.com/Azure/go-autorest/logger v0.2.2 // indirect\n\tgithub.com/Azure/go-autorest/tracing v0.6.1 // indirect\n\tgithub.com/BurntSushi/toml v1.6.0 // indirect\n\tgithub.com/Microsoft/go-winio v0.6.2 // indirect\n\tgithub.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect\n\tgithub.com/aws/aws-sdk-go-v2 v1.41.3 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/config v1.32.11 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/credentials v1.19.11 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.19 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/internal/configsources v1.4.19 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.19 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/internal/ini v1.8.5 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/ecr v1.56.0 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/ecrpublic v1.38.11 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.6 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.19 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/signin v1.0.7 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/sso v1.30.12 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.16 // indirect\n\tgithub.com/aws/aws-sdk-go-v2/service/sts v1.41.8 // indirect\n\tgithub.com/aws/smithy-go v1.24.2 // indirect\n\tgithub.com/blang/semver v3.5.1+incompatible // indirect\n\tgithub.com/cenkalti/backoff/v5 v5.0.3 // indirect\n\tgithub.com/cespare/xxhash/v2 v2.3.0 // indirect\n\tgithub.com/containerd/errdefs v1.0.0 // indirect\n\tgithub.com/containerd/errdefs/pkg v0.3.0 // indirect\n\tgithub.com/containerd/stargz-snapshotter/estargz v0.18.2 // indirect\n\tgithub.com/coreos/go-oidc/v3 v3.17.0 // indirect\n\tgithub.com/cpuguy83/go-md2man/v2 v2.0.7 // indirect\n\tgithub.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 // indirect\n\tgithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect\n\tgithub.com/digitorus/pkcs7 v0.0.0-20250730155240-ffadbf3f398c // indirect\n\tgithub.com/digitorus/timestamp v0.0.0-20250524132541-c45532741eea // indirect\n\tgithub.com/dimchansky/utfbom v1.1.1 // indirect\n\tgithub.com/distribution/reference v0.6.0 // indirect\n\tgithub.com/docker/cli v29.3.0+incompatible // indirect\n\tgithub.com/docker/distribution v2.8.3+incompatible // indirect\n\tgithub.com/docker/docker-credential-helpers v0.9.5 // indirect\n\tgithub.com/docker/go-connections v0.6.0 // indirect\n\tgithub.com/docker/go-units v0.5.0 // indirect\n\tgithub.com/dustin/go-humanize v1.0.1 // indirect\n\tgithub.com/evanphx/json-patch/v5 v5.9.11 // indirect\n\tgithub.com/felixge/httpsnoop v1.0.4 // indirect\n\tgithub.com/fsnotify/fsnotify v1.9.0 // indirect\n\tgithub.com/go-jose/go-jose/v4 v4.1.3 // indirect\n\tgithub.com/go-logr/logr v1.4.3 // indirect\n\tgithub.com/go-logr/stdr v1.2.2 // indirect\n\tgithub.com/go-openapi/analysis v0.24.3 // indirect\n\tgithub.com/go-openapi/errors v0.22.7 // indirect\n\tgithub.com/go-openapi/jsonpointer v0.22.5 // indirect\n\tgithub.com/go-openapi/jsonreference v0.21.5 // indirect\n\tgithub.com/go-openapi/loads v0.23.3 // indirect\n\tgithub.com/go-openapi/runtime v0.29.3 // indirect\n\tgithub.com/go-openapi/spec v0.22.4 // indirect\n\tgithub.com/go-openapi/strfmt v0.26.0 // indirect\n\tgithub.com/go-openapi/swag v0.25.5 // indirect\n\tgithub.com/go-openapi/swag/cmdutils v0.25.5 // indirect\n\tgithub.com/go-openapi/swag/conv v0.25.5 // indirect\n\tgithub.com/go-openapi/swag/fileutils v0.25.5 // indirect\n\tgithub.com/go-openapi/swag/jsonname v0.25.5 // indirect\n\tgithub.com/go-openapi/swag/jsonutils v0.25.5 // indirect\n\tgithub.com/go-openapi/swag/loading v0.25.5 // indirect\n\tgithub.com/go-openapi/swag/mangling v0.25.5 // indirect\n\tgithub.com/go-openapi/swag/netutils v0.25.5 // indirect\n\tgithub.com/go-openapi/swag/stringutils v0.25.5 // indirect\n\tgithub.com/go-openapi/swag/typeutils v0.25.5 // indirect\n\tgithub.com/go-openapi/swag/yamlutils v0.25.5 // indirect\n\tgithub.com/go-openapi/validate v0.25.2 // indirect\n\tgithub.com/golang-jwt/jwt/v4 v4.5.2 // indirect\n\tgithub.com/google/certificate-transparency-go v1.3.3 // indirect\n\tgithub.com/google/uuid v1.6.0 // indirect\n\tgithub.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 // indirect\n\tgithub.com/hashicorp/go-cleanhttp v0.5.2 // indirect\n\tgithub.com/hashicorp/go-retryablehttp v0.7.8 // indirect\n\tgithub.com/in-toto/attestation v1.1.2 // indirect\n\tgithub.com/in-toto/in-toto-golang v0.10.0 // indirect\n\tgithub.com/inconshreveable/mousetrap v1.1.0 // indirect\n\tgithub.com/jedisct1/go-minisign v0.0.0-20241212093149-d2f9f49435c7 // indirect\n\tgithub.com/klauspost/compress v1.18.4 // indirect\n\tgithub.com/letsencrypt/boulder v0.20251208.0 // indirect\n\tgithub.com/mattn/go-isatty v0.0.20 // indirect\n\tgithub.com/mitchellh/go-homedir v1.1.0 // indirect\n\tgithub.com/moby/docker-image-spec v1.3.1 // indirect\n\tgithub.com/moby/term v0.5.2 // indirect\n\tgithub.com/oklog/ulid/v2 v2.1.1 // indirect\n\tgithub.com/opencontainers/go-digest v1.0.0 // indirect\n\tgithub.com/pelletier/go-toml v1.9.5 // indirect\n\tgithub.com/pelletier/go-toml/v2 v2.2.4 // indirect\n\tgithub.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect\n\tgithub.com/pkg/errors v0.9.1 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect\n\tgithub.com/russross/blackfriday/v2 v2.1.0 // indirect\n\tgithub.com/sagikazarmark/locafero v0.12.0 // indirect\n\tgithub.com/sassoftware/relic v7.2.1+incompatible // indirect\n\tgithub.com/secure-systems-lab/go-securesystemslib v0.10.0 // indirect\n\tgithub.com/shibumi/go-pathspec v1.3.0 // indirect\n\tgithub.com/sigstore/protobuf-specs v0.5.0 // indirect\n\tgithub.com/sigstore/rekor v1.5.1 // indirect\n\tgithub.com/sigstore/rekor-tiles/v2 v2.2.1 // indirect\n\tgithub.com/sigstore/sigstore v1.10.4 // indirect\n\tgithub.com/sigstore/sigstore-go v1.1.4 // indirect\n\tgithub.com/sigstore/timestamp-authority/v2 v2.0.5 // indirect\n\tgithub.com/sirupsen/logrus v1.9.4 // indirect\n\tgithub.com/spf13/afero v1.15.0 // indirect\n\tgithub.com/spf13/cast v1.10.0 // indirect\n\tgithub.com/spf13/pflag v1.0.10 // indirect\n\tgithub.com/subosito/gotenv v1.6.0 // indirect\n\tgithub.com/theupdateframework/go-tuf v0.7.0 // indirect\n\tgithub.com/theupdateframework/go-tuf/v2 v2.4.1 // indirect\n\tgithub.com/transparency-dev/formats v0.1.0 // indirect\n\tgithub.com/transparency-dev/merkle v0.0.2 // indirect\n\tgithub.com/vbatts/tar-split v0.12.2 // indirect\n\tgo.opentelemetry.io/auto/sdk v1.2.1 // indirect\n\tgo.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.64.0 // indirect\n\tgo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 // indirect\n\tgo.opentelemetry.io/otel v1.42.0 // indirect\n\tgo.opentelemetry.io/otel/metric v1.42.0 // indirect\n\tgo.opentelemetry.io/otel/trace v1.42.0 // indirect\n\tgo.yaml.in/yaml/v2 v2.4.4 // indirect\n\tgo.yaml.in/yaml/v3 v3.0.4 // indirect\n\tgolang.org/x/crypto v0.49.0 // indirect\n\tgolang.org/x/mod v0.34.0 // indirect\n\tgolang.org/x/net v0.52.0 // indirect\n\tgolang.org/x/oauth2 v0.36.0 // indirect\n\tgolang.org/x/sys v0.42.0 // indirect\n\tgolang.org/x/term v0.41.0 // indirect\n\tgolang.org/x/text v0.35.0 // indirect\n\tgoogle.golang.org/genproto/googleapis/api v0.0.0-20260311181403-84a4fc48630c // indirect\n\tgoogle.golang.org/genproto/googleapis/rpc v0.0.0-20260311181403-84a4fc48630c // indirect\n\tgoogle.golang.org/grpc v1.79.3 // indirect\n\tgoogle.golang.org/protobuf v1.36.11 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n\tk8s.io/klog/v2 v2.140.0 // indirect\n\tk8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 // indirect\n\tsigs.k8s.io/yaml v1.6.0 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "al.essio.dev/pkg/shellescape v1.6.0 h1:NxFcEqzFSEVCGN2yq7Huv/9hyCEGVa/TncnOOBBeXHA=\nal.essio.dev/pkg/shellescape v1.6.0/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890=\ncloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE=\ncloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU=\ncloud.google.com/go/auth v0.18.2 h1:+Nbt5Ev0xEqxlNjd6c+yYUeosQ5TtEUaNcN/3FozlaM=\ncloud.google.com/go/auth v0.18.2/go.mod h1:xD+oY7gcahcu7G2SG2DsBerfFxgPAJz17zz2joOFF3M=\ncloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=\ncloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=\ncloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=\ncloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=\ncloud.google.com/go/iam v1.5.3 h1:+vMINPiDF2ognBJ97ABAYYwRgsaqxPbQDlMnbHMjolc=\ncloud.google.com/go/iam v1.5.3/go.mod h1:MR3v9oLkZCTlaqljW6Eb2d3HGDGK5/bDv93jhfISFvU=\ncloud.google.com/go/kms v1.25.0 h1:gVqvGGUmz0nYCmtoxWmdc1wli2L1apgP8U4fghPGSbQ=\ncloud.google.com/go/kms v1.25.0/go.mod h1:XIdHkzfj0bUO3E+LvwPg+oc7s58/Ns8Nd8Sdtljihbk=\ncloud.google.com/go/longrunning v0.8.0 h1:LiKK77J3bx5gDLi4SMViHixjD2ohlkwBi+mKA7EhfW8=\ncloud.google.com/go/longrunning v0.8.0/go.mod h1:UmErU2Onzi+fKDg2gR7dusz11Pe26aknR4kHmJJqIfk=\nfilippo.io/edwards25519 v1.1.1 h1:YpjwWWlNmGIDyXOn8zLzqiD+9TyIlPhGFG96P39uBpw=\nfilippo.io/edwards25519 v1.1.1/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=\ngithub.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230919221257-8b5d3ce2d11d h1:zjqpY4C7H15HjRPEenkS4SAn3Jy2eRRjkjZbGR30TOg=\ngithub.com/AdamKorcz/go-fuzz-headers-1 v0.0.0-20230919221257-8b5d3ce2d11d/go.mod h1:XNqJ7hv2kY++g8XEHREpi+JqZo3+0l+CH2egBVN4yqM=\ngithub.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU=\ngithub.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc=\ngithub.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0 h1:fou+2+WFTib47nS+nz/ozhEBnvU96bKHy6LjRsY4E28=\ngithub.com/Azure/azure-sdk-for-go/sdk/azcore v1.21.0/go.mod h1:t76Ruy8AHvUAC8GfMWJMa0ElSbuIcO03NLpynfbgsPA=\ngithub.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1 h1:Hk5QBxZQC1jb2Fwj6mpzme37xbCDdNTxU7O9eb5+LB4=\ngithub.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1/go.mod h1:IYus9qsFobWIc2YVwe/WPjcnyCkPKtnHAqUYeebc8z0=\ngithub.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 h1:9iefClla7iYpfYWdzPCRDozdmndjTm8DXdpCzPajMgA=\ngithub.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI=\ngithub.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.4.0 h1:E4MgwLBGeVB5f2MdcIVD3ELVAWpr+WD6MUe1i+tM/PA=\ngithub.com/Azure/azure-sdk-for-go/sdk/security/keyvault/azkeys v1.4.0/go.mod h1:Y2b/1clN4zsAoUd/pgNAQHjLDnTis/6ROkUfyob6psM=\ngithub.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0 h1:nCYfgcSyHZXJI8J0IWE5MsCGlb2xp9fJiXyxWgmOFg4=\ngithub.com/Azure/azure-sdk-for-go/sdk/security/keyvault/internal v1.2.0/go.mod h1:ucUjca2JtSZboY8IoUqyQyuuXvwbMBVwFOm0vdQPNhA=\ngithub.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=\ngithub.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=\ngithub.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs=\ngithub.com/Azure/go-autorest v14.2.0+incompatible/go.mod h1:r+4oMnoxhatjLLJ6zxSWATqVooLgysK6ZNox3g/xq24=\ngithub.com/Azure/go-autorest/autorest v0.11.28/go.mod h1:MrkzG3Y3AH668QyF9KRk5neJnGgmhQ6krbhR8Q5eMvA=\ngithub.com/Azure/go-autorest/autorest v0.11.30 h1:iaZ1RGz/ALZtN5eq4Nr1SOFSlf2E4pDI3Tcsl+dZPVE=\ngithub.com/Azure/go-autorest/autorest v0.11.30/go.mod h1:t1kpPIOpIVX7annvothKvb0stsrXa37i7b+xpmBW8Fs=\ngithub.com/Azure/go-autorest/autorest/adal v0.9.18/go.mod h1:XVVeme+LZwABT8K5Lc3hA4nAe8LDBVle26gTrguhhPQ=\ngithub.com/Azure/go-autorest/autorest/adal v0.9.22/go.mod h1:XuAbAEUv2Tta//+voMI038TrJBqjKam0me7qR+L8Cmk=\ngithub.com/Azure/go-autorest/autorest/adal v0.9.24 h1:BHZfgGsGwdkHDyZdtQRQk1WeUdW0m2WPAwuHZwUi5i4=\ngithub.com/Azure/go-autorest/autorest/adal v0.9.24/go.mod h1:7T1+g0PYFmACYW5LlG2fcoPiPlFHjClyRGL7dRlP5c8=\ngithub.com/Azure/go-autorest/autorest/azure/auth v0.5.13 h1:Ov8avRZi2vmrE2JcXw+tu5K/yB41r7xK9GZDiBF7NdM=\ngithub.com/Azure/go-autorest/autorest/azure/auth v0.5.13/go.mod h1:5BAVfWLWXihP47vYrPuBKKf4cS0bXI+KM9Qx6ETDJYo=\ngithub.com/Azure/go-autorest/autorest/azure/cli v0.4.6/go.mod h1:piCfgPho7BiIDdEQ1+g4VmKyD5y+p/XtSNqE6Hc4QD0=\ngithub.com/Azure/go-autorest/autorest/azure/cli v0.4.7 h1:Q9R3utmFg9K1B4OYtAZ7ZUUvIUdzQt7G2MN5Hi/d670=\ngithub.com/Azure/go-autorest/autorest/azure/cli v0.4.7/go.mod h1:bVrAueELJ0CKLBpUHDIvD516TwmHmzqwCpvONWRsw3s=\ngithub.com/Azure/go-autorest/autorest/date v0.3.0/go.mod h1:BI0uouVdmngYNUzGWeSYnokU+TrmwEsOqdt8Y6sso74=\ngithub.com/Azure/go-autorest/autorest/date v0.3.1 h1:o9Z8Jyt+VJJTCZ/UORishuHOusBwolhjokt9s5k8I4w=\ngithub.com/Azure/go-autorest/autorest/date v0.3.1/go.mod h1:Dz/RDmXlfiFFS/eW+b/xMUSFs1tboPVy6UjgADToWDM=\ngithub.com/Azure/go-autorest/autorest/mocks v0.4.1/go.mod h1:LTp+uSrOhSkaKrUy935gNZuuIPPVsHlr9DSOxSayd+k=\ngithub.com/Azure/go-autorest/autorest/mocks v0.4.2 h1:PGN4EDXnuQbojHbU0UWoNvmu9AGVwYHG9/fkDYhtAfw=\ngithub.com/Azure/go-autorest/autorest/mocks v0.4.2/go.mod h1:Vy7OitM9Kei0i1Oj+LvyAWMXJHeKH1MVlzFugfVrmyU=\ngithub.com/Azure/go-autorest/logger v0.2.1/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8=\ngithub.com/Azure/go-autorest/logger v0.2.2 h1:hYqBsEBywrrOSW24kkOCXRcKfKhK76OzLTfF+MYDE2o=\ngithub.com/Azure/go-autorest/logger v0.2.2/go.mod h1:I5fg9K52o+iuydlWfa9T5K6WFos9XYr9dYTFzpqgibw=\ngithub.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU=\ngithub.com/Azure/go-autorest/tracing v0.6.1 h1:YUMSrC/CeD1ZnnXcNYU4a/fzsO35u2Fsful9L/2nyR0=\ngithub.com/Azure/go-autorest/tracing v0.6.1/go.mod h1:/3EgjbsjraOqiicERAeu3m7/z0x1TzjQGAwDrJrXGkc=\ngithub.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgvJqCH0sFfrBUTnUJSBrBf7++ypk+twtRs=\ngithub.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk=\ngithub.com/BurntSushi/toml v1.6.0 h1:dRaEfpa2VI55EwlIW72hMRHdWouJeRF7TPYhI+AUQjk=\ngithub.com/BurntSushi/toml v1.6.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=\ngithub.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=\ngithub.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=\ngithub.com/alessio/shellescape v1.4.1 h1:V7yhSDDn8LP4lc4jS8pFkt0zCnzVJlG5JXy9BVKJUX0=\ngithub.com/alessio/shellescape v1.4.1/go.mod h1:PZAiSCk0LJaZkiCSkPv8qIobYglO3FPpyFjDCtHLS30=\ngithub.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so=\ngithub.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw=\ngithub.com/aws/aws-sdk-go v1.55.8 h1:JRmEUbU52aJQZ2AjX4q4Wu7t4uZjOu71uyNmaWlUkJQ=\ngithub.com/aws/aws-sdk-go v1.55.8/go.mod h1:ZkViS9AqA6otK+JBBNH2++sx1sgxrPKcSzPPvQkUtXk=\ngithub.com/aws/aws-sdk-go-v2 v1.41.3 h1:4kQ/fa22KjDt13QCy1+bYADvdgcxpfH18f0zP542kZA=\ngithub.com/aws/aws-sdk-go-v2 v1.41.3/go.mod h1:mwsPRE8ceUUpiTgF7QmQIJ7lgsKUPQOUl3o72QBrE1o=\ngithub.com/aws/aws-sdk-go-v2/config v1.32.11 h1:ftxI5sgz8jZkckuUHXfC/wMUc8u3fG1vQS0plr2F2Zs=\ngithub.com/aws/aws-sdk-go-v2/config v1.32.11/go.mod h1:twF11+6ps9aNRKEDimksp923o44w/Thk9+8YIlzWMmo=\ngithub.com/aws/aws-sdk-go-v2/credentials v1.19.11 h1:NdV8cwCcAXrCWyxArt58BrvZJ9pZ9Fhf9w6Uh5W3Uyc=\ngithub.com/aws/aws-sdk-go-v2/credentials v1.19.11/go.mod h1:30yY2zqkMPdrvxBqzI9xQCM+WrlrZKSOpSJEsylVU+8=\ngithub.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.19 h1:INUvJxmhdEbVulJYHI061k4TVuS3jzzthNvjqvVvTKM=\ngithub.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.19/go.mod h1:FpZN2QISLdEBWkayloda+sZjVJL+e9Gl0k1SyTgcswU=\ngithub.com/aws/aws-sdk-go-v2/internal/configsources v1.4.19 h1:/sECfyq2JTifMI2JPyZ4bdRN77zJmr6SrS1eL3augIA=\ngithub.com/aws/aws-sdk-go-v2/internal/configsources v1.4.19/go.mod h1:dMf8A5oAqr9/oxOfLkC/c2LU/uMcALP0Rgn2BD5LWn0=\ngithub.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.19 h1:AWeJMk33GTBf6J20XJe6qZoRSJo0WfUhsMdUKhoODXE=\ngithub.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.19/go.mod h1:+GWrYoaAsV7/4pNHpwh1kiNLXkKaSoppxQq9lbH8Ejw=\ngithub.com/aws/aws-sdk-go-v2/internal/ini v1.8.5 h1:clHU5fm//kWS1C2HgtgWxfQbFbx4b6rx+5jzhgX9HrI=\ngithub.com/aws/aws-sdk-go-v2/internal/ini v1.8.5/go.mod h1:O3h0IK87yXci+kg6flUKzJnWeziQUKciKrLjcatSNcY=\ngithub.com/aws/aws-sdk-go-v2/service/ecr v1.56.0 h1:XxNya31nOtsClGghvQ2VkhIB2S/rggb64x5vkHl4xZQ=\ngithub.com/aws/aws-sdk-go-v2/service/ecr v1.56.0/go.mod h1:T+Tz2Xp1gnvtlgvP7OyRHlr84KtI3fZW5Ax/e+s9b64=\ngithub.com/aws/aws-sdk-go-v2/service/ecrpublic v1.38.11 h1:2T9NCuNzzBh6RUrwYZBFl1D9lLJ2r2CCbg7w383DjQE=\ngithub.com/aws/aws-sdk-go-v2/service/ecrpublic v1.38.11/go.mod h1:FkD34cqOmnqfAEiNHeqOT50SoXqHEgdDsa8BrMw9t+w=\ngithub.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.6 h1:XAq62tBTJP/85lFD5oqOOe7YYgWxY9LvWq8plyDvDVg=\ngithub.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.6/go.mod h1:x0nZssQ3qZSnIcePWLvcoFisRXJzcTVvYpAAdYX8+GI=\ngithub.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.19 h1:X1Tow7suZk9UCJHE1Iw9GMZJJl0dAnKXXP1NaSDHwmw=\ngithub.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.19/go.mod h1:/rARO8psX+4sfjUQXp5LLifjUt8DuATZ31WptNJTyQA=\ngithub.com/aws/aws-sdk-go-v2/service/kms v1.49.5 h1:DKibav4XF66XSeaXcrn9GlWGHos6D/vJ4r7jsK7z5CE=\ngithub.com/aws/aws-sdk-go-v2/service/kms v1.49.5/go.mod h1:1SdcmEGUEQE1mrU2sIgeHtcMSxHuybhPvuEPANzIDfI=\ngithub.com/aws/aws-sdk-go-v2/service/signin v1.0.7 h1:Y2cAXlClHsXkkOvWZFXATr34b0hxxloeQu/pAZz2row=\ngithub.com/aws/aws-sdk-go-v2/service/signin v1.0.7/go.mod h1:idzZ7gmDeqeNrSPkdbtMp9qWMgcBwykA7P7Rzh5DXVU=\ngithub.com/aws/aws-sdk-go-v2/service/sso v1.30.12 h1:iSsvB9EtQ09YrsmIc44Heqlx5ByGErqhPK1ZQLppias=\ngithub.com/aws/aws-sdk-go-v2/service/sso v1.30.12/go.mod h1:fEWYKTRGoZNl8tZ77i61/ccwOMJdGxwOhWCkp6TXAr0=\ngithub.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.16 h1:EnUdUqRP1CNzt2DkV67tJx6XDN4xlfBFm+bzeNOQVb0=\ngithub.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.16/go.mod h1:Jic/xv0Rq/pFNCh3WwpH4BEqdbSAl+IyHro8LbibHD8=\ngithub.com/aws/aws-sdk-go-v2/service/sts v1.41.8 h1:XQTQTF75vnug2TXS8m7CVJfC2nniYPZnO1D4Np761Oo=\ngithub.com/aws/aws-sdk-go-v2/service/sts v1.41.8/go.mod h1:Xgx+PR1NUOjNmQY+tRMnouRp83JRM8pRMw/vCaVhPkI=\ngithub.com/aws/smithy-go v1.24.2 h1:FzA3bu/nt/vDvmnkg+R8Xl46gmzEDam6mZ1hzmwXFng=\ngithub.com/aws/smithy-go v1.24.2/go.mod h1:YE2RhdIuDbA5E5bTdciG9KrW3+TiEONeUWCqxX9i1Fc=\ngithub.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.12.0 h1:JFWXO6QPihCknDdnL6VaQE57km4ZKheHIGd9YiOGcTo=\ngithub.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.12.0/go.mod h1:046/oLyFlYdAghYQE2yHXi/E//VM5Cf3/dFmA+3CZ0c=\ngithub.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ=\ngithub.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=\ngithub.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8=\ngithub.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE=\ngithub.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM=\ngithub.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw=\ngithub.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=\ngithub.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=\ngithub.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589 h1:krfRl01rzPzxSxyLyrChD+U+MzsBXbm0OwYYB67uF+4=\ngithub.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589/go.mod h1:OuDyvmLnMCwa2ep4Jkm6nyA0ocJuZlGyk2gGseVzERM=\ngithub.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE=\ngithub.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4=\ngithub.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=\ngithub.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M=\ngithub.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE=\ngithub.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk=\ngithub.com/containerd/stargz-snapshotter/estargz v0.18.2 h1:yXkZFYIzz3eoLwlTUZKz2iQ4MrckBxJjkmD16ynUTrw=\ngithub.com/containerd/stargz-snapshotter/estargz v0.18.2/go.mod h1:XyVU5tcJ3PRpkA9XS2T5us6Eg35yM0214Y+wvrZTBrY=\ngithub.com/coreos/go-oidc/v3 v3.17.0 h1:hWBGaQfbi0iVviX4ibC7bk8OKT5qNr4klBaCHVNvehc=\ngithub.com/coreos/go-oidc/v3 v3.17.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.7 h1:zbFlGlXEAKlwXpmvle3d8Oe3YnkKIK4xSRTd3sHPnBo=\ngithub.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g=\ngithub.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=\ngithub.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=\ngithub.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467 h1:uX1JmpONuD549D73r6cgnxyUu18Zb7yHAy5AYU0Pm4Q=\ngithub.com/cyberphone/json-canonicalization v0.0.0-20241213102144-19d51d7fe467/go.mod h1:uzvlm1mxhHkdfqitSA92i7Se+S9ksOn3a3qmv/kyOCw=\ngithub.com/danieljoos/wincred v1.2.3 h1:v7dZC2x32Ut3nEfRH+vhoZGvN72+dQ/snVXo/vMFLdQ=\ngithub.com/danieljoos/wincred v1.2.3/go.mod h1:6qqX0WNrS4RzPZ1tnroDzq9kY3fu1KwE7MRLQK4X0bs=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=\ngithub.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/digitorus/pkcs7 v0.0.0-20230713084857-e76b763bdc49/go.mod h1:SKVExuS+vpu2l9IoOc0RwqE7NYnb0JlcFHFnEJkVDzc=\ngithub.com/digitorus/pkcs7 v0.0.0-20250730155240-ffadbf3f398c h1:g349iS+CtAvba7i0Ee9EP1TlTZ9w+UncBY6HSmsFZa0=\ngithub.com/digitorus/pkcs7 v0.0.0-20250730155240-ffadbf3f398c/go.mod h1:mCGGmWkOQvEuLdIRfPIpXViBfpWto4AhwtJlAvo62SQ=\ngithub.com/digitorus/timestamp v0.0.0-20250524132541-c45532741eea h1:ALRwvjsSP53QmnN3Bcj0NpR8SsFLnskny/EIMebAk1c=\ngithub.com/digitorus/timestamp v0.0.0-20250524132541-c45532741eea/go.mod h1:GvWntX9qiTlOud0WkQ6ewFm0LPy5JUR1Xo0Ngbd1w6Y=\ngithub.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U=\ngithub.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=\ngithub.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=\ngithub.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=\ngithub.com/docker/cli v29.3.0+incompatible h1:z3iWveU7h19Pqx7alZES8j+IeFQZ1lhTwb2F+V9SVvk=\ngithub.com/docker/cli v29.3.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8=\ngithub.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=\ngithub.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=\ngithub.com/docker/docker-credential-helpers v0.9.5 h1:EFNN8DHvaiK8zVqFA2DT6BjXE0GzfLOZ38ggPTKePkY=\ngithub.com/docker/docker-credential-helpers v0.9.5/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c=\ngithub.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94=\ngithub.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE=\ngithub.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=\ngithub.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=\ngithub.com/dprotaso/go-yit v0.0.0-20260209000607-dfb86291624d h1:/USl0X37Afc2SyjRG4/eNrbm4CZRfZLdzwTy9YXxowA=\ngithub.com/dprotaso/go-yit v0.0.0-20260209000607-dfb86291624d/go.mod h1:k03zg0AFMepR2TrssNeMUISoI0QcX2N58Sl0qPU6MZs=\ngithub.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=\ngithub.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=\ngithub.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU=\ngithub.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM=\ngithub.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=\ngithub.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU=\ngithub.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=\ngithub.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=\ngithub.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=\ngithub.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=\ngithub.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=\ngithub.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=\ngithub.com/go-chi/chi v4.1.2+incompatible h1:fGFk2Gmi/YKXk0OmGfBh0WgmN3XB8lVnEyNz34tQRec=\ngithub.com/go-chi/chi/v5 v5.2.5 h1:Eg4myHZBjyvJmAFjFvWgrqDTXFyOzjj7YIm3L3mu6Ug=\ngithub.com/go-chi/chi/v5 v5.2.5/go.mod h1:X7Gx4mteadT3eDOMTsXzmI4/rwUpOwBHLpAfupzFJP0=\ngithub.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs=\ngithub.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=\ngithub.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=\ngithub.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=\ngithub.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=\ngithub.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=\ngithub.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=\ngithub.com/go-openapi/analysis v0.24.3 h1:a1hrvMr8X0Xt69KP5uVTu5jH62DscmDifrLzNglAayk=\ngithub.com/go-openapi/analysis v0.24.3/go.mod h1:Nc+dWJ/FxZbhSow5Yh3ozg5CLJioB+XXT6MdLvJUsUw=\ngithub.com/go-openapi/errors v0.22.7 h1:JLFBGC0Apwdzw3484MmBqspjPbwa2SHvpDm0u5aGhUA=\ngithub.com/go-openapi/errors v0.22.7/go.mod h1://QW6SD9OsWtH6gHllUCddOXDL0tk0ZGNYHwsw4sW3w=\ngithub.com/go-openapi/jsonpointer v0.22.5 h1:8on/0Yp4uTb9f4XvTrM2+1CPrV05QPZXu+rvu2o9jcA=\ngithub.com/go-openapi/jsonpointer v0.22.5/go.mod h1:gyUR3sCvGSWchA2sUBJGluYMbe1zazrYWIkWPjjMUY0=\ngithub.com/go-openapi/jsonreference v0.21.5 h1:6uCGVXU/aNF13AQNggxfysJ+5ZcU4nEAe+pJyVWRdiE=\ngithub.com/go-openapi/jsonreference v0.21.5/go.mod h1:u25Bw85sX4E2jzFodh1FOKMTZLcfifd1Q+iKKOUxExw=\ngithub.com/go-openapi/loads v0.23.3 h1:g5Xap1JfwKkUnZdn+S0L3SzBDpcTIYzZ5Qaag0YDkKQ=\ngithub.com/go-openapi/loads v0.23.3/go.mod h1:NOH07zLajXo8y55hom0omlHWDVVvCwBM/S+csCK8LqA=\ngithub.com/go-openapi/runtime v0.29.3 h1:h5twGaEqxtQg40ePiYm9vFFH1q06Czd7Ot6ufdK0w/Y=\ngithub.com/go-openapi/runtime v0.29.3/go.mod h1:8A1W0/L5eyNJvKciqZtvIVQvYO66NlB7INMSZ9bw/oI=\ngithub.com/go-openapi/spec v0.22.4 h1:4pxGjipMKu0FzFiu/DPwN3CTBRlVM2yLf/YTWorYfDQ=\ngithub.com/go-openapi/spec v0.22.4/go.mod h1:WQ6Ai0VPWMZgMT4XySjlRIE6GP1bGQOtEThn3gcWLtQ=\ngithub.com/go-openapi/strfmt v0.26.0 h1:SDdQLyOEqu8W96rO1FRG1fuCtVyzmukky0zcD6gMGLU=\ngithub.com/go-openapi/strfmt v0.26.0/go.mod h1:Zslk5VZPOISLwmWTMBIS7oiVFem1o1EI6zULY8Uer7Y=\ngithub.com/go-openapi/swag v0.25.5 h1:pNkwbUEeGwMtcgxDr+2GBPAk4kT+kJ+AaB+TMKAg+TU=\ngithub.com/go-openapi/swag v0.25.5/go.mod h1:B3RT6l8q7X803JRxa2e59tHOiZlX1t8viplOcs9CwTA=\ngithub.com/go-openapi/swag/cmdutils v0.25.5 h1:yh5hHrpgsw4NwM9KAEtaDTXILYzdXh/I8Whhx9hKj7c=\ngithub.com/go-openapi/swag/cmdutils v0.25.5/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0=\ngithub.com/go-openapi/swag/conv v0.25.5 h1:wAXBYEXJjoKwE5+vc9YHhpQOFj2JYBMF2DUi+tGu97g=\ngithub.com/go-openapi/swag/conv v0.25.5/go.mod h1:CuJ1eWvh1c4ORKx7unQnFGyvBbNlRKbnRyAvDvzWA4k=\ngithub.com/go-openapi/swag/fileutils v0.25.5 h1:B6JTdOcs2c0dBIs9HnkyTW+5gC+8NIhVBUwERkFhMWk=\ngithub.com/go-openapi/swag/fileutils v0.25.5/go.mod h1:V3cT9UdMQIaH4WiTrUc9EPtVA4txS0TOmRURmhGF4kc=\ngithub.com/go-openapi/swag/jsonname v0.25.5 h1:8p150i44rv/Drip4vWI3kGi9+4W9TdI3US3uUYSFhSo=\ngithub.com/go-openapi/swag/jsonname v0.25.5/go.mod h1:jNqqikyiAK56uS7n8sLkdaNY/uq6+D2m2LANat09pKU=\ngithub.com/go-openapi/swag/jsonutils v0.25.5 h1:XUZF8awQr75MXeC+/iaw5usY/iM7nXPDwdG3Jbl9vYo=\ngithub.com/go-openapi/swag/jsonutils v0.25.5/go.mod h1:48FXUaz8YsDAA9s5AnaUvAmry1UcLcNVWUjY42XkrN4=\ngithub.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5 h1:SX6sE4FrGb4sEnnxbFL/25yZBb5Hcg1inLeErd86Y1U=\ngithub.com/go-openapi/swag/jsonutils/fixtures_test v0.25.5/go.mod h1:/2KvOTrKWjVA5Xli3DZWdMCZDzz3uV/T7bXwrKWPquo=\ngithub.com/go-openapi/swag/loading v0.25.5 h1:odQ/umlIZ1ZVRteI6ckSrvP6e2w9UTF5qgNdemJHjuU=\ngithub.com/go-openapi/swag/loading v0.25.5/go.mod h1:I8A8RaaQ4DApxhPSWLNYWh9NvmX2YKMoB9nwvv6oW6g=\ngithub.com/go-openapi/swag/mangling v0.25.5 h1:hyrnvbQRS7vKePQPHHDso+k6CGn5ZBs5232UqWZmJZw=\ngithub.com/go-openapi/swag/mangling v0.25.5/go.mod h1:6hadXM/o312N/h98RwByLg088U61TPGiltQn71Iw0NY=\ngithub.com/go-openapi/swag/netutils v0.25.5 h1:LZq2Xc2QI8+7838elRAaPCeqJnHODfSyOa7ZGfxDKlU=\ngithub.com/go-openapi/swag/netutils v0.25.5/go.mod h1:lHbtmj4m57APG/8H7ZcMMSWzNqIQcu0RFiXrPUara14=\ngithub.com/go-openapi/swag/stringutils v0.25.5 h1:NVkoDOA8YBgtAR/zvCx5rhJKtZF3IzXcDdwOsYzrB6M=\ngithub.com/go-openapi/swag/stringutils v0.25.5/go.mod h1:PKK8EZdu4QJq8iezt17HM8RXnLAzY7gW0O1KKarrZII=\ngithub.com/go-openapi/swag/typeutils v0.25.5 h1:EFJ+PCga2HfHGdo8s8VJXEVbeXRCYwzzr9u4rJk7L7E=\ngithub.com/go-openapi/swag/typeutils v0.25.5/go.mod h1:itmFmScAYE1bSD8C4rS0W+0InZUBrB2xSPbWt6DLGuc=\ngithub.com/go-openapi/swag/yamlutils v0.25.5 h1:kASCIS+oIeoc55j28T4o8KwlV2S4ZLPT6G0iq2SSbVQ=\ngithub.com/go-openapi/swag/yamlutils v0.25.5/go.mod h1:Gek1/SjjfbYvM+Iq4QGwa/2lEXde9n2j4a3wI3pNuOQ=\ngithub.com/go-openapi/testify/enable/yaml/v2 v2.4.1 h1:NZOrZmIb6PTv5LTFxr5/mKV/FjbUzGE7E6gLz7vFoOQ=\ngithub.com/go-openapi/testify/enable/yaml/v2 v2.4.1/go.mod h1:r7dwsujEHawapMsxA69i+XMGZrQ5tRauhLAjV/sxg3Q=\ngithub.com/go-openapi/testify/v2 v2.4.1 h1:zB34HDKj4tHwyUQHrUkpV0Q0iXQ6dUCOQtIqn8hE6Iw=\ngithub.com/go-openapi/testify/v2 v2.4.1/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54=\ngithub.com/go-openapi/validate v0.25.2 h1:12NsfLAwGegqbGWr2CnvT65X/Q2USJipmJ9b7xDJZz0=\ngithub.com/go-openapi/validate v0.25.2/go.mod h1:Pgl1LpPPGFnZ+ys4/hTlDiRYQdI1ocKypgE+8Q8BLfY=\ngithub.com/go-rod/rod v0.116.2 h1:A5t2Ky2A+5eD/ZJQr1EfsQSe5rms5Xof/qj296e+ZqA=\ngithub.com/go-rod/rod v0.116.2/go.mod h1:H+CMO9SCNc2TJ2WfrG+pKhITz57uGNYU43qYHh438Mg=\ngithub.com/go-sql-driver/mysql v1.9.3 h1:U/N249h2WzJ3Ukj8SowVFjdtZKfu9vlLZxjPXV1aweo=\ngithub.com/go-sql-driver/mysql v1.9.3/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=\ngithub.com/go-training/helloworld v0.0.0-20200225145412-ba5f4379d78b h1:0pOrjn0UzTcHdhDVdxrH8LwM7QLnAp8qiUtwXM04JEE=\ngithub.com/go-training/helloworld v0.0.0-20200225145412-ba5f4379d78b/go.mod h1:hGGmX3bRUkYkc9aKA6mkUxi6d+f1GmZF1je0FlVTgwU=\ngithub.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=\ngithub.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=\ngithub.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk=\ngithub.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=\ngithub.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=\ngithub.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg=\ngithub.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=\ngithub.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI=\ngithub.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0=\ngithub.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo=\ngithub.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=\ngithub.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=\ngithub.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=\ngithub.com/google/certificate-transparency-go v1.3.3 h1:hq/rSxztSkXN2tx/3jQqF6Xc0O565UQPdHrOWvZwybo=\ngithub.com/google/certificate-transparency-go v1.3.3/go.mod h1:iR17ZgSaXRzSa5qvjFl8TnVD5h8ky2JMVio+dzoKMgA=\ngithub.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=\ngithub.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=\ngithub.com/google/go-containerregistry v0.21.3 h1:Xr+yt3VvwOOn/5nJzd7UoOhwPGiPkYW0zWDLLUXqAi4=\ngithub.com/google/go-containerregistry v0.21.3/go.mod h1:D5ZrJF1e6dMzvInpBPuMCX0FxURz7GLq2rV3Us9aPkc=\ngithub.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=\ngithub.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=\ngithub.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=\ngithub.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=\ngithub.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4=\ngithub.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ=\ngithub.com/google/trillian v1.7.2 h1:EPBxc4YWY4Ak8tcuhyFleY+zYlbCDCa4Sn24e1Ka8Js=\ngithub.com/google/trillian v1.7.2/go.mod h1:mfQJW4qRH6/ilABtPYNBerVJAJ/upxHLX81zxNQw05s=\ngithub.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=\ngithub.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/googleapis/enterprise-certificate-proxy v0.3.12 h1:Fg+zsqzYEs1ZnvmcztTYxhgCBsx3eEhEwQ1W/lHq/sQ=\ngithub.com/googleapis/enterprise-certificate-proxy v0.3.12/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg=\ngithub.com/googleapis/gax-go/v2 v2.17.0 h1:RksgfBpxqff0EZkDWYuz9q/uWsTVz+kf43LsZ1J6SMc=\ngithub.com/googleapis/gax-go/v2 v2.17.0/go.mod h1:mzaqghpQp4JDh3HvADwrat+6M3MOIDp5YKHhb9PAgDY=\ngithub.com/grpc-ecosystem/go-grpc-middleware v1.4.0 h1:UH//fgunKIs4JdUbpDl1VZCDaL56wXCB/5+wF6uHfaI=\ngithub.com/grpc-ecosystem/go-grpc-middleware v1.4.0/go.mod h1:g5qyo/la0ALbONm6Vbp88Yd8NsDy6rZz+RcrMPxvld8=\ngithub.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0 h1:HWRh5R2+9EifMyIHV7ZV+MIZqgz+PMpZ14Jynv3O2Zs=\ngithub.com/grpc-ecosystem/grpc-gateway/v2 v2.28.0/go.mod h1:JfhWUomR1baixubs02l85lZYYOm7LV6om4ceouMv45c=\ngithub.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=\ngithub.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=\ngithub.com/hashicorp/go-cleanhttp v0.5.2 h1:035FKYIWjmULyFRBKPs8TBQoi0x6d9G4xc9neXJWAZQ=\ngithub.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48=\ngithub.com/hashicorp/go-hclog v1.6.3 h1:Qr2kF+eVWjTiYmU7Y31tYlP1h0q/X3Nl3tPGdaB11/k=\ngithub.com/hashicorp/go-hclog v1.6.3/go.mod h1:W4Qnvbt70Wk/zYJryRzDRU/4r0kIg0PVHBcfoyhpF5M=\ngithub.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=\ngithub.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=\ngithub.com/hashicorp/go-retryablehttp v0.7.8 h1:ylXZWnqa7Lhqpk0L1P1LzDtGcCR0rPVUrx/c8Unxc48=\ngithub.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw=\ngithub.com/hashicorp/go-rootcerts v1.0.2 h1:jzhAVGtqPKbwpyCPELlgNWhE1znq+qwJtW5Oi2viEzc=\ngithub.com/hashicorp/go-rootcerts v1.0.2/go.mod h1:pqUvnprVnM5bf7AOirdbb01K4ccR319Vf4pU3K5EGc8=\ngithub.com/hashicorp/go-secure-stdlib/parseutil v0.2.0 h1:U+kC2dOhMFQctRfhK0gRctKAPTloZdMU5ZJxaesJ/VM=\ngithub.com/hashicorp/go-secure-stdlib/parseutil v0.2.0/go.mod h1:Ll013mhdmsVDuoIXVfBtvgGJsXDYkTw1kooNcoCXuE0=\ngithub.com/hashicorp/go-secure-stdlib/strutil v0.1.2 h1:kes8mmyCpxJsI7FTwtzRqEy9CdjCtrXrXGuOpxEA7Ts=\ngithub.com/hashicorp/go-secure-stdlib/strutil v0.1.2/go.mod h1:Gou2R9+il93BqX25LAKCLuM+y9U2T4hlwvT1yprcna4=\ngithub.com/hashicorp/go-sockaddr v1.0.7 h1:G+pTkSO01HpR5qCxg7lxfsFEZaG+C0VssTy/9dbT+Fw=\ngithub.com/hashicorp/go-sockaddr v1.0.7/go.mod h1:FZQbEYa1pxkQ7WLpyXJ6cbjpT8q0YgQaK/JakXqGyWw=\ngithub.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k=\ngithub.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM=\ngithub.com/hashicorp/hcl v1.0.1-vault-7 h1:ag5OxFVy3QYTFTJODRzTKVZ6xvdfLLCA1cy/Y6xGI0I=\ngithub.com/hashicorp/hcl v1.0.1-vault-7/go.mod h1:XYhtn6ijBSAj6n4YqAaf7RBPS4I06AItNorpy+MoQNM=\ngithub.com/hashicorp/vault/api v1.22.0 h1:+HYFquE35/B74fHoIeXlZIP2YADVboaPjaSicHEZiH0=\ngithub.com/hashicorp/vault/api v1.22.0/go.mod h1:IUZA2cDvr4Ok3+NtK2Oq/r+lJeXkeCrHRmqdyWfpmGM=\ngithub.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef h1:A9HsByNhogrvm9cWb28sjiS3i7tcKCkflWFEkHfuAgM=\ngithub.com/howeyc/gopass v0.0.0-20210920133722-c8aef6fb66ef/go.mod h1:lADxMC39cJJqL93Duh1xhAs4I2Zs8mKS89XWXFGp9cs=\ngithub.com/in-toto/attestation v1.1.2 h1:MBFn6lsMq6dptQZJBhalXTcWMb/aJy3V+GX3VYj/V1E=\ngithub.com/in-toto/attestation v1.1.2/go.mod h1:gYFddHMZj3DiQ0b62ltNi1Vj5rC879bTmBbrv9CRHpM=\ngithub.com/in-toto/in-toto-golang v0.10.0 h1:+s2eZQSK3WmWfYV85qXVSBfqgawi/5L02MaqA4o/tpM=\ngithub.com/in-toto/in-toto-golang v0.10.0/go.mod h1:wjT4RiyFlLWCmLUJjwB8oZcjaq7HA390aMJcD3xXgmg=\ngithub.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=\ngithub.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=\ngithub.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=\ngithub.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=\ngithub.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 h1:iCEnooe7UlwOQYpKFhBabPMi4aNAfoODPEFNiAnClxo=\ngithub.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=\ngithub.com/jackc/pgx/v5 v5.8.0 h1:TYPDoleBBme0xGSAX3/+NujXXtpZn9HBONkQC7IEZSo=\ngithub.com/jackc/pgx/v5 v5.8.0/go.mod h1:QVeDInX2m9VyzvNeiCJVjCkNFqzsNb43204HshNSZKw=\ngithub.com/jackc/puddle/v2 v2.2.2 h1:PR8nw+E/1w0GLuRFSmiioY6UooMp6KJv0/61nB7icHo=\ngithub.com/jackc/puddle/v2 v2.2.2/go.mod h1:vriiEXHvEE654aYKXXjOvZM39qJ0q+azkZFrfEOc3H4=\ngithub.com/jedisct1/go-minisign v0.0.0-20241212093149-d2f9f49435c7 h1:FWpSWRD8FbVkKQu8M1DM9jF5oXFLyE+XpisIYfdzbic=\ngithub.com/jedisct1/go-minisign v0.0.0-20241212093149-d2f9f49435c7/go.mod h1:BMxO138bOokdgt4UaxZiEfypcSHX0t6SIFimVP1oRfk=\ngithub.com/jellydator/ttlcache/v3 v3.4.0 h1:YS4P125qQS0tNhtL6aeYkheEaB/m8HCqdMMP4mnWdTY=\ngithub.com/jellydator/ttlcache/v3 v3.4.0/go.mod h1:Hw9EgjymziQD3yGsQdf1FqFdpp7YjFMd4Srg5EJlgD4=\ngithub.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24 h1:liMMTbpW34dhU4az1GN0pTPADwNmvoRSeoZ6PItiqnY=\ngithub.com/jmespath/go-jmespath v0.4.1-0.20220621161143-b0104c826a24/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=\ngithub.com/klauspost/compress v1.18.4 h1:RPhnKRAQ4Fh8zU2FY/6ZFDwTVTxgJ/EMydqSTzE9a2c=\ngithub.com/klauspost/compress v1.18.4/go.mod h1:R0h/fSBs8DE4ENlcrlib3PsXS61voFxhIs2DeRhCvJ4=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=\ngithub.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=\ngithub.com/letsencrypt/boulder v0.20251208.0 h1:rG1V+1Oiy8H7i6kSf85RwXeZZ8q2Vj65dbsSk88J7wI=\ngithub.com/letsencrypt/boulder v0.20251208.0/go.mod h1:Wi99CY9yzFg4yaHamFCBIScvY8KOcBUe1rlPjUZNTJM=\ngithub.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=\ngithub.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=\ngithub.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=\ngithub.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=\ngithub.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=\ngithub.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=\ngithub.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c h1:cqn374mizHuIWj+OSJCajGr/phAmuMug9qIX3l9CflE=\ngithub.com/mitchellh/mapstructure v1.5.1-0.20231216201459-8508981c8b6c/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=\ngithub.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0=\ngithub.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo=\ngithub.com/moby/moby/api v1.54.0 h1:7kbUgyiKcoBhm0UrWbdrMs7RX8dnwzURKVbZGy2GnL0=\ngithub.com/moby/moby/api v1.54.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc=\ngithub.com/moby/moby/client v0.3.0 h1:UUGL5okry+Aomj3WhGt9Aigl3ZOxZGqR7XPo+RLPlKs=\ngithub.com/moby/moby/client v0.3.0/go.mod h1:HJgFbJRvogDQjbM8fqc1MCEm4mIAGMLjXbgwoZp6jCQ=\ngithub.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ=\ngithub.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc=\ngithub.com/natefinch/atomic v1.0.1 h1:ZPYKxkqQOx3KZ+RsbnP/YsgvxWQPGxjC0oBt2AhwV0A=\ngithub.com/natefinch/atomic v1.0.1/go.mod h1:N/D/ELrljoqDyT3rZrsUmtsuzvHkeB/wWjHV22AZRbM=\ngithub.com/nxadm/tail v1.4.11 h1:8feyoE3OzPrcshW5/MJ4sGESc5cqmGkGCWlco4l0bqY=\ngithub.com/nxadm/tail v1.4.11/go.mod h1:OTaG3NK980DZzxbRq6lEuzgU+mug70nY11sMd4JXXHc=\ngithub.com/oklog/ulid/v2 v2.1.1 h1:suPZ4ARWLOJLegGFiZZ1dFAkqzhMjL3J1TzI+5wHz8s=\ngithub.com/oklog/ulid/v2 v2.1.1/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ=\ngithub.com/onsi/ginkgo v1.16.5 h1:8xi0RTUf59SOSfEtZMvwTvXYMzG4gV23XVHOZiXNtnE=\ngithub.com/onsi/ginkgo v1.16.5/go.mod h1:+E8gABHa3K6zRBolWtd+ROzc/U5bkGt0FwiG042wbpU=\ngithub.com/onsi/gomega v1.39.1 h1:1IJLAad4zjPn2PsnhH70V4DKRFlrCzGBNrNaru+Vf28=\ngithub.com/onsi/gomega v1.39.1/go.mod h1:hL6yVALoTOxeWudERyfppUcZXjMwIMLnuSfruD2lcfg=\ngithub.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=\ngithub.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=\ngithub.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040=\ngithub.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M=\ngithub.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o=\ngithub.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=\ngithub.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=\ngithub.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=\ngithub.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=\ngithub.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ=\ngithub.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=\ngithub.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=\ngithub.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=\ngithub.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=\ngithub.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=\ngithub.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=\ngithub.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=\ngithub.com/ryanuber/go-glob v1.0.0 h1:iQh3xXAumdQ+4Ufa5b25cRpC5TYKlno6hsv6Cb3pkBk=\ngithub.com/ryanuber/go-glob v1.0.0/go.mod h1:807d1WSdnB0XRJzKNil9Om6lcp/3a0v4qIHxIXzX/Yc=\ngithub.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4=\ngithub.com/sagikazarmark/locafero v0.12.0/go.mod h1:sZh36u/YSZ918v0Io+U9ogLYQJ9tLLBmM4eneO6WwsI=\ngithub.com/sassoftware/relic v7.2.1+incompatible h1:Pwyh1F3I0r4clFJXkSI8bOyJINGqpgjJU3DYAZeI05A=\ngithub.com/sassoftware/relic v7.2.1+incompatible/go.mod h1:CWfAxv73/iLZ17rbyhIEq3K9hs5w6FpNMdUT//qR+zk=\ngithub.com/sassoftware/relic/v7 v7.6.2 h1:rS44Lbv9G9eXsukknS4mSjIAuuX+lMq/FnStgmZlUv4=\ngithub.com/sassoftware/relic/v7 v7.6.2/go.mod h1:kjmP0IBVkJZ6gXeAu35/KCEfca//+PKM6vTAsyDPY+k=\ngithub.com/secure-systems-lab/go-securesystemslib v0.10.0 h1:l+H5ErcW0PAehBNrBxoGv1jjNpGYdZ9RcheFkB2WI14=\ngithub.com/secure-systems-lab/go-securesystemslib v0.10.0/go.mod h1:MRKONWmRoFzPNQ9USRF9i1mc7MvAVvF1LlW8X5VWDvk=\ngithub.com/sergi/go-diff v1.4.0 h1:n/SP9D5ad1fORl+llWyN+D6qoUETXNZARKjyY2/KVCw=\ngithub.com/sergi/go-diff v1.4.0/go.mod h1:A0bzQcvG0E7Rwjx0REVgAGH58e96+X0MeOfepqsbeW4=\ngithub.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI=\ngithub.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE=\ngithub.com/sigstore/cosign/v3 v3.0.5 h1:c1zPqjU+H4wmirgysC+AkWMg7a7fykyOYF/m+F1150I=\ngithub.com/sigstore/cosign/v3 v3.0.5/go.mod h1:ble1vMvJagCFyTIDkibCq6MIHiWDw00JNYl0f9rB4T4=\ngithub.com/sigstore/protobuf-specs v0.5.0 h1:F8YTI65xOHw70NrvPwJ5PhAzsvTnuJMGLkA4FIkofAY=\ngithub.com/sigstore/protobuf-specs v0.5.0/go.mod h1:+gXR+38nIa2oEupqDdzg4qSBT0Os+sP7oYv6alWewWc=\ngithub.com/sigstore/rekor v1.5.1 h1:Ca1egHRWRuDvXV4tZu9aXEXc3Gej9FG+HKeapV9OAMQ=\ngithub.com/sigstore/rekor v1.5.1/go.mod h1:gTLDuZuo3SyQCuZvKqwRPA79Qo/2rw39/WtLP/rZjUQ=\ngithub.com/sigstore/rekor-tiles/v2 v2.2.1 h1:UmV1CBQ3SjxxPGpFmwDoOhoIwiKpM2Qm1pU5tPGmvNk=\ngithub.com/sigstore/rekor-tiles/v2 v2.2.1/go.mod h1:z8n6l6oidpaLjjE6rJERuQqY9X38ulnHZCXyL+DEL7U=\ngithub.com/sigstore/sigstore v1.10.4 h1:ytOmxMgLdcUed3w1SbbZOgcxqwMG61lh1TmZLN+WeZE=\ngithub.com/sigstore/sigstore v1.10.4/go.mod h1:tDiyrdOref3q6qJxm2G+JHghqfmvifB7hw+EReAfnbI=\ngithub.com/sigstore/sigstore-go v1.1.4 h1:wTTsgCHOfqiEzVyBYA6mDczGtBkN7cM8mPpjJj5QvMg=\ngithub.com/sigstore/sigstore-go v1.1.4/go.mod h1:2U/mQOT9cjjxrtIUeKDVhL+sHBKsnWddn8URlswdBsg=\ngithub.com/sigstore/sigstore/pkg/signature/kms/aws v1.10.4 h1:VZ+L6SKVWbLPHznIF0tBuO7qKMFdJiJMVwFKu9DlY5o=\ngithub.com/sigstore/sigstore/pkg/signature/kms/aws v1.10.4/go.mod h1:Rstj47WpJym25il8j4jTL0BfikzP/9AhVD+DsBcYzZc=\ngithub.com/sigstore/sigstore/pkg/signature/kms/azure v1.10.4 h1:G7yOv8bxk3zIEEZyVCixPxtePIAm+t3ZWSaKRPzVw+o=\ngithub.com/sigstore/sigstore/pkg/signature/kms/azure v1.10.4/go.mod h1:hxJelB/bRItMYOzi6qD9xEKjse2QZcikh4TbysfdDHc=\ngithub.com/sigstore/sigstore/pkg/signature/kms/gcp v1.10.4 h1:Qxt6dE4IwhJ6gIXmg2q4S/SeqEDSZ29nmfsv7Zb6LL4=\ngithub.com/sigstore/sigstore/pkg/signature/kms/gcp v1.10.4/go.mod h1:hJVeNOwarqfyALjOwsf0OR8YA/A96NABucEaQumPr30=\ngithub.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.10.4 h1:KVavYMPfSf5NryOl6VrZ9nRG3fXOOJOPp7Czk/YCPkM=\ngithub.com/sigstore/sigstore/pkg/signature/kms/hashivault v1.10.4/go.mod h1:J7CA1AaBkyK8dYq6EdQANhj+8oEcsA7PrIp088qgPiY=\ngithub.com/sigstore/timestamp-authority/v2 v2.0.5 h1:WT17MU4bNRvjRLlTvTO5gmrSIWJVbzwrNXgwsjB+53U=\ngithub.com/sigstore/timestamp-authority/v2 v2.0.5/go.mod h1:oV+Yy0GsfgNAeDZcv/WJjQE42wFtMTtuD85bPLAQk5M=\ngithub.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=\ngithub.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=\ngithub.com/spf13/afero v1.15.0 h1:b/YBCLWAJdFWJTN9cLhiXXcD7mzKn9Dm86dNnfyQw1I=\ngithub.com/spf13/afero v1.15.0/go.mod h1:NC2ByUVxtQs4b3sIUphxK0NioZnmxgyCrfzeuq8lxMg=\ngithub.com/spf13/cast v1.10.0 h1:h2x0u2shc1QuLHfxi+cTJvs30+ZAHOGRic8uyGTDWxY=\ngithub.com/spf13/cast v1.10.0/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo=\ngithub.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU=\ngithub.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4=\ngithub.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk=\ngithub.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=\ngithub.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=\ngithub.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=\ngithub.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=\ngithub.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=\ngithub.com/theupdateframework/go-tuf v0.7.0 h1:CqbQFrWo1ae3/I0UCblSbczevCCbS31Qvs5LdxRWqRI=\ngithub.com/theupdateframework/go-tuf v0.7.0/go.mod h1:uEB7WSY+7ZIugK6R1hiBMBjQftaFzn7ZCDJcp1tCUug=\ngithub.com/theupdateframework/go-tuf/v2 v2.4.1 h1:K6ewW064rKZCPkRo1W/CTbTtm/+IB4+coG1iNURAGCw=\ngithub.com/theupdateframework/go-tuf/v2 v2.4.1/go.mod h1:Nex2enPVYDFCklrnbTzl3OVwD7fgIAj0J5++z/rvCj8=\ngithub.com/tink-crypto/tink-go-awskms/v2 v2.1.0 h1:N9UxlsOzu5mttdjhxkDLbzwtEecuXmlxZVo/ds7JKJI=\ngithub.com/tink-crypto/tink-go-awskms/v2 v2.1.0/go.mod h1:PxSp9GlOkKL9rlybW804uspnHuO9nbD98V/fDX4uSis=\ngithub.com/tink-crypto/tink-go-gcpkms/v2 v2.2.0 h1:3B9i6XBXNTRspfkTC0asN5W0K6GhOSgcujNiECNRNb0=\ngithub.com/tink-crypto/tink-go-gcpkms/v2 v2.2.0/go.mod h1:jY5YN2BqD/KSCHM9SqZPIpJNG/u3zwfLXHgws4x2IRw=\ngithub.com/tink-crypto/tink-go-hcvault/v2 v2.4.0 h1:j+S+WKBQ5ya26A5EM/uXoVe+a2IaPQN8KgBJZ22cJ+4=\ngithub.com/tink-crypto/tink-go-hcvault/v2 v2.4.0/go.mod h1:OCKJIujnTzDq7f+73NhVs99oA2c1TR6nsOpuasYM6Yo=\ngithub.com/tink-crypto/tink-go/v2 v2.6.0 h1:+KHNBHhWH33Vn+igZWcsgdEPUxKwBMEe0QC60t388v4=\ngithub.com/tink-crypto/tink-go/v2 v2.6.0/go.mod h1:2WbBA6pfNsAfBwDCggboaHeB2X29wkU8XHtGwh2YIk8=\ngithub.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0=\ngithub.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399/go.mod h1:LdwHTNJT99C5fTAzDz0ud328OgXz+gierycbcIx2fRs=\ngithub.com/transparency-dev/formats v0.1.0 h1:oL0zUFuYUjg8AbtjPMnIRDmjbaHo5jCjEWU5yaNuz0g=\ngithub.com/transparency-dev/formats v0.1.0/go.mod h1:d2FibUOHfCMdCe/+/rbKt1IPLBbPTDfwj46kt541/mU=\ngithub.com/transparency-dev/merkle v0.0.2 h1:Q9nBoQcZcgPamMkGn7ghV8XiTZ/kRxn1yCG81+twTK4=\ngithub.com/transparency-dev/merkle v0.0.2/go.mod h1:pqSy+OXefQ1EDUVmAJ8MUhHB9TXGuzVAT58PqBoHz1A=\ngithub.com/vbatts/tar-split v0.12.2 h1:w/Y6tjxpeiFMR47yzZPlPj/FcPLpXbTUi/9H7d3CPa4=\ngithub.com/vbatts/tar-split v0.12.2/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA=\ngithub.com/ysmood/fetchup v0.2.3 h1:ulX+SonA0Vma5zUFXtv52Kzip/xe7aj4vqT5AJwQ+ZQ=\ngithub.com/ysmood/fetchup v0.2.3/go.mod h1:xhibcRKziSvol0H1/pj33dnKrYyI2ebIvz5cOOkYGns=\ngithub.com/ysmood/goob v0.4.0 h1:HsxXhyLBeGzWXnqVKtmT9qM7EuVs/XOgkX7T6r1o1AQ=\ngithub.com/ysmood/goob v0.4.0/go.mod h1:u6yx7ZhS4Exf2MwciFr6nIM8knHQIE22lFpWHnfql18=\ngithub.com/ysmood/got v0.40.0 h1:ZQk1B55zIvS7zflRrkGfPDrPG3d7+JOza1ZkNxcc74Q=\ngithub.com/ysmood/got v0.40.0/go.mod h1:W7DdpuX6skL3NszLmAsC5hT7JAhuLZhByVzHTq874Qg=\ngithub.com/ysmood/gson v0.7.3 h1:QFkWbTH8MxyUTKPkVWAENJhxqdBa4lYTQWqZCiLG6kE=\ngithub.com/ysmood/gson v0.7.3/go.mod h1:3Kzs5zDl21g5F/BlLTNcuAGAYLKt2lV5G8D1zF3RNmg=\ngithub.com/ysmood/leakless v0.9.0 h1:qxCG5VirSBvmi3uynXFkcnLMzkphdh3xx5FtrORwDCU=\ngithub.com/ysmood/leakless v0.9.0/go.mod h1:R8iAXPRaG97QJwqxs74RdwzcRHT1SWCGTNqY8q0JvMQ=\ngithub.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=\ngithub.com/zalando/go-keyring v0.2.3 h1:v9CUu9phlABObO4LPWycf+zwMG7nlbb3t/B5wa97yms=\ngithub.com/zalando/go-keyring v0.2.3/go.mod h1:HL4k+OXQfJUWaMnqyuSOc0drfGPX2b51Du6K+MRgZMk=\ngo.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=\ngo.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=\ngo.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.64.0 h1:RN3ifU8y4prNWeEnQp2kRRHz8UwonAEYZl8tUzHEXAk=\ngo.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.64.0/go.mod h1:habDz3tEWiFANTo6oUE99EmaFUrCNYAAg3wiVmusm70=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o=\ngo.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg=\ngo.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho=\ngo.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc=\ngo.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4=\ngo.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI=\ngo.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo=\ngo.opentelemetry.io/otel/sdk v1.42.0/go.mod h1:rGHCAxd9DAph0joO4W6OPwxjNTYWghRWmkHuGbayMts=\ngo.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9RKCAZ3YGuA=\ngo.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc=\ngo.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY=\ngo.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc=\ngo.step.sm/crypto v0.76.2 h1:JJ/yMcs/rmcCAwlo+afrHjq74XBFRTJw5B2y4Q4Z4c4=\ngo.step.sm/crypto v0.76.2/go.mod h1:m6KlB/HzIuGFep0UWI5e0SYi38UxpoKeCg6qUaHV6/Q=\ngo.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=\ngo.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=\ngo.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=\ngo.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=\ngo.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=\ngo.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=\ngo.uber.org/zap v1.27.1 h1:08RqriUEv8+ArZRYSTXy1LeBScaMpVSTBhCeaZYfMYc=\ngo.uber.org/zap v1.27.1/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=\ngo.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=\ngo.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=\ngo.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=\ngo.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=\ngo.yaml.in/yaml/v4 v4.0.0-rc.4 h1:UP4+v6fFrBIb1l934bDl//mmnoIZEDK0idg1+AIvX5U=\ngo.yaml.in/yaml/v4 v4.0.0-rc.4/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=\ngolang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=\ngolang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=\ngolang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=\ngolang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=\ngolang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=\ngolang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=\ngolang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=\ngolang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=\ngolang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=\ngolang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=\ngolang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=\ngolang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=\ngolang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=\ngolang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=\ngolang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=\ngolang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=\ngolang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=\ngolang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=\ngolang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=\ngolang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=\ngolang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=\ngolang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=\ngolang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0=\ngolang.org/x/term v0.41.0 h1:QCgPso/Q3RTJx2Th4bDLqML4W6iJiaXFq2/ftQF13YU=\ngolang.org/x/term v0.41.0/go.mod h1:3pfBgksrReYfZ5lvYM0kSO0LIkAl4Yl2bXOkKP7Ec2A=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=\ngolang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=\ngolang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=\ngolang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=\ngolang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=\ngolang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=\ngolang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI=\ngolang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=\ngolang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=\ngolang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=\ngolang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=\ngonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=\ngoogle.golang.org/api v0.269.0 h1:qDrTOxKUQ/P0MveH6a7vZ+DNHxJQjtGm/uvdbdGXCQg=\ngoogle.golang.org/api v0.269.0/go.mod h1:N8Wpcu23Tlccl0zSHEkcAZQKDLdquxK+l9r2LkwAauE=\ngoogle.golang.org/genproto v0.0.0-20260128011058-8636f8732409 h1:VQZ/yAbAtjkHgH80teYd2em3xtIkkHd7ZhqfH2N9CsM=\ngoogle.golang.org/genproto v0.0.0-20260128011058-8636f8732409/go.mod h1:rxKD3IEILWEu3P44seeNOAwZN4SaoKaQ/2eTg4mM6EM=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20260311181403-84a4fc48630c h1:OyQPd6I3pN/9gDxz6L13kYGJgqkpdrAohJRBeXyxlgI=\ngoogle.golang.org/genproto/googleapis/api v0.0.0-20260311181403-84a4fc48630c/go.mod h1:X2gu9Qwng7Nn009s/r3RUxqkzQNqOrAy79bluY7ojIg=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20260311181403-84a4fc48630c h1:xgCzyF2LFIO/0X2UAoVRiXKU5Xg6VjToG4i2/ecSswk=\ngoogle.golang.org/genproto/googleapis/rpc v0.0.0-20260311181403-84a4fc48630c/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=\ngoogle.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=\ngoogle.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=\ngoogle.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=\ngoogle.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=\ngopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=\ngopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q=\ngotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=\nk8s.io/apimachinery v0.35.3 h1:MeaUwQCV3tjKP4bcwWGgZ/cp/vpsRnQzqO6J6tJyoF8=\nk8s.io/apimachinery v0.35.3/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns=\nk8s.io/klog/v2 v2.140.0 h1:Tf+J3AH7xnUzZyVVXhTgGhEKnFqye14aadWv7bzXdzc=\nk8s.io/klog/v2 v2.140.0/go.mod h1:o+/RWfJ6PwpnFn7OyAG3QnO47BFsymfEfrz6XyYSSp0=\nk8s.io/utils v0.0.0-20260210185600-b8788abfbbc2 h1:AZYQSJemyQB5eRxqcPky+/7EdBj0xi3g0ZcxxJ7vbWU=\nk8s.io/utils v0.0.0-20260210185600-b8788abfbbc2/go.mod h1:xDxuJ0whA3d0I4mf/C4ppKHxXynQ+fxnkmQH0vTHnuk=\npgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk=\npgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04=\nsigs.k8s.io/kind v0.31.0 h1:UcT4nzm+YM7YEbqiAKECk+b6dsvc/HRZZu9U0FolL1g=\nsigs.k8s.io/kind v0.31.0/go.mod h1:FSqriGaoTPruiXWfRnUXNykF8r2t+fHtK0P0m1AbGF8=\nsigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs=\nsigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4=\nsoftware.sslmate.com/src/go-pkcs12 v0.4.0 h1:H2g08FrTvSFKUj+D309j1DPfk5APnIdAQAB8aEykJ5k=\nsoftware.sslmate.com/src/go-pkcs12 v0.4.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=\n"
  },
  {
    "path": "hack/boilerplate/boilerplate.go.txt",
    "content": "// Copyright 2023 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n"
  },
  {
    "path": "hack/boilerplate/boilerplate.sh.txt",
    "content": "#!/usr/bin/env bash\n\n# Copyright 2023 ko Build Authors All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n"
  },
  {
    "path": "hack/presubmit.sh",
    "content": "#!/usr/bin/env bash\n\n# Copyright 2021 ko Build Authors All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nset -o errexit\nset -o nounset\nset -o pipefail\n\nPROJECT_ROOT=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")/..\" && pwd)\"\n\npushd \"${PROJECT_ROOT}\"\ntrap popd EXIT\n\n# Verify that all source files are correctly formatted.\nfind . -name \"*.go\" -exec gofmt -d -e -l {} +\n\n# Verify that generated Markdown docs are up-to-date.\ntmpdir=$(mktemp -d)\ngo run cmd/help/main.go --dir \"$tmpdir\"\ndiff -Naur -I '###### Auto generated' \"$tmpdir\" docs/reference/\n"
  },
  {
    "path": "hack/tools.go",
    "content": "// Copyright 2021 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n//go:build tools\n// +build tools\n\npackage hack\n\nimport (\n\t_ \"github.com/go-training/helloworld\"\n)\n"
  },
  {
    "path": "hack/update-codegen.sh",
    "content": "#!/usr/bin/env bash\n\n# Copyright 2021 ko Build Authors All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nset -o errexit\nset -o nounset\nset -o pipefail\n\nPROJECT_ROOT=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")/..\" && pwd)\"\n\npushd ${PROJECT_ROOT}\ntrap popd EXIT\n\ngo mod tidy\n\ngo run $PROJECT_ROOT/cmd/help/main.go --dir=$PROJECT_ROOT/docs/reference/\n"
  },
  {
    "path": "hack/update-deps.sh",
    "content": "#!/usr/bin/env bash\n\n# Copyright 2018 ko Build Authors All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nset -o errexit\nset -o nounset\nset -o pipefail\n\nPROJECT_ROOT=\"$(cd \"$(dirname \"${BASH_SOURCE[0]}\")/..\" && pwd)\"\n\npushd ${PROJECT_ROOT}\ntrap popd EXIT\n\ngo mod tidy\n"
  },
  {
    "path": "integration_test.sh",
    "content": "#!/bin/bash\nset -o errexit\nset -o nounset\nset -o pipefail\n\nROOT_DIR=$(dirname \"$0\")\n\npushd \"$ROOT_DIR\"\n\nROOT_DIR=\"$(pwd)\"\n\necho \"Moving GOPATH into /tmp/ to test modules behavior.\"\nexport GOPATH=\"${GOPATH:-$(go env GOPATH)}\"\nexport ORIGINAL_GOPATH=\"$GOPATH\"\nGOPATH=\"$(mktemp -d)\"\nexport GOPATH\n\nexport CGO_ENABLED=0\n\nGOARCH=\"${GOARCH:-$(go env GOARCH)}\"\n\npushd \"$GOPATH\" || exit 1\n\necho \"Copying ko to temp gopath.\"\nmkdir -p \"$GOPATH/src/github.com/google/ko\"\ncp -r \"$ROOT_DIR/\"* \"$GOPATH/src/github.com/google/ko/\"\n\npushd \"$GOPATH/src/github.com/google/ko\" || exit 1\n\necho \"Building ko\"\n\nRESULT=\"$(go build .)\"\n\necho \"Beginning scenarios.\"\n\nFILTER=\"[^ ]local[^ ]*\"\n\necho \"1. Test should create an image that outputs 'Hello World'.\"\nRESULT=\"$(./ko build --local --platform=\"linux/$GOARCH\" \"$GOPATH/src/github.com/google/ko/test\" | grep \"$FILTER\" | xargs -I% docker run %)\"\nif [[ \"$RESULT\" != *\"Hello there\"* ]]; then\n  echo \"Test FAILED. Saw $RESULT\" && exit 1\nelse\n  echo \"Test PASSED\"\nfi\n\necho \"2. Test knative 'KO_FLAGS' variable is ignored.\"\n# https://github.com/ko-build/ko/issues/1317\nRESULT=\"$(KO_FLAGS=\"--platform=badvalue\" ./ko build --local --platform=\"linux/$GOARCH\" \"$GOPATH/src/github.com/google/ko/test\" | grep \"$FILTER\" | xargs -I% docker run %)\"\nif [[ \"$RESULT\" != *\"Hello there\"* ]]; then\n  echo \"Test FAILED. Saw $RESULT\" && exit 1\nelse\n  echo \"Test PASSED\"\nfi\n\necho \"3. Linux capabilities.\"\npushd test/build-configs || exit 1\n# run as non-root user with net_bind_service cap granted\ndocker_run_opts=\"--user 1 --cap-add=net_bind_service\"\nRESULT=\"$(../../ko build --local --platform=\"linux/$GOARCH\" ./caps/cmd | grep \"$FILTER\" | xargs -I% docker run $docker_run_opts %)\"\nif [[ \"$RESULT\" != \"No capabilities\" ]]; then\n  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\nfi\n# build with a different config requesting net_bind_service file capability\nRESULT_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 %)\"\nif [[ \"$RESULT_WITH_FILE_CAPS\" !=  \"Has capabilities\"* ]]; then\n  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\nelse\n  echo \"Test PASSED\"\nfi\npopd || exit 1\n\npopd || exit 1\npopd || exit 1\n\nexport GOPATH=\"$ORIGINAL_GOPATH\"\n"
  },
  {
    "path": "internal/sbom/sbom.go",
    "content": "// Copyright 2022 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage sbom\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"fmt\"\n\t\"runtime/debug\"\n\t\"strings\"\n\t\"unicode\"\n)\n\nfunc modulePackageName(mod *debug.Module) string {\n\treturn fmt.Sprintf(\"SPDXRef-Package-%s-%s\",\n\t\tstrings.ReplaceAll(mod.Path, \"/\", \".\"),\n\t\tmod.Version)\n}\n\nfunc goRef(mod *debug.Module) string {\n\tpath := mod.Path\n\t// Try to lowercase the first 2 path elements to comply with spec\n\t// https://github.com/package-url/purl-spec/blob/master/PURL-TYPES.rst#golang\n\tp := strings.Split(path, \"/\")\n\tif len(p) > 2 {\n\t\tpath = strings.Join(\n\t\t\tappend(\n\t\t\t\t[]string{strings.ToLower(p[0]), strings.ToLower(p[1])},\n\t\t\t\tp[2:]...,\n\t\t\t), \"/\",\n\t\t)\n\t}\n\treturn fmt.Sprintf(\"pkg:golang/%s@%s?type=module\", path, mod.Version)\n}\n\n// massageGoVersionM massages the output of `go version -m` into a form that\n// can be consumed by ParseBuildInfo.\n//\n// `go version -m` adds a line at the beginning of its output, and tabs at the\n// beginning of every line, that ParseBuildInfo doesn't like.\nfunc massageGoVersionM(b []byte) ([]byte, error) {\n\tvar out bytes.Buffer\n\tscanner := bufio.NewScanner(bytes.NewReader(b))\n\tif !scanner.Scan() {\n\t\t// Input was malformed, and doesn't contain any newlines (it\n\t\t// may even be empty). This seems to happen on Windows\n\t\t// (https://github.com/ko-build/ko/issues/535) and in unit tests.\n\t\t// Just proceed with an empty output for now, and SBOMs will be empty.\n\t\t// TODO: This should be an error.\n\t\treturn nil, nil\n\t}\n\tif err := scanner.Err(); err != nil {\n\t\treturn nil, fmt.Errorf(\"malformed input: %w\", err)\n\t}\n\tfor scanner.Scan() {\n\t\t// NOTE: debug.ParseBuildInfo relies on trailing tabs.\n\t\tline := strings.TrimLeftFunc(scanner.Text(), unicode.IsSpace)\n\t\tfmt.Fprintln(&out, line)\n\t}\n\tif err := scanner.Err(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn out.Bytes(), nil\n}\n"
  },
  {
    "path": "internal/sbom/spdx.go",
    "content": "// Copyright 2021 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage sbom\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"encoding/hex\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/url\"\n\t\"runtime/debug\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/google/go-containerregistry/pkg/name\"\n\tv1 \"github.com/google/go-containerregistry/pkg/v1\"\n\t\"github.com/google/go-containerregistry/pkg/v1/types\"\n\tspecsv1 \"github.com/opencontainers/image-spec/specs-go/v1\"\n\t\"github.com/sigstore/cosign/v3/pkg/oci\"\n)\n\ntype qualifier struct {\n\tkey   string\n\tvalue string\n}\n\n// ociRef constructs a pURL for the OCI image according to:\n// https://github.com/package-url/purl-spec/blob/master/PURL-TYPES.rst#oci\nfunc ociRef(path string, imgDigest v1.Hash, qual ...qualifier) string {\n\tparts := strings.Split(path, \"/\")\n\tpurl := fmt.Sprintf(\"pkg:oci/%s@%s\", parts[len(parts)-1], imgDigest.String())\n\tif num := len(qual); num > 0 {\n\t\tqs := make(url.Values, num)\n\t\tfor _, q := range qual {\n\t\t\tqs.Add(q.key, q.value)\n\t\t}\n\t\tpurl = purl + \"?\" + qs.Encode()\n\t}\n\treturn purl\n}\n\nfunc h1ToSHA256(s string) string {\n\tif !strings.HasPrefix(s, \"h1:\") {\n\t\treturn \"\"\n\t}\n\tb, err := base64.StdEncoding.DecodeString(s[3:])\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\treturn hex.EncodeToString(b)\n}\n\nconst dateFormat = \"2006-01-02T15:04:05Z\"\n\nfunc GenerateImageSPDX(koVersion string, mod []byte, img oci.SignedImage) ([]byte, error) {\n\tvar err error\n\tmod, err = massageGoVersionM(mod)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tbi, err := debug.ParseBuildInfo(string(mod))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\timgDigest, err := img.Digest()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tcfg, err := img.ConfigFile()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tm, err := img.Manifest()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tdoc, imageID := starterDocument(koVersion, cfg.Created.Time, imgDigest)\n\n\t// image -> main package -> transitive deps\n\t//       -> base image\n\tdoc.Packages = make([]Package, 0, 3+len(bi.Deps))\n\tdoc.Relationships = make([]Relationship, 0, 3+len(bi.Deps))\n\n\tdoc.Relationships = append(doc.Relationships, Relationship{\n\t\tElement: \"SPDXRef-DOCUMENT\",\n\t\tType:    \"DESCRIBES\",\n\t\tRelated: imageID,\n\t})\n\n\tdoc.Packages = append(doc.Packages, Package{\n\t\tID:   imageID,\n\t\tName: imgDigest.String(),\n\t\t// TODO: PackageSupplier: \"Organization: \" + bs.Main.Path\n\t\tDownloadLocation: NOASSERTION,\n\t\tFilesAnalyzed:    false,\n\t\t// TODO: PackageHomePage:  \"https://\" + bi.Main.Path,\n\t\tLicenseConcluded: NOASSERTION,\n\t\tLicenseDeclared:  NOASSERTION,\n\t\tCopyrightText:    NOASSERTION,\n\t\tPrimaryPurpose:   \"CONTAINER\",\n\t\tExternalRefs: []ExternalRef{{\n\t\t\tCategory: \"PACKAGE-MANAGER\",\n\t\t\tType:     \"purl\",\n\t\t\tLocator: ociRef(\"image\", imgDigest, qualifier{\n\t\t\t\tkey:   \"mediaType\",\n\t\t\t\tvalue: string(m.MediaType),\n\t\t\t}),\n\t\t}},\n\t})\n\n\tif err := addBaseImage(&doc, m.Annotations, imgDigest); err != nil {\n\t\treturn nil, err\n\t}\n\n\tmainPackageID := modulePackageName(&bi.Main)\n\n\tdoc.Relationships = append(doc.Relationships, Relationship{\n\t\tElement: imageID,\n\t\tType:    \"CONTAINS\",\n\t\tRelated: mainPackageID,\n\t})\n\n\tdoc.Packages = append(doc.Packages, Package{\n\t\tName: bi.Main.Path,\n\t\tID:   mainPackageID,\n\t\t// TODO: PackageSupplier: \"Organization: \" + bs.Main.Path\n\t\tDownloadLocation: \"https://\" + bi.Main.Path,\n\t\tFilesAnalyzed:    false,\n\t\t// TODO: PackageHomePage:  \"https://\" + bi.Main.Path,\n\t\tLicenseConcluded: NOASSERTION,\n\t\tLicenseDeclared:  NOASSERTION,\n\t\tCopyrightText:    NOASSERTION,\n\t\tExternalRefs: []ExternalRef{{\n\t\t\tCategory: \"PACKAGE-MANAGER\",\n\t\t\tType:     \"purl\",\n\t\t\tLocator:  goRef(&bi.Main),\n\t\t}},\n\t})\n\n\tfor _, dep := range bi.Deps {\n\t\tdepID := modulePackageName(dep)\n\n\t\tdoc.Relationships = append(doc.Relationships, Relationship{\n\t\t\tElement: mainPackageID,\n\t\t\tType:    \"DEPENDS_ON\",\n\t\t\tRelated: depID,\n\t\t})\n\n\t\tpkg := Package{\n\t\t\tID:      depID,\n\t\t\tName:    dep.Path,\n\t\t\tVersion: dep.Version,\n\t\t\t// TODO: PackageSupplier: \"Organization: \" + dep.Path\n\t\t\tDownloadLocation: fmt.Sprintf(\"https://proxy.golang.org/%s/@v/%s.zip\", dep.Path, dep.Version),\n\t\t\tFilesAnalyzed:    false,\n\t\t\tLicenseConcluded: NOASSERTION,\n\t\t\tLicenseDeclared:  NOASSERTION,\n\t\t\tCopyrightText:    NOASSERTION,\n\t\t\tExternalRefs: []ExternalRef{{\n\t\t\t\tCategory: \"PACKAGE-MANAGER\",\n\t\t\t\tType:     \"purl\",\n\t\t\t\tLocator:  goRef(dep),\n\t\t\t}},\n\t\t}\n\n\t\tif dep.Sum != \"\" {\n\t\t\tpkg.Checksums = []Checksum{{\n\t\t\t\tAlgorithm: \"SHA256\",\n\t\t\t\tValue:     h1ToSHA256(dep.Sum),\n\t\t\t}}\n\t\t}\n\n\t\tdoc.Packages = append(doc.Packages, pkg)\n\t}\n\n\tvar buf bytes.Buffer\n\tenc := json.NewEncoder(&buf)\n\tenc.SetIndent(\"\", \"  \")\n\tif err := enc.Encode(doc); err != nil {\n\t\treturn nil, err\n\t}\n\treturn buf.Bytes(), nil\n}\n\nfunc extractDate(sii oci.SignedImageIndex) (*time.Time, error) {\n\tim, err := sii.IndexManifest()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, desc := range im.Manifests {\n\t\tswitch desc.MediaType {\n\t\tcase types.OCIManifestSchema1, types.DockerManifestSchema2:\n\t\t\tsi, err := sii.SignedImage(desc.Digest)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tcfg, err := si.ConfigFile()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn &cfg.Created.Time, nil\n\n\t\tdefault:\n\t\t\t// We shouldn't need to handle nested indices, since we don't build\n\t\t\t// them, but if we do we will need to do some sort of recursion here.\n\t\t\treturn nil, fmt.Errorf(\"unknown media type: %v\", desc.MediaType)\n\t\t}\n\t}\n\treturn nil, errors.New(\"unable to extract date, no imaged found\")\n}\n\nfunc GenerateIndexSPDX(koVersion string, sii oci.SignedImageIndex) ([]byte, error) {\n\tindexDigest, err := sii.Digest()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tdate, err := extractDate(sii)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tim, err := sii.IndexManifest()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tdoc, indexID := starterDocument(koVersion, *date, indexDigest)\n\tdoc.Packages = []Package{{\n\t\tID:               indexID,\n\t\tName:             indexDigest.String(),\n\t\tDownloadLocation: NOASSERTION,\n\t\tFilesAnalyzed:    false,\n\t\tLicenseConcluded: NOASSERTION,\n\t\tLicenseDeclared:  NOASSERTION,\n\t\tCopyrightText:    NOASSERTION,\n\t\tPrimaryPurpose:   \"CONTAINER\",\n\t\tChecksums: []Checksum{{\n\t\t\tAlgorithm: strings.ToUpper(indexDigest.Algorithm),\n\t\t\tValue:     indexDigest.Hex,\n\t\t}},\n\t\tExternalRefs: []ExternalRef{{\n\t\t\tCategory: \"PACKAGE-MANAGER\",\n\t\t\tType:     \"purl\",\n\t\t\tLocator: ociRef(\"index\", indexDigest, qualifier{\n\t\t\t\tkey:   \"mediaType\",\n\t\t\t\tvalue: string(im.MediaType),\n\t\t\t}),\n\t\t}},\n\t}}\n\n\tif err := addBaseImage(&doc, im.Annotations, indexDigest); err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, desc := range im.Manifests {\n\t\tswitch desc.MediaType {\n\t\tcase types.OCIManifestSchema1, types.DockerManifestSchema2:\n\t\t\tsi, err := sii.SignedImage(desc.Digest)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\timageDigest, err := si.Digest()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\tdepID := ociPackageName(imageDigest)\n\n\t\t\tdoc.Relationships = append(doc.Relationships, Relationship{\n\t\t\t\tElement: ociPackageName(indexDigest),\n\t\t\t\tType:    \"VARIANT_OF\",\n\t\t\t\tRelated: depID,\n\t\t\t})\n\n\t\t\tqual := []qualifier{{\n\t\t\t\tkey:   \"mediaType\",\n\t\t\t\tvalue: string(desc.MediaType),\n\t\t\t}, {\n\t\t\t\tkey:   \"arch\",\n\t\t\t\tvalue: desc.Platform.Architecture,\n\t\t\t}, {\n\t\t\t\tkey:   \"os\",\n\t\t\t\tvalue: desc.Platform.OS,\n\t\t\t}}\n\t\t\tif desc.Platform.Variant != \"\" {\n\t\t\t\tqual = append(qual, qualifier{\n\t\t\t\t\tkey:   \"variant\",\n\t\t\t\t\tvalue: desc.Platform.Variant,\n\t\t\t\t})\n\t\t\t}\n\t\t\tif desc.Platform.OSVersion != \"\" {\n\t\t\t\tqual = append(qual, qualifier{\n\t\t\t\t\tkey:   \"os-version\",\n\t\t\t\t\tvalue: desc.Platform.OSVersion,\n\t\t\t\t})\n\t\t\t}\n\t\t\tfor _, feat := range desc.Platform.OSFeatures {\n\t\t\t\tqual = append(qual, qualifier{\n\t\t\t\t\tkey:   \"os-feature\",\n\t\t\t\t\tvalue: feat,\n\t\t\t\t})\n\t\t\t}\n\n\t\t\tdoc.Packages = append(doc.Packages, Package{\n\t\t\t\tID:      depID,\n\t\t\t\tName:    imageDigest.String(),\n\t\t\t\tVersion: desc.Platform.String(),\n\t\t\t\t// TODO: PackageSupplier: \"Organization: \" + dep.Path\n\t\t\t\tDownloadLocation: NOASSERTION,\n\t\t\t\tFilesAnalyzed:    false,\n\t\t\t\tLicenseConcluded: NOASSERTION,\n\t\t\t\tLicenseDeclared:  NOASSERTION,\n\t\t\t\tCopyrightText:    NOASSERTION,\n\t\t\t\tPrimaryPurpose:   \"CONTAINER\",\n\t\t\t\tExternalRefs: []ExternalRef{{\n\t\t\t\t\tCategory: \"PACKAGE-MANAGER\",\n\t\t\t\t\tType:     \"purl\",\n\t\t\t\t\tLocator:  ociRef(\"image\", imageDigest, qual...),\n\t\t\t\t}},\n\t\t\t\tChecksums: []Checksum{{\n\t\t\t\t\tAlgorithm: strings.ToUpper(imageDigest.Algorithm),\n\t\t\t\t\tValue:     imageDigest.Hex,\n\t\t\t\t}},\n\t\t\t})\n\n\t\tdefault:\n\t\t\t// We shouldn't need to handle nested indices, since we don't build\n\t\t\t// them, but if we do we will need to do some sort of recursion here.\n\t\t\treturn nil, fmt.Errorf(\"unknown media type: %v\", desc.MediaType)\n\t\t}\n\t}\n\n\tvar buf bytes.Buffer\n\tenc := json.NewEncoder(&buf)\n\tenc.SetIndent(\"\", \"  \")\n\tif err := enc.Encode(doc); err != nil {\n\t\treturn nil, err\n\t}\n\treturn buf.Bytes(), nil\n}\n\nfunc ociPackageName(d v1.Hash) string {\n\treturn fmt.Sprintf(\"SPDXRef-Package-%s-%s\", d.Algorithm, d.Hex)\n}\n\nfunc starterDocument(koVersion string, date time.Time, d v1.Hash) (Document, string) {\n\tdigestID := ociPackageName(d)\n\treturn Document{\n\t\tID:      \"SPDXRef-DOCUMENT\",\n\t\tVersion: Version,\n\t\tCreationInfo: CreationInfo{\n\t\t\tCreated:  date.Format(dateFormat),\n\t\t\tCreators: []string{\"Tool: ko \" + koVersion},\n\t\t},\n\t\tDataLicense:       \"CC0-1.0\",\n\t\tName:              \"sbom-\" + d.String(),\n\t\tNamespace:         \"http://spdx.org/spdxdocs/ko/\" + d.String(),\n\t\tDocumentDescribes: []string{digestID},\n\t}, digestID\n}\n\nfunc addBaseImage(doc *Document, annotations map[string]string, h v1.Hash) error {\n\t// Check for the base image annotation.\n\tbase, ok := annotations[specsv1.AnnotationBaseImageName]\n\tif !ok {\n\t\treturn nil\n\t}\n\trawHash, ok := annotations[specsv1.AnnotationBaseImageDigest]\n\tif !ok {\n\t\treturn nil\n\t}\n\tref, err := name.ParseReference(base)\n\tif err != nil {\n\t\treturn err\n\t}\n\thash, err := v1.NewHash(rawHash)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdigest := ref.Context().Digest(hash.String())\n\n\tdepID := ociPackageName(hash)\n\n\tdoc.Relationships = append(doc.Relationships, Relationship{\n\t\tElement: ociPackageName(h),\n\t\tType:    \"DESCENDANT_OF\",\n\t\tRelated: depID,\n\t})\n\n\tqual := []qualifier{{\n\t\tkey:   \"repository_url\",\n\t\tvalue: ref.Context().Name(),\n\t}}\n\n\tif t, ok := ref.(name.Tag); ok {\n\t\tqual = append(qual, qualifier{\n\t\t\tkey:   \"tag\",\n\t\t\tvalue: t.Identifier(),\n\t\t})\n\t}\n\n\tdoc.Packages = append(doc.Packages, Package{\n\t\tID:      depID,\n\t\tName:    digest.String(),\n\t\tVersion: ref.String(),\n\t\t// TODO: PackageSupplier: \"Organization: \" + dep.Path\n\t\tDownloadLocation: NOASSERTION,\n\t\tFilesAnalyzed:    false,\n\t\tLicenseConcluded: NOASSERTION,\n\t\tLicenseDeclared:  NOASSERTION,\n\t\tCopyrightText:    NOASSERTION,\n\t\tExternalRefs: []ExternalRef{{\n\t\t\tCategory: \"PACKAGE-MANAGER\",\n\t\t\tType:     \"purl\",\n\t\t\tLocator:  ociRef(\"image\", hash, qual...),\n\t\t}},\n\t\tChecksums: []Checksum{{\n\t\t\tAlgorithm: strings.ToUpper(hash.Algorithm),\n\t\t\tValue:     hash.Hex,\n\t\t}},\n\t})\n\treturn nil\n}\n\n// Below this is forked from here:\n// https://github.com/kubernetes-sigs/bom/blob/main/pkg/spdx/json/v2.2.2/types.go\n\n/*\nCopyright 2022 The Kubernetes Authors.\n\nLicensed under the Apache License, Version 2.0 (the \"License\");\nyou may not use this file except in compliance with the License.\nYou may obtain a copy of the License at\n\n    http://www.apache.org/licenses/LICENSE-2.0\n\nUnless required by applicable law or agreed to in writing, software\ndistributed under the License is distributed on an \"AS IS\" BASIS,\nWITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\nSee the License for the specific language governing permissions and\nlimitations under the License.\n*/\n\nconst (\n\tNOASSERTION = \"NOASSERTION\"\n\tVersion     = \"SPDX-2.3\"\n)\n\ntype Document struct {\n\tID                   string                `json:\"SPDXID\"`\n\tName                 string                `json:\"name\"`\n\tVersion              string                `json:\"spdxVersion\"`\n\tCreationInfo         CreationInfo          `json:\"creationInfo\"`\n\tDataLicense          string                `json:\"dataLicense\"`\n\tNamespace            string                `json:\"documentNamespace\"`\n\tDocumentDescribes    []string              `json:\"documentDescribes,omitempty\"`\n\tFiles                []File                `json:\"files,omitempty\"`\n\tPackages             []Package             `json:\"packages,omitempty\"`\n\tRelationships        []Relationship        `json:\"relationships,omitempty\"`\n\tExternalDocumentRefs []ExternalDocumentRef `json:\"externalDocumentRefs,omitempty\"`\n}\n\ntype CreationInfo struct {\n\tCreated            string   `json:\"created\"` // Date\n\tCreators           []string `json:\"creators,omitempty\"`\n\tLicenseListVersion string   `json:\"licenseListVersion,omitempty\"`\n}\n\ntype Package struct {\n\tID                   string                   `json:\"SPDXID\"`\n\tName                 string                   `json:\"name\"`\n\tVersion              string                   `json:\"versionInfo,omitempty\"`\n\tFilesAnalyzed        bool                     `json:\"filesAnalyzed\"`\n\tLicenseDeclared      string                   `json:\"licenseDeclared\"`\n\tLicenseConcluded     string                   `json:\"licenseConcluded\"`\n\tDescription          string                   `json:\"description,omitempty\"`\n\tDownloadLocation     string                   `json:\"downloadLocation\"`\n\tOriginator           string                   `json:\"originator,omitempty\"`\n\tSourceInfo           string                   `json:\"sourceInfo,omitempty\"`\n\tCopyrightText        string                   `json:\"copyrightText\"`\n\tPrimaryPurpose       string                   `json:\"primaryPackagePurpose,omitempty\"`\n\tHasFiles             []string                 `json:\"hasFiles,omitempty\"`\n\tLicenseInfoFromFiles []string                 `json:\"licenseInfoFromFiles,omitempty\"`\n\tChecksums            []Checksum               `json:\"checksums,omitempty\"`\n\tExternalRefs         []ExternalRef            `json:\"externalRefs,omitempty\"`\n\tVerificationCode     *PackageVerificationCode `json:\"packageVerificationCode,omitempty\"`\n}\n\ntype PackageVerificationCode struct {\n\tValue         string   `json:\"packageVerificationCodeValue\"`\n\tExcludedFiles []string `json:\"packageVerificationCodeExcludedFiles,omitempty\"`\n}\n\ntype File struct {\n\tID                string     `json:\"SPDXID\"`\n\tName              string     `json:\"fileName\"`\n\tCopyrightText     string     `json:\"copyrightText\"`\n\tNoticeText        string     `json:\"noticeText,omitempty\"`\n\tLicenseConcluded  string     `json:\"licenseConcluded\"`\n\tDescription       string     `json:\"description,omitempty\"`\n\tFileTypes         []string   `json:\"fileTypes,omitempty\"`\n\tLicenseInfoInFile []string   `json:\"licenseInfoInFiles\"` // List of licenses\n\tChecksums         []Checksum `json:\"checksums\"`\n}\n\ntype Checksum struct {\n\tAlgorithm string `json:\"algorithm\"`\n\tValue     string `json:\"checksumValue\"`\n}\n\ntype ExternalRef struct {\n\tCategory string `json:\"referenceCategory\"`\n\tLocator  string `json:\"referenceLocator\"`\n\tType     string `json:\"referenceType\"`\n}\n\ntype Relationship struct {\n\tElement string `json:\"spdxElementId\"`\n\tType    string `json:\"relationshipType\"`\n\tRelated string `json:\"relatedSpdxElement\"`\n}\n\ntype ExternalDocumentRef struct {\n\tChecksum           Checksum `json:\"checksum\"`\n\tExternalDocumentID string   `json:\"externalDocumentId\"`\n\tSPDXDocument       string   `json:\"spdxDocument\"`\n}\n"
  },
  {
    "path": "main.go",
    "content": "// Copyright 2020 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n//go:generate go run ./cmd/help/main.go -d docs/reference/\npackage main\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"os/signal\"\n\n\t\"github.com/google/ko/pkg/commands\"\n)\n\nfunc main() {\n\tctx, stop := signal.NotifyContext(context.Background(), os.Interrupt)\n\tdefer stop()\n\tif err := commands.Root.ExecuteContext(ctx); err != nil {\n\t\tos.Exit(1)\n\t}\n}\n"
  },
  {
    "path": "mkdocs.yml",
    "content": "site_name: 'ko: Easy Go Containers'\nsite_url: https://ko.build\nrepo_url: https://github.com/ko-build/ko\nedit_uri: edit/main/docs/\n\ntheme:\n  name: material\n  logo: images/favicon-96x96.png\n  favicon: images/favicon-96x96.png\n  custom_dir: docs/custom/\n  palette:\n    # Palette toggle for automatic mode\n    - media: \"(prefers-color-scheme)\"\n      toggle:\n        icon: material/brightness-auto\n        name: Switch to light mode\n\n    # Palette toggle for light mode\n    - media: \"(prefers-color-scheme: light)\"\n      primary: light blue\n      toggle:\n        icon: material/brightness-7\n        name: Switch to dark mode\n\n    # Palette toggle for dark mode\n    - media: \"(prefers-color-scheme: dark)\"\n      scheme: slate\n      primary: light blue\n      toggle:\n        icon: material/brightness-4\n        name: Switch to system preference\n\nnav:\n  - index.md\n  - install.md\n  - get-started.md\n  - configuration.md\n  - deployment.md\n  - community.md\n  - Features:\n    - features/multi-platform.md\n    - features/sboms.md\n    - features/k8s.md\n    - features/static-assets.md\n    - features/build-cache.md\n    - features/debugging.md\n  - Advanced:\n    - advanced/go-packages.md\n    - advanced/limitations.md\n    - advanced/migrating-from-dockerfile.md\n    - advanced/faq.md\n    - advanced/terraform.md\n    - advanced/lambda.md\n    - advanced/linux-capabilities.md\n    - advanced/root-ca-certificates.md\n  - CLI Reference:\n    - 'ko': reference/ko.md\n    - 'ko apply': reference/ko_apply.md\n    - 'ko build': reference/ko_build.md\n    - 'ko create': reference/ko_create.md\n    - 'ko delete': reference/ko_delete.md\n    - 'ko login': reference/ko_login.md\n    - 'ko resolve': reference/ko_resolve.md\n    - 'ko run': reference/ko_run.md\n    - 'ko version': reference/ko_version.md\n  - Releases: \"https://github.com/ko-build/ko/releases\"\n\nplugins:\n  - search\n  - redirects:\n      redirect_maps:\n        'repo.md':      'https://github.com/ko-build/ko'\n        'issues.md':    'https://github.com/ko-build/ko/issues'\n        'prs.md':       'https://github.com/ko-build/ko/pulls'\n        'releases.md':  'https://github.com/ko-build/ko/releases'\n        'godoc.md':     'https://pkg.go.dev/github.com/google/ko'\n        'terraform.md': 'https://github.com/ko-build/terraform-provider-ko'\n        'action.md':    'https://github.com/ko-build/setup-ko'\n        'slack.md':     'https://kubernetes.slack.com/archives/C01T7DTP65S'\n        'agenda.md':    'https://docs.google.com/document/d/1eQ67Qxwf1tkTv0yU_dw9bIRnlwJZz-5GXCRVOhqbgvU/edit'\n        'meet.md':      'meet.google.com/xvn-dzzk-wur'\n\nmarkdown_extensions:\n  - pymdownx.highlight:\n      anchor_linenums: true\n      line_spans: __span\n      pygments_lang_class: true\n  - pymdownx.inlinehilite\n  - pymdownx.snippets\n  - pymdownx.superfences\n"
  },
  {
    "path": "pkg/build/build.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage build\n\nimport (\n\t\"context\"\n\n\tv1 \"github.com/google/go-containerregistry/pkg/v1\"\n\t\"github.com/google/go-containerregistry/pkg/v1/types\"\n)\n\n// Interface abstracts different methods for turning a supported importpath\n// reference into a v1.Image.\ntype Interface interface {\n\t// QualifyImport turns relative importpath references into complete importpaths.\n\t// It also adds the ko scheme prefix if necessary.\n\t// E.g., \"github.com/ko-build/ko/test\" => \"ko://github.com/ko-build/ko/test\"\n\t// and \"./test\" => \"ko://github.com/ko-build/ko/test\"\n\tQualifyImport(string) (string, error)\n\n\t// IsSupportedReference determines whether the given reference is to an\n\t// importpath reference that Ko supports building, returning an error\n\t// if it is not.\n\t// TODO(mattmoor): Verify that some base repo: foo.io/bar can be suffixed with this reference and parsed.\n\tIsSupportedReference(string) error\n\n\t// Build turns the given importpath reference into a v1.Image containing the Go binary\n\t// (or a set of images as a v1.ImageIndex).\n\tBuild(context.Context, string) (Result, error)\n}\n\n// Result represents the product of a Build.\n// This is generally one of:\n// - v1.Image      (or oci.SignedImage), or\n// - v1.ImageIndex (or oci.SignedImageIndex)\ntype Result interface {\n\tMediaType() (types.MediaType, error)\n\tSize() (int64, error)\n\tDigest() (v1.Hash, error)\n\tRawManifest() ([]byte, error)\n}\n\n// Assert that Image and ImageIndex implement Result.\nvar _ Result = (v1.Image)(nil)\nvar _ Result = (v1.ImageIndex)(nil)\n"
  },
  {
    "path": "pkg/build/cache.go",
    "content": "// Copyright 2021 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage build\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/google/go-containerregistry/pkg/logs\"\n\tv1 \"github.com/google/go-containerregistry/pkg/v1\"\n\t\"github.com/google/go-containerregistry/pkg/v1/partial\"\n)\n\ntype diffIDToDescriptor map[v1.Hash]v1.Descriptor\ntype buildIDToDiffID map[string]v1.Hash\n\ntype layerCache struct {\n\tbuildToDiff map[string]buildIDToDiffID\n\tdiffToDesc  map[string]diffIDToDescriptor\n\tsync.Mutex\n}\n\ntype layerFactory func() (v1.Layer, error)\n\nfunc (c *layerCache) get(ctx context.Context, file string, miss layerFactory) (v1.Layer, error) {\n\tif os.Getenv(\"KOCACHE\") == \"\" {\n\t\treturn miss()\n\t}\n\n\t// Cache hit.\n\tif diffid, desc, err := c.getMeta(ctx, file); err != nil {\n\t\tlogs.Debug.Printf(\"getMeta(%q): %v\", file, err)\n\t} else {\n\t\treturn &lazyLayer{\n\t\t\tdiffid:     *diffid,\n\t\t\tdesc:       *desc,\n\t\t\tbuildLayer: miss,\n\t\t}, nil\n\t}\n\n\t// Cache miss.\n\tlayer, err := miss()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"miss(%q): %w\", file, err)\n\t}\n\tif err := c.put(ctx, file, layer); err != nil {\n\t\tlog.Printf(\"failed to cache metadata %s: %v\", file, err)\n\t}\n\treturn layer, nil\n}\n\nfunc (c *layerCache) getMeta(ctx context.Context, file string) (*v1.Hash, *v1.Descriptor, error) {\n\tbuildid, err := getBuildID(ctx, file)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tif buildid == \"\" {\n\t\treturn nil, nil, fmt.Errorf(\"no buildid for %q\", file)\n\t}\n\n\t// TODO: Implement better per-file locking.\n\tc.Lock()\n\tdefer c.Unlock()\n\n\tbtod, err := c.readBuildToDiff(file)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tdtod, err := c.readDiffToDesc(file)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tdiffid, ok := btod[buildid]\n\tif !ok {\n\t\treturn nil, nil, fmt.Errorf(\"no diffid for %q\", buildid)\n\t}\n\n\tdesc, ok := dtod[diffid]\n\tif !ok {\n\t\treturn nil, nil, fmt.Errorf(\"no desc for %q\", diffid)\n\t}\n\n\treturn &diffid, &desc, nil\n}\n\n// Compute new layer metadata and cache it in-mem and on-disk.\nfunc (c *layerCache) put(ctx context.Context, file string, layer v1.Layer) error {\n\tbuildid, err := getBuildID(ctx, file)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tdesc, err := partial.Descriptor(layer)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tdiffid, err := layer.DiffID()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tbtod, ok := c.buildToDiff[file]\n\tif !ok {\n\t\tbtod = buildIDToDiffID{}\n\t}\n\tbtod[buildid] = diffid\n\n\tdtod, ok := c.diffToDesc[file]\n\tif !ok {\n\t\tdtod = diffIDToDescriptor{}\n\t}\n\tdtod[diffid] = *desc\n\n\t// TODO: Implement better per-file locking.\n\tc.Lock()\n\tdefer c.Unlock()\n\n\tbtodf, err := os.OpenFile(filepath.Join(filepath.Dir(file), \"buildid-to-diffid\"), os.O_RDWR|os.O_CREATE, 0755)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"opening buildid-to-diffid: %w\", err)\n\t}\n\tdefer btodf.Close()\n\n\tdtodf, err := os.OpenFile(filepath.Join(filepath.Dir(file), \"diffid-to-descriptor\"), os.O_RDWR|os.O_CREATE, 0755)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"opening diffid-to-descriptor: %w\", err)\n\t}\n\tdefer dtodf.Close()\n\n\tenc := json.NewEncoder(btodf)\n\tenc.SetIndent(\"\", \"  \")\n\tif err := enc.Encode(&btod); err != nil {\n\t\treturn err\n\t}\n\n\tenc = json.NewEncoder(dtodf)\n\tenc.SetIndent(\"\", \"  \")\n\treturn enc.Encode(&dtod)\n}\n\nfunc (c *layerCache) readDiffToDesc(file string) (diffIDToDescriptor, error) {\n\tif dtod, ok := c.diffToDesc[file]; ok {\n\t\treturn dtod, nil\n\t}\n\n\tdtodf, err := os.Open(filepath.Join(filepath.Dir(file), \"diffid-to-descriptor\"))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"opening diffid-to-descriptor: %w\", err)\n\t}\n\tdefer dtodf.Close()\n\n\tvar dtod diffIDToDescriptor\n\tif err := json.NewDecoder(dtodf).Decode(&dtod); err != nil {\n\t\treturn nil, err\n\t}\n\tc.diffToDesc[file] = dtod\n\treturn dtod, nil\n}\n\nfunc (c *layerCache) readBuildToDiff(file string) (buildIDToDiffID, error) {\n\tif btod, ok := c.buildToDiff[file]; ok {\n\t\treturn btod, nil\n\t}\n\n\tbtodf, err := os.Open(filepath.Join(filepath.Dir(file), \"buildid-to-diffid\"))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"opening buildid-to-diffid: %w\", err)\n\t}\n\tdefer btodf.Close()\n\n\tvar btod buildIDToDiffID\n\tif err := json.NewDecoder(btodf).Decode(&btod); err != nil {\n\t\treturn nil, err\n\t}\n\tc.buildToDiff[file] = btod\n\treturn btod, nil\n}\n\nfunc getBuildID(ctx context.Context, file string) (string, error) {\n\tgobin := getGoBinary()\n\n\tcmd := exec.CommandContext(ctx, gobin, \"tool\", \"buildid\", file)\n\tvar output bytes.Buffer\n\tcmd.Stderr = &output\n\tcmd.Stdout = &output\n\n\tif err := cmd.Run(); err != nil {\n\t\tlog.Printf(\"Unexpected error running \\\"go tool buildid %s\\\": %v\\n%v\", err, file, output.String())\n\t\treturn \"\", fmt.Errorf(\"go tool buildid %s: %w\", file, err)\n\t}\n\treturn strings.TrimSpace(output.String()), nil\n}\n"
  },
  {
    "path": "pkg/build/config.go",
    "content": "// Copyright 2021 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage build\n\nimport \"strings\"\n\n// Note: The structs, types, and functions are based upon GoReleaser build\n// configuration to have a loosely compatible YAML configuration:\n// https://github.com/goreleaser/goreleaser/blob/master/pkg/config/config.go\n\n// StringArray is a wrapper for an array of strings.\ntype StringArray []string\n\n// UnmarshalYAML is a custom unmarshaler that wraps strings in arrays.\nfunc (a *StringArray) UnmarshalYAML(unmarshal func(any) error) error {\n\tvar strings []string\n\tif err := unmarshal(&strings); err != nil {\n\t\tvar str string\n\t\tif err := unmarshal(&str); err != nil {\n\t\t\treturn err\n\t\t}\n\t\t*a = []string{str}\n\t} else {\n\t\t*a = strings\n\t}\n\treturn nil\n}\n\n// FlagArray is a wrapper for an array of strings.\ntype FlagArray []string\n\n// UnmarshalYAML is a custom unmarshaler that wraps strings in arrays.\nfunc (a *FlagArray) UnmarshalYAML(unmarshal func(any) error) error {\n\tvar flags []string\n\tif err := unmarshal(&flags); err != nil {\n\t\tvar flagstr string\n\t\tif err := unmarshal(&flagstr); err != nil {\n\t\t\treturn err\n\t\t}\n\t\t*a = strings.Fields(flagstr)\n\t} else {\n\t\t*a = flags\n\t}\n\treturn nil\n}\n\n// Config contains the build configuration section. The name was changed from\n// the original GoReleaser name to match better with the ko naming.\n//\n// TODO: Introduce support for more fields where possible and where it makes\n// /      sense for `ko`, for example ModTimestamp or GoBinary.\ntype Config struct {\n\t// ID only serves as an identifier internally\n\tID string `yaml:\",omitempty\"`\n\n\t// Dir is the directory out of which the build should be triggered\n\tDir string `yaml:\",omitempty\"`\n\n\t// Main points to the main package, or the source file with the main\n\t// function, in which case only the package will be used for the importpath\n\tMain string `yaml:\",omitempty\"`\n\n\t// Ldflags and Flags will be used for the Go build command line arguments\n\tLdflags StringArray `yaml:\",omitempty\"`\n\tFlags   FlagArray   `yaml:\",omitempty\"`\n\n\t// Env allows setting environment variables for `go build`\n\tEnv []string `yaml:\",omitempty\"`\n\n\t// Other GoReleaser fields that are not supported or do not make sense\n\t// in the context of ko, for reference or for future use:\n\t// Goos         []string    `yaml:\",omitempty\"`\n\t// Goarch       []string    `yaml:\",omitempty\"`\n\t// Goarm        []string    `yaml:\",omitempty\"`\n\t// Gomips       []string    `yaml:\",omitempty\"`\n\t// Targets      []string    `yaml:\",omitempty\"`\n\t// Binary       string      `yaml:\",omitempty\"`\n\t// Lang         string      `yaml:\",omitempty\"`\n\t// Asmflags     StringArray `yaml:\",omitempty\"`\n\t// Gcflags      StringArray `yaml:\",omitempty\"`\n\t// ModTimestamp string      `yaml:\"mod_timestamp,omitempty\"`\n\t// GoBinary     string      `yaml:\",omitempty\"`\n\n\t// extension: Linux capabilities to enable on the executable, applies\n\t// to Linux targets.\n\tLinuxCapabilities FlagArray `yaml:\"linux_capabilities,omitempty\"`\n}\n"
  },
  {
    "path": "pkg/build/doc.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// Package build defines methods for building a v1.Image reference from a\n// Go binary reference.\npackage build\n"
  },
  {
    "path": "pkg/build/future.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage build\n\nimport (\n\t\"sync\"\n)\n\nfunc newFuture(work func() (Result, error)) *future {\n\t// Create a channel on which to send the result.\n\tch := make(chan *result)\n\t// Initiate the actual work, sending its result\n\t// along the above channel.\n\tgo func() {\n\t\timg, err := work()\n\t\tch <- &result{img: img, err: err}\n\t}()\n\t// Return a future for the above work.  Callers should\n\t// call .Get() on this result (as many times as needed).\n\t// One of these calls will receive the result, store it,\n\t// and close the channel so that the rest of the callers\n\t// can consume it.\n\treturn &future{\n\t\tpromise: ch,\n\t}\n}\n\ntype result struct {\n\timg Result\n\terr error\n}\n\ntype future struct {\n\tm sync.RWMutex\n\n\tresult  *result\n\tpromise chan *result\n}\n\n// Get blocks on the result of the future.\nfunc (f *future) Get() (Result, error) {\n\t// Block on the promise of a result until we get one.\n\tresult, ok := <-f.promise\n\tif ok {\n\t\tfunc() {\n\t\t\tf.m.Lock()\n\t\t\tdefer f.m.Unlock()\n\t\t\t// If we got the result, then store it so that\n\t\t\t// others may access it.\n\t\t\tf.result = result\n\t\t\t// Close the promise channel so that others\n\t\t\t// are signaled that the result is available.\n\t\t\tclose(f.promise)\n\t\t}()\n\t}\n\n\tf.m.RLock()\n\tdefer f.m.RUnlock()\n\n\treturn f.result.img, f.result.err\n}\n"
  },
  {
    "path": "pkg/build/future_test.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage build\n\nimport (\n\t\"testing\"\n\n\t\"github.com/google/go-containerregistry/pkg/v1/random\"\n)\n\nfunc makeImage() (Result, error) {\n\treturn random.Index(256, 8, 1)\n}\n\nfunc digest(t *testing.T, img Result) string {\n\td, err := img.Digest()\n\tif err != nil {\n\t\tt.Fatalf(\"Digest() = %v\", err)\n\t}\n\treturn d.String()\n}\n\nfunc TestSameFutureSameImage(t *testing.T) {\n\tf := newFuture(makeImage)\n\n\ti1, err := f.Get()\n\tif err != nil {\n\t\tt.Errorf(\"Get() = %v\", err)\n\t}\n\td1 := digest(t, i1)\n\n\ti2, err := f.Get()\n\tif err != nil {\n\t\tt.Errorf(\"Get() = %v\", err)\n\t}\n\td2 := digest(t, i2)\n\n\tif d1 != d2 {\n\t\tt.Errorf(\"Got different digests %s and %s\", d1, d2)\n\t}\n}\n\nfunc TestDiffFutureDiffImage(t *testing.T) {\n\tf1 := newFuture(makeImage)\n\tf2 := newFuture(makeImage)\n\n\ti1, err := f1.Get()\n\tif err != nil {\n\t\tt.Errorf(\"Get() = %v\", err)\n\t}\n\td1 := digest(t, i1)\n\n\ti2, err := f2.Get()\n\tif err != nil {\n\t\tt.Errorf(\"Get() = %v\", err)\n\t}\n\td2 := digest(t, i2)\n\n\tif d1 == d2 {\n\t\tt.Errorf(\"Got same digest %s, wanted different\", d1)\n\t}\n}\n"
  },
  {
    "path": "pkg/build/gobuild.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage build\n\nimport (\n\t\"archive/tar\"\n\t\"bufio\"\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\tgb \"go/build\"\n\t\"io\"\n\t\"log\"\n\t\"maps\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n\t\"text/template\"\n\t\"time\"\n\n\t\"github.com/google/go-containerregistry/pkg/name\"\n\tv1 \"github.com/google/go-containerregistry/pkg/v1\"\n\t\"github.com/google/go-containerregistry/pkg/v1/empty\"\n\t\"github.com/google/go-containerregistry/pkg/v1/mutate\"\n\t\"github.com/google/go-containerregistry/pkg/v1/tarball\"\n\t\"github.com/google/go-containerregistry/pkg/v1/types\"\n\t\"github.com/google/ko/internal/sbom\"\n\t\"github.com/google/ko/pkg/caps\"\n\t\"github.com/google/ko/pkg/internal/git\"\n\tspecsv1 \"github.com/opencontainers/image-spec/specs-go/v1\"\n\t\"github.com/sigstore/cosign/v3/pkg/oci\"\n\tocimutate \"github.com/sigstore/cosign/v3/pkg/oci/mutate\"\n\t\"github.com/sigstore/cosign/v3/pkg/oci/signed\"\n\t\"github.com/sigstore/cosign/v3/pkg/oci/static\"\n\tctypes \"github.com/sigstore/cosign/v3/pkg/types\"\n\t\"golang.org/x/sync/errgroup\"\n\t\"golang.org/x/sync/semaphore\"\n\t\"golang.org/x/tools/go/packages\"\n)\n\nconst (\n\tdefaultAppFilename = \"ko-app\"\n\n\tdefaultGoBin = \"go\"         // defaults to first go binary found in PATH\n\tgoBinPathEnv = \"KO_GO_PATH\" // env lookup for optional relative or full go binary path\n)\n\n// GetBase takes an importpath and returns a base image reference and base image (or index).\ntype GetBase func(context.Context, string) (name.Reference, Result, error)\n\n// buildContext provides parameters for a builder function.\ntype buildContext struct {\n\tcreationTime v1.Time\n\tip           string\n\tdir          string\n\tenv          []string\n\tflags        []string\n\tldflags      []string\n\tplatform     v1.Platform\n}\n\ntype builder func(context.Context, buildContext) (string, error)\n\ntype sbomber func(context.Context, string, string, string, oci.SignedEntity, string) ([]byte, types.MediaType, error)\n\ntype platformMatcher struct {\n\tspec      []string\n\tplatforms []v1.Platform\n}\n\ntype gobuild struct {\n\tctx                  context.Context\n\tgetBase              GetBase\n\tcreationTime         v1.Time\n\tkodataCreationTime   v1.Time\n\tbuild                builder\n\tsbom                 sbomber\n\tsbomDir              string\n\tdisableOptimizations bool\n\ttrimpath             bool\n\tbuildConfigs         map[string]Config\n\tdefaultEnv           []string\n\tdefaultFlags         []string\n\tdefaultLdflags       []string\n\tplatformMatcher      *platformMatcher\n\tdir                  string\n\tlabels               map[string]string\n\tannotations          map[string]string\n\tuser                 string\n\tdebug                bool\n\tsemaphore            *semaphore.Weighted\n\n\tcache *layerCache\n}\n\n// Option is a functional option for NewGo.\ntype Option func(*gobuildOpener) error\n\ntype gobuildOpener struct {\n\tctx                  context.Context\n\tgetBase              GetBase\n\tcreationTime         v1.Time\n\tkodataCreationTime   v1.Time\n\tbuild                builder\n\tsbom                 sbomber\n\tsbomDir              string\n\tdisableOptimizations bool\n\ttrimpath             bool\n\tbuildConfigs         map[string]Config\n\tdefaultEnv           []string\n\tdefaultFlags         []string\n\tdefaultLdflags       []string\n\tplatforms            []string\n\tlabels               map[string]string\n\tannotations          map[string]string\n\tuser                 string\n\tdir                  string\n\tjobs                 int\n\tdebug                bool\n}\n\nfunc (gbo *gobuildOpener) Open() (Interface, error) {\n\tif gbo.getBase == nil {\n\t\treturn nil, errors.New(\"a way of providing base images must be specified, see build.WithBaseImages\")\n\t}\n\tmatcher, err := parseSpec(gbo.platforms)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif gbo.jobs == 0 {\n\t\tgbo.jobs = runtime.GOMAXPROCS(0)\n\t}\n\tif gbo.annotations == nil {\n\t\tgbo.annotations = map[string]string{}\n\t}\n\treturn &gobuild{\n\t\tctx:                  gbo.ctx,\n\t\tgetBase:              gbo.getBase,\n\t\tuser:                 gbo.user,\n\t\tcreationTime:         gbo.creationTime,\n\t\tkodataCreationTime:   gbo.kodataCreationTime,\n\t\tbuild:                gbo.build,\n\t\tsbom:                 gbo.sbom,\n\t\tsbomDir:              gbo.sbomDir,\n\t\tdisableOptimizations: gbo.disableOptimizations,\n\t\ttrimpath:             gbo.trimpath,\n\t\tbuildConfigs:         gbo.buildConfigs,\n\t\tdefaultEnv:           gbo.defaultEnv,\n\t\tdefaultFlags:         gbo.defaultFlags,\n\t\tdefaultLdflags:       gbo.defaultLdflags,\n\t\tlabels:               gbo.labels,\n\t\tannotations:          gbo.annotations,\n\t\tdir:                  gbo.dir,\n\t\tdebug:                gbo.debug,\n\t\tplatformMatcher:      matcher,\n\t\tcache: &layerCache{\n\t\t\tbuildToDiff: map[string]buildIDToDiffID{},\n\t\t\tdiffToDesc:  map[string]diffIDToDescriptor{},\n\t\t},\n\t\tsemaphore: semaphore.NewWeighted(int64(gbo.jobs)),\n\t}, nil\n}\n\n// NewGo returns a build.Interface implementation that:\n//  1. builds go binaries named by importpath,\n//  2. containerizes the binary on a suitable base.\n//\n// The `dir` argument is the working directory for executing the `go` tool.\n// If `dir` is empty, the function uses the current process working directory.\nfunc NewGo(ctx context.Context, dir string, options ...Option) (Interface, error) {\n\tgbo := &gobuildOpener{\n\t\tctx:   ctx,\n\t\tbuild: build,\n\t\tdir:   dir,\n\t\tsbom:  spdx(\"(none)\"),\n\t}\n\n\tfor _, option := range options {\n\t\tif err := option(gbo); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn gbo.Open()\n}\n\nfunc (g *gobuild) qualifyLocalImport(importpath string) (string, error) {\n\tdir := filepath.Clean(g.dir)\n\tif dir == \".\" {\n\t\tdir = \"\"\n\t}\n\tcfg := &packages.Config{\n\t\tMode: packages.NeedName,\n\t\tDir:  dir,\n\t}\n\tpkgs, err := packages.Load(cfg, importpath)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tif len(pkgs) != 1 {\n\t\treturn \"\", fmt.Errorf(\"found %d local packages, expected 1\", len(pkgs))\n\t}\n\treturn pkgs[0].PkgPath, nil\n}\n\n// QualifyImport implements build.Interface\nfunc (g *gobuild) QualifyImport(importpath string) (string, error) {\n\tif gb.IsLocalImport(importpath) {\n\t\tvar err error\n\t\timportpath, err = g.qualifyLocalImport(importpath)\n\t\tif err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"qualifying local import %s: %w\", importpath, err)\n\t\t}\n\t}\n\tif !strings.HasPrefix(importpath, StrictScheme) {\n\t\timportpath = StrictScheme + importpath\n\t}\n\treturn importpath, nil\n}\n\n// IsSupportedReference implements build.Interface\n//\n// Only valid importpaths that provide commands (i.e., are \"package main\") are\n// supported.\nfunc (g *gobuild) IsSupportedReference(s string) error {\n\tref := newRef(s)\n\tif !ref.IsStrict() {\n\t\treturn errors.New(\"importpath does not start with ko://\")\n\t}\n\tdir := filepath.Clean(g.dir)\n\tif dir == \".\" {\n\t\tdir = \"\"\n\t}\n\tpkgs, err := packages.Load(&packages.Config{Dir: dir, Mode: packages.NeedName}, ref.Path())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error loading package from %s: %w\", ref.Path(), err)\n\t}\n\tif len(pkgs) != 1 {\n\t\treturn fmt.Errorf(\"found %d local packages, expected 1\", len(pkgs))\n\t}\n\tif pkgs[0].Name != \"main\" {\n\t\treturn errors.New(\"importpath is not `package main`\")\n\t}\n\treturn nil\n}\n\nfunc getGoarm(platform v1.Platform) (string, error) {\n\tif !strings.HasPrefix(platform.Variant, \"v\") {\n\t\treturn \"\", fmt.Errorf(\"strange arm variant: %v\", platform.Variant)\n\t}\n\n\tvs := strings.TrimPrefix(platform.Variant, \"v\")\n\tvariant, err := strconv.Atoi(vs)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"cannot parse arm variant %q: %w\", platform.Variant, err)\n\t}\n\tif variant >= 5 {\n\t\t// TODO(golang/go#29373): Allow for 8 in later go versions if this is fixed.\n\t\tif variant > 7 {\n\t\t\tvs = \"7\"\n\t\t}\n\t\treturn vs, nil\n\t}\n\treturn \"\", nil\n}\n\nfunc getGoBinary() string {\n\tif env := os.Getenv(goBinPathEnv); env != \"\" {\n\t\treturn env\n\t}\n\treturn defaultGoBin\n}\n\nfunc doesPlatformSupportDebugging(platform v1.Platform) bool {\n\t// Here's the list of supported platforms by Delve:\n\t//\n\t// https://github.com/go-delve/delve/blob/master/Documentation/faq.md#unsupportedplatforms\n\t//\n\t// For the time being, we'll support only linux/amd64 and linux/arm64.\n\n\treturn platform.OS == \"linux\" && (platform.Architecture == \"amd64\" || platform.Architecture == \"arm64\")\n}\n\nfunc getDelve(ctx context.Context, platform v1.Platform) (string, error) {\n\tconst delveCloneURL = \"https://github.com/go-delve/delve.git\"\n\n\tif platform.OS == \"\" || platform.Architecture == \"\" {\n\t\treturn \"\", fmt.Errorf(\"platform os (%q) or arch (%q) is empty\",\n\t\t\tplatform.OS,\n\t\t\tplatform.Architecture,\n\t\t)\n\t}\n\n\tenv, err := buildEnv(platform, os.Environ(), nil)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"could not create env for Delve build: %w\", err)\n\t}\n\n\ttmpInstallDir, err := os.MkdirTemp(\"\", \"delve\")\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"could not create tmp dir for Delve installation: %w\", err)\n\t}\n\tcloneDir := filepath.Join(tmpInstallDir, \"delve\")\n\terr = os.MkdirAll(cloneDir, 0755)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"making dir for delve clone: %w\", err)\n\t}\n\terr = git.Clone(ctx, cloneDir, delveCloneURL)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"cloning delve repo: %w\", err)\n\t}\n\tosArchDir := fmt.Sprintf(\"%s_%s\", platform.OS, platform.Architecture)\n\tdelveBinaryPath := filepath.Join(tmpInstallDir, \"bin\", osArchDir, \"dlv\")\n\n\t// install delve to tmp directory\n\targs := []string{\n\t\t\"build\",\n\t\t\"-trimpath\",\n\t\t\"-ldflags=-s -w\",\n\t\t\"-o\",\n\t\tdelveBinaryPath,\n\t\t\"./cmd/dlv\",\n\t}\n\n\tgobin := getGoBinary()\n\tcmd := exec.CommandContext(ctx, gobin, args...)\n\tcmd.Env = env\n\tcmd.Dir = cloneDir\n\n\tvar output bytes.Buffer\n\tcmd.Stderr = &output\n\tcmd.Stdout = &output\n\n\tlog.Printf(\"Building Delve for %s\", platform)\n\tif err := cmd.Run(); err != nil {\n\t\tos.RemoveAll(tmpInstallDir)\n\t\treturn \"\", fmt.Errorf(\"go build Delve: %w: %s\", err, output.String())\n\t}\n\n\tif _, err := os.Stat(delveBinaryPath); err != nil {\n\t\treturn \"\", fmt.Errorf(\"could not find Delve binary at %q: %w\", delveBinaryPath, err)\n\t}\n\n\treturn delveBinaryPath, nil\n}\n\nfunc build(ctx context.Context, buildCtx buildContext) (string, error) {\n\t// Create the set of build arguments from the config flags/ldflags with any\n\t// template parameters applied.\n\tbuildArgs, err := createBuildArgs(ctx, buildCtx)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\targs := make([]string, 0, 4+len(buildArgs))\n\targs = append(args, \"build\")\n\targs = append(args, buildArgs...)\n\ttmpDir := \"\"\n\n\tif dir := os.Getenv(\"KOCACHE\"); dir != \"\" {\n\t\tdirInfo, err := os.Stat(dir)\n\t\tif err != nil {\n\t\t\tif !os.IsNotExist(err) {\n\t\t\t\treturn \"\", fmt.Errorf(\"could not stat KOCACHE: %w\", err)\n\t\t\t}\n\t\t\tif err := os.MkdirAll(dir, os.ModePerm); err != nil && !os.IsExist(err) {\n\t\t\t\treturn \"\", fmt.Errorf(\"could not create KOCACHE dir %s: %w\", dir, err)\n\t\t\t}\n\t\t} else if !dirInfo.IsDir() {\n\t\t\treturn \"\", fmt.Errorf(\"KOCACHE should be a directory, %s is not a directory\", dir)\n\t\t}\n\n\t\t// TODO(#264): if KOCACHE is unset, default to filepath.Join(os.TempDir(), \"ko\").\n\t\ttmpDir = filepath.Join(dir, \"bin\", buildCtx.ip, buildCtx.platform.String())\n\t\tif err := os.MkdirAll(tmpDir, os.ModePerm); err != nil {\n\t\t\treturn \"\", fmt.Errorf(\"creating KOCACHE bin dir: %w\", err)\n\t\t}\n\t} else {\n\t\ttmpDir, err = os.MkdirTemp(\"\", \"ko\")\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\n\tfile := filepath.Join(tmpDir, \"out\")\n\n\targs = append(args, \"-o\", file)\n\targs = append(args, buildCtx.ip)\n\n\tgobin := getGoBinary()\n\tcmd := exec.CommandContext(ctx, gobin, args...)\n\tcmd.Dir = buildCtx.dir\n\tcmd.Env = buildCtx.env\n\n\tvar output bytes.Buffer\n\tcmd.Stderr = &output\n\tcmd.Stdout = &output\n\n\tlog.Printf(\"Building %s for %s\", buildCtx.ip, buildCtx.platform)\n\tif err := cmd.Run(); err != nil {\n\t\tif os.Getenv(\"KOCACHE\") == \"\" {\n\t\t\t_ = os.RemoveAll(tmpDir)\n\t\t}\n\t\treturn \"\", fmt.Errorf(\"go build: %w: %s\", err, output.String())\n\t}\n\treturn file, nil\n}\n\nfunc goenv(ctx context.Context) (map[string]string, error) {\n\tgobin := getGoBinary()\n\tcmd := exec.CommandContext(ctx, gobin, \"env\")\n\tvar stdout, stderr bytes.Buffer\n\tcmd.Stdout = &stdout\n\tcmd.Stderr = &stderr\n\tif err := cmd.Run(); err != nil {\n\t\treturn nil, fmt.Errorf(\"go env: %w: %s\", err, stderr.String())\n\t}\n\n\tenv := make(map[string]string)\n\tscanner := bufio.NewScanner(bytes.NewReader(stdout.Bytes()))\n\n\tline := 0\n\tfor scanner.Scan() {\n\t\tline++\n\t\tkv := strings.SplitN(scanner.Text(), \"=\", 2)\n\t\tif len(kv) != 2 {\n\t\t\treturn nil, fmt.Errorf(\"go env: failed parsing line: %d\", line)\n\t\t}\n\t\tkey := strings.TrimSpace(kv[0])\n\t\tvalue := strings.TrimSpace(kv[1])\n\n\t\t// Unquote the value. Handle single or double quoted strings.\n\t\tif len(value) > 1 && ((value[0] == '\\'' && value[len(value)-1] == '\\'') ||\n\t\t\t(value[0] == '\"' && value[len(value)-1] == '\"')) {\n\t\t\tvalue = value[1 : len(value)-1]\n\t\t}\n\t\tenv[key] = value\n\t}\n\tif err := scanner.Err(); err != nil {\n\t\treturn nil, fmt.Errorf(\"go env: failed parsing: %w\", err)\n\t}\n\treturn env, nil\n}\n\nfunc goversionm(ctx context.Context, file string, appPath string, appFileName string, se oci.SignedEntity, dir string) ([]byte, types.MediaType, error) {\n\tgobin := getGoBinary()\n\n\tswitch se.(type) {\n\tcase oci.SignedImage:\n\t\tsbom := bytes.NewBuffer(nil)\n\t\tcmd := exec.CommandContext(ctx, gobin, \"version\", \"-m\", file)\n\t\tcmd.Stdout = sbom\n\t\tcmd.Stderr = os.Stderr\n\t\tif err := cmd.Run(); err != nil {\n\t\t\treturn nil, \"\", fmt.Errorf(\"go version -m %s: %w\", file, err)\n\t\t}\n\n\t\t// In order to get deterministics SBOMs replace our randomized\n\t\t// file name with the path the app will get inside of the container.\n\t\ts := []byte(strings.Replace(sbom.String(), file, appPath, 1))\n\n\t\tif err := writeSBOM(s, appFileName, dir, \"go.version-m\"); err != nil {\n\t\t\treturn nil, \"\", fmt.Errorf(\"writing sbom: %w\", err)\n\t\t}\n\n\t\treturn s, \"application/vnd.go.version-m\", nil\n\n\tcase oci.SignedImageIndex:\n\t\treturn nil, \"\", nil\n\n\tdefault:\n\t\treturn nil, \"\", fmt.Errorf(\"unrecognized type: %T\", se)\n\t}\n}\n\nfunc spdx(version string) sbomber {\n\treturn func(ctx context.Context, file string, appPath string, appFileName string, se oci.SignedEntity, dir string) ([]byte, types.MediaType, error) {\n\t\tswitch obj := se.(type) {\n\t\tcase oci.SignedImage:\n\t\t\tb, _, err := goversionm(ctx, file, appPath, \"\", obj, \"\")\n\t\t\tif err != nil {\n\t\t\t\treturn nil, \"\", err\n\t\t\t}\n\n\t\t\tb, err = sbom.GenerateImageSPDX(version, b, obj)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, \"\", err\n\t\t\t}\n\n\t\t\tif err := writeSBOM(b, appFileName, dir, \"spdx.json\"); err != nil {\n\t\t\t\treturn nil, \"\", err\n\t\t\t}\n\n\t\t\treturn b, ctypes.SPDXJSONMediaType, nil\n\n\t\tcase oci.SignedImageIndex:\n\t\t\tb, err := sbom.GenerateIndexSPDX(version, obj)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, \"\", err\n\t\t\t}\n\n\t\t\tif err := writeSBOM(b, appFileName, dir, \"spdx.json\"); err != nil {\n\t\t\t\treturn nil, \"\", err\n\t\t\t}\n\n\t\t\treturn b, ctypes.SPDXJSONMediaType, err\n\n\t\tdefault:\n\t\t\treturn nil, \"\", fmt.Errorf(\"unrecognized type: %T\", se)\n\t\t}\n\t}\n}\n\nfunc writeSBOM(sbom []byte, appFileName, dir, ext string) error {\n\tif dir != \"\" {\n\t\tsbomDir := filepath.Clean(dir)\n\t\tif err := os.MkdirAll(sbomDir, os.ModePerm); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tsbomPath := filepath.Join(sbomDir, appFileName+\".\"+ext)\n\t\tlog.Printf(\"Writing SBOM to %s\", sbomPath)\n\t\treturn os.WriteFile(sbomPath, sbom, 0644) //nolint:gosec\n\t}\n\treturn nil\n}\n\n// buildEnv creates the environment variables used by the `go build` command.\n// From `os/exec.Cmd`: If there are duplicate environment keys, only the last\n// value in the slice for each duplicate key is used.\nfunc buildEnv(platform v1.Platform, osEnv, buildEnv []string) ([]string, error) {\n\t// Default env\n\tenv := []string{\n\t\t\"CGO_ENABLED=0\",\n\t\t\"GOOS=\" + platform.OS,\n\t\t\"GOARCH=\" + platform.Architecture,\n\t}\n\tif platform.Variant != \"\" {\n\t\tswitch platform.Architecture {\n\t\tcase \"arm\":\n\t\t\t// See: https://pkg.go.dev/cmd/go#hdr-Environment_variables\n\t\t\tgoarm, err := getGoarm(platform)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"goarm failure: %w\", err)\n\t\t\t}\n\t\t\tif goarm != \"\" {\n\t\t\t\tenv = append(env, \"GOARM=\"+goarm)\n\t\t\t}\n\t\tcase \"amd64\":\n\t\t\t// See: https://tip.golang.org/doc/go1.18#amd64\n\t\t\tenv = append(env, \"GOAMD64=\"+platform.Variant)\n\t\t}\n\t}\n\n\tenv = append(env, osEnv...)\n\tenv = append(env, buildEnv...)\n\treturn env, nil\n}\n\nfunc appFilename(importpath string) string {\n\tbase := filepath.Base(importpath)\n\n\t// If we fail to determine a good name from the importpath then use a\n\t// safe default.\n\tif base == \".\" || base == string(filepath.Separator) {\n\t\treturn defaultAppFilename\n\t}\n\n\treturn base\n}\n\n// userOwnerAndGroupSID is a magic value needed to make the binary executable\n// in a Windows container.\n//\n// owner: BUILTIN/Users group: BUILTIN/Users ($sddlValue=\"O:BUG:BU\")\nconst userOwnerAndGroupSID = \"AQAAgBQAAAAkAAAAAAAAAAAAAAABAgAAAAAABSAAAAAhAgAAAQIAAAAAAAUgAAAAIQIAAA==\"\n\nfunc tarBinary(name, binary string, platform *v1.Platform, opts *layerOptions) (*bytes.Buffer, error) {\n\tbuf := bytes.NewBuffer(nil)\n\ttw := tar.NewWriter(buf)\n\tdefer tw.Close()\n\n\t// Write the parent directories to the tarball archive.\n\t// For Windows, the layer must contain a Hives/ directory, and the root\n\t// of the actual filesystem goes in a Files/ directory.\n\t// For Linux, the binary goes into /ko-app/\n\tdirs := []string{\"ko-app\"}\n\tif platform.OS == \"windows\" {\n\t\tdirs = []string{\n\t\t\t\"Hives\",\n\t\t\t\"Files\",\n\t\t\t\"Files/ko-app\",\n\t\t}\n\t\tname = \"Files\" + name\n\t}\n\tfor _, dir := range dirs {\n\t\tif err := tw.WriteHeader(&tar.Header{\n\t\t\tName:     dir,\n\t\t\tTypeflag: tar.TypeDir,\n\t\t\t// Use a fixed Mode, so that this isn't sensitive to the directory and umask\n\t\t\t// under which it was created. Additionally, windows can only set 0222,\n\t\t\t// 0444, or 0666, none of which are executable.\n\t\t\tMode: 0555,\n\t\t}); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"writing dir %q to tar: %w\", dir, err)\n\t\t}\n\t}\n\n\tfile, err := os.Open(binary)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"opening binary: %w\", err)\n\t}\n\tdefer file.Close()\n\tstat, err := file.Stat()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\theader := &tar.Header{\n\t\tName:     name,\n\t\tSize:     stat.Size(),\n\t\tTypeflag: tar.TypeReg,\n\t\t// Use a fixed Mode, so that this isn't sensitive to the directory and umask\n\t\t// under which it was created. Additionally, windows can only set 0222,\n\t\t// 0444, or 0666, none of which are executable.\n\t\tMode:       0555,\n\t\tPAXRecords: map[string]string{},\n\t}\n\tswitch platform.OS {\n\tcase \"windows\":\n\t\t// This magic value is for some reason needed for Windows to be\n\t\t// able to execute the binary.\n\t\theader.PAXRecords[\"MSWINDOWS.rawsd\"] = userOwnerAndGroupSID\n\tcase \"linux\":\n\t\tif opts.linuxCapabilities != nil {\n\t\t\txattr, err := opts.linuxCapabilities.ToXattrBytes()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"caps.FileCaps.ToXattrBytes: %w\", err)\n\t\t\t}\n\t\t\theader.PAXRecords[\"SCHILY.xattr.security.capability\"] = string(xattr)\n\t\t}\n\t}\n\t// write the header to the tarball archive\n\tif err := tw.WriteHeader(header); err != nil {\n\t\treturn nil, fmt.Errorf(\"writing tar header: %w\", err)\n\t}\n\t// copy the file data to the tarball\n\tif _, err := io.Copy(tw, file); err != nil {\n\t\treturn nil, fmt.Errorf(\"copying file to tar: %w\", err)\n\t}\n\n\treturn buf, nil\n}\n\nfunc (g *gobuild) kodataPath(ref reference) (string, error) {\n\tdir := filepath.Clean(g.dir)\n\tif dir == \".\" {\n\t\tdir = \"\"\n\t}\n\tpkgs, err := packages.Load(&packages.Config{Dir: dir, Mode: packages.NeedFiles}, ref.Path())\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"error loading package from %s: %w\", ref.Path(), err)\n\t}\n\tif len(pkgs) != 1 {\n\t\treturn \"\", fmt.Errorf(\"found %d local packages, expected 1\", len(pkgs))\n\t}\n\tif len(pkgs[0].GoFiles) == 0 {\n\t\treturn \"\", fmt.Errorf(\"package %s contains no Go files\", pkgs[0])\n\t}\n\treturn filepath.Join(filepath.Dir(pkgs[0].GoFiles[0]), \"kodata\"), nil\n}\n\n// Where kodata lives in the image.\nconst kodataRoot = \"/var/run/ko\"\n\n// writeDirToTar writes a directory header to the tar writer.\nfunc writeDirToTar(tw *tar.Writer, name string, modTime time.Time) error {\n\treturn tw.WriteHeader(&tar.Header{\n\t\tName:     name,\n\t\tTypeflag: tar.TypeDir,\n\t\t// Use a fixed Mode, so that this isn't sensitive to the directory and umask\n\t\t// under which it was created. Additionally, windows can only set 0222,\n\t\t// 0444, or 0666, none of which are executable.\n\t\tMode:    0555,\n\t\tModTime: modTime,\n\t})\n}\n\n// writeFileToTar writes a file to the tar writer.\nfunc writeFileToTar(tw *tar.Writer, name, evalPath string, size int64, modTime time.Time, platform *v1.Platform) error {\n\tfile, err := os.Open(evalPath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"os.Open(%q): %w\", evalPath, err)\n\t}\n\tdefer file.Close()\n\n\theader := &tar.Header{\n\t\tName:     name,\n\t\tSize:     size,\n\t\tTypeflag: tar.TypeReg,\n\t\t// Use a fixed Mode, so that this isn't sensitive to the directory and umask\n\t\t// under which it was created. Additionally, windows can only set 0222,\n\t\t// 0444, or 0666, none of which are executable.\n\t\tMode:    0555,\n\t\tModTime: modTime,\n\t}\n\tif platform.OS == \"windows\" {\n\t\t// This magic value is for some reason needed for Windows to be\n\t\t// able to execute the binary.\n\t\theader.PAXRecords = map[string]string{\n\t\t\t\"MSWINDOWS.rawsd\": userOwnerAndGroupSID,\n\t\t}\n\t}\n\tif err := tw.WriteHeader(header); err != nil {\n\t\treturn fmt.Errorf(\"tar.Writer.WriteHeader(%q): %w\", name, err)\n\t}\n\tif _, err := io.Copy(tw, file); err != nil {\n\t\treturn fmt.Errorf(\"io.Copy(%q, %q): %w\", name, evalPath, err)\n\t}\n\treturn nil\n}\n\n// walkRecursive performs a filepath.Walk of the given root directory adding it\n// to the provided tar.Writer with root -> chroot.  All symlinks are dereferenced,\n// which is what leads to recursion when we encounter a directory symlink.\nfunc walkRecursive(tw *tar.Writer, root, chroot string, creationTime v1.Time, platform *v1.Platform) error {\n\treturn filepath.Walk(root, func(hostPath string, info os.FileInfo, err error) error {\n\t\tif hostPath == root {\n\t\t\treturn nil\n\t\t}\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"filepath.Walk(%q): %w\", root, err)\n\t\t}\n\n\t\tnewPath := path.Join(chroot, filepath.ToSlash(hostPath[len(root):]))\n\n\t\t// Handle directories: write header and let filepath.Walk recurse.\n\t\tif info.Mode().IsDir() {\n\t\t\tif err := writeDirToTar(tw, newPath, creationTime.Time); err != nil {\n\t\t\t\treturn fmt.Errorf(\"writing dir %q to tar: %w\", newPath, err)\n\t\t\t}\n\t\t\treturn nil\n\t\t}\n\n\t\t// Don't chase symlinks on Windows, where cross-compiled symlink support is not possible.\n\t\tif platform.OS == \"windows\" {\n\t\t\tif info.Mode()&os.ModeSymlink != 0 {\n\t\t\t\tlog.Println(\"skipping symlink in kodata for windows:\", info.Name())\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\n\t\tevalPath, err := filepath.EvalSymlinks(hostPath)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"filepath.EvalSymlinks(%q): %w\", hostPath, err)\n\t\t}\n\n\t\t// Get info of the symlink target.\n\t\tinfo, err = os.Stat(evalPath)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"os.Stat(%q): %w\", evalPath, err)\n\t\t}\n\n\t\t// Symlink target is a directory: write header and recurse.\n\t\tif info.Mode().IsDir() {\n\t\t\tif err := writeDirToTar(tw, newPath, creationTime.Time); err != nil {\n\t\t\t\treturn fmt.Errorf(\"writing dir %q to tar: %w\", newPath, err)\n\t\t\t}\n\t\t\treturn walkRecursive(tw, evalPath, newPath, creationTime, platform)\n\t\t}\n\n\t\t// Regular file (or symlink to file): write to tar.\n\t\treturn writeFileToTar(tw, newPath, evalPath, info.Size(), creationTime.Time, platform)\n\t})\n}\n\nfunc (g *gobuild) tarKoData(ref reference, platform *v1.Platform) (*bytes.Buffer, error) {\n\tbuf := bytes.NewBuffer(nil)\n\ttw := tar.NewWriter(buf)\n\tdefer tw.Close()\n\n\troot, err := g.kodataPath(ref)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcreationTime := g.kodataCreationTime\n\n\t// Write the parent directories to the tarball archive.\n\t// For Windows, the layer must contain a Hives/ directory, and the root\n\t// of the actual filesystem goes in a Files/ directory.\n\t// For Linux, kodata starts at /var/run/ko.\n\tchroot := kodataRoot\n\tdirs := []string{\n\t\t\"/var\",\n\t\t\"/var/run\",\n\t\t\"/var/run/ko\",\n\t}\n\tif platform.OS == \"windows\" {\n\t\tchroot = \"Files\" + kodataRoot\n\t\tdirs = []string{\n\t\t\t\"Hives\",\n\t\t\t\"Files\",\n\t\t\t\"Files/var\",\n\t\t\t\"Files/var/run\",\n\t\t\t\"Files/var/run/ko\",\n\t\t}\n\t}\n\tfor _, dir := range dirs {\n\t\tif err := writeDirToTar(tw, dir, creationTime.Time); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"writing dir %q: %w\", dir, err)\n\t\t}\n\t}\n\n\treturn buf, walkRecursive(tw, root, chroot, creationTime, platform)\n}\n\nfunc createTemplateData(ctx context.Context, buildCtx buildContext) (map[string]any, error) {\n\tenvVars := map[string]string{\n\t\t\"LDFLAGS\": \"\",\n\t}\n\tfor _, entry := range buildCtx.env {\n\t\tkv := strings.SplitN(entry, \"=\", 2)\n\t\tif len(kv) != 2 {\n\t\t\treturn nil, fmt.Errorf(\"invalid environment variable entry: %q\", entry)\n\t\t}\n\t\tenvVars[kv[0]] = kv[1]\n\t}\n\n\t// Get the go environment.\n\tgoEnv, err := goenv(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Override go env with any matching values from the environment variables.\n\tfor k, v := range envVars {\n\t\tif _, ok := goEnv[k]; ok {\n\t\t\tgoEnv[k] = v\n\t\t}\n\t}\n\n\t// Get the git information, if available.\n\tinfo, err := git.GetInfo(ctx, buildCtx.dir)\n\tif err != nil {\n\t\tlog.Printf(\"%v\", err)\n\t}\n\n\t// Use the creation time as the build date, if provided.\n\tdate := buildCtx.creationTime.Time\n\tif date.IsZero() {\n\t\tdate = time.Now()\n\t}\n\n\treturn map[string]any{\n\t\t\"Env\":       envVars,\n\t\t\"GoEnv\":     goEnv,\n\t\t\"Git\":       info.TemplateValue(),\n\t\t\"Date\":      date.Format(time.RFC3339),\n\t\t\"Timestamp\": date.UTC().Unix(),\n\t}, nil\n}\n\nfunc applyTemplating(list []string, data map[string]any) ([]string, error) {\n\tresult := make([]string, 0, len(list))\n\tfor _, entry := range list {\n\t\ttmpl, err := template.New(\"argsTmpl\").Option(\"missingkey=error\").Parse(entry)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tvar buf bytes.Buffer\n\t\tif err := tmpl.Execute(&buf, data); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tresult = append(result, buf.String())\n\t}\n\n\treturn result, nil\n}\n\nfunc createBuildArgs(ctx context.Context, buildCtx buildContext) ([]string, error) {\n\tvar args []string\n\n\tdata, err := createTemplateData(ctx, buildCtx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif len(buildCtx.flags) > 0 {\n\t\tflags, err := applyTemplating(buildCtx.flags, data)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\targs = append(args, flags...)\n\t}\n\n\tif len(buildCtx.ldflags) > 0 {\n\t\tldflags, err := applyTemplating(buildCtx.ldflags, data)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\targs = append(args, fmt.Sprintf(\"-ldflags=%s\", strings.Join(ldflags, \" \")))\n\t}\n\n\t// Reject any flags that attempt to set --toolexec (with or\n\t// without =, with one or two -s)\n\tfor _, a := range args {\n\t\tfor _, d := range []string{\"-\", \"--\"} {\n\t\t\tif a == d+\"toolexec\" || strings.HasPrefix(a, d+\"toolexec=\") {\n\t\t\t\treturn nil, fmt.Errorf(\"cannot set %s\", a)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn args, nil\n}\n\nfunc (g *gobuild) configForImportPath(ip string) Config {\n\tconfig := g.buildConfigs[ip]\n\n\t// Apply defaultFlags before any flag manipulation (trimpath, gcflags, etc.)\n\t// so that the emptiness check works on the original per-build flags.\n\tif len(config.Flags) == 0 {\n\t\tconfig.Flags = g.defaultFlags\n\t}\n\n\tif g.trimpath {\n\t\t// The `-trimpath` flag removes file system paths from the resulting binary, to aid reproducibility.\n\t\t// Ref: https://pkg.go.dev/cmd/go#hdr-Compile_packages_and_dependencies\n\t\tconfig.Flags = append(config.Flags, \"-trimpath\")\n\t}\n\n\tif g.disableOptimizations {\n\t\t// Disable optimizations (-N) and inlining (-l).\n\t\tconfig.Flags = append(config.Flags, \"-gcflags\", \"all=-N -l\")\n\t}\n\n\tif config.ID != \"\" {\n\t\tlog.Printf(\"Using build config %s for %s\", config.ID, ip)\n\t}\n\n\treturn config\n}\n\nfunc (g gobuild) useDebugging(platform v1.Platform) bool {\n\treturn g.debug && doesPlatformSupportDebugging(platform)\n}\n\nfunc (g *gobuild) buildOne(ctx context.Context, refStr string, base v1.Image, platform *v1.Platform) (oci.SignedImage, error) {\n\tif err := g.semaphore.Acquire(ctx, 1); err != nil {\n\t\treturn nil, err\n\t}\n\tdefer g.semaphore.Release(1)\n\n\tref := newRef(refStr)\n\n\t// Layers should be typed to match the underlying image, since some\n\t// registries reject mixed-type layers.\n\tvar layerMediaType types.MediaType\n\tmt, err := base.MediaType()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tswitch mt {\n\tcase types.OCIManifestSchema1:\n\t\tlayerMediaType = types.OCILayer\n\tcase types.DockerManifestSchema2:\n\t\tlayerMediaType = types.DockerLayer\n\t}\n\n\tcf, err := base.ConfigFile()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif platform == nil {\n\t\tif cf.OS == \"\" {\n\t\t\tcf.OS = \"linux\"\n\t\t}\n\t\tif cf.Architecture == \"\" {\n\t\t\tcf.Architecture = \"amd64\"\n\t\t}\n\n\t\tplatform = &v1.Platform{\n\t\t\tOS:           cf.OS,\n\t\t\tArchitecture: cf.Architecture,\n\t\t\tOSVersion:    cf.OSVersion,\n\t\t}\n\t}\n\tif g.debug && !doesPlatformSupportDebugging(*platform) {\n\t\tlog.Printf(\"image for platform %q will be built without debugging enabled because debugging is not supported for that platform\", *platform)\n\t}\n\n\tif !g.platformMatcher.matches(platform) {\n\t\treturn nil, fmt.Errorf(\"base image platform %q does not match desired platforms %v\", platform, g.platformMatcher.platforms)\n\t}\n\n\tconfig := g.configForImportPath(ref.Path())\n\n\t// Merge the system and build environment variables.\n\tenv := config.Env\n\tif len(env) == 0 {\n\t\t// Use the default, if any.\n\t\tenv = g.defaultEnv\n\t}\n\tenv, err = buildEnv(*platform, os.Environ(), env)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not create env for %s: %w\", ref.Path(), err)\n\t}\n\n\t// Get the build flags (defaultFlags already applied in configForImportPath).\n\tflags := config.Flags\n\n\t// Get the build ldflags.\n\tldflags := config.Ldflags\n\tif len(ldflags) == 0 {\n\t\t// Use the default, if any\n\t\tldflags = g.defaultLdflags\n\t}\n\n\t// Do the build into a temporary file.\n\tfile, err := g.build(ctx, buildContext{\n\t\tcreationTime: g.creationTime,\n\t\tip:           ref.Path(),\n\t\tdir:          g.dir,\n\t\tenv:          env,\n\t\tflags:        flags,\n\t\tldflags:      ldflags,\n\t\tplatform:     *platform,\n\t})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"build: %w\", err)\n\t}\n\tif os.Getenv(\"KOCACHE\") == \"\" {\n\t\tdefer os.RemoveAll(filepath.Dir(file))\n\t}\n\n\tvar layers []mutate.Addendum\n\n\t// Create a layer from the kodata directory under this import path.\n\tdataLayerBuf, err := g.tarKoData(ref, platform)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"tarring kodata: %w\", err)\n\t}\n\tdataLayerBytes := dataLayerBuf.Bytes()\n\tdataLayer, err := tarball.LayerFromOpener(func() (io.ReadCloser, error) {\n\t\treturn io.NopCloser(bytes.NewBuffer(dataLayerBytes)), nil\n\t}, tarball.WithCompressedCaching, tarball.WithMediaType(layerMediaType))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tlayers = append(layers, mutate.Addendum{\n\t\tLayer: dataLayer,\n\t\tHistory: v1.History{\n\t\t\tAuthor:    \"ko\",\n\t\t\tCreatedBy: \"ko build \" + ref.String(),\n\t\t\tCreated:   g.kodataCreationTime,\n\t\t\tComment:   \"kodata contents, at $KO_DATA_PATH\",\n\t\t},\n\t})\n\n\tappDir := \"/ko-app\"\n\tappFileName := appFilename(ref.Path())\n\tappPath := path.Join(appDir, appFileName)\n\n\tvar lo layerOptions\n\tlo.linuxCapabilities, err = caps.NewFileCaps(config.LinuxCapabilities...)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"linux_capabilities: %w\", err)\n\t}\n\n\tmiss := func() (v1.Layer, error) {\n\t\treturn buildLayer(appPath, file, platform, layerMediaType, &lo)\n\t}\n\n\tvar binaryLayer v1.Layer\n\tswitch {\n\tcase lo.linuxCapabilities != nil:\n\t\tlog.Printf(\"Some options prevent us from using layer cache\")\n\t\tbinaryLayer, err = miss()\n\tdefault:\n\t\tbinaryLayer, err = g.cache.get(ctx, file, miss)\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"cache.get(%q): %w\", file, err)\n\t}\n\n\tlayers = append(layers, mutate.Addendum{\n\t\tLayer:     binaryLayer,\n\t\tMediaType: layerMediaType,\n\t\tHistory: v1.History{\n\t\t\tAuthor:    \"ko\",\n\t\t\tCreated:   g.creationTime,\n\t\t\tCreatedBy: \"ko build \" + ref.String(),\n\t\t\tComment:   \"go build output, at \" + appPath,\n\t\t},\n\t})\n\n\tdelvePath := \"\" // path for delve in image\n\tif g.useDebugging(*platform) {\n\t\t// get delve locally\n\t\tdelveBinary, err := getDelve(ctx, *platform)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"building Delve: %w\", err)\n\t\t}\n\t\tdefer os.RemoveAll(filepath.Dir(delveBinary))\n\n\t\tdelvePath = path.Join(\"/ko-app\", filepath.Base(delveBinary))\n\n\t\t// add layer with delve binary\n\t\tdelveLayer, err := g.cache.get(ctx, delveBinary, func() (v1.Layer, error) {\n\t\t\treturn buildLayer(delvePath, delveBinary, platform, layerMediaType, &lo)\n\t\t})\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"cache.get(%q): %w\", delveBinary, err)\n\t\t}\n\n\t\tlayers = append(layers, mutate.Addendum{\n\t\t\tLayer:     delveLayer,\n\t\t\tMediaType: layerMediaType,\n\t\t\tHistory: v1.History{\n\t\t\t\tAuthor:    \"ko\",\n\t\t\t\tCreated:   g.creationTime,\n\t\t\t\tCreatedBy: \"ko build \" + ref.String(),\n\t\t\t\tComment:   \"Delve debugger, at \" + delvePath,\n\t\t\t},\n\t\t})\n\t}\n\tdelveArgs := []string{\n\t\t\"exec\",\n\t\t\"--listen=:40000\",\n\t\t\"--headless\",\n\t\t\"--log\",\n\t\t\"--accept-multiclient\",\n\t\t\"--api-version=2\",\n\t\t\"--\",\n\t}\n\n\t// Augment the base image with our application layer.\n\twithApp, err := mutate.Append(base, layers...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Start from a copy of the base image's config file, and set\n\t// the entrypoint to our app.\n\tcfg, err := withApp.ConfigFile()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcfg = cfg.DeepCopy()\n\tcfg.Config.Entrypoint = []string{appPath}\n\tcfg.Config.Cmd = nil\n\tif platform.OS == \"windows\" {\n\t\tappPath := `C:\\ko-app\\` + appFileName\n\t\tif g.debug {\n\t\t\tcfg.Config.Entrypoint = append([]string{\"C:\\\\\" + delvePath}, delveArgs...)\n\t\t\tcfg.Config.Entrypoint = append(cfg.Config.Entrypoint, appPath)\n\t\t} else {\n\t\t\tcfg.Config.Entrypoint = []string{appPath}\n\t\t}\n\n\t\tupdatePath(cfg, `C:\\ko-app`)\n\t\tcfg.Config.Env = append(cfg.Config.Env, `KO_DATA_PATH=C:\\var\\run\\ko`)\n\t} else {\n\t\tif g.useDebugging(*platform) {\n\t\t\tcfg.Config.Entrypoint = append([]string{delvePath}, delveArgs...)\n\t\t\tcfg.Config.Entrypoint = append(cfg.Config.Entrypoint, appPath)\n\t\t}\n\n\t\tupdatePath(cfg, appDir)\n\t\tcfg.Config.Env = append(cfg.Config.Env, \"KO_DATA_PATH=\"+kodataRoot)\n\t}\n\tcfg.Author = \"github.com/ko-build/ko\"\n\n\tif cfg.Config.Labels == nil {\n\t\tcfg.Config.Labels = map[string]string{}\n\t}\n\tmaps.Copy(cfg.Config.Labels, g.labels)\n\n\tif g.user != \"\" {\n\t\tcfg.Config.User = g.user\n\t}\n\n\tempty := v1.Time{}\n\tif g.creationTime != empty {\n\t\tcfg.Created = g.creationTime\n\t}\n\n\timage, err := mutate.ConfigFile(withApp, cfg)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsi := signed.Image(image)\n\n\tif g.sbom != nil {\n\t\t// Construct a path-safe encoding of platform.\n\t\tpf := strings.ReplaceAll(strings.ReplaceAll(platform.String(), \"/\", \"-\"), \":\", \"-\")\n\t\tsbom, mt, err := g.sbom(ctx, file, appPath, fmt.Sprintf(\"%s-%s\", appFileName, pf), si, g.sbomDir)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tf, err := static.NewFile(sbom, static.WithLayerMediaType(mt))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tsi, err = ocimutate.AttachFileToImage(si, \"sbom\", f)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn si, nil\n}\n\n// layerOptions captures additional options to apply when authoring layer\ntype layerOptions struct {\n\tlinuxCapabilities *caps.FileCaps\n}\n\nfunc buildLayer(appPath, file string, platform *v1.Platform, layerMediaType types.MediaType, opts *layerOptions) (v1.Layer, error) {\n\t// Construct a tarball with the binary and produce a layer.\n\tbinaryLayerBuf, err := tarBinary(appPath, file, platform, opts)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"tarring binary: %w\", err)\n\t}\n\tbinaryLayerBytes := binaryLayerBuf.Bytes()\n\treturn tarball.LayerFromOpener(func() (io.ReadCloser, error) {\n\t\treturn io.NopCloser(bytes.NewBuffer(binaryLayerBytes)), nil\n\t}, tarball.WithCompressedCaching, tarball.WithMediaType(layerMediaType))\n}\n\n// Append appPath to the PATH environment variable, if it exists. Otherwise,\n// set the PATH environment variable to appPath.\nfunc updatePath(cf *v1.ConfigFile, appPath string) {\n\tfor i, env := range cf.Config.Env {\n\t\tparts := strings.SplitN(env, \"=\", 2)\n\t\tif len(parts) != 2 {\n\t\t\t// Expect environment variables to be in the form KEY=VALUE, so this is unexpected.\n\t\t\tcontinue\n\t\t}\n\t\tkey, value := parts[0], parts[1]\n\t\tif key == \"PATH\" {\n\t\t\tvalue = fmt.Sprintf(\"%s:%s\", value, appPath)\n\t\t\tcf.Config.Env[i] = \"PATH=\" + value\n\t\t\treturn\n\t\t}\n\t}\n\n\t// If we get here, we never saw PATH.\n\tcf.Config.Env = append(cf.Config.Env, \"PATH=\"+appPath)\n}\n\n// Build implements build.Interface\nfunc (g *gobuild) Build(ctx context.Context, s string) (Result, error) {\n\t// Determine the appropriate base image for this import path.\n\t// We use the overall gobuild.ctx because the Build ctx gets cancelled\n\t// early, and we lazily use the ctx within ggcr's remote package.\n\tbaseRef, base, err := g.getBase(g.ctx, s)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"fetching base image: %w\", err)\n\t}\n\n\t// Determine what kind of base we have and if we should publish an image or an index.\n\tmt, err := base.MediaType()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Annotate the base image we pass to the build function with\n\t// annotations indicating the digest (and possibly tag) of the\n\t// base image.  This will be inherited by the image produced.\n\tif mt != types.DockerManifestList {\n\t\tbaseDigest, err := base.Digest()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tannotations := maps.Clone(g.annotations)\n\t\tannotations[specsv1.AnnotationBaseImageDigest] = baseDigest.String()\n\t\tannotations[specsv1.AnnotationBaseImageName] = baseRef.Name()\n\t\tbase = mutate.Annotations(base, annotations).(Result)\n\t}\n\n\tswitch mt {\n\tcase types.OCIImageIndex, types.DockerManifestList:\n\t\tbaseIndex, ok := base.(v1.ImageIndex)\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\"failed to interpret base as index: %v\", base)\n\t\t}\n\t\treturn g.buildAll(ctx, s, baseRef, baseIndex)\n\tcase types.OCIManifestSchema1, types.DockerManifestSchema2:\n\t\tbaseImage, ok := base.(v1.Image)\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\"failed to interpret base as image: %v\", base)\n\t\t}\n\t\treturn g.buildOne(ctx, s, baseImage, nil)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"base image media type: %s\", mt)\n\t}\n}\n\nfunc (g *gobuild) buildAll(ctx context.Context, ref string, baseRef name.Reference, baseIndex v1.ImageIndex) (Result, error) {\n\tim, err := baseIndex.IndexManifest()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tmatches := make([]v1.Descriptor, 0)\n\tfor _, desc := range im.Manifests {\n\t\t// Nested index is pretty rare. We could support this in theory, but return an error for now.\n\t\tif desc.MediaType != types.OCIManifestSchema1 && desc.MediaType != types.DockerManifestSchema2 {\n\t\t\treturn nil, fmt.Errorf(\"%q has unexpected mediaType %q in base for %q\", desc.Digest, desc.MediaType, ref)\n\t\t}\n\n\t\tif g.platformMatcher.matches(desc.Platform) {\n\t\t\tmatches = append(matches, desc)\n\t\t}\n\t}\n\tif len(matches) == 0 {\n\t\treturn nil, errors.New(\"no matching platforms in base image index\")\n\t}\n\tif len(matches) == 1 {\n\t\t// Filters resulted in a single matching platform; just produce\n\t\t// a single-platform image.\n\t\timg, err := baseIndex.Image(matches[0].Digest)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error getting matching image from index: %w\", err)\n\t\t}\n\n\t\tannotations := maps.Clone(g.annotations)\n\t\t// Decorate the image with the ref of the index, and the matching\n\t\t// platform's digest.\n\t\tannotations[specsv1.AnnotationBaseImageDigest] = matches[0].Digest.String()\n\t\tannotations[specsv1.AnnotationBaseImageName] = baseRef.Name()\n\t\timg = mutate.Annotations(img, annotations).(v1.Image)\n\t\treturn g.buildOne(ctx, ref, img, matches[0].Platform)\n\t}\n\n\tannotations := maps.Clone(g.annotations)\n\tannotations[specsv1.AnnotationBaseImageName] = baseRef.Name()\n\tbaseDigest, _ := baseIndex.Digest()\n\tannotations[specsv1.AnnotationBaseImageDigest] = baseDigest.String()\n\n\t// Build an image for each matching platform from the base and append\n\t// it to a new index to produce the result. We use the indices to\n\t// preserve the base image ordering here.\n\terrg, gctx := errgroup.WithContext(ctx)\n\tadds := make([]ocimutate.IndexAddendum, len(matches))\n\tfor i, desc := range matches {\n\t\terrg.Go(func() error {\n\t\t\tbaseImage, err := baseIndex.Image(desc.Digest)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tannotations := maps.Clone(g.annotations)\n\t\t\t// Decorate the image with the ref of the index, and the matching\n\t\t\t// platform's digest.  The ref of the index encodes the critical\n\t\t\t// repository information for fetching the base image's digest, but\n\t\t\t// we leave `name` pointing at the index's full original ref to that\n\t\t\t// folks could conceivably check for updates to the index over time.\n\t\t\t// While the `digest` doesn't give us enough information to check\n\t\t\t// for changes with a simple HEAD (because we need the full index\n\t\t\t// manifest to get the per-architecture image), that optimization\n\t\t\t// mainly matters for DockerHub where HEAD's are exempt from rate\n\t\t\t// limiting.  However, in practice, the way DockerHub updates the\n\t\t\t// indices for official images is to rebuild per-arch images and\n\t\t\t// replace the per-arch images in the existing index, so an index\n\t\t\t// with N manifest receives N updates.  If we only record the digest\n\t\t\t// of the index here, then we cannot tell when the index updates are\n\t\t\t// no-ops for us because we didn't record the digest of the actual\n\t\t\t// image we used, and we would potentially end up doing Nx more work\n\t\t\t// than we really need to do.\n\t\t\tannotations[specsv1.AnnotationBaseImageDigest] = desc.Digest.String()\n\t\t\tannotations[specsv1.AnnotationBaseImageName] = baseRef.Name()\n\n\t\t\tbaseImage = mutate.Annotations(baseImage, annotations).(v1.Image)\n\n\t\t\timg, err := g.buildOne(gctx, ref, baseImage, desc.Platform)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tadds[i] = ocimutate.IndexAddendum{\n\t\t\t\tAdd: img,\n\t\t\t\tDescriptor: v1.Descriptor{\n\t\t\t\t\tURLs:        desc.URLs,\n\t\t\t\t\tMediaType:   desc.MediaType,\n\t\t\t\t\tAnnotations: desc.Annotations,\n\t\t\t\t\tPlatform:    desc.Platform,\n\t\t\t\t},\n\t\t\t}\n\t\t\treturn nil\n\t\t})\n\t}\n\tif err := errg.Wait(); err != nil {\n\t\treturn nil, err\n\t}\n\n\tbaseType, err := baseIndex.MediaType()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tidx := ocimutate.AppendManifests(\n\t\tmutate.Annotations(\n\t\t\tmutate.IndexMediaType(empty.Index, baseType),\n\t\t\tannotations).(v1.ImageIndex),\n\t\tadds...)\n\n\tif g.sbom != nil {\n\t\tref := newRef(ref)\n\t\tappFileName := appFilename(ref.Path())\n\t\tsbom, mt, err := g.sbom(ctx, \"\", \"\", fmt.Sprintf(\"%s-index\", appFileName), idx, g.sbomDir)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif sbom != nil {\n\t\t\tf, err := static.NewFile(sbom, static.WithLayerMediaType(mt))\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tidx, err = ocimutate.AttachFileToImageIndex(idx, \"sbom\", f)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t}\n\n\treturn idx, nil\n}\n\nfunc parseSpec(spec []string) (*platformMatcher, error) {\n\t// Don't bother parsing \"all\".\n\t// Empty slice should never happen because we default to linux/amd64 (or GOOS/GOARCH).\n\tif len(spec) == 0 || spec[0] == \"all\" {\n\t\treturn &platformMatcher{spec: spec}, nil\n\t}\n\n\tplatforms := make([]v1.Platform, 0)\n\tfor _, s := range spec {\n\t\tp, err := v1.ParsePlatform(s)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tplatforms = append(platforms, *p)\n\t}\n\treturn &platformMatcher{spec: spec, platforms: platforms}, nil\n}\n\nfunc (pm *platformMatcher) matches(base *v1.Platform) bool {\n\t// Strip out manifests with \"unknown/unknown\" platform, which Docker uses\n\t// to store provenance attestations.\n\tif base != nil &&\n\t\t(base.OS == \"unknown\" || base.Architecture == \"unknown\") {\n\t\treturn false\n\t}\n\n\tif len(pm.spec) > 0 && pm.spec[0] == \"all\" {\n\t\treturn true\n\t}\n\n\t// Don't build anything without a platform field unless \"all\". Unclear what we should do here.\n\tif base == nil {\n\t\treturn false\n\t}\n\n\tfor _, p := range pm.platforms {\n\t\tif p.OS != \"\" && base.OS != p.OS {\n\t\t\tcontinue\n\t\t}\n\t\tif p.Architecture != \"\" && base.Architecture != p.Architecture {\n\t\t\tcontinue\n\t\t}\n\t\tif p.Variant != \"\" && base.Variant != p.Variant {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Windows is... weird. Windows base images use osversion to\n\t\t// communicate what Windows version is used, which matters for image\n\t\t// selection at runtime.\n\t\t//\n\t\t// Windows osversions include the usual major/minor/patch version\n\t\t// components, as well as an incrementing \"build number\" which can\n\t\t// change when new Windows base images are released.\n\t\t//\n\t\t// In order to avoid having to match the entire osversion including the\n\t\t// incrementing build number component, we allow matching a platform\n\t\t// that only matches the first three osversion components, only for\n\t\t// Windows images.\n\t\t//\n\t\t// If the X.Y.Z components don't match (or aren't formed as we expect),\n\t\t// the platform doesn't match. Only if X.Y.Z matches and the extra\n\t\t// build number component doesn't, do we consider the platform to\n\t\t// match.\n\t\t//\n\t\t// Ref: https://docs.microsoft.com/en-us/virtualization/windowscontainers/deploy-containers/version-compatibility?tabs=windows-server-2022%2Cwindows-10-21H1#build-number-new-release-of-windows\n\t\tif p.OSVersion != \"\" && p.OSVersion != base.OSVersion {\n\t\t\tif p.OS != \"windows\" {\n\t\t\t\t// osversion mismatch is only possibly allowed when os == windows.\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif pcount, bcount := strings.Count(p.OSVersion, \".\"), strings.Count(base.OSVersion, \".\"); pcount == 2 && bcount == 3 {\n\t\t\t\tif p.OSVersion != base.OSVersion[:strings.LastIndex(base.OSVersion, \".\")] {\n\t\t\t\t\t// If requested osversion is X.Y.Z and potential match is X.Y.Z.A, all of X.Y.Z must match.\n\t\t\t\t\t// Any other form of these osversions are not a match.\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\t// Partial osversion matching only allows X.Y.Z to match X.Y.Z.A.\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\treturn true\n\t}\n\n\treturn false\n}\n"
  },
  {
    "path": "pkg/build/gobuild_test.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage build\n\nimport (\n\t\"archive/tar\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"github.com/google/go-cmp/cmp/cmpopts\"\n\t\"github.com/google/go-containerregistry/pkg/name\"\n\tv1 \"github.com/google/go-containerregistry/pkg/v1\"\n\t\"github.com/google/go-containerregistry/pkg/v1/empty\"\n\t\"github.com/google/go-containerregistry/pkg/v1/mutate\"\n\t\"github.com/google/go-containerregistry/pkg/v1/random\"\n\t\"github.com/google/go-containerregistry/pkg/v1/types\"\n\t\"github.com/google/ko/pkg/internal/gittesting\"\n\tspecsv1 \"github.com/opencontainers/image-spec/specs-go/v1\"\n\t\"github.com/sigstore/cosign/v3/pkg/oci\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc repoRootDir() (string, error) {\n\t_, filename, _, ok := runtime.Caller(0)\n\tif !ok {\n\t\treturn \"\", fmt.Errorf(\"could not get current filename\")\n\t}\n\tbasepath := filepath.Dir(filename)\n\trepoDir := filepath.Join(basepath, \"..\", \"..\")\n\treturn filepath.Rel(basepath, repoDir)\n}\n\nfunc TestGoBuildQualifyImport(t *testing.T) {\n\tbase, err := random.Image(1024, 1)\n\tif err != nil {\n\t\tt.Fatalf(\"random.Image() = %v\", err)\n\t}\n\n\trepoDir, err := repoRootDir()\n\tif err != nil {\n\t\tt.Fatalf(\"could not get Git repository root directory\")\n\t}\n\n\ttests := []struct {\n\t\tdescription         string\n\t\trawImportpath       string\n\t\tdir                 string\n\t\tqualifiedImportpath string\n\t\texpectError         bool\n\t}{\n\t\t{\n\t\t\tdescription:         \"strict qualified import path\",\n\t\t\trawImportpath:       \"ko://github.com/google/ko\",\n\t\t\tdir:                 \"\",\n\t\t\tqualifiedImportpath: \"ko://github.com/google/ko\",\n\t\t\texpectError:         false,\n\t\t},\n\t\t{\n\t\t\tdescription:         \"strict qualified import path in subdirectory of go.mod\",\n\t\t\trawImportpath:       \"ko://github.com/google/ko/test\",\n\t\t\tdir:                 \"\",\n\t\t\tqualifiedImportpath: \"ko://github.com/google/ko/test\",\n\t\t\texpectError:         false,\n\t\t},\n\t\t{\n\t\t\tdescription:         \"non-strict qualified import path\",\n\t\t\trawImportpath:       \"github.com/google/ko\",\n\t\t\tdir:                 \"\",\n\t\t\tqualifiedImportpath: \"ko://github.com/google/ko\",\n\t\t\texpectError:         false,\n\t\t},\n\t\t{\n\t\t\tdescription:         \"non-strict local import path in repository root directory\",\n\t\t\trawImportpath:       \"./test\",\n\t\t\tdir:                 repoDir,\n\t\t\tqualifiedImportpath: \"ko://github.com/google/ko/test\",\n\t\t\texpectError:         false,\n\t\t},\n\t\t{\n\t\t\tdescription:         \"non-strict local import path in subdirectory\",\n\t\t\trawImportpath:       \".\",\n\t\t\tdir:                 filepath.Join(repoDir, \"test\"),\n\t\t\tqualifiedImportpath: \"ko://github.com/google/ko/test\",\n\t\t\texpectError:         false,\n\t\t},\n\t\t{\n\t\t\tdescription:         \"non-existent non-strict local import path\",\n\t\t\trawImportpath:       \"./does-not-exist\",\n\t\t\tdir:                 \"/\",\n\t\t\tqualifiedImportpath: \"should return error\",\n\t\t\texpectError:         true,\n\t\t},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.description, func(t *testing.T) {\n\t\t\tng, err := NewGo(context.Background(), test.dir, WithBaseImages(func(context.Context, string) (name.Reference, Result, error) { return nil, base, nil }))\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"NewGo() = %v\", err)\n\t\t\t}\n\t\t\tgotImportpath, err := ng.QualifyImport(test.rawImportpath)\n\t\t\tif err != nil && test.expectError {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif err != nil && !test.expectError {\n\t\t\t\tt.Errorf(\"QualifyImport(dir=%q)(%q) was error (%v), want nil error\", test.dir, test.rawImportpath, err)\n\t\t\t}\n\t\t\tif err == nil && test.expectError {\n\t\t\t\tt.Errorf(\"QualifyImport(dir=%q)(%q) was nil error, want non-nil error\", test.dir, test.rawImportpath)\n\t\t\t}\n\t\t\tif gotImportpath != test.qualifiedImportpath {\n\t\t\t\tt.Errorf(\"QualifyImport(dir=%q)(%q) = (%q, nil), want (%q, nil)\", test.dir, test.rawImportpath, gotImportpath, test.qualifiedImportpath)\n\t\t\t}\n\t\t})\n\t}\n}\n\nvar baseRef = name.MustParseReference(\"all.your/base\")\n\nfunc TestGoBuildIsSupportedRef(t *testing.T) {\n\tbase, err := random.Image(1024, 3)\n\tif err != nil {\n\t\tt.Fatalf(\"random.Image() = %v\", err)\n\t}\n\n\tng, err := NewGo(context.Background(), \"\", WithBaseImages(func(context.Context, string) (name.Reference, Result, error) { return nil, base, nil }))\n\tif err != nil {\n\t\tt.Fatalf(\"NewGo() = %v\", err)\n\t}\n\n\t// Supported import paths.\n\tfor _, importpath := range []string{\n\t\t\"ko://github.com/google/ko\", // ko can build itself.\n\t} {\n\t\tt.Run(importpath, func(t *testing.T) {\n\t\t\tif err := ng.IsSupportedReference(importpath); err != nil {\n\t\t\t\tt.Errorf(\"IsSupportedReference(%q) = (%v), want nil\", importpath, err)\n\t\t\t}\n\t\t})\n\t}\n\n\t// Unsupported import paths.\n\tfor _, importpath := range []string{\n\t\t\"ko://github.com/google/ko/pkg/build\",       // not a command.\n\t\t\"ko://github.com/google/ko/pkg/nonexistent\", // does not exist.\n\t} {\n\t\tt.Run(importpath, func(t *testing.T) {\n\t\t\tif err := ng.IsSupportedReference(importpath); err == nil {\n\t\t\t\tt.Errorf(\"IsSupportedReference(%v) = nil, want error\", importpath)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestGoBuildIsSupportedRefWithModules(t *testing.T) {\n\tbase, err := random.Image(1024, 3)\n\tif err != nil {\n\t\tt.Fatalf(\"random.Image() = %v\", err)\n\t}\n\n\topts := []Option{\n\t\tWithBaseImages(func(context.Context, string) (name.Reference, Result, error) { return baseRef, base, nil }),\n\t}\n\n\tng, err := NewGo(context.Background(), \"\", opts...)\n\tif err != nil {\n\t\tt.Fatalf(\"NewGo() = %v\", err)\n\t}\n\n\t// Supported import paths.\n\tfor _, importpath := range []string{\n\t\t\"ko://github.com/google/ko/test\",         // ko can build the test package.\n\t\t\"ko://github.com/go-training/helloworld\", // ko can build commands in dependent modules\n\t} {\n\t\tt.Run(importpath, func(t *testing.T) {\n\t\t\tif err := ng.IsSupportedReference(importpath); err != nil {\n\t\t\t\tt.Errorf(\"IsSupportedReference(%q) = (%v), want nil\", err, importpath)\n\t\t\t}\n\t\t})\n\t}\n\n\t// Unsupported import paths.\n\tfor _, importpath := range []string{\n\t\t\"ko://github.com/google/ko/pkg/build\",       // not a command.\n\t\t\"ko://github.com/google/ko/pkg/nonexistent\", // does not exist.\n\t\t\"ko://github.com/google/go-github\",          // not in this module.\n\t} {\n\t\tt.Run(importpath, func(t *testing.T) {\n\t\t\tif err := ng.IsSupportedReference(importpath); err == nil {\n\t\t\t\tt.Errorf(\"IsSupportedReference(%v) = nil, want error\", importpath)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestBuildEnv(t *testing.T) {\n\ttests := []struct {\n\t\tdescription  string\n\t\tplatform     v1.Platform\n\t\tosEnv        []string\n\t\tbuildEnv     []string\n\t\texpectedEnvs map[string]string\n\t}{{\n\t\tdescription: \"defaults\",\n\t\tplatform: v1.Platform{\n\t\t\tOS:           \"linux\",\n\t\t\tArchitecture: \"amd64\",\n\t\t},\n\t\texpectedEnvs: map[string]string{\n\t\t\t\"GOOS\":        \"linux\",\n\t\t\t\"GOARCH\":      \"amd64\",\n\t\t\t\"CGO_ENABLED\": \"0\",\n\t\t},\n\t}, {\n\t\tdescription: \"build override a system value\",\n\t\tosEnv:       []string{\"CGO_ENABLED=0\"},\n\t\tbuildEnv:    []string{\"CGO_ENABLED=1\"},\n\t\texpectedEnvs: map[string]string{\n\t\t\t\"CGO_ENABLED\": \"1\",\n\t\t},\n\t}, {\n\t\tdescription: \"override an envvar and add an envvar\",\n\t\tosEnv:       []string{\"CGO_ENABLED=0\"},\n\t\tbuildEnv:    []string{\"CGO_ENABLED=1\", \"GOPRIVATE=git.internal.example.com,source.developers.google.com\"},\n\t\texpectedEnvs: map[string]string{\n\t\t\t\"CGO_ENABLED\": \"1\",\n\t\t\t\"GOPRIVATE\":   \"git.internal.example.com,source.developers.google.com\",\n\t\t},\n\t}, {\n\t\tdescription: \"arm variant\",\n\t\tplatform: v1.Platform{\n\t\t\tArchitecture: \"arm\",\n\t\t\tVariant:      \"v7\",\n\t\t},\n\t\texpectedEnvs: map[string]string{\n\t\t\t\"GOARCH\": \"arm\",\n\t\t\t\"GOARM\":  \"7\",\n\t\t},\n\t}, {\n\t\t// GOARM is ignored for arm64.\n\t\tdescription: \"arm64 variant\",\n\t\tplatform: v1.Platform{\n\t\t\tArchitecture: \"arm64\",\n\t\t\tVariant:      \"v8\",\n\t\t},\n\t\texpectedEnvs: map[string]string{\n\t\t\t\"GOARCH\": \"arm64\",\n\t\t\t\"GOARM\":  \"\",\n\t\t},\n\t}, {\n\t\tdescription: \"amd64 variant\",\n\t\tplatform: v1.Platform{\n\t\t\tArchitecture: \"amd64\",\n\t\t\tVariant:      \"v3\",\n\t\t},\n\t\texpectedEnvs: map[string]string{\n\t\t\t\"GOARCH\":  \"amd64\",\n\t\t\t\"GOAMD64\": \"v3\",\n\t\t},\n\t}}\n\tfor _, test := range tests {\n\t\tt.Run(test.description, func(t *testing.T) {\n\t\t\tenv, err := buildEnv(test.platform, test.osEnv, test.buildEnv)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"unexpected error running buildEnv(): %v\", err)\n\t\t\t}\n\t\t\tenvs := map[string]string{}\n\t\t\tfor _, e := range env {\n\t\t\t\tsplit := strings.SplitN(e, \"=\", 2)\n\t\t\t\tenvs[split[0]] = split[1]\n\t\t\t}\n\t\t\tfor key, val := range test.expectedEnvs {\n\t\t\t\tif envs[key] != val {\n\t\t\t\t\tt.Errorf(\"buildEnv(): expected %s=%s, got %s=%s\", key, val, key, envs[key])\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestGoEnv(t *testing.T) {\n\tgoVars, err := goenv(context.TODO())\n\trequire.NoError(t, err)\n\n\t// Just check some basic values.\n\trequire.Equal(t, runtime.GOOS, goVars[\"GOOS\"])\n\trequire.Equal(t, runtime.GOARCH, goVars[\"GOARCH\"])\n}\n\nfunc TestCreateTemplateData(t *testing.T) {\n\tt.Run(\"empty creation time\", func(t *testing.T) {\n\t\tparams, err := createTemplateData(context.TODO(), buildContext{dir: t.TempDir()})\n\t\trequire.NoError(t, err)\n\n\t\t// Make sure the date was set to time.Now().\n\t\tactualDateStr := params[\"Date\"].(string)\n\t\tactualDate, err := time.Parse(time.RFC3339, actualDateStr)\n\t\trequire.NoError(t, err)\n\t\tif time.Since(actualDate) > time.Minute {\n\t\t\tt.Fatalf(\"expected date to be now, but was %v\", actualDate)\n\t\t}\n\n\t\t// Check the timestamp.\n\t\tactualTimestampSec := params[\"Timestamp\"].(int64)\n\t\tactualTimestamp := time.Unix(actualTimestampSec, 0).UTC()\n\t\texpectedTimestamp := actualDate.Truncate(time.Second).UTC()\n\t\trequire.Equal(t, expectedTimestamp, actualTimestamp)\n\t})\n\n\tt.Run(\"creation time\", func(t *testing.T) {\n\t\t// Create a reference time for use as a creation time.\n\t\texpectedTime, err := time.Parse(time.RFC3339, \"2012-11-01T22:08:00Z\")\n\t\trequire.NoError(t, err)\n\n\t\tparams, err := createTemplateData(context.TODO(), buildContext{\n\t\t\tcreationTime: v1.Time{Time: expectedTime},\n\t\t\tdir:          t.TempDir(),\n\t\t})\n\t\trequire.NoError(t, err)\n\n\t\t// Check the date.\n\t\tactualDateStr := params[\"Date\"].(string)\n\t\tactualDate, err := time.Parse(time.RFC3339, actualDateStr)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, expectedTime, actualDate)\n\n\t\t// Check the timestamp.\n\t\tactualTimestampSec := params[\"Timestamp\"].(int64)\n\t\tactualTimestamp := time.Unix(actualTimestampSec, 0).UTC()\n\t\trequire.Equal(t, expectedTime, actualTimestamp)\n\t})\n\n\tt.Run(\"no git available\", func(t *testing.T) {\n\t\tdir := t.TempDir()\n\t\tparams, err := createTemplateData(context.TODO(), buildContext{dir: dir})\n\t\trequire.NoError(t, err)\n\n\t\tgitParams := params[\"Git\"].(map[string]any)\n\t\trequire.Equal(t, \"\", gitParams[\"Branch\"])\n\t\trequire.Equal(t, \"\", gitParams[\"Tag\"])\n\t\trequire.Equal(t, \"\", gitParams[\"ShortCommit\"])\n\t\trequire.Equal(t, \"\", gitParams[\"FullCommit\"])\n\t\trequire.Equal(t, \"clean\", gitParams[\"TreeState\"])\n\t})\n\n\tt.Run(\"git\", func(t *testing.T) {\n\t\t// Create a fake git structure under the test temp dir.\n\t\tconst fakeGitURL = \"git@github.com:foo/bar.git\"\n\t\tdir := t.TempDir()\n\t\tgittesting.GitInit(t, dir)\n\t\tgittesting.GitRemoteAdd(t, dir, fakeGitURL)\n\t\tgittesting.GitCommit(t, dir, \"commit1\")\n\t\tgittesting.GitTag(t, dir, \"v0.0.1\")\n\n\t\tparams, err := createTemplateData(context.TODO(), buildContext{dir: dir})\n\t\trequire.NoError(t, err)\n\n\t\tgitParams := params[\"Git\"].(map[string]any)\n\t\trequire.Equal(t, \"main\", gitParams[\"Branch\"])\n\t\trequire.Equal(t, \"v0.0.1\", gitParams[\"Tag\"])\n\t\trequire.Equal(t, \"clean\", gitParams[\"TreeState\"])\n\t})\n\n\tt.Run(\"env\", func(t *testing.T) {\n\t\tparams, err := createTemplateData(context.TODO(), buildContext{\n\t\t\tdir: t.TempDir(),\n\t\t\tenv: []string{\"FOO=bar\"},\n\t\t})\n\t\trequire.NoError(t, err)\n\t\tvars := params[\"Env\"].(map[string]string)\n\t\trequire.Equal(t, \"bar\", vars[\"FOO\"])\n\t})\n\n\tt.Run(\"bad env\", func(t *testing.T) {\n\t\t_, err := createTemplateData(context.TODO(), buildContext{\n\t\t\tdir: t.TempDir(),\n\t\t\tenv: []string{\"bad var\"},\n\t\t})\n\t\trequire.Error(t, err)\n\t})\n\n\tt.Run(\"default go env\", func(t *testing.T) {\n\t\tparams, err := createTemplateData(context.TODO(), buildContext{dir: t.TempDir()})\n\t\trequire.NoError(t, err)\n\t\tvars := params[\"GoEnv\"].(map[string]string)\n\t\trequire.Equal(t, runtime.GOOS, vars[\"GOOS\"])\n\t\trequire.Equal(t, runtime.GOARCH, vars[\"GOARCH\"])\n\t})\n\n\tt.Run(\"env overrides go env\", func(t *testing.T) {\n\t\tparams, err := createTemplateData(context.TODO(), buildContext{\n\t\t\tdir: t.TempDir(),\n\t\t\tenv: []string{\n\t\t\t\t\"GOOS=testgoos\",\n\t\t\t\t\"GOARCH=testgoarch\",\n\t\t\t},\n\t\t})\n\t\trequire.NoError(t, err)\n\t\tvars := params[\"GoEnv\"].(map[string]string)\n\t\trequire.Equal(t, \"testgoos\", vars[\"GOOS\"])\n\t\trequire.Equal(t, \"testgoarch\", vars[\"GOARCH\"])\n\t})\n}\n\nfunc TestBuildConfig(t *testing.T) {\n\ttests := []struct {\n\t\tdescription  string\n\t\toptions      []Option\n\t\timportpath   string\n\t\texpectConfig Config\n\t}{\n\t\t{\n\t\t\tdescription: \"minimal options\",\n\t\t\toptions: []Option{\n\t\t\t\tWithBaseImages(nilGetBase),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdescription: \"trimpath flag\",\n\t\t\toptions: []Option{\n\t\t\t\tWithBaseImages(nilGetBase),\n\t\t\t\tWithTrimpath(true),\n\t\t\t},\n\t\t\texpectConfig: Config{\n\t\t\t\tFlags: FlagArray{\"-trimpath\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdescription: \"no trimpath flag\",\n\t\t\toptions: []Option{\n\t\t\t\tWithBaseImages(nilGetBase),\n\t\t\t\tWithTrimpath(false),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdescription: \"build config and trimpath\",\n\t\t\toptions: []Option{\n\t\t\t\tWithBaseImages(nilGetBase),\n\t\t\t\tWithConfig(map[string]Config{\n\t\t\t\t\t\"example.com/foo\": {\n\t\t\t\t\t\tFlags: FlagArray{\"-v\"},\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t\tWithTrimpath(true),\n\t\t\t},\n\t\t\timportpath: \"example.com/foo\",\n\t\t\texpectConfig: Config{\n\t\t\t\tFlags: FlagArray{\"-v\", \"-trimpath\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdescription: \"no trimpath overridden by build config flag\",\n\t\t\toptions: []Option{\n\t\t\t\tWithBaseImages(nilGetBase),\n\t\t\t\tWithConfig(map[string]Config{\n\t\t\t\t\t\"example.com/bar\": {\n\t\t\t\t\t\tFlags: FlagArray{\"-trimpath\"},\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t\tWithTrimpath(false),\n\t\t\t},\n\t\t\timportpath: \"example.com/bar\",\n\t\t\texpectConfig: Config{\n\t\t\t\tFlags: FlagArray{\"-trimpath\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdescription: \"disable optimizations\",\n\t\t\toptions: []Option{\n\t\t\t\tWithBaseImages(nilGetBase),\n\t\t\t\tWithDisabledOptimizations(),\n\t\t\t},\n\t\t\texpectConfig: Config{\n\t\t\t\tFlags: FlagArray{\"-gcflags\", \"all=-N -l\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdescription: \"defaultFlags applied when no per-build flags\",\n\t\t\toptions: []Option{\n\t\t\t\tWithBaseImages(nilGetBase),\n\t\t\t\tWithDefaultFlags([]string{\"-v\", \"-tags\", \"netgo\"}),\n\t\t\t},\n\t\t\texpectConfig: Config{\n\t\t\t\tFlags: FlagArray{\"-v\", \"-tags\", \"netgo\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdescription: \"defaultFlags applied with trimpath\",\n\t\t\toptions: []Option{\n\t\t\t\tWithBaseImages(nilGetBase),\n\t\t\t\tWithDefaultFlags([]string{\"-v\"}),\n\t\t\t\tWithTrimpath(true),\n\t\t\t},\n\t\t\texpectConfig: Config{\n\t\t\t\tFlags: FlagArray{\"-v\", \"-trimpath\"},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdescription: \"per-build flags override defaultFlags\",\n\t\t\toptions: []Option{\n\t\t\t\tWithBaseImages(nilGetBase),\n\t\t\t\tWithConfig(map[string]Config{\n\t\t\t\t\t\"example.com/foo\": {\n\t\t\t\t\t\tFlags: FlagArray{\"-race\"},\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t\tWithDefaultFlags([]string{\"-v\"}),\n\t\t\t\tWithTrimpath(true),\n\t\t\t},\n\t\t\timportpath: \"example.com/foo\",\n\t\t\texpectConfig: Config{\n\t\t\t\tFlags: FlagArray{\"-race\", \"-trimpath\"},\n\t\t\t},\n\t\t},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.description, func(t *testing.T) {\n\t\t\ti, err := NewGo(context.Background(), \"\", test.options...)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"NewGo(): unexpected error: %+v\", err)\n\t\t\t}\n\t\t\tgb, ok := i.(*gobuild)\n\t\t\tif !ok {\n\t\t\t\tt.Fatal(\"NewGo() did not return *gobuild{} as expected\")\n\t\t\t}\n\t\t\tconfig := gb.configForImportPath(test.importpath)\n\t\t\tif diff := cmp.Diff(test.expectConfig, config, cmpopts.EquateEmpty(),\n\t\t\t\tcmpopts.SortSlices(func(x, y string) bool { return x < y })); diff != \"\" {\n\t\t\t\tt.Errorf(\"%T differ (-got, +want): %s\", test.expectConfig, diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc nilGetBase(context.Context, string) (name.Reference, Result, error) {\n\treturn nil, nil, nil\n}\n\nconst wantSBOM = \"This is our fake SBOM\"\n\n// A helper method we use to substitute for the default \"build\" method.\nfunc fauxSBOM(context.Context, string, string, string, oci.SignedEntity, string) ([]byte, types.MediaType, error) {\n\treturn []byte(wantSBOM), \"application/vnd.garbage\", nil\n}\n\n// A helper method we use to substitute for the default \"build\" method.\nfunc writeTempFile(_ context.Context, buildCtx buildContext) (string, error) {\n\ttmpDir, err := os.MkdirTemp(\"\", \"ko\")\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tfile, err := os.CreateTemp(tmpDir, \"out\")\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tdefer file.Close()\n\tif _, err := file.WriteString(filepath.ToSlash(buildCtx.ip)); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn file.Name(), nil\n}\n\nfunc TestGoBuildNoKoData(t *testing.T) {\n\tbaseLayers := int64(3)\n\tbase, err := random.Image(1024, baseLayers)\n\tif err != nil {\n\t\tt.Fatalf(\"random.Image() = %v\", err)\n\t}\n\timportpath := \"github.com/google/ko\"\n\n\tcreationTime := v1.Time{Time: time.Unix(5000, 0)}\n\tng, err := NewGo(\n\t\tcontext.Background(),\n\t\t\"\",\n\t\tWithCreationTime(creationTime),\n\t\tWithBaseImages(func(context.Context, string) (name.Reference, Result, error) { return baseRef, base, nil }),\n\t\twithBuilder(writeTempFile),\n\t\twithSBOMber(fauxSBOM),\n\t\tWithPlatforms(\"all\"),\n\t)\n\tif err != nil {\n\t\tt.Fatalf(\"NewGo() = %v\", err)\n\t}\n\n\tresult, err := ng.Build(context.Background(), StrictScheme+importpath)\n\tif err != nil {\n\t\tt.Fatalf(\"Build() = %v\", err)\n\t}\n\n\timg, ok := result.(v1.Image)\n\tif !ok {\n\t\tt.Fatalf(\"Build() not an Image: %T\", result)\n\t}\n\n\tls, err := img.Layers()\n\tif err != nil {\n\t\tt.Fatalf(\"Layers() = %v\", err)\n\t}\n\n\t// Check that we have the expected number of layers.\n\tt.Run(\"check layer count\", func(t *testing.T) {\n\t\t// We get a layer for the go binary and a layer for the kodata/\n\t\tif got, want := int64(len(ls)), baseLayers+2; got != want {\n\t\t\tt.Fatalf(\"len(Layers()) = %v, want %v\", got, want)\n\t\t}\n\t})\n\n\t// Check that rebuilding the image again results in the same image digest.\n\tt.Run(\"check determinism\", func(t *testing.T) {\n\t\tresult2, err := ng.Build(context.Background(), StrictScheme+importpath)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Build() = %v\", err)\n\t\t}\n\n\t\td1, err := result.Digest()\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Digest() = %v\", err)\n\t\t}\n\t\td2, err := result2.Digest()\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Digest() = %v\", err)\n\t\t}\n\n\t\tif d1 != d2 {\n\t\t\tt.Errorf(\"Digest mismatch: %s != %s\", d1, d2)\n\t\t}\n\t})\n\n\t// Check that the entrypoint of the image is configured to invoke our Go application\n\tt.Run(\"check entrypoint\", func(t *testing.T) {\n\t\tcfg, err := img.ConfigFile()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"ConfigFile() = %v\", err)\n\t\t}\n\t\tentrypoint := cfg.Config.Entrypoint\n\t\tif got, want := len(entrypoint), 1; got != want {\n\t\t\tt.Errorf(\"len(entrypoint) = %v, want %v\", got, want)\n\t\t}\n\n\t\tif got, want := entrypoint[0], \"/ko-app/ko\"; got != want {\n\t\t\tt.Errorf(\"entrypoint = %v, want %v\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"check creation time\", func(t *testing.T) {\n\t\tcfg, err := img.ConfigFile()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"ConfigFile() = %v\", err)\n\t\t}\n\n\t\tactual := cfg.Created\n\t\tif actual.Time != creationTime.Time {\n\t\t\tt.Errorf(\"created = %v, want %v\", actual, creationTime)\n\t\t}\n\t})\n}\n\nfunc validateImage(t *testing.T, img oci.SignedImage, baseLayers int64, creationTime v1.Time, checkAnnotations bool, expectSBOM bool) {\n\tt.Helper()\n\n\tls, err := img.Layers()\n\tif err != nil {\n\t\tt.Fatalf(\"Layers() = %v\", err)\n\t}\n\n\t// Check that we have the expected number of layers.\n\tt.Run(\"check layer count\", func(t *testing.T) {\n\t\t// We get a layer for the go binary and a layer for the kodata/\n\t\tif got, want := int64(len(ls)), baseLayers+2; got != want {\n\t\t\tt.Fatalf(\"len(Layers()) = %v, want %v\", got, want)\n\t\t}\n\t})\n\n\tt.Run(\"check app layer contents\", func(t *testing.T) {\n\t\tdataLayer := ls[baseLayers]\n\n\t\tif _, err := dataLayer.Digest(); err != nil {\n\t\t\tt.Errorf(\"Digest() = %v\", err)\n\t\t}\n\t\t// We don't check the data layer here because it includes a symlink of refs and\n\t\t// will produce a distinct hash each time we commit something.\n\n\t\tr, err := dataLayer.Uncompressed()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Uncompressed() = %v\", err)\n\t\t}\n\t\tdefer r.Close()\n\t\ttr := tar.NewReader(r)\n\t\tif _, err := tr.Next(); errors.Is(err, io.EOF) {\n\t\t\tt.Errorf(\"Layer contained no files\")\n\t\t}\n\t})\n\n\t// Check that the kodata layer contains the expected data (even though it was a symlink\n\t// outside kodata).\n\tt.Run(\"check kodata\", func(t *testing.T) {\n\t\tdataLayer := ls[baseLayers]\n\t\tr, err := dataLayer.Uncompressed()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Uncompressed() = %v\", err)\n\t\t}\n\t\tdefer r.Close()\n\t\tfound := false\n\t\ttr := tar.NewReader(r)\n\t\tfor {\n\t\t\theader, err := tr.Next()\n\t\t\tif errors.Is(err, io.EOF) {\n\t\t\t\tbreak\n\t\t\t} else if err != nil {\n\t\t\t\tt.Errorf(\"Next() = %v\", err)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif header.Name != path.Join(kodataRoot, \"kenobi\") {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfound = true\n\t\t\tbody, err := io.ReadAll(tr)\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"ReadAll() = %v\", err)\n\t\t\t} else if want, got := \"Hello there\\n\", string(body); got != want {\n\t\t\t\tt.Errorf(\"ReadAll() = %v, wanted %v\", got, want)\n\t\t\t}\n\t\t}\n\t\tif !found {\n\t\t\tt.Error(\"Didn't find expected file in tarball\")\n\t\t}\n\t})\n\n\t// Check that directory headers are written for subdirectories in kodata.\n\tt.Run(\"check kodata directory headers\", func(t *testing.T) {\n\t\tdataLayer := ls[baseLayers]\n\t\tr, err := dataLayer.Uncompressed()\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Uncompressed() = %v\", err)\n\t\t}\n\t\tdefer r.Close()\n\n\t\texpectedDir := path.Join(kodataRoot, \"subdir\")\n\t\texpectedFile := path.Join(kodataRoot, \"subdir\", \"file.txt\")\n\t\tfoundDir := false\n\t\tfoundFile := false\n\n\t\ttr := tar.NewReader(r)\n\t\tfor {\n\t\t\theader, err := tr.Next()\n\t\t\tif errors.Is(err, io.EOF) {\n\t\t\t\tbreak\n\t\t\t} else if err != nil {\n\t\t\t\tt.Errorf(\"Next() = %v\", err)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif header.Name == expectedDir {\n\t\t\t\tfoundDir = true\n\t\t\t\tif header.Typeflag != tar.TypeDir {\n\t\t\t\t\tt.Errorf(\"expected %q to be a directory (typeflag %d), got typeflag %d\",\n\t\t\t\t\t\texpectedDir, tar.TypeDir, header.Typeflag)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif header.Name == expectedFile {\n\t\t\t\tfoundFile = true\n\t\t\t\tif header.Typeflag != tar.TypeReg {\n\t\t\t\t\tt.Errorf(\"expected %q to be a regular file (typeflag %d), got typeflag %d\",\n\t\t\t\t\t\texpectedFile, tar.TypeReg, header.Typeflag)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif !foundDir {\n\t\t\tt.Errorf(\"directory header for %q not found in tarball\", expectedDir)\n\t\t}\n\t\tif !foundFile {\n\t\t\tt.Errorf(\"file %q not found in tarball\", expectedFile)\n\t\t}\n\t})\n\n\t// Check that the entrypoint of the image is configured to invoke our Go application\n\tt.Run(\"check entrypoint\", func(t *testing.T) {\n\t\tcfg, err := img.ConfigFile()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"ConfigFile() = %v\", err)\n\t\t}\n\t\tentrypoint := cfg.Config.Entrypoint\n\t\tif got, want := len(entrypoint), 1; got != want {\n\t\t\tt.Errorf(\"len(entrypoint) = %v, want %v\", got, want)\n\t\t}\n\n\t\tif got, want := entrypoint[0], \"/ko-app/test\"; got != want {\n\t\t\tt.Errorf(\"entrypoint = %v, want %v\", got, want)\n\t\t}\n\t})\n\n\t// Check that the environment contains the KO_DATA_PATH environment variable.\n\tt.Run(\"check KO_DATA_PATH env var\", func(t *testing.T) {\n\t\tcfg, err := img.ConfigFile()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"ConfigFile() = %v\", err)\n\t\t}\n\t\tfound := false\n\t\tfor _, entry := range cfg.Config.Env {\n\t\t\tif entry == \"KO_DATA_PATH=\"+kodataRoot {\n\t\t\t\tfound = true\n\t\t\t}\n\t\t}\n\t\tif !found {\n\t\t\tt.Error(\"Didn't find KO_DATA_PATH.\")\n\t\t}\n\t})\n\n\t// Check that PATH contains the directory of the produced binary.\n\tt.Run(\"check PATH env var\", func(t *testing.T) {\n\t\tcfg, err := img.ConfigFile()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"ConfigFile() = %v\", err)\n\t\t}\n\t\tfound := false\n\t\tfor _, envVar := range cfg.Config.Env {\n\t\t\tif after, ok := strings.CutPrefix(envVar, \"PATH=\"); ok {\n\t\t\t\tpathValue := after\n\t\t\t\tpathEntries := strings.SplitSeq(pathValue, \":\")\n\t\t\t\tfor pathEntry := range pathEntries {\n\t\t\t\t\tif pathEntry == \"/ko-app\" {\n\t\t\t\t\t\tfound = true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif !found {\n\t\t\tt.Error(\"Didn't find entrypoint in PATH.\")\n\t\t}\n\t})\n\n\tt.Run(\"check creation time\", func(t *testing.T) {\n\t\tcfg, err := img.ConfigFile()\n\t\tif err != nil {\n\t\t\tt.Errorf(\"ConfigFile() = %v\", err)\n\t\t}\n\n\t\tactual := cfg.Created\n\t\tif actual.Time != creationTime.Time {\n\t\t\tt.Errorf(\"created = %v, want %v\", actual, creationTime)\n\t\t}\n\t})\n\n\tt.Run(\"check annotations\", func(t *testing.T) {\n\t\tif !checkAnnotations {\n\t\t\tt.Skip(\"skipping annotations check\")\n\t\t}\n\t\tmf, err := img.Manifest()\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Manifest() = %v\", err)\n\t\t}\n\t\tt.Logf(\"Got annotations: %v\", mf.Annotations)\n\t\tif _, found := mf.Annotations[specsv1.AnnotationBaseImageDigest]; !found {\n\t\t\tt.Errorf(\"image annotations did not contain base image digest\")\n\t\t}\n\t\twant := baseRef.Name()\n\t\tif got := mf.Annotations[specsv1.AnnotationBaseImageName]; got != want {\n\t\t\tt.Errorf(\"base image ref; got %q, want %q\", got, want)\n\t\t}\n\t})\n\n\tif expectSBOM {\n\t\tt.Run(\"checking for SBOM\", func(t *testing.T) {\n\t\t\tf, err := img.Attachment(\"sbom\")\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Attachment() = %v\", err)\n\t\t\t}\n\t\t\tb, err := f.Payload()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Payload() = %v\", err)\n\t\t\t}\n\t\t\tt.Logf(\"Got SBOM: %v\", string(b))\n\t\t\tif string(b) != wantSBOM {\n\t\t\t\tt.Errorf(\"got SBOM %s, wanted %s\", string(b), wantSBOM)\n\t\t\t}\n\t\t})\n\t} else {\n\t\tt.Run(\"checking for no SBOM\", func(t *testing.T) {\n\t\t\tf, err := img.Attachment(\"sbom\")\n\t\t\tif err == nil {\n\t\t\t\tb, err := f.Payload()\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatalf(\"Payload() = %v\", err)\n\t\t\t\t}\n\t\t\t\tt.Fatalf(\"Attachment() = %v, wanted error\", string(b))\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestGoBuild(t *testing.T) {\n\tbaseLayers := int64(3)\n\tbase, err := random.Image(1024, baseLayers)\n\tif err != nil {\n\t\tt.Fatalf(\"random.Image() = %v\", err)\n\t}\n\timportpath := \"github.com/google/ko\"\n\n\tcreationTime := v1.Time{Time: time.Unix(5000, 0)}\n\tng, err := NewGo(\n\t\tcontext.Background(),\n\t\t\"\",\n\t\tWithCreationTime(creationTime),\n\t\tWithBaseImages(func(context.Context, string) (name.Reference, Result, error) { return baseRef, base, nil }),\n\t\twithBuilder(writeTempFile),\n\t\twithSBOMber(fauxSBOM),\n\t\tWithLabel(\"foo\", \"bar\"),\n\t\tWithLabel(\"hello\", \"world\"),\n\t\tWithAnnotation(\"fizz\", \"buzz\"),\n\t\tWithAnnotation(\"goodbye\", \"world\"),\n\t\tWithUser(\"1234:1234\"),\n\t\tWithPlatforms(\"all\"),\n\t)\n\tif err != nil {\n\t\tt.Fatalf(\"NewGo() = %v\", err)\n\t}\n\n\tresult, err := ng.Build(context.Background(), StrictScheme+filepath.Join(importpath, \"test\"))\n\tif err != nil {\n\t\tt.Fatalf(\"Build() = %v\", err)\n\t}\n\n\timg, ok := result.(oci.SignedImage)\n\tif !ok {\n\t\tt.Fatalf(\"Build() not a SignedImage: %T\", result)\n\t}\n\n\tvalidateImage(t, img, baseLayers, creationTime, true, true)\n\n\t// Check that rebuilding the image again results in the same image digest.\n\tt.Run(\"check determinism\", func(t *testing.T) {\n\t\tresult2, err := ng.Build(context.Background(), StrictScheme+filepath.Join(importpath, \"test\"))\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Build() = %v\", err)\n\t\t}\n\n\t\td1, err := result.Digest()\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Digest() = %v\", err)\n\t\t}\n\t\td2, err := result2.Digest()\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Digest() = %v\", err)\n\t\t}\n\n\t\tif d1 != d2 {\n\t\t\tt.Errorf(\"Digest mismatch: %s != %s\", d1, d2)\n\t\t}\n\t})\n\n\tt.Run(\"check labels\", func(t *testing.T) {\n\t\tcfg, err := img.ConfigFile()\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"ConfigFile() = %v\", err)\n\t\t}\n\n\t\twant := map[string]string{\n\t\t\t\"foo\":   \"bar\",\n\t\t\t\"hello\": \"world\",\n\t\t}\n\t\tgot := cfg.Config.Labels\n\t\tif d := cmp.Diff(got, want); d != \"\" {\n\t\t\tt.Fatalf(\"Labels diff (-got,+want): %s\", d)\n\t\t}\n\t})\n\n\tt.Run(\"check annotations\", func(t *testing.T) {\n\t\tbaseDigest, err := base.Digest()\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"base.Digest() = %v\", err)\n\t\t}\n\t\tman, err := img.Manifest()\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Manifest() = %v\", err)\n\t\t}\n\t\twant := map[string]string{\n\t\t\tspecsv1.AnnotationBaseImageName:   baseRef.Name(),\n\t\t\tspecsv1.AnnotationBaseImageDigest: baseDigest.String(),\n\t\t\t\"fizz\":                            \"buzz\",\n\t\t\t\"goodbye\":                         \"world\",\n\t\t}\n\t\tgot := man.Annotations\n\t\tif d := cmp.Diff(got, want); d != \"\" {\n\t\t\tt.Fatalf(\"Annotations diff (-got,+want): %s\", d)\n\t\t}\n\t})\n\n\tt.Run(\"check user\", func(t *testing.T) {\n\t\tcfg, err := img.ConfigFile()\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"ConfigFile() = %v\", err)\n\t\t}\n\n\t\twant := \"1234:1234\"\n\t\tgot := cfg.Config.User\n\t\tif got != want {\n\t\t\tt.Fatalf(\"User: %s != %s\", want, got)\n\t\t}\n\t})\n}\n\nfunc TestGoBuild_Defaults(t *testing.T) {\n\tbaseLayers := int64(3)\n\tbase, err := random.Image(1024, baseLayers)\n\tif err != nil {\n\t\tt.Fatalf(\"random.Image() = %v\", err)\n\t}\n\timportpath := \"github.com/google/ko\"\n\n\tcreationTime := v1.Time{Time: time.Unix(5000, 0)}\n\tvar buildCtx buildContext\n\tng, err := NewGo(\n\t\tcontext.Background(),\n\t\t\"\",\n\t\tWithCreationTime(creationTime),\n\t\tWithBaseImages(func(context.Context, string) (name.Reference, Result, error) { return baseRef, base, nil }),\n\t\twithBuilder(func(_ context.Context, b buildContext) (string, error) {\n\t\t\tbuildCtx = b\n\t\t\treturn \"\", errors.New(\"fake build error\")\n\t\t}),\n\t\twithSBOMber(fauxSBOM),\n\t\tWithPlatforms(\"all\"),\n\t\tWithDefaultEnv([]string{\"FOO=foo\", \"BAR=bar\"}),\n\t\tWithDefaultFlags([]string{\"-v\"}),\n\t\tWithDefaultLdflags([]string{\"-s\"}),\n\t\tWithConfig(map[string]Config{\n\t\t\t\"github.com/google/ko/test\": {},\n\t\t}),\n\t)\n\trequire.NoError(t, err)\n\n\t// Build and capture the buildContext.\n\t_, err = ng.Build(context.Background(), StrictScheme+filepath.Join(importpath, \"test\"))\n\trequire.ErrorContains(t, err, \"fake build error\")\n\trequire.Equal(t, []string{\"-v\"}, buildCtx.flags)\n\trequire.Equal(t, []string{\"-s\"}, buildCtx.ldflags)\n\n\tenvVars := make(map[string]string)\n\tfor _, val := range buildCtx.env {\n\t\tkv := strings.SplitN(val, \"=\", 2)\n\t\tenvVars[kv[0]] = kv[1]\n\t}\n\trequire.Equal(t, \"foo\", envVars[\"FOO\"])\n\trequire.Equal(t, \"bar\", envVars[\"BAR\"])\n}\n\nfunc TestGoBuild_ConfigOverrideDefaults(t *testing.T) {\n\tbaseLayers := int64(3)\n\tbase, err := random.Image(1024, baseLayers)\n\tif err != nil {\n\t\tt.Fatalf(\"random.Image() = %v\", err)\n\t}\n\timportpath := \"github.com/google/ko\"\n\n\tcreationTime := v1.Time{Time: time.Unix(5000, 0)}\n\tvar buildCtx buildContext\n\tng, err := NewGo(\n\t\tcontext.Background(),\n\t\t\"\",\n\t\tWithCreationTime(creationTime),\n\t\tWithBaseImages(func(context.Context, string) (name.Reference, Result, error) { return baseRef, base, nil }),\n\t\twithBuilder(func(_ context.Context, b buildContext) (string, error) {\n\t\t\tbuildCtx = b\n\t\t\treturn \"\", errors.New(\"fake build error\")\n\t\t}),\n\t\twithSBOMber(fauxSBOM),\n\t\tWithPlatforms(\"all\"),\n\t\tWithDefaultEnv([]string{\"FOO=foo\", \"BAR=bar\"}),\n\t\tWithDefaultFlags([]string{\"-v\"}),\n\t\tWithDefaultLdflags([]string{\"-s\"}),\n\t\tWithConfig(map[string]Config{\n\t\t\t\"github.com/google/ko/test\": {\n\t\t\t\tEnv:     StringArray{\"FOO=baz\"},\n\t\t\t\tFlags:   FlagArray{\"-trimpath\"},\n\t\t\t\tLdflags: StringArray{\"-w\"},\n\t\t\t},\n\t\t}),\n\t)\n\trequire.NoError(t, err)\n\n\t// Build and capture the buildContext.\n\t_, err = ng.Build(context.Background(), StrictScheme+filepath.Join(importpath, \"test\"))\n\trequire.ErrorContains(t, err, \"fake build error\")\n\trequire.Equal(t, []string{\"-trimpath\"}, buildCtx.flags)\n\trequire.Equal(t, []string{\"-w\"}, buildCtx.ldflags)\n\n\tenvVars := make(map[string]string)\n\tfor _, val := range buildCtx.env {\n\t\tkv := strings.SplitN(val, \"=\", 2)\n\t\tenvVars[kv[0]] = kv[1]\n\t}\n\trequire.Equal(t, \"baz\", envVars[\"FOO\"])\n\trequire.Equal(t, \"\", envVars[\"BAR\"])\n}\n\nfunc TestGoBuildWithKOCACHE(t *testing.T) {\n\tnow := time.Now() // current local time\n\tsec := now.Unix()\n\ttmpDir := t.TempDir()\n\tkoCacheDir := filepath.Join(tmpDir, strconv.FormatInt(sec, 10))\n\n\tt.Setenv(\"KOCACHE\", koCacheDir)\n\tbaseLayers := int64(3)\n\tbase, err := random.Image(1024, baseLayers)\n\tif err != nil {\n\t\tt.Fatalf(\"random.Image() = %v\", err)\n\t}\n\timportpath := \"github.com/google/ko\"\n\n\tcreationTime := v1.Time{Time: time.Unix(5000, 0)}\n\tng, err := NewGo(\n\t\tcontext.Background(),\n\t\t\"\",\n\t\tWithCreationTime(creationTime),\n\t\tWithBaseImages(func(context.Context, string) (name.Reference, Result, error) { return baseRef, base, nil }),\n\t\tWithPlatforms(\"all\"),\n\t)\n\tif err != nil {\n\t\tt.Fatalf(\"NewGo() = %v\", err)\n\t}\n\n\t_, err = ng.Build(context.Background(), StrictScheme+filepath.Join(importpath, \"test\"))\n\tif err != nil {\n\t\tt.Fatalf(\"Build() = %v\", err)\n\t}\n\n\tt.Run(\"check KOCACHE exists\", func(t *testing.T) {\n\t\t_, err := os.Stat(koCacheDir)\n\t\tif os.IsNotExist(err) {\n\t\t\tt.Fatalf(\"KOCACHE directory %s should be exists= %v\", koCacheDir, err)\n\t\t}\n\t})\n}\n\nfunc TestGoBuildWithoutSBOM(t *testing.T) {\n\tbaseLayers := int64(3)\n\tbase, err := random.Image(1024, baseLayers)\n\tif err != nil {\n\t\tt.Fatalf(\"random.Image() = %v\", err)\n\t}\n\timportpath := \"github.com/google/ko\"\n\n\tcreationTime := v1.Time{Time: time.Unix(5000, 0)}\n\tng, err := NewGo(\n\t\tcontext.Background(),\n\t\t\"\",\n\t\tWithCreationTime(creationTime),\n\t\tWithBaseImages(func(context.Context, string) (name.Reference, Result, error) { return baseRef, base, nil }),\n\t\twithBuilder(writeTempFile),\n\t\twithSBOMber(fauxSBOM),\n\t\tWithLabel(\"foo\", \"bar\"),\n\t\tWithLabel(\"hello\", \"world\"),\n\t\tWithDisabledSBOM(),\n\t\tWithPlatforms(\"all\"),\n\t)\n\tif err != nil {\n\t\tt.Fatalf(\"NewGo() = %v\", err)\n\t}\n\n\tresult, err := ng.Build(context.Background(), StrictScheme+filepath.Join(importpath, \"test\"))\n\tif err != nil {\n\t\tt.Fatalf(\"Build() = %v\", err)\n\t}\n\n\timg, ok := result.(oci.SignedImage)\n\tif !ok {\n\t\tt.Fatalf(\"Build() not a SignedImage: %T\", result)\n\t}\n\n\tvalidateImage(t, img, baseLayers, creationTime, true, false)\n}\n\nfunc TestGoBuildIndex(t *testing.T) {\n\tbaseLayers := int64(3)\n\timages := int64(2)\n\tbase, err := random.Index(1024, baseLayers, images)\n\tif err != nil {\n\t\tt.Fatalf(\"random.Image() = %v\", err)\n\t}\n\timportpath := \"github.com/google/ko\"\n\n\tcreationTime := v1.Time{Time: time.Unix(5000, 0)}\n\tng, err := NewGo(\n\t\tcontext.Background(),\n\t\t\"\",\n\t\tWithCreationTime(creationTime),\n\t\tWithBaseImages(func(context.Context, string) (name.Reference, Result, error) { return baseRef, base, nil }),\n\t\tWithPlatforms(\"all\"),\n\t\twithBuilder(writeTempFile),\n\t\twithSBOMber(fauxSBOM),\n\t)\n\tif err != nil {\n\t\tt.Fatalf(\"NewGo() = %v\", err)\n\t}\n\n\tresult, err := ng.Build(context.Background(), StrictScheme+filepath.Join(importpath, \"test\"))\n\tif err != nil {\n\t\tt.Fatalf(\"Build() = %v\", err)\n\t}\n\n\tidx, ok := result.(oci.SignedImageIndex)\n\tif !ok {\n\t\tt.Fatalf(\"Build() not a SignedImageIndex: %T\", result)\n\t}\n\n\tim, err := idx.IndexManifest()\n\tif err != nil {\n\t\tt.Fatalf(\"IndexManifest() = %v\", err)\n\t}\n\n\tfor _, desc := range im.Manifests {\n\t\timg, err := idx.SignedImage(desc.Digest)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"idx.Image(%s) = %v\", desc.Digest, err)\n\t\t}\n\t\tvalidateImage(t, img, baseLayers, creationTime, false, true)\n\t}\n\n\tif want, got := images, int64(len(im.Manifests)); want != got {\n\t\tt.Fatalf(\"len(Manifests()) = %v, want %v\", got, want)\n\t}\n\n\t// Check that rebuilding the image again results in the same image digest.\n\tt.Run(\"check determinism\", func(t *testing.T) {\n\t\tresult2, err := ng.Build(context.Background(), StrictScheme+filepath.Join(importpath, \"test\"))\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Build() = %v\", err)\n\t\t}\n\n\t\td1, err := result.Digest()\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Digest() = %v\", err)\n\t\t}\n\t\td2, err := result2.Digest()\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Digest() = %v\", err)\n\t\t}\n\n\t\tif d1 != d2 {\n\t\t\tt.Errorf(\"Digest mismatch: %s != %s\", d1, d2)\n\t\t}\n\t})\n}\n\nfunc TestNestedIndex(t *testing.T) {\n\tbaseLayers := int64(3)\n\timages := int64(2)\n\tbase, err := random.Index(1024, baseLayers, images)\n\tif err != nil {\n\t\tt.Fatalf(\"random.Image() = %v\", err)\n\t}\n\timportpath := \"github.com/google/ko\"\n\n\tnestedBase := mutate.AppendManifests(empty.Index, mutate.IndexAddendum{Add: base})\n\n\tcreationTime := v1.Time{Time: time.Unix(5000, 0)}\n\tng, err := NewGo(\n\t\tcontext.Background(),\n\t\t\"\",\n\t\tWithCreationTime(creationTime),\n\t\tWithBaseImages(func(context.Context, string) (name.Reference, Result, error) { return baseRef, nestedBase, nil }),\n\t\twithBuilder(writeTempFile),\n\t\twithSBOMber(fauxSBOM),\n\t)\n\tif err != nil {\n\t\tt.Fatalf(\"NewGo() = %v\", err)\n\t}\n\n\t_, err = ng.Build(context.Background(), StrictScheme+filepath.Join(importpath, \"test\"))\n\tif err == nil {\n\t\tt.Fatal(\"Build() expected err\")\n\t}\n\n\tif !strings.Contains(err.Error(), \"unexpected mediaType\") {\n\t\tt.Errorf(\"Build() expected unexpected mediaType error, got: %s\", err)\n\t}\n}\n\nfunc TestGoarm(t *testing.T) {\n\t// From golang@sha256:1ba0da74b20aad52b091877b0e0ece503c563f39e37aa6b0e46777c4d820a2ae\n\t// and made up invalid cases.\n\tfor _, tc := range []struct {\n\t\tplatform v1.Platform\n\t\tvariant  string\n\t\terr      bool\n\t}{{\n\t\tplatform: v1.Platform{\n\t\t\tArchitecture: \"arm\",\n\t\t\tOS:           \"linux\",\n\t\t\tVariant:      \"vnot-a-number\",\n\t\t},\n\t\terr: true,\n\t}, {\n\t\tplatform: v1.Platform{\n\t\t\tArchitecture: \"arm\",\n\t\t\tOS:           \"linux\",\n\t\t\tVariant:      \"wrong-prefix\",\n\t\t},\n\t\terr: true,\n\t}, {\n\t\tplatform: v1.Platform{\n\t\t\tArchitecture: \"arm64\",\n\t\t\tOS:           \"linux\",\n\t\t\tVariant:      \"v3\",\n\t\t},\n\t\tvariant: \"\",\n\t}, {\n\t\tplatform: v1.Platform{\n\t\t\tArchitecture: \"arm\",\n\t\t\tOS:           \"linux\",\n\t\t\tVariant:      \"v5\",\n\t\t},\n\t\tvariant: \"5\",\n\t}, {\n\t\tplatform: v1.Platform{\n\t\t\tArchitecture: \"arm\",\n\t\t\tOS:           \"linux\",\n\t\t\tVariant:      \"v7\",\n\t\t},\n\t\tvariant: \"7\",\n\t}, {\n\t\tplatform: v1.Platform{\n\t\t\tArchitecture: \"arm64\",\n\t\t\tOS:           \"linux\",\n\t\t\tVariant:      \"v8\",\n\t\t},\n\t\tvariant: \"7\",\n\t},\n\t} {\n\t\tvariant, err := getGoarm(tc.platform)\n\t\tif tc.err {\n\t\t\tif err == nil {\n\t\t\t\tt.Errorf(\"getGoarm(%v) expected err\", tc.platform)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"getGoarm failed for %v: %v\", tc.platform, err)\n\t\t}\n\t\tif got, want := variant, tc.variant; got != want {\n\t\t\tt.Errorf(\"wrong variant for %v: want %q got %q\", tc.platform, want, got)\n\t\t}\n\t}\n}\n\nfunc TestMatchesPlatformSpec(t *testing.T) {\n\tfor _, tc := range []struct {\n\t\tplatform *v1.Platform\n\t\tspec     []string\n\t\tresult   bool\n\t\terr      bool\n\t}{{\n\t\tplatform: nil,\n\t\tspec:     []string{\"all\"},\n\t\tresult:   true,\n\t}, {\n\t\tplatform: nil,\n\t\tspec:     []string{\"linux/amd64\"},\n\t\tresult:   false,\n\t}, {\n\t\tplatform: &v1.Platform{\n\t\t\tArchitecture: \"amd64\",\n\t\t\tOS:           \"linux\",\n\t\t},\n\t\tspec:   []string{\"all\"},\n\t\tresult: true,\n\t}, {\n\t\tplatform: &v1.Platform{\n\t\t\tArchitecture: \"amd64\",\n\t\t\tOS:           \"windows\",\n\t\t},\n\t\tspec:   []string{\"linux\"},\n\t\tresult: false,\n\t}, {\n\t\tplatform: &v1.Platform{\n\t\t\tArchitecture: \"arm64\",\n\t\t\tOS:           \"linux\",\n\t\t\tVariant:      \"v3\",\n\t\t},\n\t\tspec:   []string{\"linux/amd64\", \"linux/arm64\"},\n\t\tresult: true,\n\t}, {\n\t\tplatform: &v1.Platform{\n\t\t\tArchitecture: \"arm64\",\n\t\t\tOS:           \"linux\",\n\t\t\tVariant:      \"v3\",\n\t\t},\n\t\tspec:   []string{\"linux/amd64\", \"linux/arm64/v4\"},\n\t\tresult: false,\n\t}, {\n\t\tplatform: &v1.Platform{\n\t\t\tArchitecture: \"arm64\",\n\t\t\tOS:           \"linux\",\n\t\t\tVariant:      \"v3\",\n\t\t},\n\t\tspec: []string{\"linux/amd64\", \"linux/arm64/v3/z5\"},\n\t\terr:  true,\n\t}, {\n\t\tspec: []string{},\n\t\tplatform: &v1.Platform{\n\t\t\tArchitecture: \"amd64\",\n\t\t\tOS:           \"linux\",\n\t\t},\n\t\tresult: false,\n\t}, {\n\t\t// Exact match w/ osversion\n\t\tspec: []string{\"windows/amd64:10.0.17763.1234\"},\n\t\tplatform: &v1.Platform{\n\t\t\tOS:           \"windows\",\n\t\t\tArchitecture: \"amd64\",\n\t\t\tOSVersion:    \"10.0.17763.1234\",\n\t\t},\n\t\tresult: true,\n\t}, {\n\t\t// OSVersion partial match using relaxed semantics.\n\t\tspec: []string{\"windows/amd64:10.0.17763\"},\n\t\tplatform: &v1.Platform{\n\t\t\tOS:           \"windows\",\n\t\t\tArchitecture: \"amd64\",\n\t\t\tOSVersion:    \"10.0.17763.1234\",\n\t\t},\n\t\tresult: true,\n\t}, {\n\t\t// Not windows and osversion isn't exact match.\n\t\tspec: []string{\"linux/amd64:10.0.17763\"},\n\t\tplatform: &v1.Platform{\n\t\t\tOS:           \"linux\",\n\t\t\tArchitecture: \"amd64\",\n\t\t\tOSVersion:    \"10.0.17763.1234\",\n\t\t},\n\t\tresult: false,\n\t}, {\n\t\t// Not matching X.Y.Z\n\t\tspec: []string{\"windows/amd64:10\"},\n\t\tplatform: &v1.Platform{\n\t\t\tOS:           \"windows\",\n\t\t\tArchitecture: \"amd64\",\n\t\t\tOSVersion:    \"10.0.17763.1234\",\n\t\t},\n\t\tresult: false,\n\t}, {\n\t\t// Requirement is more specific.\n\t\tspec: []string{\"windows/amd64:10.0.17763.1234\"},\n\t\tplatform: &v1.Platform{\n\t\t\tOS:           \"windows\",\n\t\t\tArchitecture: \"amd64\",\n\t\t\tOSVersion:    \"10.0.17763\", // this won't happen in the wild, but it shouldn't match.\n\t\t},\n\t\tresult: false,\n\t}, {\n\t\t// Requirement is not specific enough.\n\t\tspec: []string{\"windows/amd64:10.0.17763.1234\"},\n\t\tplatform: &v1.Platform{\n\t\t\tOS:           \"windows\",\n\t\t\tArchitecture: \"amd64\",\n\t\t\tOSVersion:    \"10.0.17763.1234.5678\", // this won't happen in the wild, but it shouldn't match.\n\t\t},\n\t\tresult: false,\n\t}, {\n\t\t// Even --platform=all does not match unknown/unknown.\n\t\tplatform: &v1.Platform{Architecture: \"unknown\", OS: \"unknown\"},\n\t\tspec:     []string{\"all\"},\n\t\tresult:   false,\n\t}} {\n\t\tpm, err := parseSpec(tc.spec)\n\t\tif tc.err {\n\t\t\tif err == nil {\n\t\t\t\tt.Errorf(\"parseSpec(%v, %q) expected err\", tc.platform, tc.spec)\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"parseSpec failed for %v %q: %v\", tc.platform, tc.spec, err)\n\t\t}\n\t\tmatches := pm.matches(tc.platform)\n\t\tif got, want := matches, tc.result; got != want {\n\t\t\tt.Errorf(\"wrong result for %v %q: want %t got %t\", tc.platform, tc.spec, want, got)\n\t\t}\n\t}\n}\n\nfunc TestGoBuildConsistentMediaTypes(t *testing.T) {\n\tfor _, c := range []struct {\n\t\tdesc                      string\n\t\tmediaType, layerMediaType types.MediaType\n\t}{{\n\t\tdesc:           \"docker types\",\n\t\tmediaType:      types.DockerManifestSchema2,\n\t\tlayerMediaType: types.DockerLayer,\n\t}, {\n\t\tdesc:           \"oci types\",\n\t\tmediaType:      types.OCIManifestSchema1,\n\t\tlayerMediaType: types.OCILayer,\n\t}} {\n\t\tt.Run(c.desc, func(t *testing.T) {\n\t\t\tbase := mutate.MediaType(empty.Image, c.mediaType)\n\t\t\tl, err := random.Layer(10, c.layerMediaType)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tbase, err = mutate.AppendLayers(base, l)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\n\t\t\tng, err := NewGo(\n\t\t\t\tcontext.Background(),\n\t\t\t\t\"\",\n\t\t\t\tWithBaseImages(func(context.Context, string) (name.Reference, Result, error) { return baseRef, base, nil }),\n\t\t\t\twithBuilder(writeTempFile),\n\t\t\t\twithSBOMber(fauxSBOM),\n\t\t\t\tWithPlatforms(\"all\"),\n\t\t\t)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"NewGo() = %v\", err)\n\t\t\t}\n\n\t\t\timportpath := \"github.com/google/ko\"\n\n\t\t\tresult, err := ng.Build(context.Background(), StrictScheme+importpath)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Build() = %v\", err)\n\t\t\t}\n\n\t\t\timg, ok := result.(v1.Image)\n\t\t\tif !ok {\n\t\t\t\tt.Fatalf(\"Build() not an Image: %T\", result)\n\t\t\t}\n\n\t\t\tls, err := img.Layers()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"Layers() = %v\", err)\n\t\t\t}\n\n\t\t\tfor i, l := range ls {\n\t\t\t\tgotMT, err := l.MediaType()\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatal(err)\n\t\t\t\t}\n\t\t\t\tif gotMT != c.layerMediaType {\n\t\t\t\t\tt.Errorf(\"layer %d: got mediaType %q, want %q\", i, gotMT, c.layerMediaType)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tgotMT, err := img.MediaType()\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tif gotMT != c.mediaType {\n\t\t\t\tt.Errorf(\"got image mediaType %q, want %q\", gotMT, c.layerMediaType)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestDebugger(t *testing.T) {\n\tbase, err := random.Image(1024, 3)\n\tif err != nil {\n\t\tt.Fatalf(\"random.Image() = %v\", err)\n\t}\n\timportpath := \"github.com/google/ko\"\n\n\tng, err := NewGo(\n\t\tcontext.Background(),\n\t\t\"\",\n\t\tWithBaseImages(func(context.Context, string) (name.Reference, Result, error) { return baseRef, base, nil }),\n\t\tWithPlatforms(\"linux/amd64\"),\n\t\tWithDebugger(),\n\t)\n\tif err != nil {\n\t\tt.Fatalf(\"NewGo() = %v\", err)\n\t}\n\n\tresult, err := ng.Build(context.Background(), StrictScheme+importpath)\n\tif err != nil {\n\t\tt.Fatalf(\"Build() = %v\", err)\n\t}\n\n\timg, ok := result.(v1.Image)\n\tif !ok {\n\t\tt.Fatalf(\"Build() not an Image: %T\", result)\n\t}\n\n\t// Check that the entrypoint of the image is not overwritten\n\tcfg, err := img.ConfigFile()\n\tif err != nil {\n\t\tt.Errorf(\"ConfigFile() = %v\", err)\n\t}\n\tgotEntrypoint := cfg.Config.Entrypoint\n\twantEntrypoint := []string{\n\t\t\"/ko-app/dlv\",\n\t\t\"exec\",\n\t\t\"--listen=:40000\",\n\t\t\"--headless\",\n\t\t\"--log\",\n\t\t\"--accept-multiclient\",\n\t\t\"--api-version=2\",\n\t\t\"--\",\n\t\t\"/ko-app/ko\",\n\t}\n\n\tif got, want := len(gotEntrypoint), len(wantEntrypoint); got != want {\n\t\tt.Fatalf(\"len(entrypoint) = %v, want %v\", got, want)\n\t}\n\n\tfor i := range wantEntrypoint {\n\t\tif got, want := gotEntrypoint[i], wantEntrypoint[i]; got != want {\n\t\t\tt.Errorf(\"entrypoint[%d] = %v, want %v\", i, got, want)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "pkg/build/gobuilds.go",
    "content": "// Copyright 2021 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage build\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\tgb \"go/build\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"strings\"\n)\n\ntype gobuilds struct {\n\t// Map of fully qualified import path to go builder with config\n\tbuilders map[string]builderWithConfig\n\n\t// Default go builder used if there's no matching build config.\n\tdefaultBuilder Interface\n\n\t// workingDirectory is typically \".\", but it may be a different value if ko is embedded as a library.\n\tworkingDirectory string\n}\n\n// builderWithConfig is not an imaginative name.\ntype builderWithConfig struct {\n\tbuilder Interface\n\tconfig  Config\n}\n\n// NewGobuilds returns a build.Interface that can dispatch to builders based on matching the import path to a build config in .ko.yaml.\nfunc NewGobuilds(ctx context.Context, workingDirectory string, buildConfigs map[string]Config, opts ...Option) (Interface, error) {\n\tif workingDirectory == \"\" {\n\t\tworkingDirectory = \".\"\n\t}\n\tdefaultBuilder, err := NewGo(ctx, workingDirectory, opts...)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not create default go builder: %w\", err)\n\t}\n\tg := &gobuilds{\n\t\tbuilders:         map[string]builderWithConfig{},\n\t\tdefaultBuilder:   defaultBuilder,\n\t\tworkingDirectory: workingDirectory,\n\t}\n\tfor importpath, buildConfig := range buildConfigs {\n\t\tbuilderDirectory := path.Join(workingDirectory, buildConfig.Dir)\n\t\tbuilder, err := NewGo(ctx, builderDirectory, opts...)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"could not create go builder for config (%q): %w\", importpath, err)\n\t\t}\n\t\tg.builders[importpath] = builderWithConfig{\n\t\t\tbuilder: builder,\n\t\t\tconfig:  buildConfig,\n\t\t}\n\t}\n\treturn g, nil\n}\n\n// QualifyImport implements build.Interface\nfunc (g *gobuilds) QualifyImport(importpath string) (string, error) {\n\tb := g.builder(importpath)\n\tif b.config.Dir != \"\" {\n\t\tvar err error\n\t\timportpath, err = relativePath(b.config.Dir, importpath)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\treturn b.builder.QualifyImport(importpath)\n}\n\n// IsSupportedReference implements build.Interface\nfunc (g *gobuilds) IsSupportedReference(importpath string) error {\n\treturn g.builder(importpath).builder.IsSupportedReference(importpath)\n}\n\n// Build implements build.Interface\nfunc (g *gobuilds) Build(ctx context.Context, importpath string) (Result, error) {\n\treturn g.builder(importpath).builder.Build(ctx, importpath)\n}\n\n// builder selects a go builder for the provided import path.\n// The `importpath` argument can be either local (e.g., `./cmd/foo`) or not (e.g., `example.com/app/cmd/foo`).\nfunc (g *gobuilds) builder(importpath string) builderWithConfig {\n\timportpath = strings.TrimPrefix(importpath, StrictScheme)\n\tif len(g.builders) == 0 {\n\t\treturn builderWithConfig{\n\t\t\tbuilder: g.defaultBuilder,\n\t\t}\n\t}\n\t// first, try to find go builder by fully qualified import path\n\tif builderWithConfig, exists := g.builders[importpath]; exists {\n\t\treturn builderWithConfig\n\t}\n\t// second, try to find go builder by local path\n\tfor _, builderWithConfig := range g.builders {\n\t\t// Match go builder by trying to resolve the local path to a fully qualified import path. If successful, we have a winner.\n\t\trelPath, err := relativePath(builderWithConfig.config.Dir, importpath)\n\t\tif err != nil {\n\t\t\t// Cannot determine a relative path. Move on and try the next go builder.\n\t\t\tcontinue\n\t\t}\n\t\t_, err = builderWithConfig.builder.QualifyImport(relPath)\n\t\tif err != nil {\n\t\t\t// There's an error turning the local path into a fully qualified import path. Move on and try the next go builder.\n\t\t\tcontinue\n\t\t}\n\t\treturn builderWithConfig\n\t}\n\t// fall back to default go builder\n\treturn builderWithConfig{\n\t\tbuilder: g.defaultBuilder,\n\t}\n}\n\n// relativePath takes as input a local import path, and returns a path relative to the base directory.\n//\n// For example, given the following inputs:\n// - baseDir: \"app\"\n// - importpath: \"./app/cmd/foo\n// The output is: \"./cmd/foo\"\n//\n// If the input is a not a local import path as determined by go/build.IsLocalImport(), the input is returned unchanged.\n//\n// If the import path is _not_ a subdirectory of baseDir, the result is an error.\nfunc relativePath(baseDir string, importpath string) (string, error) {\n\t// Return input unchanged if the import path is a fully qualified import path\n\tif !gb.IsLocalImport(importpath) {\n\t\treturn importpath, nil\n\t}\n\trelPath, err := filepath.Rel(baseDir, importpath)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"cannot determine relative path of baseDir (%q) and local path (%q): %w\", baseDir, importpath, err)\n\t}\n\tif strings.HasPrefix(relPath, \"..\") {\n\t\t// TODO Is this assumption correct?\n\t\treturn \"\", fmt.Errorf(\"import path (%q) must be a subdirectory of build config directory (%q)\", importpath, baseDir)\n\t}\n\tif !strings.HasPrefix(relPath, \".\") && relPath != \".\" {\n\t\trelPath = \"./\" + relPath // ensure go/build.IsLocalImport() interprets this as a local path\n\t}\n\treturn relPath, nil\n}\n"
  },
  {
    "path": "pkg/build/gobuilds_test.go",
    "content": "// Copyright 2021 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage build\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/google/go-containerregistry/pkg/name\"\n\t\"github.com/google/go-containerregistry/pkg/v1/random\"\n)\n\nfunc Test_gobuilds(t *testing.T) {\n\tbase, err := random.Image(1024, 1)\n\tif err != nil {\n\t\tt.Fatalf(\"random.Image() = %v\", err)\n\t}\n\tbaseRef := name.MustParseReference(\"all.your/base\")\n\topts := []Option{\n\t\tWithBaseImages(func(context.Context, string) (name.Reference, Result, error) { return baseRef, base, nil }),\n\t\twithBuilder(writeTempFile),\n\t\twithSBOMber(fauxSBOM),\n\t\tWithPlatforms(\"all\"),\n\t}\n\n\ttests := []struct {\n\t\tdescription       string\n\t\tworkingDirectory  string\n\t\tbuildConfigs      map[string]Config\n\t\topts              []Option\n\t\tnilDefaultBuilder bool // set to true if you want to test build config and don't want the test to fall back to the default builder\n\t\timportpath        string\n\t}{\n\t\t{\n\t\t\tdescription: \"default builder used when no build configs provided\",\n\t\t\topts:        opts,\n\t\t\timportpath:  \"github.com/google/ko\",\n\t\t},\n\t\t{\n\t\t\tdescription:      \"match build config using fully qualified import path\",\n\t\t\tworkingDirectory: \"../..\",\n\t\t\tbuildConfigs: map[string]Config{\n\t\t\t\t\"github.com/google/ko/test\": {\n\t\t\t\t\tID:  \"build-config-0\",\n\t\t\t\t\tDir: \"test\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tnilDefaultBuilder: true,\n\t\t\topts:              opts,\n\t\t\timportpath:        \"github.com/google/ko/test\",\n\t\t},\n\t\t{\n\t\t\tdescription:      \"match build config using ko scheme-prefixed fully qualified import path\",\n\t\t\tworkingDirectory: \"../..\",\n\t\t\tbuildConfigs: map[string]Config{\n\t\t\t\t\"github.com/google/ko/test\": {\n\t\t\t\t\tID:  \"build-config-1\",\n\t\t\t\t\tDir: \"test\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tnilDefaultBuilder: true,\n\t\t\topts:              opts,\n\t\t\timportpath:        \"ko://github.com/google/ko/test\",\n\t\t},\n\t\t{\n\t\t\tdescription:      \"find build config by resolving local import path to fully qualified import path\",\n\t\t\tworkingDirectory: \"../../test\",\n\t\t\tbuildConfigs: map[string]Config{\n\t\t\t\t\"github.com/google/ko/test\": {\n\t\t\t\t\tID: \"build-config-2\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tnilDefaultBuilder: true,\n\t\t\topts:              opts,\n\t\t\timportpath:        \".\",\n\t\t},\n\t\t{\n\t\t\tdescription:      \"find build config by matching local import path to build config directory\",\n\t\t\tworkingDirectory: \"../..\",\n\t\t\tbuildConfigs: map[string]Config{\n\t\t\t\t\"github.com/google/ko/tes12t\": {\n\t\t\t\t\tID:  \"build-config-3\",\n\t\t\t\t\tDir: \"test\",\n\t\t\t\t},\n\t\t\t},\n\t\t\tnilDefaultBuilder: true,\n\t\t\topts:              opts,\n\t\t\timportpath:        \"./test\",\n\t\t},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.description, func(t *testing.T) {\n\t\t\tctx := context.Background()\n\t\t\tbi, err := NewGobuilds(ctx, test.workingDirectory, test.buildConfigs, test.opts...)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"NewGobuilds(): unexpected error: %v\", err)\n\t\t\t}\n\t\t\tgbs := bi.(*gobuilds)\n\t\t\tif test.nilDefaultBuilder {\n\t\t\t\tgbs.defaultBuilder = nil\n\t\t\t}\n\t\t\tqualifiedImportpath, err := gbs.QualifyImport(test.importpath)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"gobuilds.QualifyImport(%s): unexpected error: %v\", test.importpath, err)\n\t\t\t}\n\t\t\tif err = gbs.IsSupportedReference(qualifiedImportpath); err != nil {\n\t\t\t\tt.Fatalf(\"gobuilds.IsSupportedReference(%s): unexpected error: %v\", qualifiedImportpath, err)\n\t\t\t}\n\t\t\tresult, err := gbs.Build(ctx, qualifiedImportpath)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"gobuilds.Build(%s): unexpected error = %v\", qualifiedImportpath, err)\n\t\t\t}\n\t\t\tif result == nil {\n\t\t\t\tt.Fatalf(\"gobuilds.Build(%s): expected non-nil result\", qualifiedImportpath)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc Test_relativePath(t *testing.T) {\n\ttests := []struct {\n\t\tdescription string\n\t\tbaseDir     string\n\t\timportpath  string\n\t\twant        string\n\t\twantErr     bool\n\t}{\n\t\t{\n\t\t\tdescription: \"all empty string\",\n\t\t},\n\t\t{\n\t\t\tdescription: \"all current directory\",\n\t\t\tbaseDir:     \".\",\n\t\t\timportpath:  \".\",\n\t\t\twant:        \".\",\n\t\t},\n\t\t{\n\t\t\tdescription: \"fully qualified import path without ko prefix\",\n\t\t\tbaseDir:     \"also-any-value-because-it-is-ignored\",\n\t\t\timportpath:  \"example.com/app/cmd/foo\",\n\t\t\twant:        \"example.com/app/cmd/foo\",\n\t\t},\n\t\t{\n\t\t\tdescription: \"fully qualified import path with ko prefix\",\n\t\t\tbaseDir:     \"also-any-value-because-it-is-ignored\",\n\t\t\timportpath:  \"ko://example.com/app/cmd/foo\",\n\t\t\twant:        \"ko://example.com/app/cmd/foo\",\n\t\t},\n\t\t{\n\t\t\tdescription: \"importpath is local subdirectory\",\n\t\t\tbaseDir:     \"foo\",\n\t\t\timportpath:  \"./foo/bar\",\n\t\t\twant:        \"./bar\",\n\t\t},\n\t\t{\n\t\t\tdescription: \"importpath is same local directory\",\n\t\t\tbaseDir:     \"foo/bar\",\n\t\t\timportpath:  \"./foo/bar\",\n\t\t\twant:        \".\",\n\t\t},\n\t\t{\n\t\t\tdescription: \"importpath is not subdirectory or same local directory\",\n\t\t\tbaseDir:     \"foo\",\n\t\t\timportpath:  \"./bar\",\n\t\t\twantErr:     true,\n\t\t},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.description, func(t *testing.T) {\n\t\t\tgot, err := relativePath(test.baseDir, test.importpath)\n\t\t\tif (err != nil) != test.wantErr {\n\t\t\t\tt.Errorf(\"relativePath() error = %v, wantErr %v\", err, test.wantErr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif got != test.want {\n\t\t\t\tt.Errorf(\"relativePath() got = %v, want %v\", got, test.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/build/layer.go",
    "content": "// Copyright 2020 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage build\n\nimport (\n\t\"io\"\n\t\"sync\"\n\n\tv1 \"github.com/google/go-containerregistry/pkg/v1\"\n\t\"github.com/google/go-containerregistry/pkg/v1/types\"\n)\n\ntype lazyLayer struct {\n\tdiffid v1.Hash\n\tdesc   v1.Descriptor\n\n\tsync.Once\n\tbuildLayer func() (v1.Layer, error)\n\tlayer      v1.Layer\n\terr        error\n}\n\n// All this info is cached by previous builds.\nfunc (l *lazyLayer) Digest() (v1.Hash, error) {\n\treturn l.desc.Digest, nil\n}\n\nfunc (l *lazyLayer) DiffID() (v1.Hash, error) {\n\treturn l.diffid, nil\n}\n\nfunc (l *lazyLayer) Size() (int64, error) {\n\treturn l.desc.Size, nil\n}\n\nfunc (l *lazyLayer) MediaType() (types.MediaType, error) {\n\treturn l.desc.MediaType, nil\n}\n\n// This is only called if the registry doesn't have this blob already.\nfunc (l *lazyLayer) Compressed() (io.ReadCloser, error) {\n\tlayer, err := l.compute()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn layer.Compressed()\n}\n\n// This should never actually be called but we need it to impl v1.Layer.\nfunc (l *lazyLayer) Uncompressed() (io.ReadCloser, error) {\n\tlayer, err := l.compute()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn layer.Uncompressed()\n}\n\nfunc (l *lazyLayer) compute() (v1.Layer, error) {\n\tl.Do(func() {\n\t\tl.layer, l.err = l.buildLayer()\n\t})\n\treturn l.layer, l.err\n}\n"
  },
  {
    "path": "pkg/build/limit.go",
    "content": "// Copyright 2019 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage build\n\nimport (\n\t\"context\"\n\n\t\"golang.org/x/sync/semaphore\"\n)\n\n// Limiter composes with another Interface to limit the number of concurrent builds.\ntype Limiter struct {\n\tBuilder   Interface\n\tsemaphore *semaphore.Weighted\n}\n\n// Limiter implements Interface\nvar _ Interface = (*Recorder)(nil)\n\n// QualifyImport implements Interface\nfunc (l *Limiter) QualifyImport(ip string) (string, error) {\n\treturn l.Builder.QualifyImport(ip)\n}\n\n// IsSupportedReference implements Interface\nfunc (l *Limiter) IsSupportedReference(ip string) error {\n\treturn l.Builder.IsSupportedReference(ip)\n}\n\n// Build implements Interface\nfunc (l *Limiter) Build(ctx context.Context, ip string) (Result, error) {\n\tif err := l.semaphore.Acquire(ctx, 1); err != nil {\n\t\treturn nil, err\n\t}\n\tdefer l.semaphore.Release(1)\n\n\treturn l.Builder.Build(ctx, ip)\n}\n\n// NewLimiter returns a new builder that only allows n concurrent builds of b.\n//\n// Deprecated: Obsoleted by WithJobs option.\nfunc NewLimiter(b Interface, n int) *Limiter {\n\treturn &Limiter{\n\t\tBuilder:   b,\n\t\tsemaphore: semaphore.NewWeighted(int64(n)),\n\t}\n}\n"
  },
  {
    "path": "pkg/build/limit_test.go",
    "content": "// Copyright 2019 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage build\n\nimport (\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n\n\t\"golang.org/x/sync/errgroup\"\n)\n\ntype sleeper struct{}\n\nvar _ Interface = (*sleeper)(nil)\n\n// QualifyImport implements Interface\nfunc (*sleeper) QualifyImport(ip string) (string, error) {\n\treturn ip, nil\n}\n\n// IsSupportedReference implements Interface\nfunc (*sleeper) IsSupportedReference(_ string) error {\n\treturn nil\n}\n\n// Build implements Interface\nfunc (*sleeper) Build(_ context.Context, _ string) (Result, error) {\n\ttime.Sleep(50 * time.Millisecond)\n\treturn nil, nil\n}\n\nfunc TestLimiter(t *testing.T) {\n\tb := NewLimiter(&sleeper{}, 2)\n\n\tstart := time.Now()\n\tg, _ := errgroup.WithContext(context.TODO())\n\tfor i := 0; i <= 10; i++ {\n\t\tg.Go(func() error {\n\t\t\t_, _ = b.Build(context.Background(), \"whatever\")\n\t\t\treturn nil\n\t\t})\n\t}\n\tg.Wait()\n\n\t// 50 ms * 10 builds / 2 concurrency = ~250ms\n\tif time.Now().Before(start.Add(250 * time.Millisecond)) {\n\t\tt.Fatal(\"Too many builds\")\n\t}\n}\n"
  },
  {
    "path": "pkg/build/options.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage build\n\nimport (\n\t\"strings\"\n\n\tv1 \"github.com/google/go-containerregistry/pkg/v1\"\n)\n\n// WithBaseImages is a functional option for overriding the base images\n// that are used for different images.\nfunc WithBaseImages(gb GetBase) Option {\n\treturn func(gbo *gobuildOpener) error {\n\t\tgbo.getBase = gb\n\t\treturn nil\n\t}\n}\n\n// WithCreationTime is a functional option for overriding the creation\n// time given to images.\nfunc WithCreationTime(t v1.Time) Option {\n\treturn func(gbo *gobuildOpener) error {\n\t\tgbo.creationTime = t\n\t\treturn nil\n\t}\n}\n\n// WithKoDataCreationTime is a functional option for overriding the creation\n// time given to the files in the kodata directory.\nfunc WithKoDataCreationTime(t v1.Time) Option {\n\treturn func(gbo *gobuildOpener) error {\n\t\tgbo.kodataCreationTime = t\n\t\treturn nil\n\t}\n}\n\n// WithDisabledOptimizations is a functional option for disabling optimizations\n// when compiling.\nfunc WithDisabledOptimizations() Option {\n\treturn func(gbo *gobuildOpener) error {\n\t\tgbo.disableOptimizations = true\n\t\treturn nil\n\t}\n}\n\n// WithDisabledSBOM is a functional option for disabling SBOM generation.\nfunc WithDisabledSBOM() Option {\n\treturn func(gbo *gobuildOpener) error {\n\t\tgbo.sbom = nil\n\t\treturn nil\n\t}\n}\n\n// WithTrimpath is a functional option that controls whether the `-trimpath`\n// flag is added to `go build`.\nfunc WithTrimpath(v bool) Option {\n\treturn func(gbo *gobuildOpener) error {\n\t\tgbo.trimpath = v\n\t\treturn nil\n\t}\n}\n\n// WithConfig is a functional option for providing GoReleaser Build influenced\n// build settings for importpaths.\n//\n// Set a fully qualified importpath (e.g. github.com/my-user/my-repo/cmd/app)\n// as the mapping key for the respective Config.\nfunc WithConfig(buildConfigs map[string]Config) Option {\n\treturn func(gbo *gobuildOpener) error {\n\t\tgbo.buildConfigs = buildConfigs\n\t\treturn nil\n\t}\n}\n\n// WithDefaultEnv is a functional option for providing a default set of environment\n// variables across all builds.\nfunc WithDefaultEnv(env []string) Option {\n\treturn func(gbo *gobuildOpener) error {\n\t\tgbo.defaultEnv = env\n\t\treturn nil\n\t}\n}\n\n// WithDefaultFlags is a functional option for providing a default set of flags across all builds.\nfunc WithDefaultFlags(flags []string) Option {\n\treturn func(gbo *gobuildOpener) error {\n\t\tgbo.defaultFlags = flags\n\t\treturn nil\n\t}\n}\n\n// WithDefaultLdflags is a functional option for providing a default set of ldflags across all builds.\nfunc WithDefaultLdflags(ldflags []string) Option {\n\treturn func(gbo *gobuildOpener) error {\n\t\tgbo.defaultLdflags = ldflags\n\t\treturn nil\n\t}\n}\n\n// WithPlatforms is a functional option for building certain platforms for\n// multi-platform base images. To build everything from the base, use \"all\",\n// otherwise use a list of platform specs, i.e.:\n//\n// platform = <os>[/<arch>[/<variant>]]\n// allowed = \"all\" | []string{platform[,platform]*}\n//\n// Note: a string of comma-separated platforms (i.e. \"platform[,platform]*\")\n// has been deprecated and only exist for backwards compatibility reasons,\n// which will be removed in the future.\nfunc WithPlatforms(platforms ...string) Option {\n\treturn func(gbo *gobuildOpener) error {\n\t\tif len(platforms) == 1 {\n\t\t\t// TODO: inform users that they are using deprecated flow?\n\t\t\tplatforms = strings.Split(platforms[0], \",\")\n\t\t}\n\t\tgbo.platforms = platforms\n\t\treturn nil\n\t}\n}\n\n// WithLabel is a functional option for adding labels to built images.\nfunc WithLabel(k, v string) Option {\n\treturn func(gbo *gobuildOpener) error {\n\t\tif gbo.labels == nil {\n\t\t\tgbo.labels = map[string]string{}\n\t\t}\n\t\tgbo.labels[k] = v\n\t\treturn nil\n\t}\n}\n\n// WithAnnotation is a functional option for adding annotations to built manifests\nfunc WithAnnotation(k, v string) Option {\n\treturn func(gbo *gobuildOpener) error {\n\t\tif gbo.annotations == nil {\n\t\t\tgbo.annotations = map[string]string{}\n\t\t}\n\t\tgbo.annotations[k] = v\n\t\treturn nil\n\t}\n}\n\n// WithUser is a functional option for overriding the user in the image config.\nfunc WithUser(user string) Option {\n\treturn func(gbo *gobuildOpener) error {\n\t\tgbo.user = user\n\t\treturn nil\n\t}\n}\n\n// withBuilder is a functional option for overriding the way go binaries\n// are built.\nfunc withBuilder(b builder) Option {\n\treturn func(gbo *gobuildOpener) error {\n\t\tgbo.build = b\n\t\treturn nil\n\t}\n}\n\n// WithSPDX is a functional option to direct ko to use\n// SPDX for SBOM format.\nfunc WithSPDX(version string) Option {\n\treturn func(gbo *gobuildOpener) error {\n\t\tgbo.sbom = spdx(version)\n\t\treturn nil\n\t}\n}\n\n// withSBOMber is a functional option for overriding the way SBOMs\n// are generated.\nfunc withSBOMber(sbom sbomber) Option {\n\treturn func(gbo *gobuildOpener) error {\n\t\tgbo.sbom = sbom\n\t\treturn nil\n\t}\n}\n\n// WithJobs limits the number of concurrent builds.\nfunc WithJobs(jobs int) Option {\n\treturn func(gbo *gobuildOpener) error {\n\t\tgbo.jobs = jobs\n\t\treturn nil\n\t}\n}\n\n// WithSBOMDir is a functional option for overriding the directory\nfunc WithSBOMDir(dir string) Option {\n\treturn func(gbo *gobuildOpener) error {\n\t\tgbo.sbomDir = dir\n\t\treturn nil\n\t}\n}\n\nfunc WithDebugger() Option {\n\treturn func(gbo *gobuildOpener) error {\n\t\tgbo.debug = true\n\t\treturn nil\n\t}\n}\n"
  },
  {
    "path": "pkg/build/recorder.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage build\n\nimport (\n\t\"context\"\n\t\"sync\"\n)\n\n// Recorder composes with another Interface to record the built import paths.\ntype Recorder struct {\n\tm           sync.Mutex\n\tImportPaths []string\n\tBuilder     Interface\n}\n\n// Recorder implements Interface\nvar _ Interface = (*Recorder)(nil)\n\n// QualifyImport implements Interface\nfunc (r *Recorder) QualifyImport(ip string) (string, error) {\n\treturn r.Builder.QualifyImport(ip)\n}\n\n// IsSupportedReference implements Interface\nfunc (r *Recorder) IsSupportedReference(ip string) error {\n\treturn r.Builder.IsSupportedReference(ip)\n}\n\n// Build implements Interface\nfunc (r *Recorder) Build(ctx context.Context, ip string) (Result, error) {\n\tfunc() {\n\t\tr.m.Lock()\n\t\tdefer r.m.Unlock()\n\t\tr.ImportPaths = append(r.ImportPaths, ip)\n\t}()\n\treturn r.Builder.Build(ctx, ip)\n}\n"
  },
  {
    "path": "pkg/build/recorder_test.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage build\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n)\n\ntype fake struct {\n\tisr func(string) error\n\tb   func(string) (Result, error)\n}\n\nvar _ Interface = (*fake)(nil)\n\n// QualifyImport implements Interface\nfunc (r *fake) QualifyImport(ip string) (string, error) { return ip, nil }\n\n// IsSupportedReference implements Interface\nfunc (r *fake) IsSupportedReference(ip string) error { return r.isr(ip) }\n\n// Build implements Interface\nfunc (r *fake) Build(_ context.Context, ip string) (Result, error) { return r.b(ip) }\n\nfunc TestISRPassThrough(t *testing.T) {\n\ttests := []struct {\n\t\tname  string\n\t\tinput string\n\t}{{\n\t\tname: \"empty string\",\n\t}, {\n\t\tname:  \"non-empty string\",\n\t\tinput: \"asdf asdf asdf\",\n\t}}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tcalled := false\n\t\t\tinner := &fake{\n\t\t\t\tisr: func(ip string) error {\n\t\t\t\t\tcalled = true\n\t\t\t\t\tif ip != test.input {\n\t\t\t\t\t\tt.Errorf(\"ISR = %v, wanted %v\", ip, test.input)\n\t\t\t\t\t}\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t}\n\t\t\trec := &Recorder{\n\t\t\t\tBuilder: inner,\n\t\t\t}\n\t\t\trec.IsSupportedReference(test.input)\n\t\t\tif !called {\n\t\t\t\tt.Error(\"IsSupportedReference wasn't called, wanted called\")\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestBuildRecording(t *testing.T) {\n\ttests := []struct {\n\t\tname   string\n\t\tinputs []string\n\t}{{\n\t\tname: \"no calls\",\n\t}, {\n\t\tname: \"one call\",\n\t\tinputs: []string{\n\t\t\t\"github.com/foo/bar\",\n\t\t},\n\t}, {\n\t\tname: \"two calls\",\n\t\tinputs: []string{\n\t\t\t\"github.com/foo/bar\",\n\t\t\t\"github.com/foo/baz\",\n\t\t},\n\t}, {\n\t\tname: \"duplicates\",\n\t\tinputs: []string{\n\t\t\t\"github.com/foo/bar\",\n\t\t\t\"github.com/foo/baz\",\n\t\t\t\"github.com/foo/bar\",\n\t\t\t\"github.com/foo/baz\",\n\t\t},\n\t}}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tinner := &fake{\n\t\t\t\tb: func(_ string) (Result, error) {\n\t\t\t\t\treturn nil, nil\n\t\t\t\t},\n\t\t\t}\n\t\t\trec := &Recorder{\n\t\t\t\tBuilder: inner,\n\t\t\t}\n\t\t\tfor _, in := range test.inputs {\n\t\t\t\trec.Build(context.Background(), in)\n\t\t\t}\n\t\t\tif diff := cmp.Diff(test.inputs, rec.ImportPaths); diff != \"\" {\n\t\t\t\tt.Errorf(\"Build (-want, +got): %s\", diff)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/build/shared.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage build\n\nimport (\n\t\"context\"\n\t\"sync\"\n)\n\n// Caching wraps a builder implementation in a layer that shares build results\n// for the same inputs using a simple \"future\" implementation.  Cached results\n// may be invalidated by calling Invalidate with the same input passed to Build.\ntype Caching struct {\n\tinner Interface\n\n\tm       sync.Mutex\n\tresults map[string]*future\n}\n\n// Caching implements Interface\nvar _ Interface = (*Caching)(nil)\n\n// NewCaching wraps the provided build.Interface in an implementation that\n// shares build results for a given path until the result has been invalidated.\nfunc NewCaching(inner Interface) (*Caching, error) {\n\treturn &Caching{\n\t\tinner:   inner,\n\t\tresults: make(map[string]*future),\n\t}, nil\n}\n\n// Build implements Interface\nfunc (c *Caching) Build(ctx context.Context, ip string) (Result, error) {\n\tf := func() *future {\n\t\t// Lock the map of futures.\n\t\tc.m.Lock()\n\t\tdefer c.m.Unlock()\n\n\t\t// If a future for \"ip\" exists, then return it.\n\t\tf, ok := c.results[ip]\n\t\tif ok {\n\t\t\treturn f\n\t\t}\n\t\t// Otherwise create and record a future for a Build of \"ip\".\n\t\tf = newFuture(func() (Result, error) {\n\t\t\treturn c.inner.Build(ctx, ip)\n\t\t})\n\t\tc.results[ip] = f\n\t\treturn f\n\t}()\n\n\treturn f.Get()\n}\n\n// QualifyImport implements Interface\nfunc (c *Caching) QualifyImport(ip string) (string, error) {\n\treturn c.inner.QualifyImport(ip)\n}\n\n// IsSupportedReference implements Interface\nfunc (c *Caching) IsSupportedReference(ip string) error {\n\treturn c.inner.IsSupportedReference(ip)\n}\n\n// Invalidate removes an import path's cached results.\nfunc (c *Caching) Invalidate(ip string) {\n\tc.m.Lock()\n\tdefer c.m.Unlock()\n\n\tdelete(c.results, ip)\n}\n"
  },
  {
    "path": "pkg/build/shared_test.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage build\n\nimport (\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/google/go-containerregistry/pkg/v1/random\"\n)\n\ntype slowbuild struct {\n\tsleep time.Duration\n}\n\n// slowbuild implements Interface\nvar _ Interface = (*slowbuild)(nil)\n\nfunc (sb *slowbuild) QualifyImport(ip string) (string, error) { return ip, nil }\n\nfunc (sb *slowbuild) IsSupportedReference(string) error { return nil }\n\nfunc (sb *slowbuild) Build(context.Context, string) (Result, error) {\n\ttime.Sleep(sb.sleep)\n\treturn random.Index(256, 8, 3)\n}\n\nfunc TestCaching(t *testing.T) {\n\tduration := 100 * time.Millisecond\n\tip := \"foo\"\n\n\tsb := &slowbuild{duration}\n\tcb, _ := NewCaching(sb)\n\n\tif err := cb.IsSupportedReference(ip); err != nil {\n\t\tt.Errorf(\"ISR(%q) = (%v), wanted nil\", err, ip)\n\t}\n\n\tpreviousDigest := \"not-a-digest\"\n\t// Each iteration, we test that the first build is slow and subsequent\n\t// builds are fast and return the same image.  Then we invalidate the\n\t// cache and iterate.\n\tfor range 3 {\n\t\tstart := time.Now()\n\t\timg1, err := cb.Build(context.Background(), ip)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Build() = %v\", err)\n\t\t}\n\t\tend := time.Now()\n\n\t\telapsed := end.Sub(start)\n\t\tif elapsed < duration {\n\t\t\tt.Errorf(\"Elapsed time %v, wanted >= %s\", elapsed, duration)\n\t\t}\n\t\td1 := digest(t, img1)\n\n\t\tif d1 == previousDigest {\n\t\t\tt.Errorf(\"Got same digest as previous iteration, wanted different: %v\", d1)\n\t\t}\n\t\tpreviousDigest = d1\n\n\t\tstart = time.Now()\n\t\timg2, err := cb.Build(context.Background(), ip)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Build() = %v\", err)\n\t\t}\n\t\tend = time.Now()\n\n\t\telapsed = end.Sub(start)\n\t\tif elapsed >= duration {\n\t\t\tt.Errorf(\"Elapsed time %v, wanted < %s\", elapsed, duration)\n\t\t}\n\t\td2 := digest(t, img2)\n\n\t\tif d1 != d2 {\n\t\t\tt.Error(\"Got different images, wanted same\")\n\t\t}\n\n\t\tcb.Invalidate(ip)\n\t}\n}\n"
  },
  {
    "path": "pkg/build/strict.go",
    "content": "// Copyright 2020 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage build\n\nimport \"strings\"\n\n// StrictScheme is a prefix that can be placed on import paths that users\n// think MUST be supported references.\nconst StrictScheme = \"ko://\"\n\ntype reference struct {\n\tstrict bool\n\tpath   string\n}\n\nfunc newRef(s string) reference {\n\treturn reference{\n\t\tstrict: strings.HasPrefix(s, StrictScheme),\n\t\tpath:   strings.TrimPrefix(s, StrictScheme),\n\t}\n}\n\nfunc (r reference) IsStrict() bool {\n\treturn r.strict\n}\n\nfunc (r reference) Path() string {\n\treturn r.path\n}\n\nfunc (r reference) String() string {\n\tif r.IsStrict() {\n\t\treturn StrictScheme + r.Path()\n\t}\n\treturn r.Path()\n}\n"
  },
  {
    "path": "pkg/build/strict_test.go",
    "content": "// Copyright 2020 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage build\n\nimport \"testing\"\n\nfunc TestStrictReference(t *testing.T) {\n\ttests := []struct {\n\t\tname   string\n\t\tinput  string\n\t\tstrict bool\n\t\tpath   string\n\t}{{\n\t\tname:   \"loose\",\n\t\tinput:  \"github.com/foo/bar\",\n\t\tstrict: false,\n\t\tpath:   \"github.com/foo/bar\",\n\t}, {\n\t\tname:   \"strict\",\n\t\tinput:  \"ko://github.com/foo/bar\",\n\t\tstrict: true,\n\t\tpath:   \"github.com/foo/bar\",\n\t}}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tref := newRef(test.input)\n\t\t\tif got, want := ref.IsStrict(), test.strict; got != want {\n\t\t\t\tt.Errorf(\"got: %v, want: %v\", got, want)\n\t\t\t}\n\t\t\tif got, want := ref.Path(), test.path; got != want {\n\t\t\t\tt.Errorf(\"got: %v, want: %v\", got, want)\n\t\t\t}\n\t\t\tif got, want := ref.String(), test.input; got != want {\n\t\t\t\tt.Errorf(\"got: %v, want: %v\", got, want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/caps/caps.go",
    "content": "// Copyright 2024 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// Package caps implements a subset of Linux capabilities handling\n// relevant in the context of authoring container images.\npackage caps\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// Mask captures a set of Linux capabilities\ntype Mask uint64\n\n// Parse text representation of a single Linux capability.\n//\n// It accepts all variations recognized by Docker's --cap-add, such as\n// 'chown', 'cap_chown', and 'CHOWN'. Additionally, we allow numeric\n// values, e.g. '42' to support future capabilities that are not yet\n// known to us.\nfunc Parse(s string) (Mask, error) {\n\tif index, err := strconv.ParseUint(s, 10, 6); err == nil {\n\t\treturn 1 << index, nil\n\t}\n\tname := strings.ToUpper(s)\n\tif name == \"ALL\" {\n\t\treturn allKnownCaps(), nil\n\t}\n\tname = strings.TrimPrefix(name, \"CAP_\")\n\tif index, ok := nameToIndex[name]; ok {\n\t\treturn 1 << index, nil\n\t}\n\treturn 0, fmt.Errorf(\"unknown capability: %#v\", s)\n}\n\nfunc allKnownCaps() Mask {\n\tvar mask Mask\n\tfor _, index := range nameToIndex {\n\t\tmask |= 1 << index\n\t}\n\treturn mask\n}\n\nvar nameToIndex = map[string]int{\n\t\"CHOWN\":            0,\n\t\"DAC_OVERRIDE\":     1,\n\t\"DAC_READ_SEARCH\":  2,\n\t\"FOWNER\":           3,\n\t\"FSETID\":           4,\n\t\"KILL\":             5,\n\t\"SETGID\":           6,\n\t\"SETUID\":           7,\n\t\"SETPCAP\":          8,\n\t\"LINUX_IMMUTABLE\":  9,\n\t\"NET_BIND_SERVICE\": 10,\n\t\"NET_BROADCAST\":    11,\n\t\"NET_ADMIN\":        12,\n\t\"NET_RAW\":          13,\n\t\"IPC_LOCK\":         14,\n\t\"IPC_OWNER\":        15,\n\t\"SYS_MODULE\":       16,\n\t\"SYS_RAWIO\":        17,\n\t\"SYS_CHROOT\":       18,\n\t\"SYS_PTRACE\":       19,\n\t\"SYS_PACCT\":        20,\n\t\"SYS_ADMIN\":        21,\n\t\"SYS_BOOT\":         22,\n\t\"SYS_NICE\":         23,\n\t\"SYS_RESOURCE\":     24,\n\t\"SYS_TIME\":         25,\n\t\"SYS_TTY_CONFIG\":   26,\n\t\"MKNOD\":            27,\n\t\"LEASE\":            28,\n\t\"AUDIT_WRITE\":      29,\n\t\"AUDIT_CONTROL\":    30,\n\t\"SETFCAP\":          31,\n\n\t\"MAC_OVERRIDE\":       32,\n\t\"MAC_ADMIN\":          33,\n\t\"SYSLOG\":             34,\n\t\"WAKE_ALARM\":         35,\n\t\"BLOCK_SUSPEND\":      36,\n\t\"AUDIT_READ\":         37,\n\t\"PERFMON\":            38,\n\t\"BPF\":                39,\n\t\"CHECKPOINT_RESTORE\": 40,\n}\n\n// Flags alter certain aspects of capabilities handling\ntype Flags uint32\n\nconst (\n\t// FlagEffective causes all of the new permitted capabilities to be\n\t// also raised in the effective set diring execve(2)\n\tFlagEffective Flags = 1\n)\n\n// XattrBytes encodes capabilities in the format of\n// security.capability extended filesystem attribute. This is how Linux\n// tracks file capabilities internally.\nfunc XattrBytes(permitted, inheritable Mask, flags Flags) ([]byte, error) {\n\t// Underlying data layout as defined by Linux kernel (vfs_ns_cap_data)\n\ttype vfsNsCapData struct {\n\t\tMagicEtc uint32\n\t\tData     [2]struct {\n\t\t\tPermitted   uint32\n\t\t\tInheritable uint32\n\t\t}\n\t}\n\n\tconst vfsCapRevision2 = 0x02000000\n\n\tdata := vfsNsCapData{MagicEtc: vfsCapRevision2 | uint32(flags)}\n\tdata.Data[0].Permitted = uint32(permitted)\n\tdata.Data[0].Inheritable = uint32(inheritable)\n\tdata.Data[1].Permitted = uint32(permitted >> 32)\n\tdata.Data[1].Inheritable = uint32(inheritable >> 32)\n\n\tbuf := &bytes.Buffer{}\n\tif err := binary.Write(buf, binary.LittleEndian, data); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn buf.Bytes(), nil\n}\n\n// FileCaps encodes Linux file capabilities\ntype FileCaps struct {\n\tpermitted, inheritable Mask\n\tflags                  Flags\n}\n\n// NewFileCaps produces file capabilities object from a list of string\n// terms. A term is either a single capability name (added as permitted)\n// or a cap_from_text(3) clause.\nfunc NewFileCaps(terms ...string) (*FileCaps, error) {\n\tvar permitted, inheritable, effective Mask\n\tfor _, term := range terms {\n\t\tvar caps, actionList string\n\t\tif index := strings.IndexAny(term, \"+-=\"); index != -1 {\n\t\t\tcaps, actionList = term[:index], term[index:]\n\t\t} else {\n\t\t\tmask, err := Parse(term)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tpermitted |= mask\n\t\t\tcontinue\n\t\t}\n\t\t// Handling cap_from_text(3) syntax, e.g. cap1,cap2=pie\n\t\tif caps == \"\" && actionList[0] == '=' {\n\t\t\tcaps = \"all\"\n\t\t}\n\t\tvar mask, mask2 Mask\n\t\tfor capname := range strings.SplitSeq(caps, \",\") {\n\t\t\tm, err := Parse(capname)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"%#v: %w\", term, err)\n\t\t\t}\n\t\t\tmask |= m\n\t\t}\n\t\tfor _, c := range actionList {\n\t\t\tswitch c {\n\t\t\tcase '+':\n\t\t\t\tmask2 = ^Mask(0)\n\t\t\tcase '-':\n\t\t\t\tmask2 = ^mask\n\t\t\tcase '=':\n\t\t\t\tmask2 = ^Mask(0)\n\t\t\t\tpermitted &= ^mask\n\t\t\t\tinheritable &= ^mask\n\t\t\t\teffective &= ^mask\n\t\t\tcase 'p':\n\t\t\t\tpermitted = (permitted | mask) & mask2\n\t\t\tcase 'i':\n\t\t\t\tinheritable = (inheritable | mask) & mask2\n\t\t\tcase 'e':\n\t\t\t\teffective = (effective | mask) & mask2\n\t\t\tdefault:\n\t\t\t\treturn nil, fmt.Errorf(\"%#v: unknown flag '%c'\", term, c)\n\t\t\t}\n\t\t}\n\t}\n\tif permitted != 0 || inheritable != 0 {\n\t\tvar flags Flags\n\t\tif effective != 0 {\n\t\t\tflags = FlagEffective\n\t\t}\n\t\treturn &FileCaps{permitted: permitted, inheritable: inheritable, flags: flags}, nil\n\t}\n\treturn nil, nil\n}\n\n// ToXattrBytes encodes capabilities in the format of\n// security.capability extended filesystem attribute.\nfunc (fc *FileCaps) ToXattrBytes() ([]byte, error) {\n\treturn XattrBytes(fc.permitted, fc.inheritable, fc.flags)\n}\n"
  },
  {
    "path": "pkg/caps/caps_dd_test.go",
    "content": "// Generated file, do not edit.\n\n// Copyright 2024 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage caps\n\nvar ddTests = []ddTest{\n\t{permitted: \"chown\", inheritable: \"\", effective: false, res: \"AAAAAgEAAAAAAAAAAAAAAAAAAAA=\"},\n\t{permitted: \"chown\", inheritable: \"\", effective: true, res: \"AQAAAgEAAAAAAAAAAAAAAAAAAAA=\"},\n\t{permitted: \"\", inheritable: \"chown\", effective: false, res: \"AAAAAgAAAAABAAAAAAAAAAAAAAA=\"},\n\t{permitted: \"chown\", inheritable: \"chown\", effective: true, res: \"AQAAAgEAAAABAAAAAAAAAAAAAAA=\"},\n\t{permitted: \"dac_override\", inheritable: \"dac_override\", effective: true, res: \"AQAAAgIAAAACAAAAAAAAAAAAAAA=\"},\n\t{permitted: \"dac_read_search\", inheritable: \"dac_read_search\", effective: true, res: \"AQAAAgQAAAAEAAAAAAAAAAAAAAA=\"},\n\t{permitted: \"fowner\", inheritable: \"fowner\", effective: true, res: \"AQAAAggAAAAIAAAAAAAAAAAAAAA=\"},\n\t{permitted: \"fsetid\", inheritable: \"fsetid\", effective: true, res: \"AQAAAhAAAAAQAAAAAAAAAAAAAAA=\"},\n\t{permitted: \"kill\", inheritable: \"kill\", effective: true, res: \"AQAAAiAAAAAgAAAAAAAAAAAAAAA=\"},\n\t{permitted: \"setgid\", inheritable: \"setgid\", effective: true, res: \"AQAAAkAAAABAAAAAAAAAAAAAAAA=\"},\n\t{permitted: \"setuid\", inheritable: \"setuid\", effective: true, res: \"AQAAAoAAAACAAAAAAAAAAAAAAAA=\"},\n\t{permitted: \"setpcap\", inheritable: \"setpcap\", effective: true, res: \"AQAAAgABAAAAAQAAAAAAAAAAAAA=\"},\n\t{permitted: \"linux_immutable\", inheritable: \"linux_immutable\", effective: true, res: \"AQAAAgACAAAAAgAAAAAAAAAAAAA=\"},\n\t{permitted: \"net_bind_service\", inheritable: \"net_bind_service\", effective: true, res: \"AQAAAgAEAAAABAAAAAAAAAAAAAA=\"},\n\t{permitted: \"net_broadcast\", inheritable: \"net_broadcast\", effective: true, res: \"AQAAAgAIAAAACAAAAAAAAAAAAAA=\"},\n\t{permitted: \"net_admin\", inheritable: \"net_admin\", effective: true, res: \"AQAAAgAQAAAAEAAAAAAAAAAAAAA=\"},\n\t{permitted: \"net_raw\", inheritable: \"net_raw\", effective: true, res: \"AQAAAgAgAAAAIAAAAAAAAAAAAAA=\"},\n\t{permitted: \"ipc_lock\", inheritable: \"ipc_lock\", effective: true, res: \"AQAAAgBAAAAAQAAAAAAAAAAAAAA=\"},\n\t{permitted: \"ipc_owner\", inheritable: \"ipc_owner\", effective: true, res: \"AQAAAgCAAAAAgAAAAAAAAAAAAAA=\"},\n\t{permitted: \"sys_module\", inheritable: \"sys_module\", effective: true, res: \"AQAAAgAAAQAAAAEAAAAAAAAAAAA=\"},\n\t{permitted: \"sys_rawio\", inheritable: \"sys_rawio\", effective: true, res: \"AQAAAgAAAgAAAAIAAAAAAAAAAAA=\"},\n\t{permitted: \"sys_chroot\", inheritable: \"sys_chroot\", effective: true, res: \"AQAAAgAABAAAAAQAAAAAAAAAAAA=\"},\n\t{permitted: \"sys_ptrace\", inheritable: \"sys_ptrace\", effective: true, res: \"AQAAAgAACAAAAAgAAAAAAAAAAAA=\"},\n\t{permitted: \"sys_pacct\", inheritable: \"sys_pacct\", effective: true, res: \"AQAAAgAAEAAAABAAAAAAAAAAAAA=\"},\n\t{permitted: \"sys_admin\", inheritable: \"sys_admin\", effective: true, res: \"AQAAAgAAIAAAACAAAAAAAAAAAAA=\"},\n\t{permitted: \"sys_boot\", inheritable: \"sys_boot\", effective: true, res: \"AQAAAgAAQAAAAEAAAAAAAAAAAAA=\"},\n\t{permitted: \"sys_nice\", inheritable: \"sys_nice\", effective: true, res: \"AQAAAgAAgAAAAIAAAAAAAAAAAAA=\"},\n\t{permitted: \"sys_resource\", inheritable: \"sys_resource\", effective: true, res: \"AQAAAgAAAAEAAAABAAAAAAAAAAA=\"},\n\t{permitted: \"sys_time\", inheritable: \"sys_time\", effective: true, res: \"AQAAAgAAAAIAAAACAAAAAAAAAAA=\"},\n\t{permitted: \"sys_tty_config\", inheritable: \"sys_tty_config\", effective: true, res: \"AQAAAgAAAAQAAAAEAAAAAAAAAAA=\"},\n\t{permitted: \"mknod\", inheritable: \"mknod\", effective: true, res: \"AQAAAgAAAAgAAAAIAAAAAAAAAAA=\"},\n\t{permitted: \"lease\", inheritable: \"lease\", effective: true, res: \"AQAAAgAAABAAAAAQAAAAAAAAAAA=\"},\n\t{permitted: \"audit_write\", inheritable: \"audit_write\", effective: true, res: \"AQAAAgAAACAAAAAgAAAAAAAAAAA=\"},\n\t{permitted: \"audit_control\", inheritable: \"audit_control\", effective: true, res: \"AQAAAgAAAEAAAABAAAAAAAAAAAA=\"},\n\t{permitted: \"setfcap\", inheritable: \"setfcap\", effective: true, res: \"AQAAAgAAAIAAAACAAAAAAAAAAAA=\"},\n\t{permitted: \"mac_override\", inheritable: \"mac_override\", effective: true, res: \"AQAAAgAAAAAAAAAAAQAAAAEAAAA=\"},\n\t{permitted: \"mac_admin\", inheritable: \"mac_admin\", effective: true, res: \"AQAAAgAAAAAAAAAAAgAAAAIAAAA=\"},\n\t{permitted: \"syslog\", inheritable: \"syslog\", effective: true, res: \"AQAAAgAAAAAAAAAABAAAAAQAAAA=\"},\n\t{permitted: \"wake_alarm\", inheritable: \"wake_alarm\", effective: true, res: \"AQAAAgAAAAAAAAAACAAAAAgAAAA=\"},\n\t{permitted: \"block_suspend\", inheritable: \"block_suspend\", effective: true, res: \"AQAAAgAAAAAAAAAAEAAAABAAAAA=\"},\n\t{permitted: \"audit_read\", inheritable: \"audit_read\", effective: true, res: \"AQAAAgAAAAAAAAAAIAAAACAAAAA=\"},\n\t{permitted: \"perfmon\", inheritable: \"perfmon\", effective: true, res: \"AQAAAgAAAAAAAAAAQAAAAEAAAAA=\"},\n\t{permitted: \"bpf\", inheritable: \"bpf\", effective: true, res: \"AQAAAgAAAAAAAAAAgAAAAIAAAAA=\"},\n\t{permitted: \"checkpoint_restore\", inheritable: \"checkpoint_restore\", effective: true, res: \"AQAAAgAAAAAAAAAAAAEAAAABAAA=\"},\n}\n"
  },
  {
    "path": "pkg/caps/caps_test.go",
    "content": "// Copyright 2024 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage caps\n\nimport (\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"testing\"\n)\n\nfunc TestParse(t *testing.T) {\n\ttests := []struct {\n\t\targ      string\n\t\tres      Mask\n\t\tmustFail bool\n\t}{\n\t\t{arg: \"chown\", res: 1},\n\t\t{arg: \"cap_chown\", res: 1},\n\t\t{arg: \"cAp_cHoWn\", res: 1},\n\t\t{arg: \"unknown\", mustFail: true},\n\t\t{arg: \"63\", res: 1 << 63},\n\t\t{arg: \"64\", mustFail: true},\n\t\t{arg: \"all\", res: allKnownCaps()},\n\t}\n\tfor _, tc := range tests {\n\t\tt.Run(tc.arg, func(t *testing.T) {\n\t\t\tmask, err := Parse(tc.arg)\n\t\t\tif err == nil && tc.mustFail {\n\t\t\t\tt.Fatal(\"invalid input accepted\")\n\t\t\t}\n\t\t\tif err != nil && !tc.mustFail {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\t\t\tif mask != tc.res {\n\t\t\t\tt.Fatalf(\"unexpected result: %x\", mask)\n\t\t\t}\n\t\t})\n\t}\n}\n\n//go:generate ./gen.sh\n\ntype ddTest struct {\n\tpermitted, inheritable string\n\teffective              bool\n\tres                    string\n}\n\nfunc TestDd(t *testing.T) {\n\tfor _, test := range ddTests {\n\t\tlabel := fmt.Sprintf(\"%s,%s,%v\", test.permitted, test.inheritable, test.effective)\n\t\tt.Run(label, func(t *testing.T) {\n\t\t\tvar permitted, inheritable Mask\n\t\t\tvar flags Flags\n\n\t\t\tif test.permitted != \"\" {\n\t\t\t\tmask, err := Parse(test.permitted)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatal(err)\n\t\t\t\t}\n\t\t\t\tpermitted = mask\n\t\t\t}\n\n\t\t\tif test.inheritable != \"\" {\n\t\t\t\tmask, err := Parse(test.inheritable)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Fatal(err)\n\t\t\t\t}\n\t\t\t\tinheritable = mask\n\t\t\t}\n\n\t\t\tif test.effective {\n\t\t\t\tflags = FlagEffective\n\t\t\t}\n\n\t\t\tres, err := XattrBytes(permitted, inheritable, flags)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatal(err)\n\t\t\t}\n\n\t\t\tresBase64 := make([]byte, base64.StdEncoding.EncodedLen(len(res)))\n\t\t\tbase64.StdEncoding.Encode(resBase64, res)\n\t\t\tif string(resBase64) != test.res {\n\t\t\t\tt.Fatalf(\"expected %s, result %s\", test.res, resBase64)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/caps/gen.sh",
    "content": "#!/usr/bin/env bash\n\n# Copyright 2024 ko Build Authors All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#     http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\n# This script assigns different capabilities to files and captures\n# resulting xattr blobs for testing (generates caps_dd_test.go).\n#\n# It has to be run on a reasonably recent Linux to ensure that the full\n# set of capabilities is supported. Setting capabilities requires\n# privileges; the script assumes paswordless sudo is available.\n\nset -o errexit\nset -o nounset\nset -o pipefail\nshopt -s inherit_errexit\n\n# capblob CAP_STRING\n# Obtain base64-encoded value of the underlying xattr that implemens\n# specified capabilities, setcap syntax.\n# Example: capblob cap_chown=eip\ncapblob() {\n  f=$(mktemp)\n  sudo -n setcap $1 $f\n  getfattr -n security.capability --absolute-names --only-values $f | base64\n  rm $f\n}\n\n(\n  license=$(sed -e '/^$/,$d' caps.go)\n\n  echo \"// Generated file, do not edit.\"\n  echo \"\"\n  echo \"$license\"\n  echo \"\"\n  echo \"package caps\"\n  echo \"var ddTests = []ddTest{\"\n\n  res=$(capblob cap_chown=p)\n  echo \"{permitted: \\\"chown\\\", inheritable: \\\"\\\", effective: false, res: \\\"$res\\\"},\"\n\n  res=$(capblob cap_chown=ep)\n  echo \"{permitted: \\\"chown\\\", inheritable: \\\"\\\", effective: true, res: \\\"$res\\\"},\"\n\n  res=$(capblob cap_chown=i)\n  echo \"{permitted: \\\"\\\", inheritable: \\\"chown\\\", effective: false, res: \\\"$res\\\"},\"\n\n  CAPS=\"chown dac_override dac_read_search fowner fsetid kill setgid setuid\n    setpcap linux_immutable net_bind_service net_broadcast net_admin net_raw ipc_lock ipc_owner\n    sys_module sys_rawio sys_chroot sys_ptrace sys_pacct sys_admin sys_boot sys_nice\n    sys_resource sys_time sys_tty_config mknod lease audit_write audit_control setfcap\n    mac_override mac_admin syslog wake_alarm block_suspend audit_read perfmon bpf\n    checkpoint_restore\"\n  for cap in $CAPS; do\n    res=$(capblob cap_$cap=eip)\n    echo \"{permitted: \\\"$cap\\\", inheritable: \\\"$cap\\\", effective: true, res: \\\"$res\\\"},\"\n  done\n\n  echo \"}\"\n) > caps_dd_test.go\n\ngofmt -w -s ./caps_dd_test.go\n"
  },
  {
    "path": "pkg/caps/new_file_caps_test.go",
    "content": "// Copyright 2024 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage caps\n\nimport (\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n)\n\nfunc TestNewFileCaps(t *testing.T) {\n\ttests := []struct {\n\t\targs     []string\n\t\tres      *FileCaps\n\t\tmustFail bool\n\t}{\n\t\t{},\n\t\t{\n\t\t\targs: []string{\"chown\", \"dac_override\", \"dac_read_search\"},\n\t\t\tres:  &FileCaps{permitted: 7},\n\t\t},\n\t\t{\n\t\t\targs: []string{\"chown,dac_override,dac_read_search=p\"},\n\t\t\tres:  &FileCaps{permitted: 7},\n\t\t},\n\t\t{\n\t\t\targs: []string{\"chown,dac_override,dac_read_search=i\"},\n\t\t\tres:  &FileCaps{inheritable: 7},\n\t\t},\n\t\t{\n\t\t\targs: []string{\"chown,dac_override,dac_read_search=e\"},\n\t\t},\n\t\t{\n\t\t\targs: []string{\"chown,dac_override,dac_read_search=pe\"},\n\t\t\tres:  &FileCaps{permitted: 7, flags: FlagEffective},\n\t\t},\n\t\t{\n\t\t\targs: []string{\"=pe\"},\n\t\t\tres:  &FileCaps{permitted: allKnownCaps(), flags: FlagEffective},\n\t\t},\n\t\t{\n\t\t\targs: []string{\"chown=ie\", \"chown=p\"},\n\t\t\tres:  &FileCaps{permitted: 1},\n\t\t},\n\t\t{\n\t\t\targs: []string{\"chown=ie\", \"chown=\"},\n\t\t},\n\t\t{\n\t\t\targs: []string{\"chown=ie\", \"chown+p\"},\n\t\t\tres:  &FileCaps{permitted: 1, inheritable: 1, flags: FlagEffective},\n\t\t},\n\t\t{\n\t\t\targs: []string{\"chown=pie\", \"dac_override,chown-p\"},\n\t\t\tres:  &FileCaps{inheritable: 1, flags: FlagEffective},\n\t\t},\n\t\t{args: []string{\"chown,=pie\"}, mustFail: true},\n\t\t{args: []string{\"-pie\"}, mustFail: true},\n\t\t{args: []string{\"+pie\"}, mustFail: true},\n\t\t{args: []string{\"=\"}},\n\t}\n\tfor _, tc := range tests {\n\t\tlabel := strings.Join(tc.args, \":\")\n\t\tt.Run(label, func(t *testing.T) {\n\t\t\tres, err := NewFileCaps(tc.args...)\n\t\t\tif tc.mustFail && err == nil {\n\t\t\t\tt.Fatal(\"didn't fail\")\n\t\t\t}\n\t\t\tif !tc.mustFail && err != nil {\n\t\t\t\tt.Fatalf(\"unexpectedly failed: %v\", err)\n\t\t\t}\n\t\t\tif !reflect.DeepEqual(res, tc.res) {\n\t\t\t\tt.Fatalf(\"got %v expected %v\", res, tc.res)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/commands/apply.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage commands\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\n\t\"github.com/google/ko/pkg/commands/options\"\n\t\"github.com/spf13/cobra\"\n\t\"golang.org/x/sync/errgroup\"\n)\n\n// addApply augments our CLI surface with apply.\nfunc addApply(topLevel *cobra.Command) {\n\tpo := &options.PublishOptions{}\n\tfo := &options.FilenameOptions{}\n\tso := &options.SelectorOptions{}\n\tbo := &options.BuildOptions{}\n\tapply := &cobra.Command{\n\t\tUse:   \"apply -f FILENAME\",\n\t\tShort: \"Apply the input files with image references resolved to built/pushed image digests.\",\n\t\tLong:  `This sub-command finds import path references within the provided files, builds them into Go binaries, containerizes them, publishes them, and then feeds the resulting yaml into \"kubectl apply\".`,\n\t\tExample: `\n  # Build and publish import path references to a Docker\n  # Registry as:\n  #   ${KO_DOCKER_REPO}/<package name>-<hash of import path>\n  # Then, feed the resulting yaml into \"kubectl apply\".\n  # When KO_DOCKER_REPO is ko.local, it is the same as if\n  # --local was passed.\n  ko apply -f config/\n\n  # Build and publish import path references to a Docker\n  # Registry preserving import path names as:\n  #   ${KO_DOCKER_REPO}/<import path>\n  # Then, feed the resulting yaml into \"kubectl apply\".\n  ko apply --preserve-import-paths -f config/\n\n  # Build and publish import path references to a Docker\n  # daemon as:\n  #   ko.local/<import path>\n  # Then, feed the resulting yaml into \"kubectl apply\".\n  ko apply --local -f config/\n\n  # Apply from stdin:\n  cat config.yaml | ko apply -f -\n\n  # Any flags passed after '--' are passed to 'kubectl apply' directly:\n  ko apply -f config -- --namespace=foo --kubeconfig=cfg.yaml\n`,\n\t\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\tif err := options.Validate(po, bo); err != nil {\n\t\t\t\treturn fmt.Errorf(\"validating options: %w\", err)\n\t\t\t}\n\n\t\t\tif !isKubectlAvailable() {\n\t\t\t\treturn errors.New(\"error: kubectl is not available. kubectl must be installed to use ko apply\")\n\t\t\t}\n\t\t\tctx := cmd.Context()\n\n\t\t\tbo.InsecureRegistry = po.InsecureRegistry\n\t\t\tbuilder, err := makeBuilder(ctx, bo)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"error creating builder: %w\", err)\n\t\t\t}\n\t\t\tpublisher, err := makePublisher(po)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"error creating publisher: %w\", err)\n\t\t\t}\n\t\t\tdefer publisher.Close()\n\n\t\t\t// Issue a \"kubectl apply\" command reading from stdin,\n\t\t\t// to which we will pipe the resolved files, and any\n\t\t\t// remaining flags passed after '--'.\n\t\t\tkubectlCmd := exec.CommandContext(ctx, \"kubectl\", append([]string{\"apply\", \"-f\", \"-\"}, args...)...) //nolint:gosec\n\n\t\t\t// Pass through our environment\n\t\t\tkubectlCmd.Env = os.Environ()\n\t\t\t// Pass through our std{out,err} and make our resolved buffer stdin.\n\t\t\tkubectlCmd.Stderr = os.Stderr\n\t\t\tkubectlCmd.Stdout = os.Stdout\n\n\t\t\t// Wire up kubectl stdin to resolveFilesToWriter.\n\t\t\tstdin, err := kubectlCmd.StdinPipe()\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"error piping to 'kubectl apply': %w\", err)\n\t\t\t}\n\n\t\t\t// Make sure builds are cancelled if kubectl apply fails.\n\t\t\tg, ctx := errgroup.WithContext(ctx)\n\t\t\tg.Go(func() error {\n\t\t\t\t// kubectl buffers data before starting to apply it, which\n\t\t\t\t// can lead to resources being created more slowly than desired.\n\t\t\t\t// In the case of --watch, it can lead to resources not being\n\t\t\t\t// applied at all until enough iteration has occurred.  To work\n\t\t\t\t// around this, we prime the stream with a bunch of empty objects\n\t\t\t\t// which kubectl will discard.\n\t\t\t\t// See https://github.com/google/go-containerregistry/pull/348\n\t\t\t\tfor range 1000 {\n\t\t\t\t\tstdin.Write([]byte(\"---\\n\"))\n\t\t\t\t}\n\t\t\t\t// Once primed kick things off.\n\t\t\t\treturn ResolveFilesToWriter(ctx, builder, publisher, fo, so, stdin)\n\t\t\t})\n\n\t\t\tg.Go(func() error {\n\t\t\t\t// Run it.\n\t\t\t\tif err := kubectlCmd.Run(); err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"error executing 'kubectl apply': %w\", err)\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t})\n\n\t\t\treturn g.Wait()\n\t\t},\n\t}\n\toptions.AddPublishArg(apply, po)\n\toptions.AddFileArg(apply, fo)\n\toptions.AddSelectorArg(apply, so)\n\toptions.AddBuildOptions(apply, bo)\n\n\ttopLevel.AddCommand(apply)\n}\n"
  },
  {
    "path": "pkg/commands/build.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage commands\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/google/ko/pkg/commands/options\"\n\t\"github.com/spf13/cobra\"\n)\n\n// addBuild augments our CLI surface with build.\nfunc addBuild(topLevel *cobra.Command) {\n\tpo := &options.PublishOptions{}\n\tbo := &options.BuildOptions{}\n\n\tbuild := &cobra.Command{\n\t\tUse:     \"build IMPORTPATH...\",\n\t\tShort:   \"Build and publish container images from the given importpaths.\",\n\t\tLong:    `This sub-command builds the provided import paths into Go binaries, containerizes them, and publishes them.`,\n\t\tAliases: []string{\"publish\"},\n\t\tExample: `\n  # Build and publish import path references to a Docker Registry as:\n  #   ${KO_DOCKER_REPO}/<package name>-<hash of import path>\n  # When KO_DOCKER_REPO is ko.local, it is the same as if --local and\n  # --preserve-import-paths were passed.\n  # If the import path is not provided, the current working directory is the\n  # default.\n  ko build github.com/foo/bar/cmd/baz github.com/foo/bar/cmd/blah\n\n  # Build and publish a relative import path as:\n  #   ${KO_DOCKER_REPO}/<package name>-<hash of import path>\n  # When KO_DOCKER_REPO is ko.local, it is the same as if --local and\n  # --preserve-import-paths were passed.\n  ko build ./cmd/blah\n\n  # Build and publish a relative import path as:\n  #   ${KO_DOCKER_REPO}/<import path>\n  # When KO_DOCKER_REPO is ko.local, it is the same as if --local was passed.\n  ko build --preserve-import-paths ./cmd/blah\n\n  # Build and publish import path references to a Docker daemon as:\n  #   ko.local/<import path>\n  # This always preserves import paths.\n  ko build --local github.com/foo/bar/cmd/baz github.com/foo/bar/cmd/blah`,\n\t\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\tif err := options.Validate(po, bo); err != nil {\n\t\t\t\treturn fmt.Errorf(\"validating options: %w\", err)\n\t\t\t}\n\n\t\t\tif len(args) == 0 {\n\t\t\t\t// Build the current directory by default.\n\t\t\t\targs = []string{\".\"}\n\t\t\t}\n\n\t\t\tctx := cmd.Context()\n\n\t\t\tbo.InsecureRegistry = po.InsecureRegistry\n\t\t\tbuilder, err := makeBuilder(ctx, bo)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"error creating builder: %w\", err)\n\t\t\t}\n\t\t\tpublisher, err := makePublisher(po)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"error creating publisher: %w\", err)\n\t\t\t}\n\t\t\tdefer publisher.Close()\n\t\t\timages, err := publishImages(ctx, args, publisher, builder)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to publish images: %w\", err)\n\t\t\t}\n\t\t\tfor _, img := range images {\n\t\t\t\tfmt.Println(img)\n\t\t\t}\n\t\t\treturn nil\n\t\t},\n\t}\n\toptions.AddPublishArg(build, po)\n\toptions.AddBuildOptions(build, bo)\n\ttopLevel.AddCommand(build)\n}\n"
  },
  {
    "path": "pkg/commands/cache.go",
    "content": "// Copyright 2023 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage commands\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"sync\"\n\n\t\"github.com/google/go-containerregistry/pkg/logs\"\n\t\"github.com/google/go-containerregistry/pkg/name\"\n\tv1 \"github.com/google/go-containerregistry/pkg/v1\"\n\t\"github.com/google/go-containerregistry/pkg/v1/empty\"\n\t\"github.com/google/go-containerregistry/pkg/v1/layout\"\n\t\"github.com/google/go-containerregistry/pkg/v1/match\"\n\t\"github.com/google/go-containerregistry/pkg/v1/partial\"\n\t\"github.com/google/go-containerregistry/pkg/v1/remote\"\n\t\"github.com/google/go-containerregistry/pkg/v1/types\"\n\t\"github.com/google/ko/pkg/build\"\n)\n\ntype imageCache struct {\n\t// In memory\n\tcache sync.Map\n\n\t// On disk\n\tmu sync.Mutex\n\tp  *layout.Path\n\n\t// Over the network\n\tpuller *remote.Puller\n}\n\nfunc newCache(puller *remote.Puller) (*imageCache, error) {\n\tcache := &imageCache{\n\t\tpuller: puller,\n\t}\n\tif kc := os.Getenv(\"KOCACHE\"); kc != \"\" {\n\t\tpath := filepath.Join(kc, \"img\")\n\t\tp, err := layout.FromPath(path)\n\t\tif err != nil {\n\t\t\tp, err = layout.Write(path, empty.Index)\n\t\t\tif err != nil {\n\t\t\t\treturn cache, err\n\t\t\t}\n\t\t}\n\t\tcache.p = &p\n\t}\n\n\treturn cache, nil\n}\n\nfunc (i *imageCache) get(ctx context.Context, ref name.Reference, missFunc baseFactory) (build.Result, error) {\n\tif v, ok := i.cache.Load(ref.String()); ok {\n\t\tlogs.Debug.Printf(\"cache hit: %s\", ref.String())\n\n\t\treturn v.(build.Result), nil\n\t}\n\n\tvar (\n\t\tonce       sync.Once\n\t\tmissResult build.Result\n\t\tmissErr    error\n\t)\n\tmiss := func(ctx context.Context, ref name.Reference) (build.Result, error) {\n\t\tonce.Do(func() {\n\t\t\tmissResult, missErr = missFunc(ctx, ref)\n\t\t})\n\n\t\treturn missResult, missErr\n\t}\n\n\tif i.p != nil {\n\t\tkey := \"\"\n\t\tif _, ok := ref.(name.Digest); ok {\n\t\t\tkey = ref.Identifier()\n\t\t} else {\n\t\t\tlogs.Debug.Printf(\"cache miss due to tag: %s\", ref.String())\n\t\t\tresult, err := miss(ctx, ref)\n\t\t\tif err != nil {\n\t\t\t\treturn result, err\n\t\t\t}\n\n\t\t\tdig, err := result.Digest()\n\t\t\tif err != nil {\n\t\t\t\treturn result, err\n\t\t\t}\n\n\t\t\tkey = dig.String()\n\t\t}\n\n\t\t// Use a pretty broad lock on the on-disk cache to avoid races.\n\t\ti.mu.Lock()\n\t\tdefer i.mu.Unlock()\n\n\t\tii, err := i.p.ImageIndex()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"loading cache index: %w\", err)\n\t\t}\n\n\t\th, err := v1.NewHash(key)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tdescs, err := partial.FindManifests(ii, match.Digests(h))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif len(descs) != 0 {\n\t\t\tlogs.Debug.Printf(\"cache hit: %s\", ref.String())\n\t\t\tdesc := descs[0]\n\n\t\t\tvar br build.Result\n\t\t\tif desc.MediaType.IsIndex() {\n\t\t\t\tidx, err := ii.ImageIndex(h)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tbr, err = i.newLazyIndex(ref, idx, missFunc)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\timg, err := ii.Image(h)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tbr, err = i.newLazyImage(ref, img, missFunc)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\t\t\ti.cache.Store(ref.String(), br)\n\t\t\treturn br, nil\n\t\t}\n\t}\n\n\tlogs.Debug.Printf(\"cache miss: %s\", ref.String())\n\tresult, err := miss(ctx, ref)\n\tif err != nil {\n\t\treturn result, err\n\t}\n\n\tif i.p != nil {\n\t\tlogs.Debug.Printf(\"cache store: %s\", ref.String())\n\n\t\tdesc, err := partial.Descriptor(result)\n\t\tif err != nil {\n\t\t\treturn result, err\n\t\t}\n\n\t\tmanifest, err := result.RawManifest()\n\t\tif err != nil {\n\t\t\treturn result, err\n\t\t}\n\n\t\tif err := i.p.WriteBlob(desc.Digest, io.NopCloser(bytes.NewReader(manifest))); err != nil {\n\t\t\treturn result, err\n\t\t}\n\n\t\tif _, ok := result.(v1.ImageIndex); ok {\n\t\t\tresult = &lazyIndex{\n\t\t\t\tref:      ref,\n\t\t\t\tdesc:     *desc,\n\t\t\t\tmanifest: manifest,\n\t\t\t\tmiss:     missFunc,\n\t\t\t\tcache:    i,\n\t\t\t}\n\t\t} else if img, ok := result.(v1.Image); ok {\n\t\t\tcf, err := img.RawConfigFile()\n\t\t\tif err != nil {\n\t\t\t\treturn result, err\n\t\t\t}\n\n\t\t\tid, err := img.ConfigName()\n\t\t\tif err != nil {\n\t\t\t\treturn result, err\n\t\t\t}\n\n\t\t\tif err := i.p.WriteBlob(id, io.NopCloser(bytes.NewReader(cf))); err != nil {\n\t\t\t\treturn result, err\n\t\t\t}\n\n\t\t\tresult = &lazyImage{\n\t\t\t\tref:      ref,\n\t\t\t\tdesc:     *desc,\n\t\t\t\tmanifest: manifest,\n\t\t\t\tconfig:   cf,\n\t\t\t\tid:       id,\n\t\t\t\tmiss:     missFunc,\n\t\t\t\tcache:    i,\n\t\t\t}\n\t\t}\n\n\t\tif err := i.p.AppendDescriptor(*desc); err != nil {\n\t\t\treturn result, err\n\t\t}\n\t}\n\n\ti.cache.Store(ref.String(), result)\n\treturn result, nil\n}\n\nfunc (i *imageCache) newLazyIndex(ref name.Reference, idx v1.ImageIndex, missFunc baseFactory) (*lazyIndex, error) {\n\tdesc, err := partial.Descriptor(idx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tmanifest, err := idx.RawManifest()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &lazyIndex{\n\t\tref:      ref,\n\t\tdesc:     *desc,\n\t\tmanifest: manifest,\n\t\tmiss:     missFunc,\n\t\tcache:    i,\n\t}, nil\n}\n\nfunc (i *imageCache) newLazyImage(ref name.Reference, img v1.Image, missFunc baseFactory) (*lazyImage, error) {\n\tdesc, err := partial.Descriptor(img)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tmanifest, err := img.RawManifest()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tcf, err := img.RawConfigFile()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tid, err := img.ConfigName()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &lazyImage{\n\t\tref:      ref,\n\t\tdesc:     *desc,\n\t\tmanifest: manifest,\n\t\tconfig:   cf,\n\t\tid:       id,\n\t\tmiss:     missFunc,\n\t\tcache:    i,\n\t}, nil\n}\n\ntype lazyIndex struct {\n\tref      name.Reference\n\tdesc     v1.Descriptor\n\tmanifest []byte\n\n\tmiss  baseFactory\n\tcache *imageCache\n}\n\nfunc (i *lazyIndex) MediaType() (types.MediaType, error) {\n\treturn i.desc.MediaType, nil\n}\n\nfunc (i *lazyIndex) Digest() (v1.Hash, error) {\n\treturn i.desc.Digest, nil\n}\n\nfunc (i *lazyIndex) Size() (int64, error) {\n\treturn i.desc.Size, nil\n}\n\nfunc (i *lazyIndex) IndexManifest() (*v1.IndexManifest, error) {\n\treturn v1.ParseIndexManifest(bytes.NewReader(i.manifest))\n}\n\nfunc (i *lazyIndex) RawManifest() ([]byte, error) {\n\treturn i.manifest, nil\n}\n\nfunc (i *lazyIndex) Image(h v1.Hash) (v1.Image, error) {\n\tbr, err := i.cache.get(context.TODO(), i.ref.Context().Digest(h.String()), i.miss)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"Image(%q): %w\", h.String(), err)\n\t}\n\n\timg, ok := br.(v1.Image)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"Image(%q) is a type %T\", h.String(), br)\n\t}\n\n\treturn img, nil\n}\n\nfunc (i *lazyIndex) ImageIndex(h v1.Hash) (v1.ImageIndex, error) {\n\tbr, err := i.cache.get(context.TODO(), i.ref.Context().Digest(h.String()), i.miss)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tii, ok := br.(v1.ImageIndex)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"ImageIndex(%q) is a type %T\", h.String(), br)\n\t}\n\n\treturn ii, nil\n}\n\ntype lazyImage struct {\n\tref      name.Reference\n\tdesc     v1.Descriptor\n\tmanifest []byte\n\tconfig   []byte\n\tid       v1.Hash\n\n\tmiss  baseFactory\n\tcache *imageCache\n}\n\n// Layers returns the ordered collection of filesystem layers that comprise this image.\n// The order of the list is oldest/base layer first, and most-recent/top layer last.\nfunc (i *lazyImage) Layers() ([]v1.Layer, error) {\n\tm, err := i.Manifest()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlayers := make([]v1.Layer, 0, len(m.Layers))\n\tfor _, desc := range m.Layers {\n\t\tdiffid, err := partial.BlobToDiffID(i, desc.Digest)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlayers = append(layers, &lazyLayer{\n\t\t\tref:    i.ref.Context().Digest(desc.Digest.String()),\n\t\t\tdesc:   desc,\n\t\t\tdiffid: diffid,\n\t\t\tmiss:   i.miss,\n\t\t\tcache:  i.cache,\n\t\t})\n\t}\n\n\treturn layers, nil\n}\n\nfunc (i *lazyImage) MediaType() (types.MediaType, error) {\n\treturn i.desc.MediaType, nil\n}\n\nfunc (i *lazyImage) Digest() (v1.Hash, error) {\n\treturn i.desc.Digest, nil\n}\n\nfunc (i *lazyImage) Size() (int64, error) {\n\treturn i.desc.Size, nil\n}\n\nfunc (i *lazyImage) ConfigName() (v1.Hash, error) {\n\treturn i.id, nil\n}\n\nfunc (i *lazyImage) ConfigFile() (*v1.ConfigFile, error) {\n\treturn v1.ParseConfigFile(bytes.NewReader(i.config))\n}\n\nfunc (i *lazyImage) RawConfigFile() ([]byte, error) {\n\treturn i.config, nil\n}\n\nfunc (i *lazyImage) Manifest() (*v1.Manifest, error) {\n\treturn v1.ParseManifest(bytes.NewReader(i.manifest))\n}\n\nfunc (i *lazyImage) RawManifest() ([]byte, error) {\n\treturn i.manifest, nil\n}\n\nfunc (i *lazyImage) LayerByDigest(h v1.Hash) (v1.Layer, error) {\n\tif h == i.id {\n\t\treturn partial.ConfigLayer(i)\n\t}\n\n\tlayers, err := i.Layers()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfor _, layer := range layers {\n\t\tdigest, err := layer.Digest()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif digest == h {\n\t\t\treturn layer, nil\n\t\t}\n\t}\n\n\treturn nil, fmt.Errorf(\"could not find layer %q in lazyImage %q\", h.String(), i.ref)\n}\n\nfunc (i *lazyImage) LayerByDiffID(h v1.Hash) (v1.Layer, error) {\n\td, err := partial.DiffIDToBlob(i, h)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn i.LayerByDigest(d)\n}\n\ntype lazyLayer struct {\n\tref    name.Digest\n\tdesc   v1.Descriptor\n\tdiffid v1.Hash\n\n\tmiss  baseFactory\n\tcache *imageCache\n}\n\nfunc (l *lazyLayer) Digest() (v1.Hash, error) {\n\treturn l.desc.Digest, nil\n}\n\nfunc (l *lazyLayer) DiffID() (v1.Hash, error) {\n\treturn l.diffid, nil\n}\n\nfunc (l *lazyLayer) Size() (int64, error) {\n\treturn l.desc.Size, nil\n}\n\nfunc (l *lazyLayer) MediaType() (types.MediaType, error) {\n\treturn l.desc.MediaType, nil\n}\n\nfunc (l *lazyLayer) Compressed() (io.ReadCloser, error) {\n\tif rc, err := l.cache.p.Blob(l.desc.Digest); err == nil {\n\t\treturn rc, nil\n\t}\n\n\trl, err := l.cache.puller.Layer(context.TODO(), l.ref)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Note that we intentionally don't cache this because it will slow down cases where registry has it.\n\treturn rl.Compressed()\n}\n\nfunc (l *lazyLayer) Uncompressed() (io.ReadCloser, error) {\n\tcl, err := partial.CompressedToLayer(l)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn cl.Uncompressed()\n}\n"
  },
  {
    "path": "pkg/commands/commands.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage commands\n\nimport (\n\t\"os/exec\"\n\n\t\"github.com/spf13/cobra\"\n)\n\n// AddKubeCommands augments our CLI surface with a passthru delete command, and an apply\n// command that realizes the promise of ko, as outlined here:\n//\n//\thttps://github.com/google/go-containerregistry/issues/80\nfunc AddKubeCommands(topLevel *cobra.Command) {\n\taddDelete(topLevel)\n\taddVersion(topLevel)\n\taddCreate(topLevel)\n\taddApply(topLevel)\n\taddResolve(topLevel)\n\taddBuild(topLevel)\n\taddRun(topLevel)\n}\n\n// check if kubectl is installed\nfunc isKubectlAvailable() bool {\n\tif _, err := exec.LookPath(\"kubectl\"); err != nil {\n\t\treturn false\n\t}\n\treturn true\n}\n"
  },
  {
    "path": "pkg/commands/config.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage commands\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\tecr \"github.com/awslabs/amazon-ecr-credential-helper/ecr-login\"\n\t\"github.com/chrismellard/docker-credential-acr-env/pkg/credhelper\"\n\t\"github.com/google/go-containerregistry/pkg/authn\"\n\t\"github.com/google/go-containerregistry/pkg/authn/github\"\n\t\"github.com/google/go-containerregistry/pkg/name\"\n\tv1 \"github.com/google/go-containerregistry/pkg/v1\"\n\t\"github.com/google/go-containerregistry/pkg/v1/daemon\"\n\t\"github.com/google/go-containerregistry/pkg/v1/google\"\n\t\"github.com/google/go-containerregistry/pkg/v1/remote\"\n\n\t\"github.com/google/ko/pkg/build\"\n\t\"github.com/google/ko/pkg/commands/options\"\n\t\"github.com/google/ko/pkg/publish\"\n)\n\nvar (\n\tamazonKeychain authn.Keychain = authn.NewKeychainFromHelper(ecr.NewECRHelper(ecr.WithLogger(io.Discard)))\n\tazureKeychain  authn.Keychain = authn.NewKeychainFromHelper(credhelper.NewACRCredentialsHelper())\n\tkeychain                      = authn.NewMultiKeychain(\n\t\tamazonKeychain,\n\t\tauthn.DefaultKeychain,\n\t\tgoogle.Keychain,\n\t\tgithub.Keychain,\n\t\tazureKeychain,\n\t)\n)\n\n// getBaseImage returns a function that determines the base image for a given import path.\nfunc getBaseImage(bo *options.BuildOptions) build.GetBase {\n\tuserAgent := ua()\n\tif bo.UserAgent != \"\" {\n\t\tuserAgent = bo.UserAgent\n\t}\n\n\tropt := []remote.Option{\n\t\tremote.WithAuthFromKeychain(keychain),\n\t\tremote.WithUserAgent(userAgent),\n\t}\n\tpuller, err := remote.NewPuller(ropt...)\n\tif err != nil {\n\t\t// This can't really happen.\n\t\tpanic(err)\n\t}\n\tropt = append(ropt, remote.Reuse(puller))\n\n\tcache, err := newCache(puller)\n\tif err != nil {\n\t\tlog.Printf(\"Image cache init failed: %v\", err)\n\t}\n\n\tfetch := func(ctx context.Context, ref name.Reference) (build.Result, error) {\n\t\tropt = append(ropt, remote.WithContext(ctx))\n\n\t\tdesc, err := remote.Get(ref, ropt...)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif desc.MediaType.IsIndex() {\n\t\t\treturn desc.ImageIndex()\n\t\t}\n\t\treturn desc.Image()\n\t}\n\n\treturn func(ctx context.Context, s string) (name.Reference, build.Result, error) {\n\t\ts = strings.TrimPrefix(s, build.StrictScheme)\n\t\t// Viper configuration file keys are case insensitive, and are\n\t\t// returned as all lowercase.  This means that import paths with\n\t\t// uppercase must be normalized for matching here, e.g.\n\t\t//    github.com/GoogleCloudPlatform/foo/cmd/bar\n\t\t// comes through as:\n\t\t//    github.com/googlecloudplatform/foo/cmd/bar\n\t\tbaseImage, ok := bo.BaseImageOverrides[strings.ToLower(s)]\n\t\tif !ok || baseImage == \"\" {\n\t\t\tbaseImage = bo.BaseImage\n\t\t}\n\t\tvar nameOpts []name.Option\n\t\tif bo.InsecureRegistry {\n\t\t\tnameOpts = append(nameOpts, name.Insecure)\n\t\t}\n\t\tref, err := name.ParseReference(baseImage, nameOpts...)\n\t\tif err != nil {\n\t\t\treturn nil, nil, fmt.Errorf(\"parsing base image (%q): %w\", baseImage, err)\n\t\t}\n\n\t\tvar result build.Result\n\n\t\t// For ko.local, look in the daemon.\n\t\tif ref.Context().RegistryStr() == publish.LocalDomain {\n\t\t\tresult, err = daemon.Image(ref)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, nil, fmt.Errorf(\"loading %s from daemon: %w\", ref, err)\n\t\t\t}\n\t\t} else {\n\t\t\tresult, err = cache.get(ctx, ref, fetch)\n\t\t\tif err != nil {\n\t\t\t\t// We don't expect this to fail, usually, but the cache should also not be fatal.\n\t\t\t\t// Log it so people can complain about it and we can fix the cache.\n\t\t\t\tlog.Printf(\"cache.get(%q) failed with %v\", ref.String(), err)\n\n\t\t\t\tresult, err = fetch(ctx, ref)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, nil, fmt.Errorf(\"pulling %s: %w\", ref, err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif _, ok := ref.(name.Digest); ok {\n\t\t\tlog.Printf(\"Using base %s for %s\", ref, s)\n\t\t} else {\n\t\t\tdig, err := result.Digest()\n\t\t\tif err != nil {\n\t\t\t\treturn ref, result, err\n\t\t\t}\n\t\t\tlog.Printf(\"Using base %s@%s for %s\", ref, dig, s)\n\t\t}\n\n\t\treturn ref, result, nil\n\t}\n}\n\nfunc getTimeFromEnv(env string) (*v1.Time, error) {\n\tepoch := os.Getenv(env)\n\tif epoch == \"\" {\n\t\treturn nil, nil\n\t}\n\n\tseconds, err := strconv.ParseInt(epoch, 10, 64)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"the environment variable %s should be the number of seconds since January 1st 1970, 00:00 UTC, got: %w\", env, err)\n\t}\n\treturn &v1.Time{Time: time.Unix(seconds, 0)}, nil\n}\n\nfunc getCreationTime() (*v1.Time, error) {\n\treturn getTimeFromEnv(\"SOURCE_DATE_EPOCH\")\n}\n\nfunc getKoDataCreationTime() (*v1.Time, error) {\n\treturn getTimeFromEnv(\"KO_DATA_DATE_EPOCH\")\n}\n\ntype baseFactory func(context.Context, name.Reference) (build.Result, error)\n"
  },
  {
    "path": "pkg/commands/config_test.go",
    "content": "// Copyright 2021 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage commands\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/google/go-containerregistry/pkg/crane\"\n\n\t\"github.com/google/ko/pkg/commands/options\"\n)\n\nfunc TestOverrideDefaultBaseImageUsingBuildOption(t *testing.T) {\n\tnamespace := \"base\"\n\ts, err := registryServerWithImage(namespace)\n\tif err != nil {\n\t\tt.Fatalf(\"could not create test registry server: %v\", err)\n\t}\n\tdefer s.Close()\n\tbaseImage := fmt.Sprintf(\"%s/%s\", s.Listener.Addr().String(), namespace)\n\twantDigest, err := crane.Digest(baseImage)\n\tif err != nil {\n\t\tt.Fatalf(\"crane.Digest(%s): %v\", baseImage, err)\n\t}\n\twantImage := fmt.Sprintf(\"%s@%s\", baseImage, wantDigest)\n\tbo := &options.BuildOptions{\n\t\tBaseImage: wantImage,\n\t\tPlatforms: []string{\"all\"},\n\t}\n\n\tbaseFn := getBaseImage(bo)\n\t_, res, err := baseFn(context.Background(), \"ko://example.com/helloworld\")\n\tif err != nil {\n\t\tt.Fatalf(\"getBaseImage(): %v\", err)\n\t}\n\n\tdigest, err := res.Digest()\n\tif err != nil {\n\t\tt.Fatalf(\"res.Digest(): %v\", err)\n\t}\n\tgotDigest := digest.String()\n\tif gotDigest != wantDigest {\n\t\tt.Errorf(\"got digest %s, wanted %s\", gotDigest, wantDigest)\n\t}\n}\n"
  },
  {
    "path": "pkg/commands/create.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage commands\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\n\t\"github.com/google/ko/pkg/commands/options\"\n\t\"github.com/spf13/cobra\"\n\t\"golang.org/x/sync/errgroup\"\n)\n\n// addCreate augments our CLI surface with apply.\nfunc addCreate(topLevel *cobra.Command) {\n\tpo := &options.PublishOptions{}\n\tfo := &options.FilenameOptions{}\n\tso := &options.SelectorOptions{}\n\tbo := &options.BuildOptions{}\n\tcreate := &cobra.Command{\n\t\tUse:   \"create -f FILENAME\",\n\t\tShort: \"Create the input files with image references resolved to built/pushed image digests.\",\n\t\tLong:  `This sub-command finds import path references within the provided files, builds them into Go binaries, containerizes them, publishes them, and then feeds the resulting yaml into \"kubectl create\".`,\n\t\tExample: `\n  # Build and publish import path references to a Docker\n  # Registry as:\n  #   ${KO_DOCKER_REPO}/<package name>-<hash of import path>\n  # Then, feed the resulting yaml into \"kubectl create\".\n  # When KO_DOCKER_REPO is ko.local, it is the same as if\n  # --local was passed.\n  ko create -f config/\n\n  # Build and publish import path references to a Docker\n  # Registry preserving import path names as:\n  #   ${KO_DOCKER_REPO}/<import path>\n  # Then, feed the resulting yaml into \"kubectl create\".\n  ko create --preserve-import-paths -f config/\n\n  # Build and publish import path references to a Docker\n  # daemon as:\n  #   ko.local/<import path>\n  # Then, feed the resulting yaml into \"kubectl create\".\n  ko create --local -f config/\n\n  # Create from stdin:\n  cat config.yaml | ko create -f -\n\n  # Any flags passed after '--' are passed to 'kubectl apply' directly:\n  ko apply -f config -- --namespace=foo --kubeconfig=cfg.yaml\n`,\n\t\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\tif err := options.Validate(po, bo); err != nil {\n\t\t\t\treturn fmt.Errorf(\"validating options: %w\", err)\n\t\t\t}\n\n\t\t\tif !isKubectlAvailable() {\n\t\t\t\treturn errors.New(\"error: kubectl is not available. kubectl must be installed to use ko create\")\n\t\t\t}\n\t\t\tctx := cmd.Context()\n\n\t\t\tbo.InsecureRegistry = po.InsecureRegistry\n\t\t\tbuilder, err := makeBuilder(ctx, bo)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"error creating builder: %w\", err)\n\t\t\t}\n\t\t\tpublisher, err := makePublisher(po)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"error creating publisher: %w\", err)\n\t\t\t}\n\t\t\tdefer publisher.Close()\n\n\t\t\t// Issue a \"kubectl create\" command reading from stdin,\n\t\t\t// to which we will pipe the resolved files, and any\n\t\t\t// remaining flags passed after '--'.\n\t\t\tkubectlCmd := exec.CommandContext(ctx, \"kubectl\", append([]string{\"create\", \"-f\", \"-\"}, args...)...) //nolint:gosec\n\n\t\t\t// Pass through our environment\n\t\t\tkubectlCmd.Env = os.Environ()\n\t\t\t// Pass through our std{out,err} and make our resolved buffer stdin.\n\t\t\tkubectlCmd.Stderr = os.Stderr\n\t\t\tkubectlCmd.Stdout = os.Stdout\n\n\t\t\t// Wire up kubectl stdin to resolveFilesToWriter.\n\t\t\tstdin, err := kubectlCmd.StdinPipe()\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"error piping to 'kubectl create': %w\", err)\n\t\t\t}\n\n\t\t\t// Make sure builds are cancelled if kubectl create fails.\n\t\t\tg, ctx := errgroup.WithContext(ctx)\n\t\t\tg.Go(func() error {\n\t\t\t\t// kubectl buffers data before starting to create it, which\n\t\t\t\t// can lead to resources being created more slowly than desired.\n\t\t\t\t// In the case of --watch, it can lead to resources not being\n\t\t\t\t// applied at all until enough iteration has occurred.  To work\n\t\t\t\t// around this, we prime the stream with a bunch of empty objects\n\t\t\t\t// which kubectl will discard.\n\t\t\t\t// See https://github.com/google/go-containerregistry/pull/348\n\t\t\t\tfor range 1000 {\n\t\t\t\t\tstdin.Write([]byte(\"---\\n\"))\n\t\t\t\t}\n\t\t\t\t// Once primed kick things off.\n\t\t\t\treturn ResolveFilesToWriter(ctx, builder, publisher, fo, so, stdin)\n\t\t\t})\n\n\t\t\tg.Go(func() error {\n\t\t\t\t// Run it.\n\t\t\t\tif err := kubectlCmd.Run(); err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"error executing 'kubectl create': %w\", err)\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t})\n\n\t\t\treturn g.Wait()\n\t\t},\n\t}\n\toptions.AddPublishArg(create, po)\n\toptions.AddFileArg(create, fo)\n\toptions.AddSelectorArg(create, so)\n\toptions.AddBuildOptions(create, bo)\n\n\ttopLevel.AddCommand(create)\n}\n"
  },
  {
    "path": "pkg/commands/delete.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage commands\n\nimport (\n\t\"errors\"\n\t\"os\"\n\t\"os/exec\"\n\n\t\"github.com/spf13/cobra\"\n)\n\n// runCmd is suitable for use with cobra.Command's Run field.\ntype runCmd func(*cobra.Command, []string) error\n\n// passthru returns a runCmd that simply passes our CLI arguments\n// through to a binary named command.\nfunc passthru(command string) runCmd {\n\treturn func(cmd *cobra.Command, _ []string) error {\n\t\tif !isKubectlAvailable() {\n\t\t\treturn errors.New(\"error: kubectl is not available. kubectl must be installed to use ko delete\")\n\t\t}\n\t\tctx := cmd.Context()\n\n\t\t// Start building a command line invocation by passing\n\t\t// through our arguments to command's CLI.\n\t\t//nolint:gosec // We actively want to pass arguments through, so this is fine.\n\t\tecmd := exec.CommandContext(ctx, command, os.Args[1:]...)\n\n\t\t// Pass through our environment\n\t\tecmd.Env = os.Environ()\n\t\t// Pass through our stdfoo\n\t\tecmd.Stderr = os.Stderr\n\t\tecmd.Stdout = os.Stdout\n\t\tecmd.Stdin = os.Stdin\n\n\t\t// Run it.\n\t\treturn ecmd.Run()\n\t}\n}\n\n// addDelete augments our CLI surface with publish.\nfunc addDelete(topLevel *cobra.Command) {\n\ttopLevel.AddCommand(&cobra.Command{\n\t\tUse:   \"delete\",\n\t\tShort: `See \"kubectl help delete\" for detailed usage.`,\n\t\tRunE:  passthru(\"kubectl\"),\n\t\t// We ignore unknown flags to avoid importing everything Go exposes\n\t\t// from our commands.\n\t\tFParseErrWhitelist: cobra.FParseErrWhitelist{\n\t\t\tUnknownFlags: true,\n\t\t},\n\t})\n}\n"
  },
  {
    "path": "pkg/commands/options/build.go",
    "content": "// Copyright 2019 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage options\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"reflect\"\n\n\t\"github.com/go-viper/mapstructure/v2\"\n\t\"github.com/google/go-containerregistry/pkg/name\"\n\t\"github.com/google/ko/pkg/build\"\n\t\"github.com/spf13/cobra\"\n\t\"github.com/spf13/viper\"\n\t\"golang.org/x/tools/go/packages\"\n)\n\nconst (\n\t// configDefaultBaseImage is the default base image if not specified in .ko.yaml.\n\tconfigDefaultBaseImage = \"cgr.dev/chainguard/static:latest\"\n)\n\n// BuildOptions represents options for the ko builder.\ntype BuildOptions struct {\n\t// BaseImage enables setting the default base image programmatically.\n\t// If non-empty, this takes precedence over the value in `.ko.yaml`.\n\tBaseImage string\n\n\t// BaseImageOverrides stores base image overrides for import paths.\n\tBaseImageOverrides map[string]string\n\n\t// DefaultPlatforms defines the default platforms when Platforms is not explicitly defined\n\tDefaultPlatforms []string\n\n\t// DefaultEnv defines the default environment when per-build value is not explicitly defined.\n\tDefaultEnv []string\n\n\t// DefaultFlags defines the default flags when per-build value is not explicitly defined.\n\tDefaultFlags []string\n\n\t// DefaultLdflags defines the default ldflags when per-build value is not explicitly defined.\n\tDefaultLdflags []string\n\n\t// WorkingDirectory allows for setting the working directory for invocations of the `go` tool.\n\t// Empty string means the current working directory.\n\tWorkingDirectory string\n\n\tConcurrentBuilds     int\n\tDisableOptimizations bool\n\tSBOM                 string\n\tSBOMDir              string\n\tPlatforms            []string\n\tLabels               []string\n\tAnnotations          []string\n\tUser                 string\n\tDebug                bool\n\t// UserAgent enables overriding the default value of the `User-Agent` HTTP\n\t// request header used when retrieving the base image.\n\tUserAgent string\n\n\tInsecureRegistry bool\n\n\t// Trimpath controls whether ko adds the `-trimpath` flag to `go build` by default.\n\t// The `-trimpath` flags aids in achieving reproducible builds, but it removes path information that is useful for interactive debugging.\n\t// Set this field to `false` and `DisableOptimizations` to `true` if you want to interactively debug the binary in the resulting image.\n\t// `AddBuildOptions()` defaults this field to `true`.\n\tTrimpath bool\n\n\t// BuildConfigs stores the per-image build config from `.ko.yaml`.\n\tBuildConfigs map[string]build.Config\n}\n\nfunc AddBuildOptions(cmd *cobra.Command, bo *BuildOptions) {\n\tcmd.Flags().IntVarP(&bo.ConcurrentBuilds, \"jobs\", \"j\", 0,\n\t\t\"The maximum number of concurrent builds (default GOMAXPROCS)\")\n\tcmd.Flags().BoolVar(&bo.DisableOptimizations, \"disable-optimizations\", bo.DisableOptimizations,\n\t\t\"Disable optimizations when building Go code. Useful when you want to interactively debug the created container.\")\n\tcmd.Flags().StringVar(&bo.SBOM, \"sbom\", \"spdx\",\n\t\t\"The SBOM media type to use (none will disable SBOM synthesis and upload).\")\n\tcmd.Flags().StringVar(&bo.SBOMDir, \"sbom-dir\", \"\",\n\t\t\"Path to file where the SBOM will be written.\")\n\tcmd.Flags().StringSliceVar(&bo.Platforms, \"platform\", []string{},\n\t\t\"Which platform to use when pulling a multi-platform base. Format: all | <os>[/<arch>[/<variant>]][,platform]*\")\n\tcmd.Flags().StringSliceVar(&bo.Labels, \"image-label\", []string{},\n\t\t\"Which labels (key=value[,key=value]) to add to the image.\")\n\tcmd.Flags().StringSliceVar(&bo.Annotations, \"image-annotation\", []string{},\n\t\t\"Which annotations (key=value[,key=value]) to add to the OCI manifest.\")\n\tcmd.Flags().StringVar(&bo.User, \"image-user\", \"\",\n\t\t\"The default user the image should be run as.\")\n\tcmd.Flags().BoolVar(&bo.Debug, \"debug\", bo.Debug,\n\t\t\"Include Delve debugger into image and wrap around ko-app. This debugger will listen to port 40000.\")\n\tbo.Trimpath = true\n}\n\n// LoadConfig reads build configuration from defaults, environment variables, and the `.ko.yaml` config file.\nfunc (bo *BuildOptions) LoadConfig() error {\n\tv := viper.New()\n\tif bo.WorkingDirectory == \"\" {\n\t\tbo.WorkingDirectory = \".\"\n\t}\n\t// If omitted, use this base image.\n\tv.SetDefault(\"defaultBaseImage\", configDefaultBaseImage)\n\tconst configName = \".ko\"\n\n\tv.SetConfigName(configName) // .yaml is implicit\n\tv.SetEnvPrefix(\"KO\")\n\tv.AutomaticEnv()\n\n\tif override := os.Getenv(\"KO_CONFIG_PATH\"); override != \"\" {\n\t\tfile, err := os.Stat(override)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"error looking for config file: %w\", err)\n\t\t}\n\t\tif file.Mode().IsRegular() {\n\t\t\tv.SetConfigFile(override)\n\t\t} else if file.IsDir() {\n\t\t\tpath := filepath.Join(override, \".ko.yaml\")\n\t\t\tfile, err = os.Stat(path)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"error looking for config file: %w\", err)\n\t\t\t}\n\t\t\tif file.Mode().IsRegular() {\n\t\t\t\tv.SetConfigFile(path)\n\t\t\t} else {\n\t\t\t\treturn fmt.Errorf(\"config file %s is not a regular file\", path)\n\t\t\t}\n\t\t} else {\n\t\t\treturn fmt.Errorf(\"config file %s is not a regular file\", override)\n\t\t}\n\t}\n\tv.AddConfigPath(bo.WorkingDirectory)\n\n\tif err := v.ReadInConfig(); err != nil {\n\t\tif !errors.As(err, &viper.ConfigFileNotFoundError{}) {\n\t\t\treturn fmt.Errorf(\"error reading config file: %w\", err)\n\t\t}\n\t}\n\n\tif dp := v.GetStringSlice(\"defaultPlatforms\"); len(dp) > 0 {\n\t\tbo.DefaultPlatforms = dp\n\t}\n\n\tif env := v.GetStringSlice(\"defaultEnv\"); len(env) > 0 {\n\t\tbo.DefaultEnv = env\n\t}\n\n\tif flags := v.GetStringSlice(\"defaultFlags\"); len(flags) > 0 {\n\t\tbo.DefaultFlags = flags\n\t}\n\n\tif ldflags := v.GetStringSlice(\"defaultLdflags\"); len(ldflags) > 0 {\n\t\tbo.DefaultLdflags = ldflags\n\t}\n\n\tif bo.BaseImage == \"\" {\n\t\tref := v.GetString(\"defaultBaseImage\")\n\t\tif _, err := name.ParseReference(ref); err != nil {\n\t\t\treturn fmt.Errorf(\"'defaultBaseImage': error parsing %q as image reference: %w\", ref, err)\n\t\t}\n\t\tbo.BaseImage = ref\n\t}\n\n\tif len(bo.BaseImageOverrides) == 0 {\n\t\tbaseImageOverrides := map[string]string{}\n\t\toverrides := v.GetStringMapString(\"baseImageOverrides\")\n\t\tfor key, value := range overrides {\n\t\t\tif _, err := name.ParseReference(value); err != nil {\n\t\t\t\treturn fmt.Errorf(\"'baseImageOverrides': error parsing %q as image reference: %w\", value, err)\n\t\t\t}\n\t\t\tbaseImageOverrides[key] = value\n\t\t}\n\t\tbo.BaseImageOverrides = baseImageOverrides\n\t}\n\n\tif len(bo.BuildConfigs) == 0 {\n\t\tvar builds []build.Config\n\t\tuseYAMLTagsAndUnmarshallers := func(c *mapstructure.DecoderConfig) {\n\t\t\tc.TagName = \"yaml\" // defaults to `mapstructure:\"\"`\n\t\t\tc.DecodeHook = yamlUnmarshallerHookFunc\n\t\t}\n\t\tif err := v.UnmarshalKey(\"builds\", &builds, useYAMLTagsAndUnmarshallers); err != nil {\n\t\t\treturn fmt.Errorf(\"configuration section 'builds' cannot be parsed: %w\", err)\n\t\t}\n\t\tbuildConfigs, err := createBuildConfigMap(bo.WorkingDirectory, builds)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"could not create build config map: %w\", err)\n\t\t}\n\t\tbo.BuildConfigs = buildConfigs\n\t}\n\n\treturn nil\n}\n\nfunc yamlUnmarshallerHookFunc(_ reflect.Type, to reflect.Type, data any) (any, error) {\n\ttype yamlUnmarshaller interface {\n\t\tUnmarshalYAML(func(any) error) error\n\t}\n\tresult := reflect.New(to).Interface()\n\tunmarshaller, ok := result.(yamlUnmarshaller)\n\tif !ok {\n\t\treturn data, nil\n\t}\n\tif err := unmarshaller.UnmarshalYAML(func(target any) error {\n\t\tdest := reflect.Indirect(reflect.ValueOf(target))\n\t\tsrc := reflect.ValueOf(data)\n\t\tif dest.CanSet() && src.Type().AssignableTo(dest.Type()) {\n\t\t\tdest.Set(src)\n\t\t\treturn nil\n\t\t}\n\t\treturn fmt.Errorf(\"want %v, got %v\", dest.Type(), src.Type())\n\t}); err != nil {\n\t\t// We do not implement []string <- []any above, therefore YAML\n\t\t// unmarshaller could fail given perfectly valid input. Return\n\t\t// data AS IS, allowing mapstructure's logic to perform the\n\t\t// conversion.\n\t\treturn data, nil\n\t}\n\treturn result, nil\n}\n\nfunc createBuildConfigMap(workingDirectory string, configs []build.Config) (map[string]build.Config, error) {\n\tbuildConfigsByImportPath := make(map[string]build.Config)\n\tfor i, config := range configs {\n\t\t// In case no ID is specified, use the index of the build config in\n\t\t// the ko YAML file as a reference (debug help).\n\t\tif config.ID == \"\" {\n\t\t\tconfig.ID = fmt.Sprintf(\"#%d\", i)\n\t\t}\n\n\t\t// Make sure to behave like GoReleaser by defaulting to the current\n\t\t// directory in case the build or main field is not set, check\n\t\t// https://goreleaser.com/customization/build/ for details\n\t\tif config.Dir == \"\" {\n\t\t\tconfig.Dir = \".\"\n\t\t}\n\t\tif config.Main == \"\" {\n\t\t\tconfig.Main = \".\"\n\t\t}\n\n\t\t// baseDir is the directory where `go list` will be run to look for package information\n\t\tbaseDir := filepath.Join(workingDirectory, config.Dir)\n\n\t\t// To behave like GoReleaser, check whether the configured `main` config value points to a\n\t\t// source file, and if so, just use the directory it is in\n\t\tpath := config.Main\n\t\tif fi, err := os.Stat(filepath.Join(baseDir, config.Main)); err == nil && fi.Mode().IsRegular() {\n\t\t\tpath = filepath.Dir(config.Main)\n\t\t}\n\n\t\t// Verify that the path actually leads to a local file (https://github.com/google/ko/issues/483)\n\t\tif _, err := os.Stat(filepath.Join(baseDir, path)); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\t// By default, paths configured in the builds section are considered\n\t\t// local import paths, therefore add a \"./\" equivalent as a prefix to\n\t\t// the constructured import path\n\t\tlocalImportPath := fmt.Sprint(\".\", string(filepath.Separator), path)\n\t\tdir := filepath.Clean(baseDir)\n\t\tif dir == \".\" {\n\t\t\tdir = \"\"\n\t\t}\n\t\tpkgs, err := packages.Load(&packages.Config{Mode: packages.NeedName, Dir: dir}, localImportPath)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"'builds': entry #%d does not contain a valid local import path (%s) for directory (%s): %w\", i, localImportPath, baseDir, err)\n\t\t}\n\n\t\tif len(pkgs) != 1 {\n\t\t\treturn nil, fmt.Errorf(\"'builds': entry #%d results in %d local packages, only 1 is expected\", i, len(pkgs))\n\t\t}\n\t\timportPath := pkgs[0].PkgPath\n\t\tbuildConfigsByImportPath[importPath] = config\n\t}\n\n\treturn buildConfigsByImportPath, nil\n}\n"
  },
  {
    "path": "pkg/commands/options/build_test.go",
    "content": "// Copyright 2021 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage options\n\nimport (\n\t\"os\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/google/ko/pkg/build\"\n\t\"github.com/spf13/cobra\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestDefaultBaseImage(t *testing.T) {\n\tbo := &BuildOptions{\n\t\tWorkingDirectory: \"testdata/config\",\n\t}\n\terr := bo.LoadConfig()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\twantDefaultBaseImage := \"alpine\" // matches value in ./testdata/config/.ko.yaml\n\tif bo.BaseImage != wantDefaultBaseImage {\n\t\tt.Fatalf(\"wanted BaseImage %s, got %s\", wantDefaultBaseImage, bo.BaseImage)\n\t}\n}\n\nfunc TestDefaultPlatformsAll(t *testing.T) {\n\tallBo := &BuildOptions{\n\t\tWorkingDirectory: \"testdata/config\",\n\t}\n\terr := allBo.LoadConfig()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\twantDefaultPlatformsAll := []string{\"all\"} // matches value in ./testdata/config/.ko.yaml\n\tif !reflect.DeepEqual(allBo.DefaultPlatforms, wantDefaultPlatformsAll) {\n\t\tt.Fatalf(\"wanted DefaultPlatforms %s, got %s\", wantDefaultPlatformsAll, allBo.DefaultPlatforms)\n\t}\n\n\tmultipleBo := &BuildOptions{\n\t\tWorkingDirectory: \"testdata/multiple-platforms\",\n\t}\n\terr = multipleBo.LoadConfig()\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\twantDefaultPlatformsMultiple := []string{\"linux/arm64\", \"linux/amd64\"} // matches value in ./testdata/multiple-platforms/.ko.yaml\n\tif !reflect.DeepEqual(multipleBo.DefaultPlatforms, wantDefaultPlatformsMultiple) {\n\t\tt.Fatalf(\"wanted DefaultPlatforms %s, got %s\", wantDefaultPlatformsMultiple, multipleBo.DefaultPlatforms)\n\t}\n}\n\nfunc TestDefaultEnv(t *testing.T) {\n\tbo := &BuildOptions{\n\t\tWorkingDirectory: \"testdata/config\",\n\t}\n\terr := bo.LoadConfig()\n\trequire.NoError(t, err)\n\trequire.Equal(t, []string{\"FOO=bar\"}, bo.DefaultEnv)\n}\n\nfunc TestDefaultFlags(t *testing.T) {\n\tbo := &BuildOptions{\n\t\tWorkingDirectory: \"testdata/config\",\n\t}\n\terr := bo.LoadConfig()\n\trequire.NoError(t, err)\n\trequire.Equal(t, []string{\"-tags\", \"netgo\"}, bo.DefaultFlags)\n}\n\nfunc TestDefaultLdFlags(t *testing.T) {\n\tbo := &BuildOptions{\n\t\tWorkingDirectory: \"testdata/config\",\n\t}\n\terr := bo.LoadConfig()\n\trequire.NoError(t, err)\n\trequire.Equal(t, []string{\"-s -w\"}, bo.DefaultLdflags)\n}\n\nfunc TestBuildConfigWithWorkingDirectoryAndDirAndMain(t *testing.T) {\n\tbo := &BuildOptions{\n\t\tWorkingDirectory: \"testdata/paths\",\n\t}\n\terr := bo.LoadConfig()\n\tif err != nil {\n\t\tt.Fatalf(\"NewBuilder(): %v\", err)\n\t}\n\n\tif len(bo.BuildConfigs) != 1 {\n\t\tt.Fatalf(\"expected 1 build config, got %d\", len(bo.BuildConfigs))\n\t}\n\texpectedImportPath := \"example.com/testapp/cmd/foo\" // module from app/go.mod + `main` from .ko.yaml\n\tif _, exists := bo.BuildConfigs[expectedImportPath]; !exists {\n\t\tt.Fatalf(\"expected build config for import path [%s], got %+v\", expectedImportPath, bo.BuildConfigs)\n\t}\n}\n\nfunc TestCreateBuildConfigs(t *testing.T) {\n\tcompare := func(expected string, actual string) {\n\t\tif expected != actual {\n\t\t\tt.Errorf(\"test case failed: expected '%#v', but actual value is '%#v'\", expected, actual)\n\t\t}\n\t}\n\n\tbuildConfigs := []build.Config{\n\t\t{ID: \"defaults\"},\n\t\t{ID: \"OnlyMain\", Main: \"test\"},\n\t\t{ID: \"OnlyMainWithFile\", Main: \"test/main.go\"},\n\t\t{ID: \"OnlyDir\", Dir: \"test\"},\n\t\t{ID: \"DirAndMain\", Dir: \"test\", Main: \"main.go\"},\n\t}\n\n\tfor _, b := range buildConfigs {\n\t\tbuildConfigMap, err := createBuildConfigMap(\"../../..\", []build.Config{b})\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tfor importPath, buildCfg := range buildConfigMap {\n\t\t\tswitch buildCfg.ID {\n\t\t\tcase \"defaults\":\n\t\t\t\tcompare(\"github.com/google/ko\", importPath)\n\n\t\t\tcase \"OnlyMain\", \"OnlyMainWithFile\", \"OnlyDir\", \"DirAndMain\":\n\t\t\t\tcompare(\"github.com/google/ko/test\", importPath)\n\n\t\t\tdefault:\n\t\t\t\tt.Fatalf(\"unknown test case: %s\", buildCfg.ID)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc TestAddBuildOptionsSetsDefaultsForNonFlagOptions(t *testing.T) {\n\tcmd := &cobra.Command{}\n\tbo := &BuildOptions{}\n\tAddBuildOptions(cmd, bo)\n\tif !bo.Trimpath {\n\t\tt.Error(\"expected Trimpath=true\")\n\t}\n}\n\nfunc TestOverrideConfigPath(t *testing.T) {\n\tconst envName = \"KO_CONFIG_PATH\"\n\tbo := &BuildOptions{}\n\tfor _, tc := range []struct {\n\t\tname         string\n\t\tkoConfigPath string\n\t\terr          string\n\t}{{\n\t\tname:         \".ko.yaml does not exist\",\n\t\tkoConfigPath: \"testdata\",\n\t\terr:          \"testdata/.ko.yaml: no such file or directory\",\n\t}, {\n\t\tname:         \"config path does not contain .ko.yaml\",\n\t\tkoConfigPath: \"testdata/bad-config\",\n\t\terr:          \"testdata/bad-config/.ko.yaml is not a regular file\",\n\t}, {\n\t\tname:         \"config path is a directory containing a .ko.yaml\",\n\t\tkoConfigPath: \"testdata/config\",\n\t}, {\n\t\tname:         \"config path points to a file\",\n\t\tkoConfigPath: \"testdata/config/my-ko.yaml\",\n\t}} {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\toldEnv := os.Getenv(envName)\n\t\t\tdefer os.Setenv(envName, oldEnv)\n\n\t\t\tos.Setenv(envName, tc.koConfigPath)\n\t\t\terr := bo.LoadConfig()\n\t\t\tif err == nil {\n\t\t\t\tif tc.err != \"\" {\n\t\t\t\t\tt.Fatalf(\"expected error %q, saw nil\", tc.err)\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif tc.err == \"\" {\n\t\t\t\t\tt.Errorf(\"expected no error, saw: %v\", err)\n\t\t\t\t}\n\t\t\t\tif !strings.Contains(err.Error(), tc.err) {\n\t\t\t\t\tt.Errorf(\"expected error to contain %q, saw: %v\", tc.err, err)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "pkg/commands/options/filestuff.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage options\n\nimport (\n\t\"log\"\n\t\"os\"\n\t\"path/filepath\"\n\n\t\"github.com/spf13/cobra\"\n)\n\n// FilenameOptions is from pkg/kubectl.\ntype FilenameOptions struct {\n\tFilenames []string\n\tRecursive bool\n}\n\nfunc AddFileArg(cmd *cobra.Command, fo *FilenameOptions) {\n\t// From pkg/kubectl\n\tcmd.Flags().StringSliceVarP(&fo.Filenames, \"filename\", \"f\", fo.Filenames,\n\t\t\"Filename, directory, or URL to files to use to create the resource\")\n\tcmd.Flags().BoolVarP(&fo.Recursive, \"recursive\", \"R\", fo.Recursive,\n\t\t\"Process the directory used in -f, --filename recursively. Useful when you want to manage related manifests organized within the same directory.\")\n}\n\n// Based heavily on pkg/kubectl\nfunc EnumerateFiles(fo *FilenameOptions) chan string {\n\tfiles := make(chan string)\n\tgo func() {\n\t\t// When we're done enumerating files, close the channel\n\t\tdefer close(files)\n\t\tfor _, paths := range fo.Filenames {\n\t\t\t// Just pass through '-' as it is indicative of stdin.\n\t\t\tif paths == \"-\" {\n\t\t\t\tfiles <- paths\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t// For each of the \"filenames\" we are passed (file or directory) start a\n\t\t\t// \"Walk\" to enumerate all of the contained files recursively.\n\t\t\terr := filepath.Walk(paths, func(path string, fi os.FileInfo, err error) error {\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\n\t\t\t\t// If this is a directory, skip it if it isn't the current directory we are\n\t\t\t\t// processing (unless we are in recursive mode).  If we decide to process\n\t\t\t\t// the directory, and we're in watch mode, then we set up a watch on the\n\t\t\t\t// directory.\n\t\t\t\tif fi.IsDir() {\n\t\t\t\t\tif path != paths && !fo.Recursive {\n\t\t\t\t\t\treturn filepath.SkipDir\n\t\t\t\t\t}\n\t\t\t\t\t// We don't stream back directories, we just decide to skip them, or not.\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\n\t\t\t\t// Don't check extension if the filepath was passed explicitly\n\t\t\t\tif path != paths {\n\t\t\t\t\tswitch filepath.Ext(path) {\n\t\t\t\t\tcase \".json\", \".yaml\":\n\t\t\t\t\t\t// Process these.\n\t\t\t\t\tdefault:\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tfiles <- path\n\t\t\t\treturn nil\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\tlog.Fatalf(\"Error enumerating files: %v\", err)\n\t\t\t}\n\t\t}\n\t}()\n\treturn files\n}\n"
  },
  {
    "path": "pkg/commands/options/namer_test.go",
    "content": "// Copyright 2021 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage options_test\n\nimport (\n\t\"path\"\n\t\"testing\"\n\n\t\"github.com/google/ko/pkg/commands/options\"\n)\n\nfunc TestMakeNamer(t *testing.T) {\n\tforeachTestCaseMakeNamer(func(tc testMakeNamerCase) {\n\t\tt.Run(tc.name, func(t *testing.T) {\n\t\t\tnamer := options.MakeNamer(&tc.opts)\n\t\t\tgot := namer(\"registry.example.org/foo/bar\", \"example.org/sample/cmd/example\")\n\n\t\t\tif got != tc.want {\n\t\t\t\tt.Errorf(\"got image name %s, wanted %s\", got, tc.want)\n\t\t\t}\n\t\t})\n\t})\n}\n\nfunc foreachTestCaseMakeNamer(fn func(tc testMakeNamerCase)) {\n\tfor _, namerCase := range testMakeNamerCases() {\n\t\tfn(namerCase)\n\t}\n}\n\nfunc testMakeNamerCases() []testMakeNamerCase {\n\treturn []testMakeNamerCase{{\n\t\tname: \"defaults\",\n\t\twant: \"registry.example.org/foo/bar/example-51d74b7127c5f7495a338df33ecdeb19\",\n\t}, {\n\t\tname: \"with preserve import paths\",\n\t\twant: \"registry.example.org/foo/bar/example.org/sample/cmd/example\",\n\t\topts: options.PublishOptions{PreserveImportPaths: true},\n\t}, {\n\t\tname: \"with base import paths\",\n\t\twant: \"registry.example.org/foo/bar/example\",\n\t\topts: options.PublishOptions{BaseImportPaths: true},\n\t}, {\n\t\tname: \"with bare\",\n\t\twant: \"registry.example.org/foo/bar\",\n\t\topts: options.PublishOptions{Bare: true},\n\t}, {\n\t\tname: \"with custom namer\",\n\t\twant: \"registry.example.org/foo/bar-example\",\n\t\topts: options.PublishOptions{ImageNamer: func(base string, importpath string) string {\n\t\t\treturn base + \"-\" + path.Base(importpath)\n\t\t}},\n\t}}\n}\n\ntype testMakeNamerCase struct {\n\tname string\n\topts options.PublishOptions\n\twant string\n}\n"
  },
  {
    "path": "pkg/commands/options/publish.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage options\n\nimport (\n\t\"crypto/md5\" // nolint: gosec // No strong cryptography needed.\n\t\"encoding/hex\"\n\t\"os\"\n\t\"path\"\n\n\t\"github.com/google/go-containerregistry/pkg/v1/daemon\"\n\t\"github.com/google/ko/pkg/publish\"\n\t\"github.com/spf13/cobra\"\n)\n\n// PublishOptions encapsulates options when publishing.\ntype PublishOptions struct {\n\t// DockerRepo configures the destination image repository.\n\t// In normal ko usage, this is populated with the value of $KO_DOCKER_REPO.\n\tDockerRepo string\n\n\t// LocalDomain overrides the default domain for images loaded into the local Docker daemon. Use with Local=true.\n\tLocalDomain string\n\n\t// UserAgent enables overriding the default value of the `User-Agent` HTTP\n\t// request header used when pushing the built image to an image registry.\n\tUserAgent string\n\n\t// DockerClient enables overriding the default docker client when embedding\n\t// ko as a module in other tools.\n\t// If left as the zero value, ko uses github.com/docker/docker/client.FromEnv\n\tDockerClient daemon.Client\n\n\tTags []string\n\t// TagOnly resolves images into tag-only references.\n\tTagOnly bool\n\n\t// Push publishes images to a registry.\n\tPush bool\n\n\t// Local publishes images to a local docker daemon.\n\tLocal            bool\n\tInsecureRegistry bool\n\n\tOCILayoutPath string\n\tTarballFile   string\n\n\tImageRefsFile string\n\n\t// PreserveImportPaths preserves the full import path after KO_DOCKER_REPO.\n\tPreserveImportPaths bool\n\t// BaseImportPaths uses the base path without MD5 hash after KO_DOCKER_REPO.\n\tBaseImportPaths bool\n\t// Bare uses a tag on the KO_DOCKER_REPO without anything additional.\n\tBare bool\n\t// ImageNamer can be used to pass a custom image name function. When given\n\t// PreserveImportPaths, BaseImportPaths, Bare has no effect.\n\tImageNamer publish.Namer\n\n\tJobs int\n}\n\nfunc AddPublishArg(cmd *cobra.Command, po *PublishOptions) {\n\t// Set DockerRepo from the KO_DOCKER_REPO envionment variable.\n\t// See https://github.com/google/ko/pull/351 for flag discussion.\n\tif dockerRepo, exists := os.LookupEnv(\"KO_DOCKER_REPO\"); exists {\n\t\tpo.DockerRepo = dockerRepo\n\t}\n\n\tcmd.Flags().StringSliceVarP(&po.Tags, \"tags\", \"t\", []string{\"latest\"},\n\t\t\"Which tags to use for the produced image instead of the default 'latest' tag \"+\n\t\t\t\"(may not work properly with --base-import-paths or --bare).\")\n\tcmd.Flags().BoolVar(&po.TagOnly, \"tag-only\", false,\n\t\t\"Include tags but not digests in resolved image references. Useful when digests are not preserved when images are repopulated.\")\n\n\tcmd.Flags().BoolVar(&po.Push, \"push\", true, \"Push images to KO_DOCKER_REPO\")\n\n\tcmd.Flags().BoolVarP(&po.Local, \"local\", \"L\", po.Local,\n\t\t\"Load into images to local docker daemon.\")\n\tcmd.Flags().BoolVar(&po.InsecureRegistry, \"insecure-registry\", po.InsecureRegistry,\n\t\t\"Whether to skip TLS verification on the registry\")\n\n\tcmd.Flags().StringVar(&po.OCILayoutPath, \"oci-layout-path\", \"\", \"Path to save the OCI image layout of the built images\")\n\tcmd.Flags().StringVar(&po.TarballFile, \"tarball\", \"\", \"File to save images tarballs\")\n\n\tcmd.Flags().StringVar(&po.ImageRefsFile, \"image-refs\", \"\",\n\t\t\"Path to file where a list of the published image references will be written.\")\n\n\tcmd.Flags().BoolVarP(&po.PreserveImportPaths, \"preserve-import-paths\", \"P\", po.PreserveImportPaths,\n\t\t\"Whether to preserve the full import path after KO_DOCKER_REPO.\")\n\tcmd.Flags().BoolVarP(&po.BaseImportPaths, \"base-import-paths\", \"B\", po.BaseImportPaths,\n\t\t\"Whether to use the base path without MD5 hash after KO_DOCKER_REPO (may not work properly with --tags).\")\n\tcmd.Flags().BoolVar(&po.Bare, \"bare\", po.Bare,\n\t\t\"Whether to just use KO_DOCKER_REPO without additional context (may not work properly with --tags).\")\n}\n\nfunc packageWithMD5(base, importpath string) string {\n\thasher := md5.New() // nolint: gosec // No strong cryptography needed.\n\thasher.Write([]byte(importpath))\n\treturn path.Join(base, path.Base(importpath)+\"-\"+hex.EncodeToString(hasher.Sum(nil)))\n}\n\nfunc preserveImportPath(base, importpath string) string {\n\treturn path.Join(base, importpath)\n}\n\nfunc baseImportPaths(base, importpath string) string {\n\treturn path.Join(base, path.Base(importpath))\n}\n\nfunc bareDockerRepo(base, _ string) string {\n\treturn base\n}\n\nfunc MakeNamer(po *PublishOptions) publish.Namer {\n\tif po.ImageNamer != nil {\n\t\treturn po.ImageNamer\n\t} else if po.PreserveImportPaths {\n\t\treturn preserveImportPath\n\t} else if po.BaseImportPaths {\n\t\treturn baseImportPaths\n\t} else if po.Bare {\n\t\treturn bareDockerRepo\n\t}\n\treturn packageWithMD5\n}\n"
  },
  {
    "path": "pkg/commands/options/selector.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage options\n\nimport (\n\t\"github.com/spf13/cobra\"\n)\n\n// SelectorOptions allows selecting objects from the input manifests by label\ntype SelectorOptions struct {\n\tSelector string\n}\n\nfunc AddSelectorArg(cmd *cobra.Command, so *SelectorOptions) {\n\tcmd.Flags().StringVarP(&so.Selector, \"selector\", \"l\", \"\",\n\t\t\"Selector (label query) to filter on, supports '=', '==', and '!='.(e.g. -l key1=value1,key2=value2)\")\n}\n"
  },
  {
    "path": "pkg/commands/options/testdata/bad-config/.ko.yaml/.gitignore",
    "content": ".# We just want to have a practically empty directory.\n*\n!.gitignore\n"
  },
  {
    "path": "pkg/commands/options/testdata/config/.ko.yaml",
    "content": "defaultBaseImage: alpine\ndefaultPlatforms: all\ndefaultEnv: FOO=bar\ndefaultFlags:\n  - -tags\n  - netgo\ndefaultLdflags:\n  - -s -w\n"
  },
  {
    "path": "pkg/commands/options/testdata/config/my-ko.yaml",
    "content": "defaultBaseImage: wow\n"
  },
  {
    "path": "pkg/commands/options/testdata/multiple-platforms/.ko.yaml",
    "content": "defaultBaseImage: alpine\ndefaultPlatforms:\n- linux/arm64\n- linux/amd64\n"
  },
  {
    "path": "pkg/commands/options/testdata/paths/.ko.yaml",
    "content": "builds:\n- id: app-with-main-package-in-different-directory-to-go-mod-and-ko-yaml\n  dir: ./app\n  main: ./cmd/foo\n"
  },
  {
    "path": "pkg/commands/options/testdata/paths/app/cmd/foo/main.go",
    "content": "// Copyright 2021 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"cmd/foo\")\n}\n"
  },
  {
    "path": "pkg/commands/options/testdata/paths/app/go.mod",
    "content": "module example.com/testapp\n\ngo 1.15\n"
  },
  {
    "path": "pkg/commands/options/validate.go",
    "content": "// Copyright 2022 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage options\n\nimport (\n\t\"errors\"\n\t\"log\"\n\t\"slices\"\n\t\"strings\"\n)\n\nconst bareBaseFlagsWarning = `WARNING!\n-----------------------------------------------------------------\nBoth --base-import-paths and --bare were set.\n\n--base-import-paths will take precedence and ignore --bare flag.\n\nIn a future release this will be an error.\n-----------------------------------------------------------------\n`\n\nconst localFlagsWarning = `WARNING!\n-----------------------------------------------------------------\nThe --local flag is set and KO_DOCKER_REPO is set to ko.local\n\nYou can choose either one to build a local image.\n\nThe --local flag might be deprecated in the future.\n-----------------------------------------------------------------\n`\n\nfunc Validate(po *PublishOptions, bo *BuildOptions) error {\n\tpo.Jobs = bo.ConcurrentBuilds\n\tif po.Bare && po.BaseImportPaths {\n\t\tlog.Print(bareBaseFlagsWarning)\n\t\t// TODO: return error when we decided to make this an error, for now it is a warning\n\t}\n\n\tif po.Local && strings.Contains(po.DockerRepo, \"ko.local\") {\n\t\tlog.Print(localFlagsWarning)\n\t}\n\n\tif len(bo.Platforms) > 1 {\n\t\tif slices.Contains(bo.Platforms, \"all\") {\n\t\t\treturn errors.New(\"all or specific platforms should be used\")\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/commands/publisher.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage commands\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/google/go-containerregistry/pkg/name\"\n\t\"github.com/google/ko/pkg/build\"\n\t\"github.com/google/ko/pkg/publish\"\n)\n\n// PublishImages publishes images\nfunc PublishImages(ctx context.Context, importpaths []string, pub publish.Interface, b build.Interface) (map[string]name.Reference, error) {\n\treturn publishImages(ctx, importpaths, pub, b)\n}\n\nfunc publishImages(ctx context.Context, importpaths []string, pub publish.Interface, b build.Interface) (map[string]name.Reference, error) {\n\timgs := make(map[string]name.Reference)\n\tfor _, importpath := range importpaths {\n\t\timportpath, err := b.QualifyImport(importpath)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif err := b.IsSupportedReference(importpath); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"importpath %q is not supported: %w\", importpath, err)\n\t\t}\n\n\t\timg, err := b.Build(ctx, importpath)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error building %q: %w\", importpath, err)\n\t\t}\n\t\tref, err := pub.Publish(ctx, img, importpath)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error publishing %s: %w\", importpath, err)\n\t\t}\n\t\timgs[importpath] = ref\n\t}\n\treturn imgs, nil\n}\n"
  },
  {
    "path": "pkg/commands/publisher_test.go",
    "content": "// Copyright 2021 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage commands\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/google/ko/pkg/build\"\n\t\"github.com/google/ko/pkg/commands/options\"\n)\n\nfunc TestPublishImages(t *testing.T) {\n\tnamespace := \"base\"\n\ts, err := registryServerWithImage(namespace)\n\tif err != nil {\n\t\tt.Fatalf(\"could not create test registry server: %v\", err)\n\t}\n\trepo := s.Listener.Addr().String()\n\tbaseImage := fmt.Sprintf(\"%s/%s\", repo, namespace)\n\tsampleAppDir, err := sampleAppRelDir()\n\tif err != nil {\n\t\tt.Fatalf(\"sampleAppRelDir(): %v\", err)\n\t}\n\ttests := []struct {\n\t\tdescription string\n\t\tpublishArg  string\n\t\timportpath  string\n\t}{\n\t\t{\n\t\t\tdescription: \"import path with ko scheme\",\n\t\t\tpublishArg:  \"ko://github.com/google/ko/test\",\n\t\t\timportpath:  \"github.com/google/ko/test\",\n\t\t},\n\t\t{\n\t\t\tdescription: \"import path without ko scheme\",\n\t\t\tpublishArg:  \"github.com/google/ko/test\",\n\t\t\timportpath:  \"github.com/google/ko/test\",\n\t\t},\n\t\t{\n\t\t\tdescription: \"file path\",\n\t\t\tpublishArg:  sampleAppDir,\n\t\t\timportpath:  \"github.com/google/ko/test\",\n\t\t},\n\t}\n\tfor _, test := range tests {\n\t\tctx := context.Background()\n\t\tbo := &options.BuildOptions{\n\t\t\tBaseImage:        baseImage,\n\t\t\tConcurrentBuilds: 1,\n\t\t\tPlatforms:        []string{\"all\"},\n\t\t}\n\t\tbuilder, err := NewBuilder(ctx, bo)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"%s: MakeBuilder(): %v\", test.description, err)\n\t\t}\n\t\tpo := &options.PublishOptions{\n\t\t\tDockerRepo:          repo,\n\t\t\tPreserveImportPaths: true,\n\t\t}\n\t\tpublisher, err := NewPublisher(po)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"%s: MakePublisher(): %v\", test.description, err)\n\t\t}\n\t\timportpathWithScheme := build.StrictScheme + test.importpath\n\t\trefs, err := PublishImages(ctx, []string{test.publishArg}, publisher, builder)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"%s: PublishImages(): %v\", test.description, err)\n\t\t}\n\t\tref, exists := refs[importpathWithScheme]\n\t\tif !exists {\n\t\t\tt.Errorf(\"%s: could not find image for importpath %s\", test.description, importpathWithScheme)\n\t\t}\n\t\tgotImageName := ref.Context().Name()\n\t\twantImageName := strings.ToLower(fmt.Sprintf(\"%s/%s\", repo, test.importpath))\n\t\tif gotImageName != wantImageName {\n\t\t\tt.Errorf(\"%s: got %s, wanted %s\", test.description, gotImageName, wantImageName)\n\t\t}\n\t}\n}\n\nfunc sampleAppRelDir() (string, error) {\n\t_, filename, _, ok := runtime.Caller(0)\n\tif !ok {\n\t\treturn \"\", fmt.Errorf(\"could not get current filename\")\n\t}\n\tbasepath := filepath.Dir(filename)\n\ttestAppDir := filepath.Join(basepath, \"..\", \"..\", \"test\")\n\treturn filepath.Rel(basepath, testAppDir)\n}\n"
  },
  {
    "path": "pkg/commands/resolve.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage commands\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/google/ko/pkg/commands/options\"\n\t\"github.com/spf13/cobra\"\n)\n\n// addResolve augments our CLI surface with resolve.\nfunc addResolve(topLevel *cobra.Command) {\n\tpo := &options.PublishOptions{}\n\tfo := &options.FilenameOptions{}\n\tso := &options.SelectorOptions{}\n\tbo := &options.BuildOptions{}\n\n\tresolve := &cobra.Command{\n\t\tUse:   \"resolve -f FILENAME\",\n\t\tShort: \"Print the input files with image references resolved to built/pushed image digests.\",\n\t\tLong:  `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.`,\n\t\tExample: `\n  # Build and publish import path references to a Docker\n  # Registry as:\n  #   ${KO_DOCKER_REPO}/<package name>-<hash of import path>\n  # When KO_DOCKER_REPO is ko.local, it is the same as if\n  # --local and --preserve-import-paths were passed.\n  ko resolve -f config/\n\n  # Build and publish import path references to a Docker\n  # Registry preserving import path names as:\n  #   ${KO_DOCKER_REPO}/<import path>\n  # When KO_DOCKER_REPO is ko.local, it is the same as if\n  # --local was passed.\n  ko resolve --preserve-import-paths -f config/\n\n  # Build and publish import path references to a Docker\n  # daemon as:\n  #   ko.local/<import path>\n  # This always preserves import paths.\n  ko resolve --local -f config/`,\n\t\tArgs: cobra.NoArgs,\n\t\tRunE: func(cmd *cobra.Command, _ []string) error {\n\t\t\tif err := options.Validate(po, bo); err != nil {\n\t\t\t\treturn fmt.Errorf(\"validating options: %w\", err)\n\t\t\t}\n\n\t\t\tctx := cmd.Context()\n\n\t\t\tbo.InsecureRegistry = po.InsecureRegistry\n\t\t\tbuilder, err := makeBuilder(ctx, bo)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"error creating builder: %w\", err)\n\t\t\t}\n\t\t\tpublisher, err := makePublisher(po)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"error creating publisher: %w\", err)\n\t\t\t}\n\t\t\tdefer publisher.Close()\n\t\t\treturn ResolveFilesToWriter(ctx, builder, publisher, fo, so, os.Stdout)\n\t\t},\n\t}\n\toptions.AddPublishArg(resolve, po)\n\toptions.AddFileArg(resolve, fo)\n\toptions.AddSelectorArg(resolve, so)\n\toptions.AddBuildOptions(resolve, bo)\n\ttopLevel.AddCommand(resolve)\n}\n"
  },
  {
    "path": "pkg/commands/resolver.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage commands\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/google/go-containerregistry/pkg/name\"\n\t\"go.yaml.in/yaml/v4\"\n\t\"golang.org/x/sync/errgroup\"\n\t\"k8s.io/apimachinery/pkg/labels\"\n\n\t\"github.com/google/ko/pkg/build\"\n\t\"github.com/google/ko/pkg/commands/options\"\n\t\"github.com/google/ko/pkg/publish\"\n\t\"github.com/google/ko/pkg/resolve\"\n)\n\n// ua returns the ko user agent.\nfunc ua() string {\n\tif v := version(); v != \"\" {\n\t\treturn \"ko/\" + v\n\t}\n\treturn \"ko\"\n}\n\nfunc gobuildOptions(bo *options.BuildOptions) ([]build.Option, error) {\n\tcreationTime, err := getCreationTime()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tkodataCreationTime, err := getKoDataCreationTime()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif len(bo.Platforms) == 0 && len(bo.DefaultPlatforms) > 0 {\n\t\tbo.Platforms = bo.DefaultPlatforms\n\t}\n\n\tif len(bo.Platforms) == 0 {\n\t\tenvPlatform := \"linux/amd64\"\n\n\t\tgoos, goarch, goarm := os.Getenv(\"GOOS\"), os.Getenv(\"GOARCH\"), os.Getenv(\"GOARM\")\n\n\t\t// Default to linux/amd64 unless GOOS and GOARCH are set.\n\t\tif goos != \"\" && goarch != \"\" {\n\t\t\tenvPlatform = path.Join(goos, goarch)\n\t\t}\n\n\t\t// Use GOARM for variant if it's set and GOARCH is arm.\n\t\tif strings.Contains(goarch, \"arm\") && goarm != \"\" {\n\t\t\tenvPlatform = path.Join(envPlatform, \"v\"+goarm)\n\t\t}\n\n\t\tbo.Platforms = []string{envPlatform}\n\t} else {\n\t\t// Make sure these are all unset\n\t\tfor _, env := range []string{\"GOOS\", \"GOARCH\", \"GOARM\"} {\n\t\t\tif s, ok := os.LookupEnv(env); ok {\n\t\t\t\treturn nil, fmt.Errorf(\"cannot use --platform or defaultPlatforms in .ko.yaml or env KO_DEFAULTPLATFORMS combined with %s=%q\", env, s)\n\t\t\t}\n\t\t}\n\t}\n\n\topts := []build.Option{\n\t\tbuild.WithBaseImages(getBaseImage(bo)),\n\t\tbuild.WithDefaultEnv(bo.DefaultEnv),\n\t\tbuild.WithDefaultFlags(bo.DefaultFlags),\n\t\tbuild.WithDefaultLdflags(bo.DefaultLdflags),\n\t\tbuild.WithPlatforms(bo.Platforms...),\n\t\tbuild.WithJobs(bo.ConcurrentBuilds),\n\t}\n\tif creationTime != nil {\n\t\topts = append(opts, build.WithCreationTime(*creationTime))\n\t}\n\tif kodataCreationTime != nil {\n\t\topts = append(opts, build.WithKoDataCreationTime(*kodataCreationTime))\n\t}\n\tif bo.DisableOptimizations {\n\t\topts = append(opts, build.WithDisabledOptimizations())\n\t}\n\tif bo.Debug {\n\t\topts = append(opts, build.WithDebugger())\n\t\topts = append(opts, build.WithDisabledOptimizations()) // also needed for Delve\n\t}\n\tswitch bo.SBOM {\n\tcase \"none\":\n\t\topts = append(opts, build.WithDisabledSBOM())\n\tdefault: // \"spdx\"\n\t\topts = append(opts, build.WithSPDX(version()))\n\t}\n\topts = append(opts, build.WithTrimpath(bo.Trimpath))\n\tfor _, lf := range bo.Labels {\n\t\tparts := strings.SplitN(lf, \"=\", 2)\n\t\tif len(parts) != 2 {\n\t\t\treturn nil, fmt.Errorf(\"invalid label flag: %s\", lf)\n\t\t}\n\t\topts = append(opts, build.WithLabel(parts[0], parts[1]))\n\t}\n\tfor _, an := range bo.Annotations {\n\t\tk, v, ok := strings.Cut(an, \"=\")\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\"missing '=' in annotation: %s\", an)\n\t\t}\n\t\topts = append(opts, build.WithAnnotation(k, v))\n\t}\n\n\tif bo.User != \"\" {\n\t\topts = append(opts, build.WithUser(bo.User))\n\t}\n\n\tif bo.BuildConfigs != nil {\n\t\topts = append(opts, build.WithConfig(bo.BuildConfigs))\n\t}\n\n\tif bo.SBOMDir != \"\" {\n\t\topts = append(opts, build.WithSBOMDir(bo.SBOMDir))\n\t}\n\n\treturn opts, nil\n}\n\n// NewBuilder creates a ko builder\nfunc NewBuilder(ctx context.Context, bo *options.BuildOptions) (build.Interface, error) {\n\treturn makeBuilder(ctx, bo)\n}\n\nfunc makeBuilder(ctx context.Context, bo *options.BuildOptions) (*build.Caching, error) {\n\tif err := bo.LoadConfig(); err != nil {\n\t\treturn nil, err\n\t}\n\topt, err := gobuildOptions(bo)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error setting up builder options: %w\", err)\n\t}\n\tinnerBuilder, err := build.NewGobuilds(ctx, bo.WorkingDirectory, bo.BuildConfigs, opt...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// tl;dr Wrap builder in a caching builder.\n\t//\n\t// The caching builder should on Build calls:\n\t//  - Check for a valid Build future\n\t//    - if a valid Build future exists at the time of the request,\n\t//      then block on it.\n\t//    - if it does not, then initiate and record a Build future.\n\t//\n\t// This will benefit the following key cases:\n\t// 1. When the same import path is referenced across multiple yaml files\n\t//    we can elide subsequent builds by blocking on the same image future.\n\t// 2. When an affected yaml file has multiple import paths (mostly unaffected)\n\t//    we can elide the builds of unchanged import paths.\n\treturn build.NewCaching(innerBuilder)\n}\n\n// NewPublisher creates a ko publisher\nfunc NewPublisher(po *options.PublishOptions) (publish.Interface, error) {\n\treturn makePublisher(po)\n}\n\nfunc makePublisher(po *options.PublishOptions) (publish.Interface, error) {\n\t// use each tag only once\n\tpo.Tags = unique(po.Tags)\n\t// Create the publish.Interface that we will use to publish image references\n\t// to either a docker daemon or a container image registry.\n\tinnerPublisher, err := func() (publish.Interface, error) {\n\t\trepoName := po.DockerRepo\n\t\tnamer := options.MakeNamer(po)\n\t\t// Default LocalDomain if unset.\n\t\tif po.LocalDomain == \"\" {\n\t\t\tpo.LocalDomain = publish.LocalDomain\n\t\t}\n\t\t// If repoName is unset with --local, default it to the local domain.\n\t\tif po.Local && repoName == \"\" {\n\t\t\trepoName = po.LocalDomain\n\t\t}\n\t\t// When in doubt, if repoName is under the local domain, default to --local.\n\t\tpo.Local = po.Local || strings.HasPrefix(repoName, po.LocalDomain)\n\t\tif po.Local {\n\t\t\t// TODO(jonjohnsonjr): I'm assuming that nobody will\n\t\t\t// use local with other publishers, but that might\n\t\t\t// not be true.\n\t\t\tpo.LocalDomain = repoName\n\t\t\treturn publish.NewDaemon(namer, po.Tags,\n\t\t\t\tpublish.WithDockerClient(po.DockerClient),\n\t\t\t\tpublish.WithLocalDomain(po.LocalDomain),\n\t\t\t)\n\t\t}\n\t\tif strings.HasPrefix(repoName, publish.KindDomain) {\n\t\t\treturn publish.NewKindPublisher(repoName, namer, po.Tags), nil\n\t\t}\n\n\t\tif repoName == \"\" && po.Push {\n\t\t\treturn nil, errors.New(\"KO_DOCKER_REPO environment variable is unset\")\n\t\t}\n\t\tif _, err := name.NewRegistry(repoName); err != nil {\n\t\t\tif _, err := name.NewRepository(repoName); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"failed to parse %q as repository: %w\", repoName, err)\n\t\t\t}\n\t\t}\n\n\t\tpublishers := []publish.Interface{}\n\t\tif po.OCILayoutPath != \"\" {\n\t\t\tlp := publish.NewLayout(po.OCILayoutPath)\n\t\t\tpublishers = append(publishers, lp)\n\t\t}\n\t\tif po.TarballFile != \"\" {\n\t\t\ttp := publish.NewTarball(po.TarballFile, repoName, namer, po.Tags)\n\t\t\tpublishers = append(publishers, tp)\n\t\t}\n\t\tuserAgent := ua()\n\t\tif po.UserAgent != \"\" {\n\t\t\tuserAgent = po.UserAgent\n\t\t}\n\t\tif po.Push {\n\t\t\tdp, err := publish.NewDefault(repoName,\n\t\t\t\tpublish.WithUserAgent(userAgent),\n\t\t\t\tpublish.WithAuthFromKeychain(keychain),\n\t\t\t\tpublish.WithNamer(namer),\n\t\t\t\tpublish.WithTags(po.Tags),\n\t\t\t\tpublish.WithTagOnly(po.TagOnly),\n\t\t\t\tpublish.Insecure(po.InsecureRegistry),\n\t\t\t\tpublish.WithJobs(po.Jobs),\n\t\t\t)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tpublishers = append(publishers, dp)\n\t\t}\n\n\t\t// If not publishing, at least generate a digest to simulate\n\t\t// publishing.\n\t\tif len(publishers) == 0 {\n\t\t\t// If one or more tags are specified, use the first tag in the list\n\t\t\tvar tag string\n\t\t\tif len(po.Tags) >= 1 {\n\t\t\t\ttag = po.Tags[0]\n\t\t\t}\n\t\t\tpublishers = append(publishers, nopPublisher{\n\t\t\t\trepoName: repoName,\n\t\t\t\tnamer:    namer,\n\t\t\t\ttag:      tag,\n\t\t\t\ttagOnly:  po.TagOnly,\n\t\t\t})\n\t\t}\n\n\t\treturn publish.MultiPublisher(publishers...), nil\n\t}()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif po.ImageRefsFile != \"\" {\n\t\tinnerPublisher, err = publish.NewRecorder(innerPublisher, po.ImageRefsFile)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\t// Wrap publisher in a memoizing publisher implementation.\n\treturn publish.NewCaching(innerPublisher)\n}\n\n// nopPublisher simulates publishing without actually publishing anything, to\n// provide fallback behavior when the user configures no push destinations.\ntype nopPublisher struct {\n\trepoName string\n\tnamer    publish.Namer\n\ttag      string\n\ttagOnly  bool\n}\n\nfunc (n nopPublisher) Publish(_ context.Context, br build.Result, s string) (name.Reference, error) {\n\ts = strings.TrimPrefix(s, build.StrictScheme)\n\tnm := n.namer(n.repoName, s)\n\tif n.tagOnly {\n\t\tif n.tag == \"\" {\n\t\t\treturn nil, errors.New(\"must specify tag if requesting tag only\")\n\t\t}\n\t\treturn name.NewTag(fmt.Sprintf(\"%s:%s\", nm, n.tag))\n\t}\n\th, err := br.Digest()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif n.tag == \"\" {\n\t\treturn name.NewDigest(fmt.Sprintf(\"%s@%s\", nm, h))\n\t}\n\treturn name.NewDigest(fmt.Sprintf(\"%s:%s@%s\", nm, n.tag, h))\n}\n\nfunc (n nopPublisher) Close() error { return nil }\n\n// resolvedFuture represents a \"future\" for the bytes of a resolved file.\ntype resolvedFuture chan []byte\n\nfunc ResolveFilesToWriter(\n\tctx context.Context,\n\tbuilder *build.Caching,\n\tpublisher publish.Interface,\n\tfo *options.FilenameOptions,\n\tso *options.SelectorOptions,\n\tout io.WriteCloser) error {\n\tdefer out.Close()\n\n\t// By having this as a channel, we can hook this up to a filesystem\n\t// watcher and leave `fs` open to stream the names of yaml files\n\t// affected by code changes (including the modification of existing or\n\t// creation of new yaml files).\n\tfs := options.EnumerateFiles(fo)\n\n\t// This tracks filename -> []importpath\n\tvar sm sync.Map\n\n\t// This tracks resolution errors and ensures we cancel other builds if an\n\t// individual build fails.\n\terrs, ctx := errgroup.WithContext(ctx)\n\n\tvar futures []resolvedFuture\n\tfor {\n\t\t// Each iteration, if there is anything in the list of futures,\n\t\t// listen to it in addition to the file enumerating channel.\n\t\t// A nil channel is never available to receive on, so if nothing\n\t\t// is available, this will result in us exclusively selecting\n\t\t// on the file enumerating channel.\n\t\tvar bf resolvedFuture\n\t\tif len(futures) > 0 {\n\t\t\tbf = futures[0]\n\t\t} else if fs == nil {\n\t\t\t// There are no more files to enumerate and the futures\n\t\t\t// have been drained, so quit.\n\t\t\tbreak\n\t\t}\n\n\t\tselect {\n\t\tcase file, ok := <-fs:\n\t\t\tif !ok {\n\t\t\t\t// a nil channel is never available to receive on.\n\t\t\t\t// This allows us to drain the list of in-process\n\t\t\t\t// futures without this case of the select winning\n\t\t\t\t// each time.\n\t\t\t\tfs = nil\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\t// Make a new future to use to ship the bytes back and append\n\t\t\t// it to the list of futures (see comment below about ordering).\n\t\t\tch := make(resolvedFuture)\n\t\t\tfutures = append(futures, ch)\n\n\t\t\t// Kick off the resolution that will respond with its bytes on\n\t\t\t// the future.\n\t\t\tf := file // defensive copy\n\t\t\terrs.Go(func() error {\n\t\t\t\tdefer close(ch)\n\t\t\t\t// Record the builds we do via this builder.\n\t\t\t\trecordingBuilder := &build.Recorder{\n\t\t\t\t\tBuilder: builder,\n\t\t\t\t}\n\t\t\t\tb, err := resolveFile(ctx, f, recordingBuilder, publisher, so)\n\t\t\t\tif err != nil {\n\t\t\t\t\t// This error is sometimes expected during watch mode, so this\n\t\t\t\t\t// isn't fatal. Just print it and keep the watch open.\n\t\t\t\t\treturn fmt.Errorf(\"error processing import paths in %q: %w\", f, err)\n\t\t\t\t}\n\t\t\t\t// Associate with this file the collection of binary import paths.\n\t\t\t\tsm.Store(f, recordingBuilder.ImportPaths)\n\t\t\t\tch <- b\n\t\t\t\treturn nil\n\t\t\t})\n\n\t\tcase b, ok := <-bf:\n\t\t\t// Once the head channel returns something, dequeue it.\n\t\t\t// We listen to the futures in order to be respectful of\n\t\t\t// the kubectl apply ordering, which matters!\n\t\t\tfutures = futures[1:]\n\t\t\tif ok {\n\t\t\t\t// Write the next body and a trailing delimiter.\n\t\t\t\t// We write the delimiter LAST so that when streamed to\n\t\t\t\t// kubectl it knows that the resource is complete and may\n\t\t\t\t// be applied.\n\t\t\t\tout.Write(append(b, []byte(\"\\n---\\n\")...))\n\t\t\t}\n\t\t}\n\t}\n\n\t// Make sure we exit with an error.\n\t// See https://github.com/ko-build/ko/issues/84\n\treturn errs.Wait()\n}\n\nfunc resolveFile(\n\tctx context.Context,\n\tf string,\n\tbuilder build.Interface,\n\tpub publish.Interface,\n\tso *options.SelectorOptions) (b []byte, err error) {\n\tvar selector labels.Selector\n\tif so.Selector != \"\" {\n\t\tvar err error\n\t\tselector, err = labels.Parse(so.Selector)\n\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"unable to parse selector: %w\", err)\n\t\t}\n\t}\n\n\tif f == \"-\" {\n\t\tb, err = io.ReadAll(os.Stdin)\n\t} else {\n\t\tb, err = os.ReadFile(f)\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar docNodes []*yaml.Node\n\n\t// The loop is to support multi-document yaml files.\n\t// This is handled by using a yaml.Decoder and reading objects until io.EOF, see:\n\t// https://godoc.org/go.yaml.in/yaml/v4#Decoder.Decode\n\tdecoder := yaml.NewDecoder(bytes.NewBuffer(b))\n\tfor {\n\t\tvar doc yaml.Node\n\t\tif err := decoder.Decode(&doc); err != nil {\n\t\t\tif errors.Is(err, io.EOF) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif selector != nil {\n\t\t\tif match, err := resolve.MatchesSelector(&doc, selector); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"error evaluating selector: %w\", err)\n\t\t\t} else if !match {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\tdocNodes = append(docNodes, &doc)\n\t}\n\n\tif err := resolve.ImageReferences(ctx, docNodes, builder, pub); err != nil {\n\t\treturn nil, fmt.Errorf(\"error resolving image references: %w\", err)\n\t}\n\n\tbuf := &bytes.Buffer{}\n\te := yaml.NewEncoder(buf)\n\te.SetIndent(2)\n\n\tfor _, doc := range docNodes {\n\t\terr := e.Encode(doc)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to encode output: %w\", err)\n\t\t}\n\t}\n\te.Close()\n\n\treturn buf.Bytes(), nil\n}\n\n// create a set from the input slice\n// preserving the order of unique elements\nfunc unique(ss []string) []string {\n\tvar (\n\t\tseen = make(map[string]struct{}, len(ss))\n\t\tuniq = make([]string, 0, len(ss))\n\t)\n\tfor _, s := range ss {\n\t\tif _, ok := seen[s]; !ok {\n\t\t\tseen[s] = struct{}{}\n\t\t\tuniq = append(uniq, s)\n\t\t}\n\t}\n\treturn uniq\n}\n"
  },
  {
    "path": "pkg/commands/resolver_test.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage commands\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"log\"\n\t\"net/http/httptest\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"github.com/google/go-cmp/cmp/cmpopts\"\n\t\"github.com/google/go-containerregistry/pkg/crane\"\n\t\"github.com/google/go-containerregistry/pkg/name\"\n\t\"github.com/google/go-containerregistry/pkg/registry\"\n\tv1 \"github.com/google/go-containerregistry/pkg/v1\"\n\t\"github.com/google/go-containerregistry/pkg/v1/daemon\"\n\t\"github.com/google/go-containerregistry/pkg/v1/empty\"\n\t\"github.com/google/go-containerregistry/pkg/v1/random\"\n\t\"github.com/google/ko/pkg/build\"\n\t\"github.com/google/ko/pkg/commands/options\"\n\tkotesting \"github.com/google/ko/pkg/internal/testing\"\n\t\"github.com/moby/moby/api/types/image\"\n\t\"github.com/moby/moby/client\"\n\t\"go.yaml.in/yaml/v4\"\n)\n\nvar (\n\tfooRef      = \"github.com/awesomesauce/foo\"\n\tfoo         = mustRandom()\n\tfooHash     = mustDigest(foo)\n\tbarRef      = \"github.com/awesomesauce/bar\"\n\tbar         = mustRandom()\n\tbarHash     = mustDigest(bar)\n\ttestBuilder = kotesting.NewFixedBuild(map[string]build.Result{\n\t\tfooRef: foo,\n\t\tbarRef: bar,\n\t})\n\ttestHashes = map[string]v1.Hash{\n\t\tfooRef: fooHash,\n\t\tbarRef: barHash,\n\t}\n\n\terrImageLoad = fmt.Errorf(\"ImageLoad() error\")\n\terrImageTag  = fmt.Errorf(\"ImageTag() error\")\n)\n\ntype erroringClient struct {\n\tdaemon.Client\n\n\tinspectErr  error\n\tinspectResp image.InspectResponse\n}\n\nfunc (m *erroringClient) Ping(context.Context, client.PingOptions) (client.PingResult, error) {\n\treturn client.PingResult{}, nil\n}\nfunc (m *erroringClient) ImageLoad(context.Context, io.Reader, ...client.ImageLoadOption) (client.ImageLoadResult, error) {\n\treturn io.NopCloser(strings.NewReader(\"\")), errImageLoad\n}\nfunc (m *erroringClient) ImageTag(context.Context, client.ImageTagOptions) (client.ImageTagResult, error) {\n\treturn client.ImageTagResult{}, errImageTag\n}\n\nfunc (m *erroringClient) ImageInspect(_ context.Context, _ string, _ ...client.ImageInspectOption) (client.ImageInspectResult, error) {\n\treturn client.ImageInspectResult{InspectResponse: m.inspectResp}, m.inspectErr\n}\n\nfunc TestResolveMultiDocumentYAMLs(t *testing.T) {\n\trefs := []string{fooRef, barRef}\n\thashes := []v1.Hash{fooHash, barHash}\n\tbase := mustRepository(\"gcr.io/multi-pass\")\n\n\tbuf := bytes.NewBuffer(nil)\n\tencoder := yaml.NewEncoder(buf)\n\tfor _, input := range refs {\n\t\tif err := encoder.Encode(build.StrictScheme + input); err != nil {\n\t\t\tt.Fatalf(\"Encode(%v) = %v\", input, err)\n\t\t}\n\t}\n\n\tinputYAML := buf.Bytes()\n\n\toutYAML, err := resolveFile(\n\t\tcontext.Background(),\n\t\tyamlToTmpFile(t, buf.Bytes()),\n\t\ttestBuilder,\n\t\tkotesting.NewFixedPublish(base, testHashes),\n\t\t&options.SelectorOptions{})\n\n\tif err != nil {\n\t\tt.Fatalf(\"ImageReferences(%v) = %v\", string(inputYAML), err)\n\t}\n\n\tbuf = bytes.NewBuffer(outYAML)\n\tdecoder := yaml.NewDecoder(buf)\n\tvar outStructured []string\n\tfor {\n\t\tvar output string\n\t\tif err := decoder.Decode(&output); err == nil {\n\t\t\toutStructured = append(outStructured, output)\n\t\t} else if errors.Is(err, io.EOF) {\n\t\t\tbreak\n\t\t} else {\n\t\t\tt.Errorf(\"yaml.Unmarshal(%v) = %v\", string(outYAML), err)\n\t\t}\n\t}\n\n\texpectedStructured := []string{\n\t\tkotesting.ComputeDigest(base, refs[0], hashes[0]),\n\t\tkotesting.ComputeDigest(base, refs[1], hashes[1]),\n\t}\n\n\tif want, got := len(expectedStructured), len(outStructured); want != got {\n\t\tt.Errorf(\"resolveFile(%v) = %v, want %v\", string(inputYAML), got, want)\n\t}\n\n\tif diff := cmp.Diff(expectedStructured, outStructured, cmpopts.EquateEmpty()); diff != \"\" {\n\t\tt.Errorf(\"resolveFile(%v); (-want +got) = %v\", string(inputYAML), diff)\n\t}\n}\n\nfunc TestResolveMultiDocumentYAMLsWithSelector(t *testing.T) {\n\tpassesSelector := `apiVersion: something/v1\nkind: Foo\nmetadata:\n  labels:\n    qux: baz\n`\n\tfailsSelector := `apiVersion: other/v2\nkind: Bar\n`\n\t// Note that this ends in '---', so it in ends in a final null YAML document.\n\tinputYAML := fmt.Appendf(nil, \"%s---\\n%s---\", passesSelector, failsSelector)\n\tbase := mustRepository(\"gcr.io/multi-pass\")\n\n\toutputYAML, err := resolveFile(\n\t\tcontext.Background(),\n\t\tyamlToTmpFile(t, inputYAML),\n\t\ttestBuilder,\n\t\tkotesting.NewFixedPublish(base, testHashes),\n\t\t&options.SelectorOptions{\n\t\t\tSelector: \"qux=baz\",\n\t\t})\n\tif err != nil {\n\t\tt.Fatalf(\"ImageReferences(%v) = %v\", string(inputYAML), err)\n\t}\n\tif diff := cmp.Diff(passesSelector, string(outputYAML)); diff != \"\" {\n\t\tt.Errorf(\"resolveFile (-want +got) = %v\", diff)\n\t}\n}\n\nfunc TestNewBuilder(t *testing.T) {\n\tnamespace := \"base\"\n\ts, err := registryServerWithImage(namespace)\n\tif err != nil {\n\t\tt.Fatalf(\"could not create test registry server: %v\", err)\n\t}\n\tdefer s.Close()\n\tbaseImage := fmt.Sprintf(\"%s/%s\", s.Listener.Addr().String(), namespace)\n\n\ttests := []struct {\n\t\tdescription             string\n\t\timportpath              string\n\t\tbo                      *options.BuildOptions\n\t\twantQualifiedImportpath string\n\t\tshouldBuildError        bool\n\t}{\n\t\t{\n\t\t\tdescription: \"test app with already qualified import path\",\n\t\t\timportpath:  \"ko://github.com/google/ko/test\",\n\t\t\tbo: &options.BuildOptions{\n\t\t\t\tBaseImage:        baseImage,\n\t\t\t\tConcurrentBuilds: 1,\n\t\t\t\tPlatforms:        []string{\"all\"},\n\t\t\t},\n\t\t\twantQualifiedImportpath: \"ko://github.com/google/ko/test\",\n\t\t\tshouldBuildError:        false,\n\t\t},\n\t\t{\n\t\t\tdescription: \"programmatic build config\",\n\t\t\timportpath:  \"./test\",\n\t\t\tbo: &options.BuildOptions{\n\t\t\t\tBaseImage: baseImage,\n\t\t\t\tBuildConfigs: map[string]build.Config{\n\t\t\t\t\t\"github.com/google/ko/test\": {\n\t\t\t\t\t\tID: \"id-can-be-anything\",\n\t\t\t\t\t\t// no easy way to assert on the output, so trigger error to ensure config is picked up\n\t\t\t\t\t\tFlags: []string{\"-invalid-flag-should-cause-error\"},\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tConcurrentBuilds: 1,\n\t\t\t\tWorkingDirectory: \"../..\",\n\t\t\t},\n\t\t\twantQualifiedImportpath: \"ko://github.com/google/ko/test\",\n\t\t\tshouldBuildError:        true,\n\t\t},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.description, func(t *testing.T) {\n\t\t\tctx := context.Background()\n\t\t\tbuilder, err := NewBuilder(ctx, test.bo)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"NewBuilder(): %v\", err)\n\t\t\t}\n\t\t\tqualifiedImportpath, err := builder.QualifyImport(test.importpath)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"builder.QualifyImport(%s): %v\", test.importpath, err)\n\t\t\t}\n\t\t\tif qualifiedImportpath != test.wantQualifiedImportpath {\n\t\t\t\tt.Fatalf(\"incorrect qualified import path, got %s, wanted %s\", qualifiedImportpath, test.wantQualifiedImportpath)\n\t\t\t}\n\t\t\t_, err = builder.Build(ctx, qualifiedImportpath)\n\t\t\tif err != nil && !test.shouldBuildError {\n\t\t\t\tt.Fatalf(\"builder.Build(): %v\", err)\n\t\t\t}\n\t\t\tif err == nil && test.shouldBuildError {\n\t\t\t\tt.Fatalf(\"expected error got nil\")\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNewPublisherCanPublish(t *testing.T) {\n\tdockerRepo := \"registry.example.com/repo\"\n\tlocalDomain := \"localdomain.example.com/repo\"\n\timportpath := \"github.com/google/ko/test\"\n\ttests := []struct {\n\t\tdescription   string\n\t\twantImageName string\n\t\tpo            *options.PublishOptions\n\t\tshouldError   bool\n\t\twantError     error\n\t}{\n\t\t{\n\t\t\tdescription:   \"base import path\",\n\t\t\twantImageName: fmt.Sprintf(\"%s/%s\", dockerRepo, path.Base(importpath)),\n\t\t\tpo: &options.PublishOptions{\n\t\t\t\tBaseImportPaths: true,\n\t\t\t\tDockerRepo:      dockerRepo,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdescription:   \"preserve import path\",\n\t\t\twantImageName: fmt.Sprintf(\"%s/%s\", dockerRepo, importpath),\n\t\t\tpo: &options.PublishOptions{\n\t\t\t\tDockerRepo:          dockerRepo,\n\t\t\t\tPreserveImportPaths: true,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdescription:   \"override LocalDomain\",\n\t\t\twantImageName: fmt.Sprintf(\"%s/%s\", localDomain, importpath),\n\t\t\tpo: &options.PublishOptions{\n\t\t\t\tLocal:               true,\n\t\t\t\tLocalDomain:         localDomain,\n\t\t\t\tPreserveImportPaths: true,\n\t\t\t\tDockerClient:        &kotesting.MockDaemon{},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tdescription:   \"override DockerClient\",\n\t\t\twantImageName: strings.ToLower(fmt.Sprintf(\"%s/%s\", localDomain, importpath)),\n\t\t\tpo: &options.PublishOptions{\n\t\t\t\tDockerClient: &erroringClient{},\n\t\t\t\tLocal:        true,\n\t\t\t},\n\t\t\tshouldError: true,\n\t\t\twantError:   errImageTag,\n\t\t},\n\t\t{\n\t\t\tdescription:   \"bare with local domain and repo\",\n\t\t\twantImageName: strings.ToLower(fmt.Sprintf(\"%s/foo\", dockerRepo)),\n\t\t\tpo: &options.PublishOptions{\n\t\t\t\tDockerRepo: dockerRepo + \"/foo\",\n\t\t\t\tLocal:      true,\n\t\t\t\tBare:       true,\n\t\t\t},\n\t\t},\n\t}\n\tfor _, test := range tests {\n\t\tt.Run(test.description, func(t *testing.T) {\n\t\t\tpublisher, err := NewPublisher(test.po)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"NewPublisher(): %v\", err)\n\t\t\t}\n\t\t\tdefer publisher.Close()\n\t\t\tref, err := publisher.Publish(context.Background(), empty.Image, build.StrictScheme+importpath)\n\t\t\tif test.shouldError {\n\t\t\t\tif err == nil || !strings.HasSuffix(err.Error(), test.wantError.Error()) {\n\t\t\t\t\tt.Errorf(\"%s: got error %v, wanted %v\", test.description, err, test.wantError)\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"publisher.Publish(): %v\", err)\n\t\t\t}\n\t\t\tgotImageName := ref.Context().Name()\n\t\t\tif gotImageName != test.wantImageName {\n\t\t\t\tt.Errorf(\"got %s, wanted %s\", gotImageName, test.wantImageName)\n\t\t\t}\n\t\t})\n\t}\n}\n\n// registryServerWithImage starts a local registry and pushes a random image.\n// Use this to speed up tests, by not having to reach out to gcr.io for the default base image.\n// The registry uses a NOP logger to avoid spamming test logs.\n// Remember to call `defer Close()` on the returned `httptest.Server`.\nfunc registryServerWithImage(namespace string) (*httptest.Server, error) {\n\tnopLog := log.New(io.Discard, \"\", 0)\n\tr := registry.New(registry.Logger(nopLog))\n\ts := httptest.NewServer(r)\n\timageName := fmt.Sprintf(\"%s/%s\", s.Listener.Addr().String(), namespace)\n\timage, err := random.Image(1024, 1)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"random.Image(): %w\", err)\n\t}\n\tcrane.Push(image, imageName)\n\treturn s, nil\n}\n\nfunc mustRepository(s string) name.Repository {\n\tn, err := name.NewRepository(s)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn n\n}\n\nfunc mustDigest(img v1.Image) v1.Hash {\n\td, err := img.Digest()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn d\n}\n\nfunc mustRandom() v1.Image {\n\timg, err := random.Image(1024, 5)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn img\n}\n\nfunc yamlToTmpFile(t *testing.T, yaml []byte) string {\n\tt.Helper()\n\n\ttmpfile, err := os.CreateTemp(\"\", \"doc\")\n\tif err != nil {\n\t\tt.Fatalf(\"error creating temp file: %v\", err)\n\t}\n\n\tif _, err := tmpfile.Write(yaml); err != nil {\n\t\tt.Fatalf(\"error writing temp file: %v\", err)\n\t}\n\n\tif err := tmpfile.Close(); err != nil {\n\t\tt.Fatalf(\"error closing temp file: %v\", err)\n\t}\n\n\treturn tmpfile.Name()\n}\n"
  },
  {
    "path": "pkg/commands/root.go",
    "content": "// Copyright 2021 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage commands\n\nimport (\n\t\"os\"\n\n\tcranecmd \"github.com/google/go-containerregistry/cmd/crane/cmd\"\n\t\"github.com/google/go-containerregistry/pkg/logs\"\n\t\"github.com/spf13/cobra\"\n\t\"go.uber.org/automaxprocs/maxprocs\"\n)\n\nvar Root = New()\n\nfunc New() *cobra.Command {\n\tvar verbose bool\n\troot := &cobra.Command{\n\t\tUse:               \"ko\",\n\t\tShort:             \"Rapidly iterate with Go, Containers, and Kubernetes.\",\n\t\tSilenceUsage:      true, // Don't show usage on errors\n\t\tDisableAutoGenTag: true,\n\t\tPersistentPreRun: func(_ *cobra.Command, _ []string) {\n\t\t\tif verbose {\n\t\t\t\tlogs.Warn.SetOutput(os.Stderr)\n\t\t\t\tlogs.Debug.SetOutput(os.Stderr)\n\t\t\t}\n\t\t\tlogs.Progress.SetOutput(os.Stderr)\n\n\t\t\tmaxprocs.Set(maxprocs.Logger(logs.Debug.Printf))\n\t\t},\n\t\tRun: func(cmd *cobra.Command, _ []string) {\n\t\t\tcmd.Help()\n\t\t},\n\t}\n\troot.PersistentFlags().BoolVarP(&verbose, \"verbose\", \"v\", false, \"Enable debug logs\")\n\n\tAddKubeCommands(root)\n\n\t// Also add the auth group from crane to facilitate logging into a\n\t// registry.\n\tauthCmd := cranecmd.NewCmdAuth(nil, \"ko\", \"auth\")\n\t// That was a mistake, but just set it to Hidden so we don't break people.\n\tauthCmd.Hidden = true\n\troot.AddCommand(authCmd)\n\n\t// Just add a `ko login` command:\n\troot.AddCommand(cranecmd.NewCmdAuthLogin(\"ko\"))\n\treturn root\n}\n"
  },
  {
    "path": "pkg/commands/run.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage commands\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"strings\"\n\n\t\"github.com/google/ko/pkg/commands/options\"\n\t\"github.com/spf13/cobra\"\n)\n\n// addRun augments our CLI surface with run.\nfunc addRun(topLevel *cobra.Command) {\n\tpo := &options.PublishOptions{}\n\tbo := &options.BuildOptions{}\n\n\trun := &cobra.Command{\n\t\tUse:   \"run IMPORTPATH\",\n\t\tShort: \"A variant of `kubectl run` that containerizes IMPORTPATH first.\",\n\t\tLong:  `This sub-command combines \"ko build\" and \"kubectl run\" to support containerizing and running Go binaries on Kubernetes in a single command.`,\n\t\tExample: `\n  # Publish the image and run it on Kubernetes as:\n  #   ${KO_DOCKER_REPO}/<package name>-<hash of import path>\n  # When KO_DOCKER_REPO is ko.local, it is the same as if\n  # --local and --preserve-import-paths were passed.\n  ko run github.com/foo/bar/cmd/baz\n\n  # This supports relative import paths as well.\n  ko run ./cmd/baz\n\n  # You can also supply args and flags to the command.\n  ko run ./cmd/baz -- -v arg1 arg2 --yes`,\n\t\tRunE: func(cmd *cobra.Command, args []string) error {\n\t\t\tif err := options.Validate(po, bo); err != nil {\n\t\t\t\treturn fmt.Errorf(\"validating options: %w\", err)\n\t\t\t}\n\n\t\t\tctx := cmd.Context()\n\n\t\t\t// Args after -- are for kubectl, so only consider importPaths before it.\n\t\t\timportPaths := args\n\t\t\tdashes := cmd.Flags().ArgsLenAtDash()\n\t\t\tif dashes != -1 {\n\t\t\t\timportPaths = args[:cmd.Flags().ArgsLenAtDash()]\n\t\t\t}\n\t\t\tif len(importPaths) == 0 {\n\t\t\t\treturn errors.New(\"ko run: no importpaths listed\")\n\t\t\t}\n\n\t\t\tkubectlArgs := []string{}\n\t\t\tdashes = unparsedDashes()\n\t\t\tif dashes != -1 && dashes != len(os.Args) {\n\t\t\t\tkubectlArgs = os.Args[dashes+1:]\n\t\t\t}\n\n\t\t\tbo.InsecureRegistry = po.InsecureRegistry\n\t\t\tbuilder, err := makeBuilder(ctx, bo)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"error creating builder: %w\", err)\n\t\t\t}\n\t\t\tpublisher, err := makePublisher(po)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"error creating publisher: %w\", err)\n\t\t\t}\n\t\t\tdefer publisher.Close()\n\n\t\t\tif len(os.Args) < 3 {\n\t\t\t\treturn fmt.Errorf(\"usage: %s run <package>\", os.Args[0])\n\t\t\t}\n\t\t\tip := os.Args[2]\n\t\t\tif strings.HasPrefix(ip, \"-\") {\n\t\t\t\treturn fmt.Errorf(\"expected first arg to be positional, got %q\", ip)\n\t\t\t}\n\t\t\timgs, err := publishImages(ctx, importPaths, publisher, builder)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"failed to publish images: %w\", err)\n\t\t\t}\n\n\t\t\t// Usually only one, but this is the simple way to access the\n\t\t\t// reference since the import path may have been qualified.\n\t\t\tfor k, ref := range imgs {\n\t\t\t\tlog.Printf(\"Running %q\", k)\n\t\t\t\tpod := filepath.Base(ref.Context().String())\n\n\t\t\t\t// These are better defaults:\n\t\t\t\tdefaults := []string{\n\t\t\t\t\t\"--attach\",                 // stream logs back\n\t\t\t\t\t\"--rm\",                     // clean up after ourselves\n\t\t\t\t\t\"--restart=Never\",          // we just want to run once\n\t\t\t\t\t\"--log-flush-frequency=1s\", // flush logs more often\n\t\t\t\t}\n\n\t\t\t\t// Replaced \"<package>\" with \"--image=<published image>\".\n\t\t\t\targv := []string{\"--image\", ref.String()}\n\n\t\t\t\t// Add our default kubectl flags.\n\t\t\t\t// TODO: Add some way to override these.\n\t\t\t\targv = append(argv, defaults...)\n\n\t\t\t\t// If present, adds -- arg1 arg2...\n\t\t\t\targv = append(argv, kubectlArgs...)\n\n\t\t\t\t// \"run <package> <defaults> --image <ref> <kubectlArgs>\"\n\t\t\t\targv = append([]string{\"run\", pod}, argv...)\n\n\t\t\t\tlog.Printf(\"$ kubectl %s\", strings.Join(argv, \" \"))\n\t\t\t\tkubectlCmd := exec.CommandContext(ctx, \"kubectl\", argv...)\n\n\t\t\t\t// Pass through our environment\n\t\t\t\tkubectlCmd.Env = os.Environ()\n\t\t\t\t// Pass through our std*\n\t\t\t\tkubectlCmd.Stderr = os.Stderr\n\t\t\t\tkubectlCmd.Stdout = os.Stdout\n\t\t\t\tkubectlCmd.Stdin = os.Stdin\n\n\t\t\t\t// Run it.\n\t\t\t\tif err := kubectlCmd.Run(); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn nil\n\t\t},\n\t\t// We ignore unknown flags to avoid importing everything Go exposes\n\t\t// from our commands.\n\t\tFParseErrWhitelist: cobra.FParseErrWhitelist{\n\t\t\tUnknownFlags: true,\n\t\t},\n\t}\n\toptions.AddPublishArg(run, po)\n\toptions.AddBuildOptions(run, bo)\n\n\ttopLevel.AddCommand(run)\n}\n\nfunc unparsedDashes() int {\n\tfor i, s := range os.Args {\n\t\tif s == \"--\" {\n\t\t\treturn i\n\t\t}\n\t}\n\treturn -1\n}\n"
  },
  {
    "path": "pkg/commands/version.go",
    "content": "// Copyright 2019 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage commands\n\nimport (\n\t\"fmt\"\n\t\"runtime/debug\"\n\n\t\"github.com/spf13/cobra\"\n)\n\n// Version is provided by govvv at compile-time\nvar Version string\n\n// addVersion augments our CLI surface with version.\nfunc addVersion(topLevel *cobra.Command) {\n\ttopLevel.AddCommand(&cobra.Command{\n\t\tUse:   \"version\",\n\t\tShort: `Print ko version.`,\n\t\tRun: func(_ *cobra.Command, _ []string) {\n\t\t\tv := version()\n\t\t\tif v == \"\" {\n\t\t\t\tfmt.Println(\"could not determine build information\")\n\t\t\t} else {\n\t\t\t\tfmt.Println(v)\n\t\t\t}\n\t\t},\n\t})\n}\n\nfunc version() string {\n\tif Version == \"\" {\n\t\ti, ok := debug.ReadBuildInfo()\n\t\tif !ok {\n\t\t\treturn \"\"\n\t\t}\n\t\tVersion = i.Main.Version\n\t}\n\treturn Version\n}\n"
  },
  {
    "path": "pkg/doc.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// Package ko holds libraries used to implement the ko CLI.\npackage ko\n"
  },
  {
    "path": "pkg/internal/git/clone.go",
    "content": "// Copyright 2024 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// MIT License\n//\n// Copyright (c) 2016-2022 Carlos Alexandro Becker\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\npackage git\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os/exec\"\n)\n\n// Clone the git repository from the repoURL to the specified dir.\nfunc Clone(ctx context.Context, dir string, repoURL string) error {\n\trc := runConfig{\n\t\tdir:  dir,\n\t\targs: []string{\"clone\", \"--depth\", \"1\", repoURL, \".\"},\n\t}\n\n\tcmd := exec.CommandContext(ctx, \"git\", \"clone\", repoURL, dir)\n\tcmd.Dir = dir\n\n\t_, err := run(ctx, rc)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"running git clone: %w\", err)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/internal/git/errors.go",
    "content": "// Copyright 2024 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// MIT License\n//\n// Copyright (c) 2016-2022 Carlos Alexandro Becker\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\npackage git\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n)\n\nvar (\n\t// ErrNoTag happens if the underlying git repository doesn't contain any tags\n\t// but no snapshot-release was requested.\n\tErrNoTag = errors.New(\"git doesn't contain any tags. Tag info will not be available\")\n\n\t// ErrNotRepository happens if you try to run ko against a folder\n\t// which is not a git repository.\n\tErrNotRepository = errors.New(\"current folder is not a git repository. Git info will not be available\")\n\n\t// ErrNoGit happens when git is not present in PATH.\n\tErrNoGit = errors.New(\"git not present in PATH. Git info will not be available\")\n)\n\n// ErrDirty happens when the repo has uncommitted/unstashed changes.\ntype ErrDirty struct {\n\tstatus string\n}\n\nfunc (e ErrDirty) Error() string {\n\treturn 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)\n}\n"
  },
  {
    "path": "pkg/internal/git/git.go",
    "content": "// Copyright 2024 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// MIT License\n//\n// Copyright (c) 2016-2022 Carlos Alexandro Becker\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\npackage git\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"os/exec\"\n\t\"strings\"\n)\n\ntype runConfig struct {\n\tdir  string\n\tenv  []string\n\targs []string\n}\n\n// run a git command and returns its output or errors.\nfunc run(ctx context.Context, cfg runConfig) (string, error) {\n\textraArgs := []string{\n\t\t\"-c\", \"log.showSignature=false\",\n\t}\n\tcfg.args = append(extraArgs, cfg.args...)\n\t/* #nosec */\n\tcmd := exec.CommandContext(ctx, \"git\", cfg.args...)\n\tcmd.Dir = cfg.dir\n\n\tstdout := bytes.Buffer{}\n\tstderr := bytes.Buffer{}\n\n\tcmd.Stdout = &stdout\n\tcmd.Stderr = &stderr\n\tcmd.Env = append(cmd.Env, cfg.env...)\n\n\terr := cmd.Run()\n\n\tif err != nil {\n\t\treturn \"\", errors.New(stderr.String())\n\t}\n\n\treturn stdout.String(), nil\n}\n\n// clean the output.\nfunc clean(output string, err error) (string, error) {\n\toutput = strings.ReplaceAll(strings.Split(output, \"\\n\")[0], \"'\", \"\")\n\tif err != nil {\n\t\terr = errors.New(strings.TrimSuffix(err.Error(), \"\\n\"))\n\t}\n\treturn output, err\n}\n\n// cleanAllLines returns all the non-empty lines of the output, cleaned up.\nfunc cleanAllLines(output string, err error) ([]string, error) {\n\tresult := make([]string, 0)\n\tfor line := range strings.SplitSeq(output, \"\\n\") {\n\t\tl := strings.TrimSpace(strings.ReplaceAll(line, \"'\", \"\"))\n\t\tif l == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\tresult = append(result, l)\n\t}\n\t// TODO: maybe check for exec.ExitError only?\n\tif err != nil {\n\t\terr = errors.New(strings.TrimSuffix(err.Error(), \"\\n\"))\n\t}\n\treturn result, err\n}\n"
  },
  {
    "path": "pkg/internal/git/info.go",
    "content": "// Copyright 2024 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// MIT License\n//\n// Copyright (c) 2016-2022 Carlos Alexandro Becker\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\npackage git\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os/exec\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n)\n\n// Info includes tags and diffs used in some point.\ntype Info struct {\n\tBranch      string\n\tTag         string\n\tShortCommit string\n\tFullCommit  string\n\tCommitDate  time.Time\n\tDirty       bool\n}\n\n// TemplateValue converts this Info into a map for use in golang templates.\nfunc (i Info) TemplateValue() map[string]any {\n\ttreeState := \"clean\"\n\tif i.Dirty {\n\t\ttreeState = \"dirty\"\n\t}\n\n\treturn map[string]any{\n\t\t\"Branch\":          i.Branch,\n\t\t\"Tag\":             i.Tag,\n\t\t\"ShortCommit\":     i.ShortCommit,\n\t\t\"FullCommit\":      i.FullCommit,\n\t\t\"CommitDate\":      i.CommitDate.UTC().Format(time.RFC3339),\n\t\t\"CommitTimestamp\": i.CommitDate.UTC().Unix(),\n\t\t\"IsDirty\":         i.Dirty,\n\t\t\"IsClean\":         !i.Dirty,\n\t\t\"TreeState\":       treeState,\n\t}\n}\n\n// GetInfo returns git information for the given directory\nfunc GetInfo(ctx context.Context, dir string) (Info, error) {\n\tif _, err := exec.LookPath(\"git\"); err != nil {\n\t\treturn Info{}, ErrNoGit\n\t}\n\n\tif !isRepo(ctx, dir) {\n\t\treturn Info{}, ErrNotRepository\n\t}\n\n\tbranch, err := getBranch(ctx, dir)\n\tif err != nil {\n\t\treturn Info{}, fmt.Errorf(\"couldn't get current branch: %w\", err)\n\t}\n\tshort, err := getShortCommit(ctx, dir)\n\tif err != nil {\n\t\treturn Info{}, fmt.Errorf(\"couldn't get current commit: %w\", err)\n\t}\n\tfull, err := getFullCommit(ctx, dir)\n\tif err != nil {\n\t\treturn Info{}, fmt.Errorf(\"couldn't get current commit: %w\", err)\n\t}\n\tdate, err := getCommitDate(ctx, dir)\n\tif err != nil {\n\t\treturn Info{}, fmt.Errorf(\"couldn't get commit date: %w\", err)\n\t}\n\n\tdirty := checkDirty(ctx, dir)\n\n\t// TODO: allow exclusions.\n\ttag, err := getTag(ctx, dir, []string{})\n\tif err != nil {\n\t\treturn Info{\n\t\t\tBranch:      branch,\n\t\t\tFullCommit:  full,\n\t\t\tShortCommit: short,\n\t\t\tCommitDate:  date,\n\t\t\tTag:         \"v0.0.0\",\n\t\t\tDirty:       dirty != nil,\n\t\t}, errors.Join(ErrNoTag, dirty)\n\t}\n\n\treturn Info{\n\t\tBranch:      branch,\n\t\tTag:         tag,\n\t\tFullCommit:  full,\n\t\tShortCommit: short,\n\t\tCommitDate:  date,\n\t\tDirty:       dirty != nil,\n\t}, dirty\n}\n\n// isRepo returns true if current folder is a git repository.\nfunc isRepo(ctx context.Context, dir string) bool {\n\tout, err := run(ctx, runConfig{\n\t\tdir:  dir,\n\t\targs: []string{\"rev-parse\", \"--is-inside-work-tree\"},\n\t})\n\treturn err == nil && strings.TrimSpace(out) == \"true\"\n}\n\n// checkDirty returns an error if the current git repository is dirty.\nfunc checkDirty(ctx context.Context, dir string) error {\n\tout, err := run(ctx, runConfig{\n\t\tdir:  dir,\n\t\targs: []string{\"status\", \"--porcelain\"},\n\t})\n\tif strings.TrimSpace(out) != \"\" || err != nil {\n\t\treturn ErrDirty{status: out}\n\t}\n\treturn nil\n}\n\nfunc getBranch(ctx context.Context, dir string) (string, error) {\n\treturn clean(run(ctx, runConfig{\n\t\tdir:  dir,\n\t\targs: []string{\"rev-parse\", \"--abbrev-ref\", \"HEAD\", \"--quiet\"},\n\t}))\n}\n\nfunc getCommitDate(ctx context.Context, dir string) (time.Time, error) {\n\tct, err := clean(run(ctx, runConfig{\n\t\tdir:  dir,\n\t\targs: []string{\"show\", \"--format='%ct'\", \"HEAD\", \"--quiet\"},\n\t}))\n\tif err != nil {\n\t\treturn time.Time{}, err\n\t}\n\tif ct == \"\" {\n\t\treturn time.Time{}, nil\n\t}\n\ti, err := strconv.ParseInt(ct, 10, 64)\n\tif err != nil {\n\t\treturn time.Time{}, err\n\t}\n\tt := time.Unix(i, 0).UTC()\n\treturn t, nil\n}\n\nfunc getShortCommit(ctx context.Context, dir string) (string, error) {\n\treturn clean(run(ctx, runConfig{\n\t\tdir:  dir,\n\t\targs: []string{\"show\", \"--format=%h\", \"HEAD\", \"--quiet\"},\n\t}))\n}\n\nfunc getFullCommit(ctx context.Context, dir string) (string, error) {\n\treturn clean(run(ctx, runConfig{\n\t\tdir:  dir,\n\t\targs: []string{\"show\", \"--format=%H\", \"HEAD\", \"--quiet\"},\n\t}))\n}\n\nfunc getTag(ctx context.Context, dir string, excluding []string) (string, error) {\n\t// this will get the last tag, even if it wasn't made against the\n\t// last commit...\n\ttags, err := cleanAllLines(gitDescribe(ctx, dir, \"HEAD\", excluding))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\ttag := filterOut(tags, excluding)\n\treturn tag, err\n}\n\nfunc gitDescribe(ctx context.Context, dir, ref string, excluding []string) (string, error) {\n\targs := []string{\n\t\t\"describe\",\n\t\t\"--tags\",\n\t\t\"--abbrev=0\",\n\t\tref,\n\t}\n\tfor _, exclude := range excluding {\n\t\targs = append(args, \"--exclude=\"+exclude)\n\t}\n\treturn clean(run(ctx, runConfig{\n\t\tdir:  dir,\n\t\targs: args,\n\t}))\n}\n\nfunc filterOut(tags []string, exclude []string) string {\n\tif len(exclude) == 0 && len(tags) > 0 {\n\t\treturn tags[0]\n\t}\n\tfor _, tag := range tags {\n\t\tfor _, exl := range exclude {\n\t\t\tif exl != tag {\n\t\t\t\treturn tag\n\t\t\t}\n\t\t}\n\t}\n\treturn \"\"\n}\n"
  },
  {
    "path": "pkg/internal/git/info_test.go",
    "content": "// Copyright 2024 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// MIT License\n//\n// Copyright (c) 2016-2022 Carlos Alexandro Becker\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\npackage git_test\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"testing\"\n\n\t\"github.com/google/ko/pkg/internal/git\"\n\t\"github.com/google/ko/pkg/internal/gittesting\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nconst fakeGitURL = \"git@github.com:foo/bar.git\"\n\nfunc TestNotAGitFolder(t *testing.T) {\n\tdir := t.TempDir()\n\ti, err := git.GetInfo(context.TODO(), dir)\n\trequire.ErrorIs(t, err, git.ErrNotRepository)\n\n\ttpl := i.TemplateValue()\n\trequireEmpty(t, tpl)\n}\n\nfunc TestSingleCommit(t *testing.T) {\n\tdir := t.TempDir()\n\tgittesting.GitInit(t, dir)\n\tgittesting.GitRemoteAdd(t, dir, fakeGitURL)\n\tgittesting.GitCommit(t, dir, \"commit1\")\n\tgittesting.GitTag(t, dir, \"v0.0.1\")\n\ti, err := git.GetInfo(context.TODO(), dir)\n\trequire.NoError(t, err)\n\n\ttpl := i.TemplateValue()\n\trequire.Equal(t, \"main\", tpl[\"Branch\"])\n\trequire.Equal(t, \"v0.0.1\", tpl[\"Tag\"])\n\trequire.NotEmpty(t, tpl[\"ShortCommit\"].(string))\n\trequire.NotEmpty(t, tpl[\"FullCommit\"].(string))\n\trequire.NotEmpty(t, tpl[\"CommitDate\"].(string))\n\trequire.NotZero(t, tpl[\"CommitTimestamp\"].(int64))\n\trequire.False(t, tpl[\"IsDirty\"].(bool))\n\trequire.True(t, tpl[\"IsClean\"].(bool))\n\trequire.Equal(t, \"clean\", tpl[\"TreeState\"])\n}\n\nfunc TestBranch(t *testing.T) {\n\tdir := t.TempDir()\n\tgittesting.GitInit(t, dir)\n\tgittesting.GitRemoteAdd(t, dir, fakeGitURL)\n\tgittesting.GitCommit(t, dir, \"test-branch-commit\")\n\tgittesting.GitTag(t, dir, \"test-branch-tag\")\n\tgittesting.GitCheckoutBranch(t, dir, \"test-branch\")\n\ti, err := git.GetInfo(context.TODO(), dir)\n\trequire.NoError(t, err)\n\n\ttpl := i.TemplateValue()\n\trequire.Equal(t, \"test-branch\", tpl[\"Branch\"])\n\trequire.Equal(t, \"test-branch-tag\", tpl[\"Tag\"])\n\trequire.NotEmpty(t, tpl[\"ShortCommit\"].(string))\n\trequire.NotEmpty(t, tpl[\"FullCommit\"].(string))\n\trequire.NotEmpty(t, tpl[\"CommitDate\"].(string))\n\trequire.NotZero(t, tpl[\"CommitTimestamp\"].(int64))\n\trequire.False(t, tpl[\"IsDirty\"].(bool))\n\trequire.True(t, tpl[\"IsClean\"].(bool))\n\trequire.Equal(t, \"clean\", tpl[\"TreeState\"])\n}\n\nfunc TestNewRepository(t *testing.T) {\n\tdir := t.TempDir()\n\tgittesting.GitInit(t, dir)\n\ti, err := git.GetInfo(context.TODO(), dir)\n\t// TODO: improve this error handling\n\trequire.ErrorContains(t, err, `fatal: ambiguous argument 'HEAD'`)\n\n\ttpl := i.TemplateValue()\n\trequireEmpty(t, tpl)\n}\n\nfunc TestNoTags(t *testing.T) {\n\tdir := t.TempDir()\n\tgittesting.GitInit(t, dir)\n\tgittesting.GitRemoteAdd(t, dir, fakeGitURL)\n\tgittesting.GitCommit(t, dir, \"first\")\n\ti, err := git.GetInfo(context.TODO(), dir)\n\trequire.ErrorIs(t, err, git.ErrNoTag)\n\n\ttpl := i.TemplateValue()\n\trequire.Equal(t, \"main\", tpl[\"Branch\"])\n\trequire.Equal(t, \"v0.0.0\", tpl[\"Tag\"])\n\trequire.NotEmpty(t, tpl[\"ShortCommit\"].(string))\n\trequire.NotEmpty(t, tpl[\"FullCommit\"].(string))\n\trequire.NotEmpty(t, tpl[\"CommitDate\"].(string))\n\trequire.NotZero(t, tpl[\"CommitTimestamp\"].(int64))\n\trequire.False(t, tpl[\"IsDirty\"].(bool))\n\trequire.True(t, tpl[\"IsClean\"].(bool))\n\trequire.Equal(t, \"clean\", tpl[\"TreeState\"])\n}\n\nfunc TestDirty(t *testing.T) {\n\tdir := t.TempDir()\n\tgittesting.GitInit(t, dir)\n\tgittesting.GitRemoteAdd(t, dir, fakeGitURL)\n\ttestFile, err := os.Create(filepath.Join(dir, \"testFile\"))\n\trequire.NoError(t, err)\n\trequire.NoError(t, testFile.Close())\n\tgittesting.GitAdd(t, dir)\n\tgittesting.GitCommit(t, dir, \"commit2\")\n\tgittesting.GitTag(t, dir, \"v0.0.1\")\n\trequire.NoError(t, os.WriteFile(testFile.Name(), []byte(\"lorem ipsum\"), 0o644))\n\ti, err := git.GetInfo(context.TODO(), dir)\n\trequire.ErrorContains(t, err, \"git is in a dirty state\")\n\n\ttpl := i.TemplateValue()\n\trequire.Equal(t, \"main\", tpl[\"Branch\"])\n\trequire.Equal(t, \"v0.0.1\", tpl[\"Tag\"])\n\trequire.NotEmpty(t, tpl[\"ShortCommit\"].(string))\n\trequire.NotEmpty(t, tpl[\"FullCommit\"].(string))\n\trequire.NotEmpty(t, tpl[\"CommitDate\"].(string))\n\trequire.NotZero(t, tpl[\"CommitTimestamp\"].(int64))\n\trequire.True(t, tpl[\"IsDirty\"].(bool))\n\trequire.False(t, tpl[\"IsClean\"].(bool))\n\trequire.Equal(t, \"dirty\", tpl[\"TreeState\"])\n}\n\nfunc TestValidState(t *testing.T) {\n\tdir := t.TempDir()\n\tgittesting.GitInit(t, dir)\n\tgittesting.GitRemoteAdd(t, dir, fakeGitURL)\n\tgittesting.GitCommit(t, dir, \"commit3\")\n\tgittesting.GitTag(t, dir, \"v0.0.1\")\n\tgittesting.GitTag(t, dir, \"v0.0.2\")\n\tgittesting.GitCommit(t, dir, \"commit4\")\n\tgittesting.GitTag(t, dir, \"v0.0.3\")\n\ti, err := git.GetInfo(context.TODO(), dir)\n\trequire.NoError(t, err)\n\trequire.Equal(t, \"v0.0.3\", i.Tag)\n\trequire.False(t, i.Dirty)\n}\n\nfunc TestGitNotInPath(t *testing.T) {\n\tt.Setenv(\"PATH\", \"\")\n\ti, err := git.GetInfo(context.TODO(), \"\")\n\trequire.ErrorIs(t, err, git.ErrNoGit)\n\n\ttpl := i.TemplateValue()\n\trequireEmpty(t, tpl)\n}\n\nfunc requireEmpty(t *testing.T, tpl map[string]any) {\n\trequire.Equal(t, \"\", tpl[\"Branch\"])\n\trequire.Equal(t, \"\", tpl[\"Tag\"])\n\trequire.Equal(t, \"\", tpl[\"ShortCommit\"])\n\trequire.Equal(t, \"\", tpl[\"FullCommit\"])\n\trequire.False(t, tpl[\"IsDirty\"].(bool))\n\trequire.True(t, tpl[\"IsClean\"].(bool))\n\trequire.Equal(t, \"clean\", tpl[\"TreeState\"])\n}\n"
  },
  {
    "path": "pkg/internal/gittesting/git.go",
    "content": "// Copyright 2024 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// MIT License\n//\n// Copyright (c) 2016-2022 Carlos Alexandro Becker\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\npackage gittesting\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"os/exec\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\n// GitInit inits a new git project.\nfunc GitInit(t *testing.T, dir string) {\n\tt.Helper()\n\tout, err := fakeGit(dir, \"init\")\n\trequire.NoError(t, err)\n\trequire.Contains(t, out, \"Initialized empty Git repository\", \"\")\n\trequire.NoError(t, err)\n\tGitCheckoutBranch(t, dir, \"main\")\n\t_, _ = fakeGit(\"branch\", \"-D\", \"master\")\n}\n\n// GitRemoteAdd adds the given url as remote.\nfunc GitRemoteAdd(t *testing.T, dir, url string) {\n\tt.Helper()\n\tout, err := fakeGit(dir, \"remote\", \"add\", \"origin\", url)\n\trequire.NoError(t, err)\n\trequire.Empty(t, out)\n}\n\n// GitCommit creates a git commits.\nfunc GitCommit(t *testing.T, dir, msg string) {\n\tt.Helper()\n\tout, err := fakeGit(dir, \"commit\", \"--allow-empty\", \"-m\", msg)\n\trequire.NoError(t, err)\n\trequire.Contains(t, out, \"main\", msg)\n}\n\n// GitTag creates a git tag.\nfunc GitTag(t *testing.T, dir, tag string) {\n\tt.Helper()\n\tout, err := fakeGit(dir, \"tag\", tag)\n\trequire.NoError(t, err)\n\trequire.Empty(t, out)\n}\n\n// GitAdd adds all files to stage.\nfunc GitAdd(t *testing.T, dir string) {\n\tt.Helper()\n\tout, err := fakeGit(dir, \"add\", \"-A\")\n\trequire.NoError(t, err)\n\trequire.Empty(t, out)\n}\n\nfunc fakeGit(dir string, args ...string) (string, error) {\n\tallArgs := []string{\n\t\t\"-c\", \"user.name='GoReleaser'\",\n\t\t\"-c\", \"user.email='test@goreleaser.github.com'\",\n\t\t\"-c\", \"commit.gpgSign=false\",\n\t\t\"-c\", \"tag.gpgSign=false\",\n\t\t\"-c\", \"log.showSignature=false\",\n\t}\n\tallArgs = append(allArgs, args...)\n\treturn gitRun(dir, allArgs...)\n}\n\n// GitCheckoutBranch allows us to change the active branch that we're using.\nfunc GitCheckoutBranch(t *testing.T, dir, name string) {\n\tt.Helper()\n\tout, err := fakeGit(dir, \"checkout\", \"-b\", name)\n\trequire.NoError(t, err)\n\trequire.Empty(t, out)\n}\n\nfunc gitRun(dir string, args ...string) (string, error) {\n\tcmd := exec.Command(\"git\", args...)\n\tcmd.Dir = dir\n\n\tstdout := bytes.Buffer{}\n\tstderr := bytes.Buffer{}\n\n\tcmd.Stdout = &stdout\n\tcmd.Stderr = &stderr\n\n\terr := cmd.Run()\n\n\tif err != nil {\n\t\treturn \"\", errors.New(stderr.String())\n\t}\n\n\treturn stdout.String(), nil\n}\n"
  },
  {
    "path": "pkg/internal/gittesting/git_test.go",
    "content": "// Copyright 2024 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// MIT License\n//\n// Copyright (c) 2016-2022 Carlos Alexandro Becker\n//\n// Permission is hereby granted, free of charge, to any person obtaining a copy\n// of this software and associated documentation files (the \"Software\"), to deal\n// in the Software without restriction, including without limitation the rights\n// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\n// copies of the Software, and to permit persons to whom the Software is\n// furnished to do so, subject to the following conditions:\n//\n// The above copyright notice and this permission notice shall be included in all\n// copies or substantial portions of the Software.\n//\n// THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n// SOFTWARE.\n\npackage gittesting\n\nimport \"testing\"\n\nfunc TestGit(t *testing.T) {\n\tdir := t.TempDir()\n\tGitInit(t, dir)\n\tGitAdd(t, dir)\n\tGitCommit(t, dir, \"commit1\")\n\tGitRemoteAdd(t, dir, \"git@github.com:goreleaser/nope.git\")\n\tGitTag(t, dir, \"v1.0.0\")\n}\n"
  },
  {
    "path": "pkg/internal/testing/daemon.go",
    "content": "// Copyright 2021 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage testing\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"strings\"\n\n\t\"github.com/google/go-containerregistry/pkg/v1/daemon\"\n\t\"github.com/moby/moby/client\"\n)\n\ntype MockDaemon struct {\n\tdaemon.Client\n\tTags []string\n\n\tinspectErr  error\n\tinspectResp client.ImageInspectResult\n}\n\nfunc (m *MockDaemon) Ping(context.Context, client.PingOptions) (client.PingResult, error) {\n\treturn client.PingResult{}, nil\n}\nfunc (m *MockDaemon) ImageLoad(context.Context, io.Reader, ...client.ImageLoadOption) (client.ImageLoadResult, error) {\n\treturn io.NopCloser(strings.NewReader(\"Loaded\")), nil\n}\n\nfunc (m *MockDaemon) ImageTag(_ context.Context, opt client.ImageTagOptions) (client.ImageTagResult, error) {\n\tif m.Tags == nil {\n\t\tm.Tags = []string{}\n\t}\n\tm.Tags = append(m.Tags, opt.Target)\n\treturn client.ImageTagResult{}, nil\n}\n\nfunc (m *MockDaemon) ImageInspect(context.Context, string, ...client.ImageInspectOption) (client.ImageInspectResult, error) {\n\treturn m.inspectResp, m.inspectErr\n}\n"
  },
  {
    "path": "pkg/internal/testing/doc.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// Package testing holds a variety test doubles to help with testing\npackage testing\n"
  },
  {
    "path": "pkg/internal/testing/fixed.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage testing\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/google/go-containerregistry/pkg/name\"\n\tv1 \"github.com/google/go-containerregistry/pkg/v1\"\n\t\"github.com/google/ko/pkg/build\"\n\t\"github.com/google/ko/pkg/publish\"\n)\n\ntype fixedBuild struct {\n\tentries map[string]build.Result\n}\n\n// NewFixedBuild returns a build.Interface implementation that simply resolves\n// particular references to fixed v1.Image objects\nfunc NewFixedBuild(entries map[string]build.Result) build.Interface {\n\treturn &fixedBuild{entries}\n}\n\n// QualifyImport implements build.Interface\nfunc (f *fixedBuild) QualifyImport(ip string) (string, error) {\n\treturn ip, nil\n}\n\n// IsSupportedReference implements build.Interface\nfunc (f *fixedBuild) IsSupportedReference(s string) error {\n\ts = strings.TrimPrefix(s, build.StrictScheme)\n\tif _, ok := f.entries[s]; !ok {\n\t\treturn errors.New(\"importpath is not supported\")\n\t}\n\treturn nil\n}\n\n// Build implements build.Interface\nfunc (f *fixedBuild) Build(_ context.Context, s string) (build.Result, error) {\n\ts = strings.TrimPrefix(s, build.StrictScheme)\n\tif img, ok := f.entries[s]; ok {\n\t\treturn img, nil\n\t}\n\treturn nil, fmt.Errorf(\"unsupported reference: %q\", s)\n}\n\ntype fixedPublish struct {\n\tbase    name.Repository\n\tentries map[string]v1.Hash\n}\n\n// NewFixedPublish returns a publish.Interface implementation that simply\n// resolves particular references to fixed name.Digest references.\nfunc NewFixedPublish(base name.Repository, entries map[string]v1.Hash) publish.Interface {\n\treturn &fixedPublish{base, entries}\n}\n\n// Publish implements publish.Interface\nfunc (f *fixedPublish) Publish(_ context.Context, _ build.Result, s string) (name.Reference, error) {\n\ts = strings.TrimPrefix(s, build.StrictScheme)\n\th, ok := f.entries[s]\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"unsupported importpath: %q\", s)\n\t}\n\td, err := name.NewDigest(fmt.Sprintf(\"%s/%s@%s\", f.base, s, h))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &d, nil\n}\n\nfunc (f *fixedPublish) Close() error {\n\treturn nil\n}\n\nfunc ComputeDigest(base name.Repository, ref string, h v1.Hash) string {\n\td, err := name.NewDigest(fmt.Sprintf(\"%s/%s@%s\", base, ref, h))\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn d.String()\n}\n"
  },
  {
    "path": "pkg/internal/testing/fixed_test.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage testing\n\nimport (\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/google/go-containerregistry/pkg/name\"\n\tv1 \"github.com/google/go-containerregistry/pkg/v1\"\n\t\"github.com/google/go-containerregistry/pkg/v1/random\"\n\t\"github.com/google/ko/pkg/build\"\n)\n\nfunc TestFixedPublish(t *testing.T) {\n\thex1 := \"deadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeefdeadbeef\"\n\thex2 := \"baadf00dbaadf00dbaadf00dbaadf00dbaadf00dbaadf00dbaadf00dbaadf00d\"\n\tfixedBaseRepo, _ := name.NewRepository(\"gcr.io/asdf\")\n\tf := NewFixedPublish(fixedBaseRepo, map[string]v1.Hash{\n\t\t\"foo\": {\n\t\t\tAlgorithm: \"sha256\",\n\t\t\tHex:       hex1,\n\t\t},\n\t\t\"bar\": {\n\t\t\tAlgorithm: \"sha256\",\n\t\t\tHex:       hex2,\n\t\t},\n\t})\n\n\tfooDigest, err := f.Publish(context.Background(), nil, \"foo\")\n\tif err != nil {\n\t\tt.Errorf(\"Publish(foo) = %v\", err)\n\t}\n\tif got, want := fooDigest.String(), \"gcr.io/asdf/foo@sha256:\"+hex1; got != want {\n\t\tt.Errorf(\"Publish(foo) = %q, want %q\", got, want)\n\t}\n\n\tbarDigest, err := f.Publish(context.Background(), nil, \"bar\")\n\tif err != nil {\n\t\tt.Errorf(\"Publish(bar) = %v\", err)\n\t}\n\tif got, want := barDigest.String(), \"gcr.io/asdf/bar@sha256:\"+hex2; got != want {\n\t\tt.Errorf(\"Publish(bar) = %q, want %q\", got, want)\n\t}\n\n\td, err := f.Publish(context.Background(), nil, \"baz\")\n\tif err == nil {\n\t\tt.Errorf(\"Publish(baz) = %v, want error\", d)\n\t}\n}\n\nfunc TestFixedBuild(t *testing.T) {\n\ttestImage, _ := random.Image(1024, 5)\n\tf := NewFixedBuild(map[string]build.Result{\n\t\t\"asdf\": testImage,\n\t})\n\n\tif got := f.IsSupportedReference(\"asdf\"); got != nil {\n\t\tt.Errorf(\"IsSupportedReference(asdf) = (%v), want nil\", got)\n\t}\n\tif got, err := f.Build(context.Background(), \"asdf\"); err != nil {\n\t\tt.Errorf(\"Build(asdf) = %v, want %v\", err, testImage)\n\t} else if got != testImage {\n\t\tt.Errorf(\"Build(asdf) = %v, want %v\", got, testImage)\n\t}\n\n\tif got := f.IsSupportedReference(\"blah\"); got == nil {\n\t\tt.Error(\"IsSupportedReference(blah) = nil, want error\")\n\t}\n\tif got, err := f.Build(context.Background(), \"blah\"); err == nil {\n\t\tt.Errorf(\"Build(blah) = %v, want error\", got)\n\t}\n}\n"
  },
  {
    "path": "pkg/publish/daemon.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage publish\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/google/go-containerregistry/pkg/name\"\n\tv1 \"github.com/google/go-containerregistry/pkg/v1\"\n\t\"github.com/google/go-containerregistry/pkg/v1/daemon\"\n\t\"github.com/google/ko/pkg/build\"\n)\n\nconst (\n\t// LocalDomain is a sentinel \"registry\" that represents side-loading images into the daemon.\n\tLocalDomain = \"ko.local\"\n)\n\n// demon is intentionally misspelled to avoid name collision (and drive Jon nuts).\n// [Narrator: Jon wasn't the only one driven nuts.]\ntype demon struct {\n\tbase   string\n\tclient daemon.Client\n\tnamer  Namer\n\ttags   []string\n}\n\n// DaemonOption is a functional option for NewDaemon.\ntype DaemonOption func(*demon) error\n\n// WithLocalDomain is a functional option for overriding the domain used for images that are side-loaded into the daemon.\nfunc WithLocalDomain(domain string) DaemonOption {\n\treturn func(i *demon) error {\n\t\tif domain != \"\" {\n\t\t\ti.base = domain\n\t\t}\n\t\treturn nil\n\t}\n}\n\n// WithDockerClient is a functional option for overriding the docker client.\nfunc WithDockerClient(client daemon.Client) DaemonOption {\n\treturn func(i *demon) error {\n\t\tif client != nil {\n\t\t\ti.client = client\n\t\t}\n\t\treturn nil\n\t}\n}\n\n// NewDaemon returns a new publish.Interface that publishes images to a container daemon.\nfunc NewDaemon(namer Namer, tags []string, opts ...DaemonOption) (Interface, error) {\n\td := &demon{\n\t\tbase:  LocalDomain,\n\t\tnamer: namer,\n\t\ttags:  tags,\n\t}\n\tfor _, option := range opts {\n\t\tif err := option(d); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn d, nil\n}\n\nfunc (d *demon) getOpts(ctx context.Context) []daemon.Option {\n\treturn []daemon.Option{\n\t\tdaemon.WithContext(ctx),\n\t\tdaemon.WithClient(d.client),\n\t}\n}\n\n// Publish implements publish.Interface\nfunc (d *demon) Publish(ctx context.Context, br build.Result, s string) (name.Reference, error) {\n\ts = strings.TrimPrefix(s, build.StrictScheme)\n\t// https://github.com/google/go-containerregistry/issues/212\n\ts = strings.ToLower(s)\n\n\t// There's no way to write an index to a kind, so attempt to downcast it to an image.\n\tvar img v1.Image\n\tswitch i := br.(type) {\n\tcase v1.Image:\n\t\timg = i\n\tcase v1.ImageIndex:\n\t\tim, err := i.IndexManifest()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tgoos, goarch := os.Getenv(\"GOOS\"), os.Getenv(\"GOARCH\")\n\t\tif goos == \"\" {\n\t\t\tgoos = \"linux\"\n\t\t}\n\t\tif goarch == \"\" {\n\t\t\tgoarch = \"amd64\"\n\t\t}\n\t\tfor _, manifest := range im.Manifests {\n\t\t\tif manifest.Platform == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif manifest.Platform.OS != goos {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif manifest.Platform.Architecture != goarch {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\timg, err = i.Image(manifest.Digest)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t\tif img == nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to find %s/%s image in index for image: %v\", goos, goarch, s)\n\t\t}\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"failed to interpret %s result as image: %v\", s, br)\n\t}\n\n\th, err := img.Digest()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tdigestTag, err := name.NewTag(fmt.Sprintf(\"%s:%s\", d.namer(d.base, s), h.Hex))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlog.Printf(\"Loading %v\", digestTag)\n\tif resp, err := daemon.Write(digestTag, img, d.getOpts(ctx)...); err != nil {\n\t\tlog.Println(\"daemon.Write response: \", resp)\n\t\treturn nil, err\n\t}\n\tlog.Printf(\"Loaded %v\", digestTag)\n\n\tfor _, tagName := range d.tags {\n\t\tlog.Printf(\"Adding tag %v\", tagName)\n\t\ttag, err := name.NewTag(fmt.Sprintf(\"%s:%s\", d.namer(d.base, s), tagName))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif err := daemon.Tag(digestTag, tag, d.getOpts(ctx)...); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlog.Printf(\"Added tag %v\", tagName)\n\t}\n\n\treturn &digestTag, nil\n}\n\nfunc (d *demon) Close() error {\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/publish/daemon_test.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage publish_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/google/go-containerregistry/pkg/v1/random\"\n\tkotesting \"github.com/google/ko/pkg/internal/testing\"\n\t\"github.com/google/ko/pkg/publish\"\n)\n\nfunc TestDaemon(t *testing.T) {\n\timportpath := \"github.com/google/ko\"\n\timg, err := random.Image(1024, 1)\n\tif err != nil {\n\t\tt.Fatalf(\"random.Image() = %v\", err)\n\t}\n\n\tclient := &kotesting.MockDaemon{}\n\tdef, err := publish.NewDaemon(md5Hash, []string{}, publish.WithDockerClient(client))\n\tif err != nil {\n\t\tt.Fatalf(\"NewDaemon() = %v\", err)\n\t}\n\n\tif d, err := def.Publish(context.Background(), img, importpath); err != nil {\n\t\tt.Errorf(\"Publish() = %v\", err)\n\t} else if got, want := d.String(), md5Hash(\"ko.local\", importpath); !strings.HasPrefix(got, want) {\n\t\tt.Errorf(\"Publish() = %v, wanted prefix %v\", got, want)\n\t}\n}\n\nfunc TestDaemonTags(t *testing.T) {\n\timportpath := \"github.com/google/ko\"\n\timg, err := random.Image(1024, 1)\n\tif err != nil {\n\t\tt.Fatalf(\"random.Image() = %v\", err)\n\t}\n\n\tclient := &kotesting.MockDaemon{}\n\tdef, err := publish.NewDaemon(md5Hash, []string{\"v2.0.0\", \"v1.2.3\", \"production\"}, publish.WithDockerClient(client))\n\tif err != nil {\n\t\tt.Fatalf(\"NewDaemon() = %v\", err)\n\t}\n\n\tif d, err := def.Publish(context.Background(), img, importpath); err != nil {\n\t\tt.Errorf(\"Publish() = %v\", err)\n\t} else if got, want := d.String(), md5Hash(\"ko.local\", importpath); !strings.HasPrefix(got, want) {\n\t\tt.Errorf(\"Publish() = %v, wanted prefix %v\", got, want)\n\t}\n\n\timgDigest, err := img.Digest()\n\tif err != nil {\n\t\tt.Fatalf(\"img.Digest() = %v\", err)\n\t}\n\n\texpected := []string{fmt.Sprintf(\"ko.local/98b8c7facdad74510a7cae0cd368eb4e:%s\", strings.Replace(imgDigest.String(), \"sha256:\", \"\", 1)), \"ko.local/98b8c7facdad74510a7cae0cd368eb4e:v2.0.0\", \"ko.local/98b8c7facdad74510a7cae0cd368eb4e:v1.2.3\", \"ko.local/98b8c7facdad74510a7cae0cd368eb4e:production\"}\n\n\tfor i, v := range expected {\n\t\tif client.Tags[i] != v {\n\t\t\tt.Errorf(\"Expected tag %v got %v\", v, client.Tags[i])\n\t\t}\n\t}\n}\n\nfunc TestDaemonDomain(t *testing.T) {\n\timportpath := \"github.com/google/ko\"\n\timg, err := random.Image(1024, 1)\n\tif err != nil {\n\t\tt.Fatalf(\"random.Image() = %v\", err)\n\t}\n\n\tlocalDomain := \"registry.example.com/repository\"\n\tclient := &kotesting.MockDaemon{}\n\tdef, err := publish.NewDaemon(md5Hash, []string{}, publish.WithLocalDomain(localDomain), publish.WithDockerClient(client))\n\tif err != nil {\n\t\tt.Fatalf(\"NewDaemon() = %v\", err)\n\t}\n\n\tif d, err := def.Publish(context.Background(), img, importpath); err != nil {\n\t\tt.Errorf(\"Publish() = %v\", err)\n\t} else if got, want := d.String(), md5Hash(localDomain, importpath); !strings.HasPrefix(got, want) {\n\t\tt.Errorf(\"Publish() = %v, wanted prefix %v\", got, want)\n\t}\n}\n"
  },
  {
    "path": "pkg/publish/default.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage publish\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"log\"\n\t\"net/http\"\n\t\"path\"\n\t\"runtime\"\n\t\"strings\"\n\n\t\"github.com/google/go-containerregistry/pkg/authn\"\n\t\"github.com/google/go-containerregistry/pkg/name\"\n\tv1 \"github.com/google/go-containerregistry/pkg/v1\"\n\t\"github.com/google/go-containerregistry/pkg/v1/remote\"\n\t\"github.com/google/go-containerregistry/pkg/v1/types\"\n\t\"github.com/sigstore/cosign/v3/pkg/oci\"\n\tociremote \"github.com/sigstore/cosign/v3/pkg/oci/remote\"\n\t\"github.com/sigstore/cosign/v3/pkg/oci/walk\"\n\t\"golang.org/x/sync/errgroup\"\n\n\t\"github.com/google/ko/pkg/build\"\n)\n\n// defalt is intentionally misspelled to avoid keyword collision (and drive Jon nuts).\ntype defalt struct {\n\tbase     string\n\tnamer    Namer\n\ttags     []string\n\ttagOnly  bool\n\tinsecure bool\n\tjobs     int\n\n\tpusher *remote.Pusher\n\toopt   []ociremote.Option\n}\n\n// Option is a functional option for NewDefault.\ntype Option func(*defaultOpener) error\n\ntype defaultOpener struct {\n\tbase      string\n\tt         http.RoundTripper\n\tuserAgent string\n\tkeychain  authn.Keychain\n\tnamer     Namer\n\ttags      []string\n\ttagOnly   bool\n\tinsecure  bool\n\tropt      []remote.Option\n\tjobs      int\n}\n\n// Namer is a function from a supported import path to the portion of the resulting\n// image name that follows the \"base\" repository name.\ntype Namer func(string, string) string\n\n// identity is the default namer, so import paths are affixed as-is under the repository\n// name for maximum clarity, e.g.\n//\n//\tgcr.io/foo/github.com/bar/baz/cmd/blah\n//\t^--base--^ ^-------import path-------^\nfunc identity(base, in string) string { return path.Join(base, in) }\n\n// As some registries do not support pushing an image by digest, the default tag for pushing\n// is the 'latest' tag.\nconst latestTag = \"latest\"\n\nfunc (do *defaultOpener) Open() (Interface, error) {\n\tif do.tagOnly {\n\t\tif len(do.tags) != 1 {\n\t\t\treturn nil, errors.New(\"must specify exactly one tag to resolve images into tag-only references\")\n\t\t}\n\t\tif do.tags[0] == latestTag {\n\t\t\treturn nil, errors.New(\"latest tag cannot be used in tag-only references\")\n\t\t}\n\t}\n\n\tpusher, err := remote.NewPusher(do.ropt...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\toopt := []ociremote.Option{ociremote.WithRemoteOptions(do.ropt...)}\n\n\t// Respect COSIGN_REPOSITORY\n\ttargetRepoOverride, err := ociremote.GetEnvTargetRepository()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif (targetRepoOverride != name.Repository{}) {\n\t\toopt = append(oopt, ociremote.WithTargetRepository(targetRepoOverride))\n\t}\n\n\treturn &defalt{\n\t\tbase:     do.base,\n\t\tnamer:    do.namer,\n\t\ttags:     do.tags,\n\t\ttagOnly:  do.tagOnly,\n\t\tinsecure: do.insecure,\n\t\tjobs:     do.jobs,\n\t\tpusher:   pusher,\n\t\toopt:     oopt,\n\t}, nil\n}\n\n// NewDefault returns a new publish.Interface that publishes references under the provided base\n// repository using the default keychain to authenticate and the default naming scheme.\nfunc NewDefault(base string, options ...Option) (Interface, error) {\n\tdo := &defaultOpener{\n\t\tbase:      base,\n\t\tt:         remote.DefaultTransport,\n\t\tuserAgent: \"ko\",\n\t\tkeychain:  authn.DefaultKeychain,\n\t\tnamer:     identity,\n\t\ttags:      []string{latestTag},\n\t}\n\n\tfor _, option := range options {\n\t\tif err := option(do); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tdo.ropt = []remote.Option{remote.WithAuthFromKeychain(do.keychain), remote.WithTransport(do.t), remote.WithUserAgent(do.userAgent)}\n\tif do.jobs == 0 {\n\t\tdo.jobs = runtime.GOMAXPROCS(0)\n\t\tdo.ropt = append(do.ropt, remote.WithJobs(do.jobs))\n\t}\n\n\treturn do.Open()\n}\n\nfunc (d *defalt) pushResult(ctx context.Context, tag name.Tag, br build.Result) error {\n\tmt, err := br.MediaType()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tg, ctx := errgroup.WithContext(ctx)\n\tg.SetLimit(d.jobs)\n\n\tg.Go(func() error {\n\t\treturn d.pusher.Push(ctx, tag, br)\n\t})\n\n\t// writePeripherals implements walk.Fn\n\twritePeripherals := func(ctx context.Context, se oci.SignedEntity) error {\n\t\th, err := se.(interface{ Digest() (v1.Hash, error) }).Digest()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// TODO(mattmoor): We should have a WriteSBOM helper upstream.\n\t\tdigest := tag.Context().Digest(h.String()) // Don't *get* the tag, we know the digest\n\t\tref, err := ociremote.SBOMTag(digest, d.oopt...)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tf, err := se.Attachment(\"sbom\")\n\t\tif err != nil {\n\t\t\t// Some levels (e.g. the index) may not have an SBOM,\n\t\t\t// just like some levels may not have signatures/attestations.\n\t\t\treturn nil\n\t\t}\n\n\t\tg.Go(func() error {\n\t\t\tif err := d.pusher.Push(ctx, ref, f); err != nil {\n\t\t\t\treturn fmt.Errorf(\"writing sbom: %w\", err)\n\t\t\t}\n\n\t\t\tlog.Printf(\"Published SBOM %v\", ref)\n\t\t\treturn nil\n\t\t})\n\n\t\t// TODO(mattmoor): Don't enable this until we start signing or it\n\t\t// will publish empty signatures!\n\t\t// if err := ociremote.WriteSignatures(tag.Context(), se, oopt...); err != nil {\n\t\t// \treturn err\n\t\t// }\n\n\t\t// TODO(mattmoor): Are there any attestations we want to write?\n\t\t// if err := ociremote.WriteAttestations(tag.Context(), se, oopt...); err != nil {\n\t\t// \treturn err\n\t\t// }\n\n\t\treturn nil\n\t}\n\n\tswitch mt {\n\tcase types.OCIImageIndex, types.DockerManifestList:\n\t\tif sii, ok := br.(oci.SignedImageIndex); ok {\n\t\t\tif err := walk.SignedEntity(ctx, sii, writePeripherals); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\tcase types.OCIManifestSchema1, types.DockerManifestSchema2:\n\t\tif si, ok := br.(oci.SignedImage); ok {\n\t\t\tif err := writePeripherals(ctx, si); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\tdefault:\n\t\treturn fmt.Errorf(\"result image media type: %s\", mt)\n\t}\n\n\treturn g.Wait()\n}\n\n// Publish implements publish.Interface\nfunc (d *defalt) Publish(ctx context.Context, br build.Result, s string) (name.Reference, error) {\n\ts = strings.TrimPrefix(s, build.StrictScheme)\n\t// https://github.com/google/go-containerregistry/issues/212\n\ts = strings.ToLower(s)\n\n\tno := []name.Option{}\n\tif d.insecure {\n\t\tno = append(no, name.Insecure)\n\t}\n\n\tg, ctx := errgroup.WithContext(ctx)\n\tg.SetLimit(d.jobs)\n\tfor i, tagName := range d.tags {\n\t\ttag, err := name.NewTag(fmt.Sprintf(\"%s:%s\", d.namer(d.base, s), tagName), no...)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif i == 0 {\n\t\t\tlog.Printf(\"Publishing %v\", tag)\n\t\t\tg.Go(func() error {\n\t\t\t\treturn d.pushResult(ctx, tag, br)\n\t\t\t})\n\t\t} else {\n\t\t\tg.Go(func() error {\n\t\t\t\tlog.Printf(\"Tagging %v\", tag)\n\t\t\t\treturn d.pusher.Push(ctx, tag, br)\n\t\t\t})\n\t\t}\n\t}\n\tif err := g.Wait(); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif d.tagOnly {\n\t\t// We have already validated that there is a single tag (not latest).\n\t\treturn name.NewTag(fmt.Sprintf(\"%s:%s\", d.namer(d.base, s), d.tags[0]))\n\t}\n\n\th, err := br.Digest()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tref := fmt.Sprintf(\"%s@%s\", d.namer(d.base, s), h)\n\tif len(d.tags) == 1 && d.tags[0] != latestTag {\n\t\t// If a single tag is explicitly set (not latest), then this\n\t\t// is probably a release, so include the tag in the reference.\n\t\tref = fmt.Sprintf(\"%s:%s@%s\", d.namer(d.base, s), d.tags[0], h)\n\t}\n\tdig, err := name.NewDigest(ref)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tlog.Printf(\"Published %v\", dig)\n\treturn &dig, nil\n}\n\nfunc (d *defalt) Close() error {\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/publish/default_test.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage publish_test\n\nimport (\n\t\"context\"\n\t\"crypto/md5\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/google/go-containerregistry/pkg/crane\"\n\t\"github.com/google/go-containerregistry/pkg/name\"\n\t\"github.com/google/go-containerregistry/pkg/registry\"\n\t\"github.com/google/go-containerregistry/pkg/v1/random\"\n\t\"github.com/google/go-containerregistry/pkg/v1/remote\"\n\t\"github.com/google/ko/pkg/build\"\n\t\"github.com/google/ko/pkg/publish\"\n\tocimutate \"github.com/sigstore/cosign/v3/pkg/oci/mutate\"\n\t\"github.com/sigstore/cosign/v3/pkg/oci/signed\"\n\t\"github.com/sigstore/cosign/v3/pkg/oci/static\"\n)\n\nvar (\n\timg, _ = random.Image(1024, 3)\n\tidx, _ = random.Index(1024, 3, 3)\n)\n\nfunc TestDefault(t *testing.T) {\n\tf, err := static.NewFile([]byte(\"da bom\"))\n\tif err != nil {\n\t\tt.Fatalf(\"static.NewFile() = %v\", err)\n\t}\n\tsi, err := ocimutate.AttachFileToImage(signed.Image(img), \"sbom\", f)\n\tif err != nil {\n\t\tt.Fatalf(\"ocimutate.AttachFileToImage() = %v\", err)\n\t}\n\tsii, err := ocimutate.AttachFileToImageIndex(signed.ImageIndex(idx), \"sbom\", f)\n\tif err != nil {\n\t\tt.Fatalf(\"ocimutate.AttachFileToImageIndex() = %v\", err)\n\t}\n\n\tfor _, br := range []build.Result{img, idx, si, sii} {\n\t\tbase := \"blah\"\n\t\timportpath := \"github.com/Google/go-containerregistry/cmd/crane\"\n\t\texpectedRepo := fmt.Sprintf(\"%s/%s\", base, strings.ToLower(importpath))\n\n\t\tserver := httptest.NewServer(registry.New())\n\t\tdefer server.Close()\n\t\tu, err := url.Parse(server.URL)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"url.Parse(%v) = %v\", server.URL, err)\n\t\t}\n\t\ttag, err := name.NewTag(fmt.Sprintf(\"%s/%s:latest\", u.Host, expectedRepo))\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"NewTag() = %v\", err)\n\t\t}\n\n\t\trepoName := fmt.Sprintf(\"%s/%s\", u.Host, base)\n\t\tdef, err := publish.NewDefault(repoName)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"NewDefault() = %v\", err)\n\t\t}\n\t\tif d, err := def.Publish(context.Background(), br, build.StrictScheme+importpath); err != nil {\n\t\t\tt.Errorf(\"Publish() = %v\", err)\n\t\t} else if !strings.HasPrefix(d.String(), tag.Repository.String()) {\n\t\t\tt.Errorf(\"Publish() = %v, wanted prefix %v\", d, tag.Repository)\n\t\t}\n\t}\n}\n\nfunc md5Hash(base, s string) string {\n\t// md5 as hex.\n\thasher := md5.New()\n\thasher.Write([]byte(s))\n\treturn filepath.Join(base, hex.EncodeToString(hasher.Sum(nil)))\n}\n\nfunc TestDefaultWithCustomNamer(t *testing.T) {\n\tfor _, br := range []build.Result{img, idx} {\n\t\tbase := \"blah\"\n\t\timportpath := \"github.com/Google/go-containerregistry/cmd/crane\"\n\t\texpectedRepo := fmt.Sprintf(\"%s/%s\", base, strings.ToLower(importpath))\n\n\t\tserver := httptest.NewServer(registry.New())\n\t\tdefer server.Close()\n\t\tu, err := url.Parse(server.URL)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"url.Parse(%v) = %v\", server.URL, err)\n\t\t}\n\t\ttag, err := name.NewTag(fmt.Sprintf(\"%s/%s:latest\", u.Host, expectedRepo))\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"NewTag() = %v\", err)\n\t\t}\n\n\t\trepoName := fmt.Sprintf(\"%s/%s\", u.Host, base)\n\n\t\tdef, err := publish.NewDefault(repoName, publish.WithNamer(md5Hash))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"NewDefault() = %v\", err)\n\t\t}\n\t\tif d, err := def.Publish(context.Background(), br, build.StrictScheme+importpath); err != nil {\n\t\t\tt.Errorf(\"Publish() = %v\", err)\n\t\t} else if !strings.HasPrefix(d.String(), repoName) {\n\t\t\tt.Errorf(\"Publish() = %v, wanted prefix %v\", d, tag.Repository)\n\t\t} else if !strings.HasSuffix(d.Context().String(), md5Hash(\"\", strings.ToLower(importpath))) {\n\t\t\tt.Errorf(\"Publish() = %v, wanted suffix %v\", d.Context(), md5Hash(\"\", importpath))\n\t\t}\n\t}\n}\n\nfunc TestDefaultWithTags(t *testing.T) {\n\tfor _, br := range []build.Result{img, idx} {\n\t\tbase := \"blah\"\n\t\timportpath := \"github.com/Google/go-containerregistry/cmd/crane\"\n\t\texpectedRepo := fmt.Sprintf(\"%s/%s\", base, strings.ToLower(importpath))\n\n\t\tserver := httptest.NewServer(registry.New())\n\t\tdefer server.Close()\n\t\tu, err := url.Parse(server.URL)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"url.Parse(%v) = %v\", server.URL, err)\n\t\t}\n\t\ttag, err := name.NewTag(fmt.Sprintf(\"%s/%s:notLatest\", u.Host, expectedRepo))\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"NewTag() = %v\", err)\n\t\t}\n\n\t\trepoName := fmt.Sprintf(\"%s/%s\", u.Host, base)\n\n\t\tdef, err := publish.NewDefault(repoName, publish.WithTags([]string{\"notLatest\", \"v1.2.3\"}))\n\t\tif err != nil {\n\t\t\tt.Errorf(\"NewDefault() = %v\", err)\n\t\t}\n\t\tif d, err := def.Publish(context.Background(), br, build.StrictScheme+importpath); err != nil {\n\t\t\tt.Errorf(\"Publish() = %v\", err)\n\t\t} else if !strings.HasPrefix(d.String(), repoName) {\n\t\t\tt.Errorf(\"Publish() = %v, wanted prefix %v\", d, tag.Repository)\n\t\t} else if !strings.HasSuffix(d.Context().String(), strings.ToLower(importpath)) {\n\t\t\tt.Errorf(\"Publish() = %v, wanted suffix %v\", d.Context(), md5Hash(\"\", importpath))\n\t\t}\n\n\t\totherTag := fmt.Sprintf(\"%s/%s:v1.2.3\", u.Host, expectedRepo)\n\n\t\tfirst, err := crane.Digest(tag.String())\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\t\tsecond, err := crane.Digest(otherTag)\n\t\tif err != nil {\n\t\t\tt.Fatal(err)\n\t\t}\n\n\t\tif first != second {\n\t\t\tt.Errorf(\"tagging didn't work: %s != %s\", second, first)\n\t\t}\n\t}\n}\n\nfunc TestDefaultWithReleaseTag(t *testing.T) {\n\timg, err := random.Image(1024, 1)\n\tif err != nil {\n\t\tt.Fatalf(\"random.Image() = %v\", err)\n\t}\n\tbase := \"blah\"\n\treleaseTag := \"v1.2.3\"\n\timportpath := \"github.com/Google/go-containerregistry/cmd/crane\"\n\texpectedRepo := fmt.Sprintf(\"%s/%s\", base, strings.ToLower(importpath))\n\n\tserver := httptest.NewServer(registry.New())\n\tdefer server.Close()\n\n\tu, err := url.Parse(server.URL)\n\tif err != nil {\n\t\tt.Fatalf(\"url.Parse(%v) = %v\", server.URL, err)\n\t}\n\ttag, err := name.NewTag(fmt.Sprintf(\"%s/%s:notLatest\", u.Host, expectedRepo))\n\tif err != nil {\n\t\tt.Fatalf(\"NewTag() = %v\", err)\n\t}\n\n\trepoName := fmt.Sprintf(\"%s/%s\", u.Host, base)\n\n\tdef, err := publish.NewDefault(repoName, publish.WithTags([]string{releaseTag}))\n\tif err != nil {\n\t\tt.Errorf(\"NewDefault() = %v\", err)\n\t}\n\tif d, err := def.Publish(context.Background(), img, build.StrictScheme+importpath); err != nil {\n\t\tt.Errorf(\"Publish() = %v\", err)\n\t} else if !strings.HasPrefix(d.String(), repoName) {\n\t\tt.Errorf(\"Publish() = %v, wanted prefix %v\", d, tag.Repository)\n\t} else if !strings.HasSuffix(d.Context().String(), strings.ToLower(importpath)) {\n\t\tt.Errorf(\"Publish() = %v, wanted suffix %v\", d.Context(), md5Hash(\"\", importpath))\n\t} else if !strings.Contains(d.String(), releaseTag) {\n\t\tt.Errorf(\"Publish() = %v, wanted tag included: %v\", d.String(), releaseTag)\n\t}\n\n\ttags, err := remote.List(tag.Context())\n\tif err != nil {\n\t\tt.Fatalf(\"remote.List(): %v\", err)\n\t}\n\tcreatedTags := make(map[string]struct{})\n\tfor _, got := range tags {\n\t\tcreatedTags[got] = struct{}{}\n\t}\n\tif _, ok := createdTags[\"v1.2.3\"]; !ok {\n\t\tt.Errorf(\"Tag v1.2.3 was not created.\")\n\t}\n\n\tdef, err = publish.NewDefault(repoName, publish.WithTags([]string{releaseTag}), publish.WithTagOnly(true))\n\tif err != nil {\n\t\tt.Errorf(\"NewDefault() = %v\", err)\n\t}\n\tif d, err := def.Publish(context.Background(), img, build.StrictScheme+importpath); err != nil {\n\t\tt.Errorf(\"Publish() = %v\", err)\n\t} else if !strings.HasPrefix(d.String(), repoName) {\n\t\tt.Errorf(\"Publish() = %v, wanted prefix %v\", d, tag.Repository)\n\t} else if !strings.HasSuffix(d.Context().String(), strings.ToLower(importpath)) {\n\t\tt.Errorf(\"Publish() = %v, wanted suffix %v\", d.Context(), md5Hash(\"\", importpath))\n\t} else if !strings.Contains(d.String(), releaseTag) {\n\t\tt.Errorf(\"Publish() = %v, wanted tag included: %v\", d.String(), releaseTag)\n\t} else if strings.Contains(d.String(), \"@sha256:\") {\n\t\tt.Errorf(\"Publish() = %v, wanted no digest\", d.String())\n\t}\n}\n"
  },
  {
    "path": "pkg/publish/doc.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// Package publish defines methods for publishing a v1.Image reference and\n// returning the published digest for embedding back into a Kubernetes yaml.\npackage publish\n"
  },
  {
    "path": "pkg/publish/future.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage publish\n\nimport (\n\t\"sync\"\n\n\t\"github.com/google/go-containerregistry/pkg/name\"\n)\n\nfunc newFuture(work func() (name.Reference, error)) *future {\n\t// Create a channel on which to send the result.\n\tch := make(chan *result)\n\t// Initiate the actual work, sending its result\n\t// along the above channel.\n\tgo func() {\n\t\tref, err := work()\n\t\tch <- &result{ref: ref, err: err}\n\t}()\n\t// Return a future for the above work.  Callers should\n\t// call .Get() on this result (as many times as needed).\n\t// One of these calls will receive the result, store it,\n\t// and close the channel so that the rest of the callers\n\t// can consume it.\n\treturn &future{\n\t\tpromise: ch,\n\t}\n}\n\ntype result struct {\n\tref name.Reference\n\terr error\n}\n\ntype future struct {\n\tm sync.RWMutex\n\n\tresult  *result\n\tpromise chan *result\n}\n\n// Get blocks on the result of the future.\nfunc (f *future) Get() (name.Reference, error) {\n\t// Block on the promise of a result until we get one.\n\tresult, ok := <-f.promise\n\tif ok {\n\t\tfunc() {\n\t\t\tf.m.Lock()\n\t\t\tdefer f.m.Unlock()\n\t\t\t// If we got the result, then store it so that\n\t\t\t// others may access it.\n\t\t\tf.result = result\n\t\t\t// Close the promise channel so that others\n\t\t\t// are signaled that the result is available.\n\t\t\tclose(f.promise)\n\t\t}()\n\t}\n\n\tf.m.RLock()\n\tdefer f.m.RUnlock()\n\n\treturn f.result.ref, f.result.err\n}\n"
  },
  {
    "path": "pkg/publish/future_test.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage publish\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/google/go-containerregistry/pkg/name\"\n\t\"github.com/google/go-containerregistry/pkg/v1/random\"\n)\n\nfunc makeRef() (name.Reference, error) {\n\timg, err := random.Image(256, 8)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\td, err := img.Digest()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn name.NewDigest(fmt.Sprintf(\"gcr.io/foo/bar@%s\", d))\n}\n\nfunc TestSameFutureSameReference(t *testing.T) {\n\tf := newFuture(makeRef)\n\n\tref1, err := f.Get()\n\tif err != nil {\n\t\tt.Errorf(\"Get() = %v\", err)\n\t}\n\td1 := ref1.String()\n\n\tref2, err := f.Get()\n\tif err != nil {\n\t\tt.Errorf(\"Get() = %v\", err)\n\t}\n\td2 := ref2.String()\n\n\tif d1 != d2 {\n\t\tt.Errorf(\"Got different digests %s and %s\", d1, d2)\n\t}\n}\n\nfunc TestDiffFutureDiffReference(t *testing.T) {\n\tf1 := newFuture(makeRef)\n\tf2 := newFuture(makeRef)\n\n\tref1, err := f1.Get()\n\tif err != nil {\n\t\tt.Errorf(\"Get() = %v\", err)\n\t}\n\td1 := ref1.String()\n\n\tref2, err := f2.Get()\n\tif err != nil {\n\t\tt.Errorf(\"Get() = %v\", err)\n\t}\n\td2 := ref2.String()\n\n\tif d1 == d2 {\n\t\tt.Errorf(\"Got same digest %s, wanted different\", d1)\n\t}\n}\n"
  },
  {
    "path": "pkg/publish/kind/doc.go",
    "content": "// Copyright 2020 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// Package kind defines methods for publishing images into kind nodes.\npackage kind\n"
  },
  {
    "path": "pkg/publish/kind/write.go",
    "content": "// Copyright 2020 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage kind\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\n\t\"golang.org/x/sync/errgroup\"\n\n\t\"github.com/google/go-containerregistry/pkg/name\"\n\tv1 \"github.com/google/go-containerregistry/pkg/v1\"\n\t\"github.com/google/go-containerregistry/pkg/v1/tarball\"\n\n\t\"sigs.k8s.io/kind/pkg/cluster\"\n\t\"sigs.k8s.io/kind/pkg/cluster/nodes\"\n)\n\n// Supported since kind 0.8.0 (https://github.com/kubernetes-sigs/kind/releases/tag/v0.8.0)\nconst clusterNameEnvKey = \"KIND_CLUSTER_NAME\"\n\n// provider is an interface for kind providers to facilitate testing.\ntype provider interface {\n\tListInternalNodes(name string) ([]nodes.Node, error)\n}\n\n// GetProvider is a variable so we can override in tests.\nvar GetProvider = func() provider {\n\treturn cluster.NewProvider()\n}\n\n// Tag adds a tag to an already existent image.\nfunc Tag(ctx context.Context, src, dest name.Tag) error {\n\treturn onEachNode(func(n nodes.Node) error {\n\t\tvar buf bytes.Buffer\n\t\tcmd := n.CommandContext(ctx, \"ctr\", \"--namespace=k8s.io\", \"images\", \"tag\", \"--force\", src.String(), dest.String())\n\t\tcmd.SetStdout(&buf)\n\t\tcmd.SetStderr(&buf)\n\t\tif err := cmd.Run(); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to tag image: %w\\n%s\", err, buf.String())\n\t\t}\n\t\treturn nil\n\t})\n}\n\n// Write saves the image into the kind nodes as the given tag.\nfunc Write(ctx context.Context, tag name.Tag, img v1.Image) error {\n\treturn onEachNode(func(n nodes.Node) error {\n\t\tpr, pw := io.Pipe()\n\n\t\tgrp := errgroup.Group{}\n\t\tgrp.Go(func() error {\n\t\t\treturn pw.CloseWithError(tarball.Write(tag, img, pw))\n\t\t})\n\n\t\tvar buf bytes.Buffer\n\t\tcmd := n.CommandContext(ctx, \"ctr\", \"--namespace=k8s.io\", \"images\", \"import\", \"--all-platforms\", \"-\").SetStdin(pr)\n\t\tcmd.SetStdout(&buf)\n\t\tcmd.SetStderr(&buf)\n\t\tif err := cmd.Run(); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to load image to node %q: %w\\n%s\", n, err, buf.String())\n\t\t}\n\n\t\tif err := grp.Wait(); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to write intermediate tarball representation: %w\", err)\n\t\t}\n\n\t\treturn nil\n\t})\n}\n\n// onEachNode executes the given function on each node. Exits on first error.\nfunc onEachNode(f func(nodes.Node) error) error {\n\tnodeList, err := getNodes()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, n := range nodeList {\n\t\tif err := f(n); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// getNodes gets all the nodes of the default cluster.\n// Returns an error if none were found.\nfunc getNodes() ([]nodes.Node, error) {\n\tprovider := GetProvider()\n\n\tclusterName := os.Getenv(clusterNameEnvKey)\n\tif clusterName == \"\" {\n\t\tclusterName = cluster.DefaultName\n\t}\n\n\tnodeList, err := provider.ListInternalNodes(clusterName)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif len(nodeList) == 0 {\n\t\treturn nil, fmt.Errorf(\"no nodes found for cluster %q\", clusterName)\n\t}\n\n\treturn nodeList, nil\n}\n"
  },
  {
    "path": "pkg/publish/kind/write_test.go",
    "content": "// Copyright 2020 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage kind\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/google/go-containerregistry/pkg/name\"\n\t\"github.com/google/go-containerregistry/pkg/v1/random\"\n\t\"sigs.k8s.io/kind/pkg/cluster/nodes\"\n\t\"sigs.k8s.io/kind/pkg/exec\"\n)\n\nfunc TestWrite(t *testing.T) {\n\tctx := context.Background()\n\timg, err := random.Image(1024, 1)\n\tif err != nil {\n\t\tt.Fatalf(\"random.Image() = %v\", err)\n\t}\n\n\ttag, err := name.NewTag(\"kind.local/test:new\")\n\tif err != nil {\n\t\tt.Fatalf(\"name.NewTag() = %v\", err)\n\t}\n\n\tn1 := &fakeNode{}\n\tn2 := &fakeNode{}\n\tGetProvider = func() provider {\n\t\treturn &fakeProvider{nodes: []nodes.Node{n1, n2}}\n\t}\n\n\tif err := Write(ctx, tag, img); err != nil {\n\t\tt.Fatalf(\"Write() = %v\", err)\n\t}\n\n\t// Verify the respective command is executed on each node.\n\tfor _, n := range []*fakeNode{n1, n2} {\n\t\tif got, want := len(n.cmds), 1; got != want {\n\t\t\tt.Fatalf(\"len(n.cmds) = %d, want %d\", got, want)\n\t\t}\n\t\tc := n.cmds[0]\n\n\t\tif got, want := c.cmd, \"ctr --namespace=k8s.io images import --all-platforms -\"; got != want {\n\t\t\tt.Fatalf(\"c.cmd = %s, want %s\", got, want)\n\t\t}\n\t}\n}\n\nfunc TestTag(t *testing.T) {\n\tctx := context.Background()\n\toldTag, err := name.NewTag(\"kind.local/test:test\")\n\tif err != nil {\n\t\tt.Fatalf(\"name.NewTag() = %v\", err)\n\t}\n\n\tnewTag, err := name.NewTag(\"kind.local/test:new\")\n\tif err != nil {\n\t\tt.Fatalf(\"name.NewTag() = %v\", err)\n\t}\n\n\tn1 := &fakeNode{}\n\tn2 := &fakeNode{}\n\tGetProvider = func() provider {\n\t\treturn &fakeProvider{nodes: []nodes.Node{n1, n2}}\n\t}\n\n\tif err := Tag(ctx, oldTag, newTag); err != nil {\n\t\tt.Fatalf(\"Tag() = %v\", err)\n\t}\n\n\t// Verify the respective command is executed on each node.\n\tfor _, n := range []*fakeNode{n1, n2} {\n\t\tif got, want := len(n.cmds), 1; got != want {\n\t\t\tt.Fatalf(\"len(n.cmds) = %d, want %d\", got, want)\n\t\t}\n\t\tc := n.cmds[0]\n\n\t\tif got, want := c.cmd, fmt.Sprintf(\"ctr --namespace=k8s.io images tag --force %s %s\", oldTag, newTag); got != want {\n\t\t\tt.Fatalf(\"c.cmd = %s, want %s\", got, want)\n\t\t}\n\t}\n}\n\nfunc TestFailWithNoNodes(t *testing.T) {\n\tctx := context.Background()\n\timg, err := random.Image(1024, 1)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\toldTag, err := name.NewTag(\"kind.local/test:test\")\n\tif err != nil {\n\t\tt.Fatalf(\"name.NewTag() = %v\", err)\n\t}\n\n\tnewTag, err := name.NewTag(\"kind.local/test:new\")\n\tif err != nil {\n\t\tt.Fatalf(\"name.NewTag() = %v\", err)\n\t}\n\n\tGetProvider = func() provider {\n\t\treturn &fakeProvider{}\n\t}\n\n\tif err := Write(ctx, newTag, img); err == nil {\n\t\tt.Fatal(\"Write() = nil, wanted an error\")\n\t}\n\tif err := Tag(ctx, oldTag, newTag); err == nil {\n\t\tt.Fatal(\"Tag() = nil, wanted an error\")\n\t}\n}\n\nfunc TestFailCommands(t *testing.T) {\n\tctx := context.Background()\n\timg, err := random.Image(1024, 1)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\toldTag, err := name.NewTag(\"kind.local/test:test\")\n\tif err != nil {\n\t\tt.Fatalf(\"name.NewTag() = %v\", err)\n\t}\n\n\tnewTag, err := name.NewTag(\"kind.local/test:new\")\n\tif err != nil {\n\t\tt.Fatalf(\"name.NewTag() = %v\", err)\n\t}\n\n\terrTest := errors.New(\"test\")\n\n\tn1 := &fakeNode{err: errTest}\n\tn2 := &fakeNode{err: errTest}\n\tGetProvider = func() provider {\n\t\treturn &fakeProvider{nodes: []nodes.Node{n1, n2}}\n\t}\n\n\tif err := Write(ctx, newTag, img); !errors.Is(err, errTest) {\n\t\tt.Fatalf(\"Write() = %v, want %v\", err, errTest)\n\t}\n\tif err := Tag(ctx, oldTag, newTag); !errors.Is(err, errTest) {\n\t\tt.Fatalf(\"Write() = %v, want %v\", err, errTest)\n\t}\n}\n\n// fakeProvider\ntype fakeProvider struct {\n\tnodes []nodes.Node\n}\n\nfunc (f *fakeProvider) ListInternalNodes(string) ([]nodes.Node, error) {\n\treturn f.nodes, nil\n}\n\ntype fakeNode struct {\n\tcmds []*fakeCmd\n\terr  error\n}\n\nfunc (f *fakeNode) CommandContext(_ context.Context, cmd string, args ...string) exec.Cmd {\n\tcommand := &fakeCmd{\n\t\tcmd: strings.Join(append([]string{cmd}, args...), \" \"),\n\t\terr: f.err,\n\t}\n\tf.cmds = append(f.cmds, command)\n\treturn command\n}\n\nfunc (f *fakeNode) String() string {\n\treturn \"test\"\n}\n\n// The following functions are not used by our code at all.\nfunc (f *fakeNode) Command(string, ...string) exec.Cmd { return nil }\nfunc (f *fakeNode) Role() (string, error)              { return \"\", nil }\nfunc (f *fakeNode) IP() (string, string, error)        { return \"\", \"\", nil }\nfunc (f *fakeNode) SerialLogs(io.Writer) error         { return nil }\n\ntype fakeCmd struct {\n\tcmd   string\n\terr   error\n\tstdin io.Reader\n}\n\nfunc (f *fakeCmd) Run() error {\n\tif f.stdin != nil {\n\t\t// Consume the entire stdin to move the image publish forward.\n\t\tio.ReadAll(f.stdin)\n\t}\n\treturn f.err\n}\n\nfunc (f *fakeCmd) SetStdin(stdin io.Reader) exec.Cmd {\n\tf.stdin = stdin\n\treturn f\n}\n\n// The following functions are not used by our code at all.\nfunc (f *fakeCmd) SetEnv(...string) exec.Cmd    { return f }\nfunc (f *fakeCmd) SetStdout(io.Writer) exec.Cmd { return f }\nfunc (f *fakeCmd) SetStderr(io.Writer) exec.Cmd { return f }\n"
  },
  {
    "path": "pkg/publish/kind.go",
    "content": "// Copyright 2020 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage publish\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/google/go-containerregistry/pkg/name\"\n\tv1 \"github.com/google/go-containerregistry/pkg/v1\"\n\t\"github.com/google/ko/pkg/build\"\n\t\"github.com/google/ko/pkg/publish/kind\"\n)\n\nconst (\n\t// KindDomain is a sentinel \"registry\" that represents side-loading images into kind nodes.\n\tKindDomain = \"kind.local\"\n)\n\ntype kindPublisher struct {\n\tbase  string\n\tnamer Namer\n\ttags  []string\n}\n\n// NewKindPublisher returns a new publish.Interface that loads images into kind nodes.\nfunc NewKindPublisher(base string, namer Namer, tags []string) Interface {\n\treturn &kindPublisher{\n\t\tbase:  base,\n\t\tnamer: namer,\n\t\ttags:  tags,\n\t}\n}\n\n// Publish implements publish.Interface.\nfunc (t *kindPublisher) Publish(ctx context.Context, br build.Result, s string) (name.Reference, error) {\n\ts = strings.TrimPrefix(s, build.StrictScheme)\n\t// https://github.com/google/go-containerregistry/issues/212\n\ts = strings.ToLower(s)\n\n\t// There's no way to write an index to a kind, so attempt to downcast it to an image.\n\tvar img v1.Image\n\tswitch i := br.(type) {\n\tcase v1.Image:\n\t\timg = i\n\tcase v1.ImageIndex:\n\t\tim, err := i.IndexManifest()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tgoos, goarch := os.Getenv(\"GOOS\"), os.Getenv(\"GOARCH\")\n\t\tif goos == \"\" {\n\t\t\tgoos = \"linux\"\n\t\t}\n\t\tif goarch == \"\" {\n\t\t\tgoarch = \"amd64\"\n\t\t}\n\t\tfor _, manifest := range im.Manifests {\n\t\t\tif manifest.Platform == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif manifest.Platform.OS != goos {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif manifest.Platform.Architecture != goarch {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\timg, err = i.Image(manifest.Digest)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t\tif img == nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to find %s/%s image in index for image: %v\", goos, goarch, s)\n\t\t}\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"failed to interpret %s result as image: %v\", s, br)\n\t}\n\n\th, err := img.Digest()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tdigestTag, err := name.NewTag(fmt.Sprintf(\"%s:%s\", t.namer(t.base, s), h.Hex))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlog.Printf(\"Loading %v\", digestTag)\n\tif err := kind.Write(ctx, digestTag, img); err != nil {\n\t\treturn nil, err\n\t}\n\tlog.Printf(\"Loaded %v\", digestTag)\n\n\tfor _, tagName := range t.tags {\n\t\tlog.Printf(\"Adding tag %v\", tagName)\n\t\ttag, err := name.NewTag(fmt.Sprintf(\"%s:%s\", t.namer(t.base, s), tagName))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif err := kind.Tag(ctx, digestTag, tag); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlog.Printf(\"Added tag %v\", tagName)\n\t}\n\n\treturn &digestTag, nil\n}\n\nfunc (t *kindPublisher) Close() error {\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/publish/layout.go",
    "content": "// Copyright 2020 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage publish\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\n\t\"github.com/google/go-containerregistry/pkg/name\"\n\tv1 \"github.com/google/go-containerregistry/pkg/v1\"\n\t\"github.com/google/go-containerregistry/pkg/v1/empty\"\n\t\"github.com/google/go-containerregistry/pkg/v1/layout\"\n\t\"github.com/google/go-containerregistry/pkg/v1/types\"\n\t\"github.com/google/ko/pkg/build\"\n)\n\ntype LayoutPublisher struct {\n\tp string\n}\n\n// NewLayout returns a new publish.Interface that saves images to an OCI Image Layout.\nfunc NewLayout(p string) Interface {\n\treturn &LayoutPublisher{p}\n}\n\nfunc (l *LayoutPublisher) writeResult(br build.Result) (layout.Path, error) {\n\tp, err := layout.FromPath(l.p)\n\tif err != nil {\n\t\tp, err = layout.Write(l.p, empty.Index)\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\n\tmt, err := br.MediaType()\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tswitch mt {\n\tcase types.OCIImageIndex, types.DockerManifestList:\n\t\tidx, ok := br.(v1.ImageIndex)\n\t\tif !ok {\n\t\t\treturn \"\", fmt.Errorf(\"failed to interpret result as index: %v\", br)\n\t\t}\n\t\tif err := p.AppendIndex(idx); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\treturn p, nil\n\tcase types.OCIManifestSchema1, types.DockerManifestSchema2:\n\t\timg, ok := br.(v1.Image)\n\t\tif !ok {\n\t\t\treturn \"\", fmt.Errorf(\"failed to interpret result as image: %v\", br)\n\t\t}\n\t\tif err := p.AppendImage(img); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\treturn p, nil\n\tdefault:\n\t\treturn \"\", fmt.Errorf(\"result image media type: %s\", mt)\n\t}\n}\n\n// Publish implements publish.Interface.\nfunc (l *LayoutPublisher) Publish(_ context.Context, br build.Result, s string) (name.Reference, error) {\n\tlog.Printf(\"Saving %v\", s)\n\tp, err := l.writeResult(br)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tlog.Printf(\"Saved %v\", s)\n\n\th, err := br.Digest()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tdig, err := name.NewDigest(fmt.Sprintf(\"%s@%s\", p, h))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn dig, nil\n}\n\nfunc (l *LayoutPublisher) Close() error {\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/publish/layout_test.go",
    "content": "// Copyright 2020 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage publish\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/google/go-containerregistry/pkg/v1/random\"\n)\n\nfunc TestLayout(t *testing.T) {\n\timg, err := random.Image(1024, 1)\n\tif err != nil {\n\t\tt.Fatalf(\"random.Image() = %v\", err)\n\t}\n\timportpath := \"github.com/Google/go-containerregistry/cmd/crane\"\n\n\ttmp, err := os.MkdirTemp(\"/tmp\", \"ko\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer os.RemoveAll(tmp)\n\n\tlp := NewLayout(tmp)\n\tif d, err := lp.Publish(context.Background(), img, importpath); err != nil {\n\t\tt.Errorf(\"Publish() = %v\", err)\n\t} else if !strings.HasPrefix(d.String(), tmp) {\n\t\tt.Errorf(\"Publish() = %v, wanted prefix %v\", d, tmp)\n\t}\n}\n"
  },
  {
    "path": "pkg/publish/multi.go",
    "content": "// Copyright 2020 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage publish\n\nimport (\n\t\"context\"\n\t\"errors\"\n\n\t\"github.com/google/go-containerregistry/pkg/name\"\n\t\"github.com/google/ko/pkg/build\"\n)\n\n// MultiPublisher creates a publisher that publishes to all\n// the provided publishers, similar to the Unix tee(1) command.\n//\n// When calling Publish, the name.Reference returned will be the return value\n// of the last publisher passed to MultiPublisher (last one wins).\nfunc MultiPublisher(publishers ...Interface) Interface {\n\treturn &multiPublisher{publishers}\n}\n\ntype multiPublisher struct {\n\tpublishers []Interface\n}\n\n// Publish implements publish.Interface.\nfunc (p *multiPublisher) Publish(ctx context.Context, br build.Result, s string) (ref name.Reference, err error) {\n\tif len(p.publishers) == 0 {\n\t\treturn nil, errors.New(\"MultiPublisher configured with zero publishers\")\n\t}\n\n\tfor _, pub := range p.publishers {\n\t\tref, err = pub.Publish(ctx, br, s)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n}\n\nfunc (p *multiPublisher) Close() (err error) {\n\tfor _, pub := range p.publishers {\n\t\tif perr := pub.Close(); perr != nil {\n\t\t\terr = perr\n\t\t}\n\t}\n\treturn\n}\n"
  },
  {
    "path": "pkg/publish/multi_test.go",
    "content": "// Copyright 2020 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage publish_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/google/go-containerregistry/pkg/v1/random\"\n\t\"github.com/google/ko/pkg/publish\"\n)\n\nfunc TestMulti(t *testing.T) {\n\timg, err := random.Image(1024, 1)\n\tif err != nil {\n\t\tt.Fatalf(\"random.Image() = %v\", err)\n\t}\n\tbase := \"blah\"\n\trepoName := fmt.Sprintf(\"%s/%s\", \"example.com\", base)\n\timportpath := \"github.com/Google/go-containerregistry/cmd/crane\"\n\n\tfp, err := os.CreateTemp(\"\", \"\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer fp.Close()\n\tdefer os.Remove(fp.Name())\n\n\ttp := publish.NewTarball(fp.Name(), repoName, md5Hash, []string{})\n\n\ttmp, err := os.MkdirTemp(\"/tmp\", \"ko\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer os.RemoveAll(tmp)\n\n\tlp := publish.NewLayout(tmp)\n\n\tp := publish.MultiPublisher(lp, tp)\n\tif _, err := p.Publish(context.Background(), img, importpath); err != nil {\n\t\tt.Errorf(\"Publish() = %v\", err)\n\t}\n\n\tif err := p.Close(); err != nil {\n\t\tt.Errorf(\"Close() = %v\", err)\n\t}\n}\n\nfunc TestMulti_Zero(t *testing.T) {\n\timg, err := random.Image(1024, 1)\n\tif err != nil {\n\t\tt.Fatalf(\"random.Image() = %v\", err)\n\t}\n\n\tp := publish.MultiPublisher() // No publishers.\n\tif _, err := p.Publish(context.Background(), img, \"foo\"); err == nil {\n\t\tt.Errorf(\"Publish() got nil error\")\n\t}\n}\n"
  },
  {
    "path": "pkg/publish/options.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage publish\n\nimport (\n\t\"crypto/tls\"\n\t\"net/http\"\n\n\t\"github.com/google/go-containerregistry/pkg/authn\"\n)\n\ntype staticKeychain struct {\n\tauth authn.Authenticator\n}\n\nfunc (s staticKeychain) Resolve(authn.Resource) (authn.Authenticator, error) {\n\treturn s.auth, nil\n}\n\n// WithTransport is a functional option for overriding the default transport\n// on a default publisher.\nfunc WithTransport(t http.RoundTripper) Option {\n\treturn func(i *defaultOpener) error {\n\t\ti.t = t\n\t\treturn nil\n\t}\n}\n\n// WithUserAgent is a functional option for overriding the User-Agent\n// on a default publisher.\nfunc WithUserAgent(ua string) Option {\n\treturn func(i *defaultOpener) error {\n\t\ti.userAgent = ua\n\t\treturn nil\n\t}\n}\n\n// WithAuth is a functional option for overriding the default authenticator\n// on a default publisher.\nfunc WithAuth(a authn.Authenticator) Option {\n\treturn func(i *defaultOpener) error {\n\t\ti.keychain = staticKeychain{a}\n\t\treturn nil\n\t}\n}\n\n// WithAuthFromKeychain is a functional option for overriding the default\n// authenticator on a default publisher using an authn.Keychain\nfunc WithAuthFromKeychain(keys authn.Keychain) Option {\n\treturn func(i *defaultOpener) error {\n\t\ti.keychain = keys\n\t\treturn nil\n\t}\n}\n\n// WithNamer is a functional option for overriding the image naming behavior\n// in our default publisher.\nfunc WithNamer(n Namer) Option {\n\treturn func(i *defaultOpener) error {\n\t\ti.namer = n\n\t\treturn nil\n\t}\n}\n\n// WithTags is a functional option for overriding the image tags\nfunc WithTags(tags []string) Option {\n\treturn func(i *defaultOpener) error {\n\t\ti.tags = tags\n\t\treturn nil\n\t}\n}\n\n// WithTagOnly is a functional option for resolving images into tag-only references\nfunc WithTagOnly(tagOnly bool) Option {\n\treturn func(i *defaultOpener) error {\n\t\ti.tagOnly = tagOnly\n\t\treturn nil\n\t}\n}\n\nfunc Insecure(b bool) Option {\n\treturn func(i *defaultOpener) error {\n\t\ti.insecure = b\n\t\tt, ok := i.t.(*http.Transport)\n\t\tif !ok {\n\t\t\treturn nil\n\t\t}\n\t\tt = t.Clone()\n\t\tif t.TLSClientConfig == nil {\n\t\t\tt.TLSClientConfig = &tls.Config{} //nolint: gosec\n\t\t}\n\t\tt.TLSClientConfig.InsecureSkipVerify = b //nolint: gosec\n\t\ti.t = t\n\n\t\treturn nil\n\t}\n}\n\n// WithJobs limits the number of concurrent pushes.\nfunc WithJobs(jobs int) Option {\n\treturn func(i *defaultOpener) error {\n\t\ti.jobs = jobs\n\t\treturn nil\n\t}\n}\n"
  },
  {
    "path": "pkg/publish/publish.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage publish\n\nimport (\n\t\"context\"\n\n\t\"github.com/google/go-containerregistry/pkg/name\"\n\t\"github.com/google/ko/pkg/build\"\n)\n\n// Interface abstracts different methods for publishing images.\ntype Interface interface {\n\t// Publish uploads the given build.Result to a registry incorporating the\n\t// provided string into the image's repository name.  Returns the digest\n\t// of the published image.\n\tPublish(context.Context, build.Result, string) (name.Reference, error)\n\n\t// Close exists for the tarball implementation so we can\n\t// do the whole thing in one write.\n\tClose() error\n}\n"
  },
  {
    "path": "pkg/publish/recorder.go",
    "content": "// Copyright 2021 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage publish\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/google/go-containerregistry/pkg/name\"\n\tv1 \"github.com/google/go-containerregistry/pkg/v1\"\n\t\"github.com/google/ko/pkg/build\"\n\t\"github.com/sigstore/cosign/v3/pkg/oci\"\n\t\"github.com/sigstore/cosign/v3/pkg/oci/walk\"\n)\n\n// recorder wraps a publisher implementation in a layer that records the published\n// references to a file.\ntype recorder struct {\n\tinner    Interface\n\tfileName string\n\twc       io.Writer\n}\n\n// recorder implements Interface\nvar _ Interface = (*recorder)(nil)\n\n// NewRecorder wraps the provided publish.Interface in an implementation that\n// records publish results to a file.\nfunc NewRecorder(inner Interface, name string) (Interface, error) {\n\treturn &recorder{\n\t\tinner:    inner,\n\t\tfileName: name,\n\t}, nil\n}\n\n// Publish implements Interface\nfunc (r *recorder) Publish(ctx context.Context, br build.Result, ref string) (name.Reference, error) {\n\tresult, err := r.inner.Publish(ctx, br, ref)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treferences := make([]string, 0, 20 /* just try to avoid resizing*/)\n\tswitch t := br.(type) {\n\tcase oci.SignedImageIndex:\n\t\tif err := walk.SignedEntity(ctx, t, func(_ context.Context, se oci.SignedEntity) error {\n\t\t\t// Both of the SignedEntity types implement Digest()\n\t\t\th, err := se.(interface{ Digest() (v1.Hash, error) }).Digest()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treferences = append(references, result.Context().Digest(h.String()).String())\n\t\t\treturn nil\n\t\t}); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\tdefault:\n\t\treferences = append(references, result.String())\n\t}\n\n\tif r.wc == nil {\n\t\tf, err := os.OpenFile(r.fileName, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tr.wc = f\n\t}\n\n\tif _, err := r.wc.Write([]byte(strings.Join(references, \"\\n\") + \"\\n\")); err != nil {\n\t\treturn nil, err\n\t}\n\treturn result, nil\n}\n\n// Close implements Interface\nfunc (r *recorder) Close() error {\n\tif err := r.inner.Close(); err != nil {\n\t\treturn err\n\t}\n\tif c, ok := r.wc.(io.Closer); ok {\n\t\treturn c.Close()\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/publish/recorder_test.go",
    "content": "// Copyright 2021 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage publish\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"path\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/google/go-containerregistry/pkg/name\"\n\t\"github.com/google/go-containerregistry/pkg/v1/random\"\n\t\"github.com/google/ko/pkg/build\"\n\t\"github.com/sigstore/cosign/v3/pkg/oci/signed\"\n)\n\ntype cbPublish struct {\n\tcb func(context.Context, build.Result, string) (name.Reference, error)\n}\n\nvar _ Interface = (*cbPublish)(nil)\n\nfunc (sp *cbPublish) Publish(ctx context.Context, br build.Result, ref string) (name.Reference, error) {\n\treturn sp.cb(ctx, br, ref)\n}\n\nfunc (sp *cbPublish) Close() error {\n\treturn nil\n}\n\nfunc TestRecorder(t *testing.T) {\n\trepo := name.MustParseReference(\"docker.io/ubuntu:latest\")\n\tinner := &cbPublish{cb: func(_ context.Context, b build.Result, _ string) (name.Reference, error) {\n\t\th, err := b.Digest()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn repo.Context().Digest(h.String()), nil\n\t}}\n\n\tdir := t.TempDir()\n\tfile := path.Join(dir, \"testfile\")\n\n\trecorder, err := NewRecorder(inner, file)\n\tif err != nil {\n\t\tt.Fatalf(\"NewRecorder() = %v\", err)\n\t}\n\n\timg, err := random.Image(3, 3)\n\tif err != nil {\n\t\tt.Fatalf(\"random.Image() = %v\", err)\n\t}\n\tsi := signed.Image(img)\n\n\tindex, err := random.Index(3, 3, 2)\n\tif err != nil {\n\t\tt.Fatalf(\"random.Image() = %v\", err)\n\t}\n\tsii := signed.ImageIndex(index)\n\n\tif _, err := recorder.Publish(context.Background(), si, \"\"); err != nil {\n\t\tt.Errorf(\"recorder.Publish() = %v\", err)\n\t}\n\tif _, err := recorder.Publish(context.Background(), si, \"\"); err != nil {\n\t\tt.Errorf(\"recorder.Publish() = %v\", err)\n\t}\n\tif _, err := recorder.Publish(context.Background(), sii, \"\"); err != nil {\n\t\tt.Errorf(\"recorder.Publish() = %v\", err)\n\t}\n\tif err := recorder.Close(); err != nil {\n\t\tt.Errorf(\"recorder.Close() = %v\", err)\n\t}\n\n\tbuf, err := os.ReadFile(file)\n\tif err != nil {\n\t\tt.Fatalf(\"os.ReadFile() = %v\", err)\n\t}\n\trefs := strings.Split(strings.TrimSpace(string(buf)), \"\\n\")\n\n\tif want, got := len(refs), 5; got != want {\n\t\tt.Errorf(\"len(refs) = %d, wanted %d\", got, want)\n\t}\n\n\tfor _, s := range refs {\n\t\tref, err := name.ParseReference(s)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"name.ParseReference() = %v\", err)\n\t\t}\n\t\t// Don't compare the digests, they are random.\n\t\tif want, got := repo.Context().String(), ref.Context().String(); want != got {\n\t\t\tt.Errorf(\"reference repo = %v, wanted %v\", got, want)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "pkg/publish/shared.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage publish\n\nimport (\n\t\"context\"\n\t\"sync\"\n\n\t\"github.com/google/go-containerregistry/pkg/name\"\n\t\"github.com/google/ko/pkg/build\"\n)\n\n// caching wraps a publisher implementation in a layer that shares publish results\n// for the same inputs using a simple \"future\" implementation.\ntype caching struct {\n\tinner Interface\n\n\tm       sync.Mutex\n\tresults map[string]*entry\n}\n\n// entry holds the last image published and the result of publishing it for a\n// particular reference.\ntype entry struct {\n\tbr build.Result\n\tf  *future\n}\n\n// caching implements Interface\nvar _ Interface = (*caching)(nil)\n\n// NewCaching wraps the provided publish.Interface in an implementation that\n// shares publish results for a given path until the passed image object changes.\nfunc NewCaching(inner Interface) (Interface, error) {\n\treturn &caching{\n\t\tinner:   inner,\n\t\tresults: make(map[string]*entry),\n\t}, nil\n}\n\n// Publish implements Interface\nfunc (c *caching) Publish(ctx context.Context, br build.Result, ref string) (name.Reference, error) {\n\tf := func() *future {\n\t\t// Lock the map of futures.\n\t\tc.m.Lock()\n\t\tdefer c.m.Unlock()\n\n\t\t// If a future for \"ref\" exists, then return it.\n\t\tent, ok := c.results[ref]\n\t\tif ok {\n\t\t\t// If the image matches, then return the same future.\n\t\t\tif ent.br == br {\n\t\t\t\treturn ent.f\n\t\t\t}\n\t\t}\n\t\t// Otherwise create and record a future for publishing \"br\" to \"ref\".\n\t\tf := newFuture(func() (name.Reference, error) {\n\t\t\treturn c.inner.Publish(ctx, br, ref)\n\t\t})\n\t\tc.results[ref] = &entry{br: br, f: f}\n\t\treturn f\n\t}()\n\n\treturn f.Get()\n}\n\nfunc (c *caching) Close() error {\n\treturn c.inner.Close()\n}\n"
  },
  {
    "path": "pkg/publish/shared_test.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage publish\n\nimport (\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/google/go-containerregistry/pkg/name\"\n\t\"github.com/google/go-containerregistry/pkg/v1/random\"\n\t\"github.com/google/ko/pkg/build\"\n)\n\ntype slowpublish struct {\n\tsleep time.Duration\n}\n\n// slowpublish implements Interface\nvar _ Interface = (*slowpublish)(nil)\n\nfunc (sp *slowpublish) Publish(context.Context, build.Result, string) (name.Reference, error) {\n\ttime.Sleep(sp.sleep)\n\treturn makeRef()\n}\n\nfunc (sp *slowpublish) Close() error {\n\treturn nil\n}\n\nfunc TestCaching(t *testing.T) {\n\tduration := 100 * time.Millisecond\n\tref := \"foo\"\n\n\tsp := &slowpublish{duration}\n\tcb, _ := NewCaching(sp)\n\n\tpreviousDigest := \"not-a-digest\"\n\t// Each iteration, we test that the first publish is slow and subsequent\n\t// publishs are fast and return the same reference.  For each of these\n\t// iterations we use a new random image, which should invalidate the\n\t// cached reference from previous iterations.\n\tfor range 3 {\n\t\timg, _ := random.Index(256, 8, 1)\n\n\t\tstart := time.Now()\n\t\tref1, err := cb.Publish(context.Background(), img, ref)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Publish() = %v\", err)\n\t\t}\n\t\tend := time.Now()\n\n\t\telapsed := end.Sub(start)\n\t\tif elapsed < duration {\n\t\t\tt.Errorf(\"Elapsed time %v, wanted >= %s\", elapsed, duration)\n\t\t}\n\t\td1 := ref1.String()\n\n\t\tif d1 == previousDigest {\n\t\t\tt.Errorf(\"Got same digest as previous iteration, wanted different: %v\", d1)\n\t\t}\n\t\tpreviousDigest = d1\n\n\t\tstart = time.Now()\n\t\tref2, err := cb.Publish(context.Background(), img, ref)\n\t\tif err != nil {\n\t\t\tt.Errorf(\"Publish() = %v\", err)\n\t\t}\n\t\tend = time.Now()\n\n\t\telapsed = end.Sub(start)\n\t\tif elapsed >= duration {\n\t\t\tt.Errorf(\"Elapsed time %v, wanted < %s\", elapsed, duration)\n\t\t}\n\t\td2 := ref2.String()\n\n\t\tif d1 != d2 {\n\t\t\tt.Error(\"Got different references, wanted same\")\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "pkg/publish/tarball.go",
    "content": "// Copyright 2020 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage publish\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"log\"\n\t\"strings\"\n\n\t\"github.com/google/go-containerregistry/pkg/name\"\n\tv1 \"github.com/google/go-containerregistry/pkg/v1\"\n\t\"github.com/google/go-containerregistry/pkg/v1/tarball\"\n\t\"github.com/google/ko/pkg/build\"\n)\n\ntype tar struct {\n\tfile  string\n\tbase  string\n\tnamer Namer\n\ttags  []string\n\trefs  map[name.Reference]v1.Image\n}\n\n// NewTarball returns a new publish.Interface that saves images to a tarball.\nfunc NewTarball(file, base string, namer Namer, tags []string) Interface {\n\treturn &tar{\n\t\tfile:  file,\n\t\tbase:  base,\n\t\tnamer: namer,\n\t\ttags:  tags,\n\t\trefs:  make(map[name.Reference]v1.Image),\n\t}\n}\n\n// Publish implements publish.Interface.\nfunc (t *tar) Publish(_ context.Context, br build.Result, s string) (name.Reference, error) {\n\ts = strings.TrimPrefix(s, build.StrictScheme)\n\t// https://github.com/google/go-containerregistry/issues/212\n\ts = strings.ToLower(s)\n\n\t// There's no way to write an index to a tarball, so attempt to downcast it to an image.\n\timg, ok := br.(v1.Image)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"failed to interpret %s result as image: %v\", s, br)\n\t}\n\n\tfor _, tagName := range t.tags {\n\t\ttag, err := name.ParseReference(fmt.Sprintf(\"%s:%s\", t.namer(t.base, s), tagName))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tt.refs[tag] = img\n\t}\n\n\th, err := img.Digest()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif len(t.tags) == 0 {\n\t\tref, err := name.ParseReference(fmt.Sprintf(\"%s@%s\", t.namer(t.base, s), h))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tt.refs[ref] = img\n\t}\n\n\tref := fmt.Sprintf(\"%s@%s\", t.namer(t.base, s), h)\n\tif len(t.tags) == 1 && t.tags[0] != latestTag {\n\t\t// If a single tag is explicitly set (not latest), then this\n\t\t// is probably a release, so include the tag in the reference.\n\t\tref = fmt.Sprintf(\"%s:%s@%s\", t.namer(t.base, s), t.tags[0], h)\n\t}\n\tdig, err := name.NewDigest(ref)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &dig, nil\n}\n\nfunc (t *tar) Close() error {\n\tlog.Printf(\"Saving %v\", t.file)\n\tif err := tarball.MultiRefWriteToFile(t.file, t.refs); err != nil {\n\t\t// Bad practice, but we log  this here because right now we just defer the Close.\n\t\tlog.Printf(\"failed to save %q: %v\", t.file, err)\n\t\treturn err\n\t}\n\tlog.Printf(\"Saved %v\", t.file)\n\treturn nil\n}\n"
  },
  {
    "path": "pkg/publish/tarball_test.go",
    "content": "// Copyright 2020 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage publish_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/google/go-containerregistry/pkg/name\"\n\t\"github.com/google/go-containerregistry/pkg/v1/random\"\n\t\"github.com/google/ko/pkg/publish\"\n)\n\nfunc TestTarball(t *testing.T) {\n\timg, err := random.Image(1024, 1)\n\tif err != nil {\n\t\tt.Fatalf(\"random.Image() = %v\", err)\n\t}\n\tbase := \"blah\"\n\timportpath := \"github.com/Google/go-containerregistry/cmd/crane\"\n\n\tfp, err := os.CreateTemp(\"\", \"\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\tdefer fp.Close()\n\tdefer os.Remove(fp.Name())\n\n\texpectedRepo := md5Hash(base, strings.ToLower(importpath))\n\n\ttag, err := name.NewTag(fmt.Sprintf(\"%s/%s:latest\", \"example.com\", expectedRepo))\n\tif err != nil {\n\t\tt.Fatalf(\"NewTag() = %v\", err)\n\t}\n\n\trepoName := fmt.Sprintf(\"%s/%s\", \"example.com\", base)\n\ttagss := [][]string{{\n\t\t// no tags\n\t}, {\n\t\t// one tag\n\t\t\"v0.1.0\",\n\t}, {\n\t\t// multiple tags\n\t\t\"latest\",\n\t\t\"debug\",\n\t}}\n\tfor _, tags := range tagss {\n\t\ttp := publish.NewTarball(fp.Name(), repoName, md5Hash, tags)\n\t\tif d, err := tp.Publish(context.Background(), img, importpath); err != nil {\n\t\t\tt.Errorf(\"Publish() = %v\", err)\n\t\t} else if !strings.HasPrefix(d.String(), tag.Repository.String()) {\n\t\t\tt.Errorf(\"Publish() = %v, wanted prefix %v\", d, tag.Repository)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "pkg/resolve/doc.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\n// Package resolve defines logic for resolving K8s yaml inputs to ko.\npackage resolve\n"
  },
  {
    "path": "pkg/resolve/resolve.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage resolve\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/dprotaso/go-yit\"\n\t\"github.com/google/ko/pkg/build\"\n\t\"github.com/google/ko/pkg/publish\"\n\t\"go.yaml.in/yaml/v4\"\n\t\"golang.org/x/sync/errgroup\"\n)\n\n// ImageReferences resolves supported references to images within the input yaml\n// to published image digests.\n//\n// If a reference can be built and pushed, its yaml.Node will be mutated.\nfunc ImageReferences(ctx context.Context, docs []*yaml.Node, builder build.Interface, publisher publish.Interface) error {\n\t// First, walk the input objects and collect a list of supported references\n\trefs := make(map[string][]*yaml.Node)\n\n\tfor _, doc := range docs {\n\t\tit := refsFromDoc(doc)\n\n\t\tfor node, ok := it(); ok; node, ok = it() {\n\t\t\tref := strings.TrimSpace(node.Value)\n\n\t\t\tif err := builder.IsSupportedReference(ref); err != nil {\n\t\t\t\treturn fmt.Errorf(\"found strict reference but %s is not a valid import path: %w\", ref, err)\n\t\t\t}\n\n\t\t\trefs[ref] = append(refs[ref], node)\n\t\t}\n\t}\n\n\t// Next, perform parallel builds for each of the supported references.\n\tvar sm sync.Map\n\tvar errg errgroup.Group\n\tfor ref := range refs {\n\t\terrg.Go(func() error {\n\t\t\timg, err := builder.Build(ctx, ref)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tdigest, err := publisher.Publish(ctx, img, ref)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tsm.Store(ref, digest.String())\n\t\t\treturn nil\n\t\t})\n\t}\n\tif err := errg.Wait(); err != nil {\n\t\treturn err\n\t}\n\n\t// Walk the tags and update them with their digest.\n\tfor ref, nodes := range refs {\n\t\tdigest, ok := sm.Load(ref)\n\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"resolved reference to %q not found\", ref)\n\t\t}\n\n\t\tfor _, node := range nodes {\n\t\t\tnode.Value = digest.(string)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc refsFromDoc(doc *yaml.Node) yit.Iterator {\n\tit := yit.FromNode(doc).\n\t\tRecurseNodes().\n\t\tFilter(yit.StringValue)\n\n\treturn it.Filter(yit.WithPrefix(build.StrictScheme))\n}\n"
  },
  {
    "path": "pkg/resolve/resolve_test.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage resolve\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"github.com/google/go-cmp/cmp/cmpopts\"\n\t\"github.com/google/go-containerregistry/pkg/name\"\n\tv1 \"github.com/google/go-containerregistry/pkg/v1\"\n\t\"github.com/google/go-containerregistry/pkg/v1/random\"\n\t\"github.com/google/ko/pkg/build\"\n\tkotesting \"github.com/google/ko/pkg/internal/testing\"\n\t\"go.yaml.in/yaml/v4\"\n)\n\nvar (\n\tfooRef      = \"github.com/awesomesauce/foo\"\n\tfoo         = mustRandom()\n\tfooHash     = mustDigest(foo)\n\tbarRef      = \"github.com/awesomesauce/bar\"\n\tbar         = mustRandom()\n\tbarHash     = mustDigest(bar)\n\tbazRef      = \"github.com/awesomesauce/baz\"\n\tbaz         = mustRandom()\n\tbazHash     = mustDigest(baz)\n\ttestBuilder = kotesting.NewFixedBuild(map[string]build.Result{\n\t\tfooRef: foo,\n\t\tbarRef: bar,\n\t\tbazRef: baz,\n\t})\n\ttestHashes = map[string]v1.Hash{\n\t\tfooRef: fooHash,\n\t\tbarRef: barHash,\n\t\tbazRef: bazHash,\n\t}\n)\n\nfunc TestYAMLArrays(t *testing.T) {\n\ttests := []struct {\n\t\tdesc   string\n\t\trefs   []string\n\t\thashes []v1.Hash\n\t\tbase   name.Repository\n\t}{{\n\t\tdesc:   \"singleton array\",\n\t\trefs:   []string{fooRef},\n\t\thashes: []v1.Hash{fooHash},\n\t\tbase:   mustRepository(\"gcr.io/mattmoor\"),\n\t}, {\n\t\tdesc:   \"singleton array (different base)\",\n\t\trefs:   []string{fooRef},\n\t\thashes: []v1.Hash{fooHash},\n\t\tbase:   mustRepository(\"gcr.io/jasonhall\"),\n\t}, {\n\t\tdesc:   \"two element array\",\n\t\trefs:   []string{fooRef, barRef},\n\t\thashes: []v1.Hash{fooHash, barHash},\n\t\tbase:   mustRepository(\"gcr.io/jonjohnson\"),\n\t}, {\n\t\tdesc:   \"empty array\",\n\t\trefs:   []string{},\n\t\thashes: []v1.Hash{},\n\t\tbase:   mustRepository(\"gcr.io/blah\"),\n\t}}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.desc, func(t *testing.T) {\n\t\t\tinputStructured := []string{}\n\t\t\tfor _, ref := range test.refs {\n\t\t\t\tinputStructured = append(inputStructured, build.StrictScheme+ref)\n\t\t\t}\n\t\t\tinputYAML, err := yaml.Marshal(inputStructured)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"yaml.Marshal(%v) = %v\", inputStructured, err)\n\t\t\t}\n\n\t\t\tdoc := strToYAML(t, string(inputYAML))\n\t\t\terr = ImageReferences(context.Background(), []*yaml.Node{doc}, testBuilder, kotesting.NewFixedPublish(test.base, testHashes))\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"ImageReferences(%v) = %v\", string(inputYAML), err)\n\t\t\t}\n\t\t\tvar outStructured []string\n\t\t\tif err := doc.Decode(&outStructured); err != nil {\n\t\t\t\tt.Errorf(\"doc.Decode(%v) = %v\", yamlToStr(t, doc), err)\n\t\t\t}\n\n\t\t\tif want, got := len(inputStructured), len(outStructured); want != got {\n\t\t\t\tt.Errorf(\"ImageReferences(%v) = %v, want %v\", string(inputYAML), got, want)\n\t\t\t}\n\n\t\t\tvar expectedStructured []string\n\t\t\tfor i, ref := range test.refs {\n\t\t\t\thash := test.hashes[i]\n\t\t\t\texpectedStructured = append(expectedStructured,\n\t\t\t\t\tkotesting.ComputeDigest(test.base, ref, hash))\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(expectedStructured, outStructured, cmpopts.EquateEmpty()); diff != \"\" {\n\t\t\t\tt.Errorf(\"ImageReferences(%v); (-want +got) = %v\", string(inputYAML), diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestYAMLMaps(t *testing.T) {\n\tbase := mustRepository(\"gcr.io/mattmoor\")\n\ttests := []struct {\n\t\tdesc     string\n\t\tinput    map[string]string\n\t\texpected map[string]string\n\t}{{\n\t\tdesc:     \"simple value\",\n\t\tinput:    map[string]string{\"image\": build.StrictScheme + fooRef},\n\t\texpected: map[string]string{\"image\": kotesting.ComputeDigest(base, fooRef, fooHash)},\n\t}, {\n\t\tdesc:  \"simple key\",\n\t\tinput: map[string]string{build.StrictScheme + bazRef: \"blah\"},\n\t\texpected: map[string]string{\n\t\t\tkotesting.ComputeDigest(base, bazRef, bazHash): \"blah\",\n\t\t},\n\t}, {\n\t\tdesc:  \"key and value\",\n\t\tinput: map[string]string{build.StrictScheme + fooRef: build.StrictScheme + barRef},\n\t\texpected: map[string]string{\n\t\t\tkotesting.ComputeDigest(base, fooRef, fooHash): kotesting.ComputeDigest(base, barRef, barHash),\n\t\t},\n\t}, {\n\t\tdesc:     \"empty map\",\n\t\tinput:    map[string]string{},\n\t\texpected: map[string]string{},\n\t}, {\n\t\tdesc: \"multiple values\",\n\t\tinput: map[string]string{\n\t\t\t\"arg1\": build.StrictScheme + fooRef,\n\t\t\t\"arg2\": build.StrictScheme + barRef,\n\t\t},\n\t\texpected: map[string]string{\n\t\t\t\"arg1\": kotesting.ComputeDigest(base, fooRef, fooHash),\n\t\t\t\"arg2\": kotesting.ComputeDigest(base, barRef, barHash),\n\t\t},\n\t}}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.desc, func(t *testing.T) {\n\t\t\tinputStructured := test.input\n\t\t\tinputYAML, err := yaml.Marshal(inputStructured)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"yaml.Marshal(%v) = %v\", inputStructured, err)\n\t\t\t}\n\n\t\t\tdoc := strToYAML(t, string(inputYAML))\n\t\t\terr = ImageReferences(context.Background(), []*yaml.Node{doc}, testBuilder, kotesting.NewFixedPublish(base, testHashes))\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"ImageReferences(%v) = %v\", string(inputYAML), err)\n\t\t\t}\n\t\t\tvar outStructured map[string]string\n\t\t\tif err := doc.Decode(&outStructured); err != nil {\n\t\t\t\tt.Errorf(\"doc.Decode(%v) = %v\", yamlToStr(t, doc), err)\n\t\t\t}\n\n\t\t\tif want, got := len(inputStructured), len(outStructured); want != got {\n\t\t\t\tt.Errorf(\"ImageReferences(%v) = %v, want %v\", string(inputYAML), got, want)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(test.expected, outStructured, cmpopts.EquateEmpty()); diff != \"\" {\n\t\t\t\tt.Errorf(\"ImageReferences(%v); (-want +got) = %v\", string(inputYAML), diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\n// object has public fields to avoid `yaml:\"foo\"` annotations.\ntype object struct {\n\tS string\n\tM map[string]object\n\tA []object\n\tP *object\n}\n\nfunc TestYAMLObject(t *testing.T) {\n\tbase := mustRepository(\"gcr.io/bazinga\")\n\ttests := []struct {\n\t\tdesc     string\n\t\tinput    *object\n\t\texpected *object\n\t}{{\n\t\tdesc:     \"empty object\",\n\t\tinput:    &object{},\n\t\texpected: &object{},\n\t}, {\n\t\tdesc:     \"string field\",\n\t\tinput:    &object{S: build.StrictScheme + fooRef},\n\t\texpected: &object{S: kotesting.ComputeDigest(base, fooRef, fooHash)},\n\t}, {\n\t\tdesc:     \"map field\",\n\t\tinput:    &object{M: map[string]object{\"blah\": {S: build.StrictScheme + fooRef}}},\n\t\texpected: &object{M: map[string]object{\"blah\": {S: kotesting.ComputeDigest(base, fooRef, fooHash)}}},\n\t}, {\n\t\tdesc:     \"array field\",\n\t\tinput:    &object{A: []object{{S: build.StrictScheme + fooRef}}},\n\t\texpected: &object{A: []object{{S: kotesting.ComputeDigest(base, fooRef, fooHash)}}},\n\t}, {\n\t\tdesc:     \"pointer field\",\n\t\tinput:    &object{P: &object{S: build.StrictScheme + fooRef}},\n\t\texpected: &object{P: &object{S: kotesting.ComputeDigest(base, fooRef, fooHash)}},\n\t}, {\n\t\tdesc:     \"deep field\",\n\t\tinput:    &object{M: map[string]object{\"blah\": {A: []object{{P: &object{S: build.StrictScheme + fooRef}}}}}},\n\t\texpected: &object{M: map[string]object{\"blah\": {A: []object{{P: &object{S: kotesting.ComputeDigest(base, fooRef, fooHash)}}}}}},\n\t}}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.desc, func(t *testing.T) {\n\t\t\tinputStructured := test.input\n\t\t\tinputYAML, err := yaml.Marshal(inputStructured)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"yaml.Marshal(%v) = %v\", inputStructured, err)\n\t\t\t}\n\n\t\t\tdoc := strToYAML(t, string(inputYAML))\n\t\t\terr = ImageReferences(context.Background(), []*yaml.Node{doc}, testBuilder, kotesting.NewFixedPublish(base, testHashes))\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"ImageReferences(%v) = %v\", string(inputYAML), err)\n\t\t\t}\n\t\t\tvar outStructured *object\n\t\t\tif err := doc.Decode(&outStructured); err != nil {\n\t\t\t\tt.Errorf(\"doc.Decode(%v) = %v\", yamlToStr(t, doc), err)\n\t\t\t}\n\n\t\t\tif diff := cmp.Diff(test.expected, outStructured, cmpopts.EquateEmpty()); diff != \"\" {\n\t\t\t\tt.Errorf(\"ImageReferences(%v); (-want +got) = %v\", string(inputYAML), diff)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestStrict(t *testing.T) {\n\trefs := []string{\n\t\tfooRef,\n\t\tbarRef,\n\t}\n\tbuf := bytes.NewBuffer(nil)\n\tencoder := yaml.NewEncoder(buf)\n\tfor _, input := range refs {\n\t\tif err := encoder.Encode(build.StrictScheme + input); err != nil {\n\t\t\tt.Fatalf(\"Encode(%v) = %v\", input, err)\n\t\t}\n\t}\n\tbase := mustRepository(\"gcr.io/multi-pass\")\n\tdoc := strToYAML(t, buf.String())\n\n\terr := ImageReferences(context.Background(), []*yaml.Node{doc}, testBuilder, kotesting.NewFixedPublish(base, testHashes))\n\tif err != nil {\n\t\tt.Fatalf(\"ImageReferences: %v\", err)\n\t}\n\tt.Log(yamlToStr(t, doc))\n}\n\nfunc TestIsSupportedReferenceError(t *testing.T) {\n\tref := build.StrictScheme + fooRef\n\n\tbuf := bytes.NewBuffer(nil)\n\tencoder := yaml.NewEncoder(buf)\n\tif err := encoder.Encode(ref); err != nil {\n\t\tt.Fatalf(\"Encode(%v) = %v\", ref, err)\n\t}\n\n\tbase := mustRepository(\"gcr.io/multi-pass\")\n\tdoc := strToYAML(t, buf.String())\n\n\tnoMatchBuilder := kotesting.NewFixedBuild(nil)\n\n\terr := ImageReferences(context.Background(), []*yaml.Node{doc}, noMatchBuilder, kotesting.NewFixedPublish(base, testHashes))\n\tif err == nil {\n\t\tt.Fatal(\"ImageReferences should err, got nil\")\n\t}\n}\n\nfunc mustRandom() build.Result {\n\timg, err := random.Index(1024, 5, 1)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn img\n}\n\nfunc mustRepository(s string) name.Repository {\n\tn, err := name.NewRepository(s)\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn n\n}\n\nfunc mustDigest(img build.Result) v1.Hash {\n\td, err := img.Digest()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn d\n}\n"
  },
  {
    "path": "pkg/resolve/selector.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage resolve\n\nimport (\n\t\"errors\"\n\n\ty \"github.com/dprotaso/go-yit\"\n\t\"go.yaml.in/yaml/v4\"\n\t\"k8s.io/apimachinery/pkg/labels\"\n)\n\n// MatchesSelector returns true if the Kubernetes object (represented as a\n// yaml.Node) matches the selector. An error is returned if the yaml.Node is\n// not an K8s object or list.\n//\n// If the document is a list, the yaml.Node will be mutated to only include\n// items that match the selector.\nfunc MatchesSelector(doc *yaml.Node, selector labels.Selector) (bool, error) {\n\t// ignore the document node\n\tif doc.Kind == yaml.DocumentNode && len(doc.Content) > 0 {\n\t\tdoc = doc.Content[0]\n\t}\n\n\tkind, err := docKind(doc)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\tif kind == \"\" {\n\t\treturn false, nil\n\t}\n\n\tif kind == \"List\" {\n\t\treturn listMatchesSelector(doc, selector)\n\t}\n\n\treturn objMatchesSelector(doc, selector), nil\n}\n\nfunc docKind(doc *yaml.Node) (string, error) {\n\t// Null nodes will fail the check below, so simply ignore them.\n\tif doc.Tag == \"!!null\" {\n\t\treturn \"\", nil\n\t}\n\n\tit := y.FromNode(doc).\n\t\tFilter(y.Intersect(\n\t\t\ty.WithKind(yaml.MappingNode),\n\t\t\ty.WithMapKeyValue(\n\t\t\t\ty.WithStringValue(\"apiVersion\"),\n\t\t\t\ty.StringValue,\n\t\t\t),\n\t\t)).\n\t\tValuesForMap(\n\t\t\t// Key Predicate\n\t\t\ty.WithStringValue(\"kind\"),\n\t\t\t// Value Predicate\n\t\t\ty.StringValue,\n\t\t)\n\n\tnode, ok := it()\n\n\tif !ok {\n\t\treturn \"\", errors.New(\"yaml doesn't represent a k8s object\")\n\t}\n\n\treturn node.Value, nil\n}\n\nfunc objMatchesSelector(doc *yaml.Node, selector labels.Selector) bool {\n\tit := y.FromNode(doc).\n\t\tFilter(y.WithKind(yaml.MappingNode)).\n\t\t// Return the metadata map\n\t\tValuesForMap(\n\t\t\t// Key Predicate\n\t\t\ty.WithStringValue(\"metadata\"),\n\t\t\t// Value Predicate\n\t\t\ty.WithKind(yaml.MappingNode),\n\t\t).\n\t\t// Return the labels map\n\t\tValuesForMap(\n\t\t\t// Key Predicate\n\t\t\ty.WithStringValue(\"labels\"),\n\t\t\t// Value Predicate\n\t\t\ty.WithKind(yaml.MappingNode),\n\t\t)\n\n\tnode, ok := it()\n\n\t// Object has no metadata.labels, verify matching against an empty set.\n\tif !ok {\n\t\tnode = emptyMapNode\n\t}\n\n\treturn selector.Matches(labelsNode{node})\n}\n\nfunc listMatchesSelector(doc *yaml.Node, selector labels.Selector) (bool, error) {\n\tit := y.FromNode(doc).ValuesForMap(\n\t\t// Key Predicate\n\t\ty.WithStringValue(\"items\"),\n\t\t// Value Predicate\n\t\ty.WithKind(yaml.SequenceNode),\n\t)\n\n\tnode, ok := it()\n\n\t// We don't have a k8s list\n\tif !ok {\n\t\treturn false, errors.New(\"yaml is not a valid k8s list\")\n\t}\n\n\tvar matches []*yaml.Node\n\tfor _, content := range node.Content {\n\t\tif _, err := docKind(content); err != nil {\n\t\t\treturn false, err\n\t\t}\n\n\t\tif objMatchesSelector(content, selector) {\n\t\t\tmatches = append(matches, content)\n\t\t}\n\t}\n\n\tnode.Content = matches\n\treturn len(matches) != 0, nil\n}\n\nvar emptyMapNode = &yaml.Node{\n\tKind: yaml.MappingNode,\n\tTag:  \"!!map\",\n}\n\ntype labelsNode struct {\n\t*yaml.Node\n}\n\nvar _ labels.Labels = labelsNode{}\n\nfunc (n labelsNode) Get(label string) (value string) {\n\tfor i := 0; i < len(n.Content); i += 2 {\n\t\tif n.Content[i].Value == label {\n\t\t\treturn n.Content[i+1].Value\n\t\t}\n\t}\n\treturn\n}\n\nfunc (n labelsNode) Has(label string) bool {\n\tfor i := 0; i < len(n.Content); i += 2 {\n\t\tif n.Content[i].Value == label {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (n labelsNode) Lookup(label string) (value string, exists bool) {\n\tfor i := 0; i < len(n.Content); i += 2 {\n\t\tif n.Content[i].Value == label {\n\t\t\treturn n.Content[i+1].Value, true\n\t\t}\n\t}\n\treturn \"\", false\n}\n"
  },
  {
    "path": "pkg/resolve/selector_test.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage resolve\n\nimport (\n\t\"testing\"\n\n\t\"github.com/google/go-cmp/cmp\"\n\t\"go.yaml.in/yaml/v4\"\n\t\"k8s.io/apimachinery/pkg/labels\"\n)\n\nvar (\n\twebSelector    = selector(`app=web`)\n\tnotWebSelector = selector(`app!=web`)\n\tnopSelector    = selector(`foo!=bark`)\n\n\thasSelector    = selector(`app`)\n\tnotHasSelector = selector(`!app`)\n)\n\nconst (\n\twebPod = `apiVersion: v1\nkind: Pod\nmetadata:\n  labels:\n    # comments should be preserved\n    app: web\n  name: rss-site\n`\n\tdbPod = `apiVersion: v1\nkind: Pod\nmetadata:\n  labels:\n    # comments should be preserved\n    app: db\n  name: rss-db\n`\n\tpodNoLabel = `apiVersion: v1\nkind: Pod\nmetadata:\n  name: rss-site\n`\n\tpodList = `apiVersion: v1\nkind: List\nmetadata:\n  resourceVersion: \"\"\n  selfLink: \"\"\nitems:\n- apiVersion: v1\n  kind: Pod\n  metadata:\n    labels:\n      app: web\n    name: rss-site\n- apiVersion: v1\n  kind: Pod\n  metadata:\n    labels:\n      app: db\n    name: rss-db\n`\n\twebPodList = `apiVersion: v1\nkind: List\nmetadata:\n  resourceVersion: \"\"\n  selfLink: \"\"\nitems:\n- apiVersion: v1\n  kind: Pod\n  metadata:\n    labels:\n      app: web\n    name: rss-site\n`\n\tdbPodList = `apiVersion: v1\nkind: List\nmetadata:\n  resourceVersion: \"\"\n  selfLink: \"\"\nitems:\n- apiVersion: v1\n  kind: Pod\n  metadata:\n    labels:\n      app: db\n    name: rss-db\n`\n\tpodListNoLabel = `apiVersion: v1\nkind: List\nmetadata:\n  resourceVersion: \"\"\n  selfLink: \"\"\nitems:\n- apiVersion: v1\n  kind: Pod\n  metadata:\n    name: rss-site\n- apiVersion: v1\n  kind: Pod\n  metadata:\n    name: rss-db\n`\n)\n\nfunc TestMatchesSelector(t *testing.T) {\n\ttests := []struct {\n\t\tdesc     string\n\t\tinput    string\n\t\tselector labels.Selector\n\t\toutput   string\n\t\tmatches  bool\n\t}{{\n\t\tdesc:     \"single object with matching selector\",\n\t\tinput:    webPod,\n\t\tselector: webSelector,\n\t\toutput:   webPod,\n\t\tmatches:  true,\n\t}, {\n\t\tdesc:     \"single object with non-matching selector\",\n\t\tinput:    webPod,\n\t\tselector: notWebSelector,\n\t\tmatches:  false,\n\t}, {\n\t\tdesc:     \"single object with noop selector\",\n\t\tinput:    dbPod,\n\t\tselector: nopSelector,\n\t\toutput:   dbPod,\n\t\tmatches:  true,\n\t}, {\n\t\tdesc:     \"single object with has selector\",\n\t\tinput:    webPod,\n\t\tselector: hasSelector,\n\t\toutput:   webPod,\n\t\tmatches:  true,\n\t}, {\n\t\tdesc:     \"single object with not-has selector\",\n\t\tinput:    webPod,\n\t\tselector: notHasSelector,\n\t\tmatches:  false,\n\t}, {\n\t\tdesc:     \"single non-labeled object with has selector\",\n\t\tinput:    podNoLabel,\n\t\tselector: hasSelector,\n\t\tmatches:  false,\n\t}, {\n\t\tdesc:     \"single non-labeled object with not-has selector\",\n\t\tinput:    podNoLabel,\n\t\tselector: notHasSelector,\n\t\toutput:   podNoLabel,\n\t\tmatches:  true,\n\t}, {\n\t\tdesc:     \"selector matching elements of list object\",\n\t\tinput:    podList,\n\t\tselector: webSelector,\n\t\toutput:   webPodList,\n\t\tmatches:  true,\n\t}, {\n\t\tdesc:     \"selector matching other elements of list object\",\n\t\tinput:    podList,\n\t\tselector: notWebSelector,\n\t\toutput:   dbPodList,\n\t\tmatches:  true,\n\t}, {\n\t\tdesc:     \"has selector matching no non-labeled element of list object\",\n\t\tinput:    podListNoLabel,\n\t\tselector: hasSelector,\n\t\tmatches:  false,\n\t}, {\n\t\tdesc:     \"not-has selector matching all non-labeled elements of list object\",\n\t\tinput:    podListNoLabel,\n\t\tselector: notHasSelector,\n\t\toutput:   podListNoLabel,\n\t\tmatches:  true,\n\t}, {\n\t\tdesc:     \"selector matching all elements of list object\",\n\t\tinput:    podList,\n\t\tselector: labels.Everything(),\n\t\toutput:   podList,\n\t\tmatches:  true,\n\t}, {\n\t\tdesc:     \"selector matching no element of list object\",\n\t\tinput:    podList,\n\t\tselector: labels.Nothing(),\n\t\tmatches:  false,\n\t}}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.desc, func(t *testing.T) {\n\t\t\tdoc := strToYAML(t, test.input)\n\t\t\tmatches, err := MatchesSelector(doc, test.selector)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"unexpected error: %v\", err)\n\t\t\t}\n\n\t\t\tif matches != test.matches {\n\t\t\t\tt.Errorf(\"unexpected result: got %v - want %v\", matches, test.matches)\n\t\t\t}\n\n\t\t\t// assert doc is mutated correctly\n\t\t\tif test.output != \"\" {\n\t\t\t\t// Normalize whitespace formatting\n\t\t\t\toutput := normalizeYAML(t, test.output)\n\n\t\t\t\tif diff := cmp.Diff(output, yamlToStr(t, doc)); diff != \"\" {\n\t\t\t\t\tt.Errorf(\"unexpected diff (-want, +got) %v\", diff)\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSelectorFailure(t *testing.T) {\n\ttests := []struct {\n\t\tdesc  string\n\t\tinput string\n\t}{\n\t\t{\n\t\t\tdesc:  \"not an object\",\n\t\t\tinput: \"image: some.go/package\",\n\t\t},\n\t\t{\n\t\t\tdesc: \"not an object in a list\",\n\t\t\tinput: `apiVersion: v1\nkind: List\nmetadata:\n  resourceVersion: \"\"\n  selfLink: \"\"\nitems:\n- blah: ha\n`,\n\t\t},\n\t\t{\n\t\t\tdesc: \"not a valid list\",\n\t\t\tinput: `apiVersion: v1\nkind: List\n`,\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.desc, func(t *testing.T) {\n\t\t\t_, err := MatchesSelector(strToYAML(t, test.input), labels.Everything())\n\n\t\t\tif err == nil {\n\t\t\t\tt.Error(\"expected error\")\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc selector(s string) labels.Selector {\n\tselector, err := labels.Parse(s)\n\tif err != nil {\n\t\tpanic(\"unable to parse selector \" + s)\n\t}\n\treturn selector\n}\n\nfunc normalizeYAML(t *testing.T, yuml string) string {\n\tt.Helper()\n\treturn yamlToStr(t, strToYAML(t, yuml))\n}\n\nfunc yamlToStr(t *testing.T, node *yaml.Node) string {\n\tresult, err := yaml.Marshal(node)\n\tif err != nil {\n\t\tt.Fatalf(\"error marshalling yaml: %v\", err)\n\t}\n\n\treturn string(result)\n}\n\nfunc strToYAML(t *testing.T, yuml string) *yaml.Node {\n\tt.Helper()\n\tvar node yaml.Node\n\n\tif err := yaml.Unmarshal([]byte(yuml), &node); err != nil {\n\t\tt.Fatalf(\"error unmarshalling yaml: %v\\n%v\", err, yuml)\n\t}\n\n\treturn &node\n}\n\nfunc TestLabelsNode(t *testing.T) {\n\ttests := []struct {\n\t\tdesc   string\n\t\tlabels map[string]string\n\t\tlabel  string\n\t\tvalue  string\n\t\texists bool\n\t}{{\n\t\tdesc:   \"label exists\",\n\t\tlabels: map[string]string{\"app\": \"web\", \"version\": \"v1\"},\n\t\tlabel:  \"app\",\n\t\tvalue:  \"web\",\n\t\texists: true,\n\t}, {\n\t\tdesc:   \"label does not exist\",\n\t\tlabels: map[string]string{\"app\": \"web\", \"version\": \"v1\"},\n\t\tlabel:  \"tier\",\n\t\tvalue:  \"\",\n\t\texists: false,\n\t}, {\n\t\tdesc:   \"empty labels\",\n\t\tlabels: map[string]string{},\n\t\tlabel:  \"app\",\n\t\tvalue:  \"\",\n\t\texists: false,\n\t}, {\n\t\tdesc:   \"label with empty value\",\n\t\tlabels: map[string]string{\"app\": \"\", \"version\": \"v1\"},\n\t\tlabel:  \"app\",\n\t\tvalue:  \"\",\n\t\texists: true,\n\t}, {\n\t\tdesc:   \"multiple labels with special characters\",\n\t\tlabels: map[string]string{\"app.io/name\": \"web\", \"version\": \"v1\", \"owner\": \"team-a\"},\n\t\tlabel:  \"app.io/name\",\n\t\tvalue:  \"web\",\n\t\texists: true,\n\t}, {\n\t\tdesc:   \"label with numeric value\",\n\t\tlabels: map[string]string{\"replicas\": \"3\", \"port\": \"8080\"},\n\t\tlabel:  \"replicas\",\n\t\tvalue:  \"3\",\n\t\texists: true,\n\t}, {\n\t\tdesc:   \"case sensitive label lookup\",\n\t\tlabels: map[string]string{\"App\": \"web\", \"app\": \"database\"},\n\t\tlabel:  \"app\",\n\t\tvalue:  \"database\",\n\t\texists: true,\n\t}}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.desc, func(t *testing.T) {\n\t\t\t// Create a mapping node directly\n\t\t\tnode := &yaml.Node{\n\t\t\t\tKind: yaml.MappingNode,\n\t\t\t}\n\n\t\t\t// Add key-value pairs to the node\n\t\t\tfor key, value := range test.labels {\n\t\t\t\tkeyNode := &yaml.Node{\n\t\t\t\t\tKind:  yaml.ScalarNode,\n\t\t\t\t\tValue: key,\n\t\t\t\t}\n\t\t\t\tvalueNode := &yaml.Node{\n\t\t\t\t\tKind:  yaml.ScalarNode,\n\t\t\t\t\tValue: value,\n\t\t\t\t}\n\t\t\t\tnode.Content = append(node.Content, keyNode, valueNode)\n\t\t\t}\n\n\t\t\tln := labelsNode{node}\n\n\t\t\t// Test Get method\n\t\t\tgotValue := ln.Get(test.label)\n\t\t\tif gotValue != test.value {\n\t\t\t\tt.Errorf(\"Get(%q) = %q, want %q\", test.label, gotValue, test.value)\n\t\t\t}\n\n\t\t\t// Test Has method\n\t\t\tgotExists := ln.Has(test.label)\n\t\t\tif gotExists != test.exists {\n\t\t\t\tt.Errorf(\"Has(%q) = %v, want %v\", test.label, gotExists, test.exists)\n\t\t\t}\n\n\t\t\t// Test Lookup method\n\t\t\tgotLookupValue, gotLookupExists := ln.Lookup(test.label)\n\t\t\tif gotLookupValue != test.value {\n\t\t\t\tt.Errorf(\"Lookup(%q) value = %q, want %q\", test.label, gotLookupValue, test.value)\n\t\t\t}\n\t\t\tif gotLookupExists != test.exists {\n\t\t\t\tt.Errorf(\"Lookup(%q) exists = %v, want %v\", test.label, gotLookupExists, test.exists)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "test/build-configs/.ko.yaml",
    "content": "# Copyright 2021 ko Build Authors All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nbuilds:\n- id: foo-app\n  dir: ./foo\n  main: ./cmd\n  flags: -v -v # build.Config parser must handle shorthand syntax\n- id: bar-app\n  dir: ./bar\n  main: ./cmd\n- id: toolexec\n  dir: ./toolexec\n  main: ./cmd\n  flags:\n  - -toolexec\n  - go\n- id: caps-app\n  dir: ./caps\n  main: ./cmd\n"
  },
  {
    "path": "test/build-configs/bar/cmd/main.go",
    "content": "// Copyright 2021 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"bar\")\n}\n"
  },
  {
    "path": "test/build-configs/bar/go.mod",
    "content": "// Copyright 2021 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nmodule example.com/bar\n\ngo 1.16\n"
  },
  {
    "path": "test/build-configs/caps/cmd/main.go",
    "content": "// Copyright 2024 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n)\n\nfunc permittedCaps() (uint64, error) {\n\tdata, err := ioutil.ReadFile(\"/proc/self/status\")\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tconst prefix = \"CapPrm:\"\n\tfor _, line := range strings.Split(string(data), \"\\n\") {\n\t\tif strings.HasPrefix(line, prefix) {\n\t\t\treturn strconv.ParseUint(strings.TrimSpace(line[len(prefix):]), 16, 64)\n\t\t}\n\t}\n\treturn 0, fmt.Errorf(\"didn't find %#v in /proc/self/status\", prefix)\n}\n\nfunc main() {\n\tcaps, err := permittedCaps()\n\tif err != nil {\n\t\tfmt.Println(err)\n\t\tos.Exit(1)\n\t}\n\tif caps == 0 {\n\t\tfmt.Println(\"No capabilities\")\n\t} else {\n\t\tfmt.Printf(\"Has capabilities (%x)\\n\", caps)\n\t}\n}\n"
  },
  {
    "path": "test/build-configs/caps/go.mod",
    "content": "// Copyright 2024 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nmodule example.com/caps\n\ngo 1.16\n"
  },
  {
    "path": "test/build-configs/caps.ko.yaml",
    "content": "# Copyright 2024 ko Build Authors All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n\nbuilds:\n- id: caps-app-with-caps\n  dir: ./caps\n  main: ./cmd\n  linux_capabilities: net_bind_service chown\n"
  },
  {
    "path": "test/build-configs/foo/cmd/main.go",
    "content": "// Copyright 2021 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"foo\")\n}\n"
  },
  {
    "path": "test/build-configs/foo/go.mod",
    "content": "// Copyright 2021 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nmodule example.com/foo\n\ngo 1.16\n"
  },
  {
    "path": "test/build-configs/toolexec/cmd/main.go",
    "content": "// Copyright 2022 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage main\n\nimport \"fmt\"\n\nfunc main() {\n\tfmt.Println(\"toolexec\")\n}\n"
  },
  {
    "path": "test/build-configs/toolexec/go.mod",
    "content": "// Copyright 2022 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nmodule example.com/toolexec\n\ngo 1.16\n"
  },
  {
    "path": "test/kodata/a",
    "content": "hello\n"
  },
  {
    "path": "test/kodata/kenobi",
    "content": "Hello there\n"
  },
  {
    "path": "test/kodata/subdir/file.txt",
    "content": "subdir file content\n"
  },
  {
    "path": "test/main.go",
    "content": "// Copyright 2018 ko Build Authors All Rights Reserved.\n//\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n//    http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\npackage main\n\nimport (\n\t\"flag\"\n\t\"fmt\"\n\t\"log\"\n\t\"os\"\n\t\"os/signal\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"syscall\"\n\t\"time\"\n\n\t// Give this an interesting import\n\t_ \"github.com/google/go-containerregistry/pkg/registry\"\n)\n\nvar (\n\tf    = flag.String(\"f\", \"kenobi\", \"File in kodata to print\")\n\twait = flag.Bool(\"wait\", false, \"Whether to wait for SIGTERM\")\n)\n\n// This is defined so we can test build-time variable setting using ldflags.\nvar version = \"default\"\n\nfunc main() {\n\tflag.Parse()\n\n\tlog.Println(\"version =\", version)\n\n\tif runtime.GOOS == \"windows\" {\n\t\t// Go seems to not load location data from Windows, so timezone\n\t\t// conversion fails unless tzdata is embedded in the Go program\n\t\t// with the go build tag `timetzdata`. Since we want to test\n\t\t// loading tzdata provided by the base image below, we'll just\n\t\t// skip that for Windows here.\n\t\t// See https://github.com/ko-build/ko/issues/739\n\t\tlog.Println(\"skipping timezone conversion on Windows\")\n\t} else {\n\t\t// Exercise timezone conversions, which demonstrates tzdata is provided\n\t\t// by the base image.\n\t\tnow := time.Now()\n\t\tloc, _ := time.LoadLocation(\"UTC\")\n\t\tfmt.Printf(\"UTC Time: %s\\n\", now.In(loc))\n\t\tloc, _ = time.LoadLocation(\"America/New_York\")\n\t\tfmt.Printf(\"New York Time: %s\\n\", now.In(loc))\n\t}\n\n\tdp := os.Getenv(\"KO_DATA_PATH\")\n\tfile := filepath.Join(dp, *f)\n\tbytes, err := os.ReadFile(file)\n\tif err != nil {\n\t\tlog.Fatalf(\"Error reading %q: %v\", file, err)\n\t}\n\tfmt.Println(string(bytes))\n\n\t// Cause the pod to \"hang\" to allow us to check for a readiness state.\n\tif *wait {\n\t\tsigs := make(chan os.Signal, 1)\n\t\tsignal.Notify(sigs, syscall.SIGTERM)\n\t\t<-sigs\n\t}\n}\n"
  },
  {
    "path": "test/test.yaml",
    "content": "# Copyright 2018 ko Build Authors All Rights Reserved.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#    http://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\napiVersion: v1\nkind: Pod\nmetadata:\n  name: kodata\nspec:\n  containers:\n  - name: obiwan\n    image: ko://github.com/google/ko/test\n    args:\n      - --wait=true\n  restartPolicy: Never\n"
  }
]