mirror of https://github.com/buildpacks/libcnb.git
Compare commits
313 Commits
Author | SHA1 | Date |
---|---|---|
|
f7e753e42f | |
|
274a25f737 | |
|
a5d5f00dfc | |
|
babcbff863 | |
|
4c50e41bb7 | |
|
dfb149268e | |
|
9e96b2cdf8 | |
|
986aa1f129 | |
|
ec253d8a85 | |
|
0a704de531 | |
|
4d96611671 | |
|
fc02d58a39 | |
|
aa64fa6d04 | |
|
e2504aad02 | |
|
541caaca58 | |
|
b1a32e8a04 | |
|
b6de1780e4 | |
|
685a6de0df | |
|
f85e7067d6 | |
|
42eabc313b | |
|
b9fc3fec80 | |
|
b32eb02dfa | |
|
7a3158aeb9 | |
|
2002d252e9 | |
|
ecdde19b9b | |
|
29547c451a | |
|
46d41fad11 | |
|
2a303f641c | |
|
fbd6632792 | |
|
089a0f9d04 | |
|
3ba867d5de | |
|
70e38ccecc | |
|
777d800b23 | |
|
084bd173f5 | |
|
193b480460 | |
|
4b0cdf2fb8 | |
|
1d2cd33a8b | |
|
5e1b803e2d | |
|
976fce60fc | |
|
7a8d6df0fb | |
|
9343310624 | |
|
68f1d7dc99 | |
|
09148d831e | |
|
432f14274b | |
|
59903afb4c | |
|
d6b2574ae1 | |
|
3e3992aaa5 | |
|
663213ffbf | |
|
ff0986080e | |
|
19d88148ff | |
|
cf9baef95d | |
|
4f8f92a58c | |
|
e7c2e5da91 | |
|
5d3d3f6e6d | |
|
0ffaf167f2 | |
|
6dcaa2eeb2 | |
|
d77e5b49f3 | |
|
a8abacf488 | |
|
ff323e4cbb | |
|
2b39329ef3 | |
|
0f889eda5e | |
|
d4c34358cd | |
|
82f0d2fde6 | |
|
d7fde7baad | |
|
f10dc6660e | |
|
2313404efa | |
|
53ea2d4112 | |
|
d13fcca61b | |
|
7ef9b73067 | |
|
21fa5b9f3e | |
|
70be45f510 | |
|
bacbd31203 | |
|
bb82ce0702 | |
|
e9f2cb8583 | |
|
9541f56830 | |
|
a3eccb730e | |
|
438a986000 | |
|
9b489461d0 | |
|
3175e42c71 | |
|
8c162d5042 | |
|
5f52f3ecc8 | |
|
7185622fa0 | |
|
3308e77997 | |
|
78810648dd | |
|
48a6ad033c | |
|
cc26ad5bcc | |
|
7fcb08c9fd | |
|
b2aa734851 | |
|
daf9de1766 | |
|
87b531972f | |
|
734dbf9b26 | |
|
39445a8884 | |
|
5234f9c837 | |
|
f7f9919675 | |
|
8542bee9d0 | |
|
8347fd9e5b | |
|
05f5c35c95 | |
|
1f740aba88 | |
|
8553694890 | |
|
646bf6b465 | |
|
6f71a8467d | |
|
b7d3d515ef | |
|
2696858ab0 | |
|
4892e3b4c5 | |
|
4b96ab802c | |
|
98647956ab | |
|
272cbe263f | |
|
8c92bce2dc | |
|
46bbd4601e | |
|
a9404c31cb | |
|
2b53a1d8a9 | |
|
a54cf8ab45 | |
|
a81dbdf586 | |
|
3285cf1a1b | |
|
f2d102431a | |
|
9f998abc47 | |
|
5143e3dcfa | |
|
6882b47e95 | |
|
6ea59bbedc | |
|
3354bfcda5 | |
|
5e36043456 | |
|
5bb9c7b3d8 | |
|
5114b77d7f | |
|
71efa8132a | |
|
e6b42e9dc7 | |
|
31c63bc860 | |
|
87f95b2b80 | |
|
66af9c5c22 | |
|
9f40fa2bec | |
|
a9cb6b2dd6 | |
|
1dfbc993cc | |
|
3e35e4513b | |
|
f1b4378a23 | |
|
60e9ca7aff | |
|
d61ffa8660 | |
|
8a82a0ef1a | |
|
34e343460c | |
|
4d5ea0c017 | |
|
a244d9ffc0 | |
|
587cf4b267 | |
|
aaff56ad46 | |
|
4d4003ef78 | |
|
3b38a59476 | |
|
63cd2b0f30 | |
|
da9b109ec8 | |
|
5beb81e48b | |
|
11abe2b4f0 | |
|
1031c7329e | |
|
48c2123e6c | |
|
f7b68c6ccf | |
|
9c5c713940 | |
|
481034cb02 | |
|
73483d77e8 | |
|
33398bde5c | |
|
3cebafc8e4 | |
|
1d06c7f6e3 | |
|
c3a7795dbb | |
|
aed8887609 | |
|
00601e4588 | |
|
31c9b286f3 | |
|
e90023ce44 | |
|
9b80891fe5 | |
|
da8314616c | |
|
5e05852376 | |
|
d29fc724c0 | |
|
0496d9ff6e | |
|
c36f48d861 | |
|
8496ef4039 | |
|
45321cb1ac | |
|
0a77d8caf7 | |
|
a1b0928708 | |
|
4454619860 | |
|
7eb31bf136 | |
|
d893d2a8de | |
|
42462272ec | |
|
81cf3b415a | |
|
16b7007d6f | |
|
03d8910db4 | |
|
26e4e63625 | |
|
0f1f628f40 | |
|
4ae60411a2 | |
|
1112c1f8ee | |
|
7394cb3817 | |
|
3a76485b9c | |
|
615e972098 | |
|
6906ce9b95 | |
|
baf6c4c366 | |
|
8290e25d26 | |
|
66f602326d | |
|
6ed27036bc | |
|
ccab05cded | |
|
076d273d9b | |
|
c07e39f248 | |
|
fb349312eb | |
|
2b18648b96 | |
|
767778e6e0 | |
|
297a22fd5f | |
|
53bcfba959 | |
|
85ac10e790 | |
|
9a10bbb1d2 | |
|
611b3e5347 | |
|
ad7e23c798 | |
|
902eaf02a3 | |
|
f26c239b90 | |
|
9785aecd0d | |
|
dccb6e6bcd | |
|
982e2d4909 | |
|
36f4784377 | |
|
478b9311b1 | |
|
c0a47f69ab | |
|
ae6b978c89 | |
|
5b825cbe68 | |
|
ab7d1d48e0 | |
|
97e6b3127f | |
|
6f587bfaed | |
|
cd6b493b58 | |
|
0f3d181082 | |
|
a7304c7a00 | |
|
8b9574e869 | |
|
60f4cf3f97 | |
|
fe3488ce08 | |
|
04ccf6dfca | |
|
21d6c1dff0 | |
|
30ddc330a7 | |
|
d530f08ce3 | |
|
e9086e7c30 | |
|
4df41b6b03 | |
|
62b6135e91 | |
|
c9042c2175 | |
|
c46cf4bf31 | |
|
0d976195e1 | |
|
a35db7d6c3 | |
|
72c83eef49 | |
|
e773fada93 | |
|
ed2f9ee178 | |
|
2a242a7d1b | |
|
c3964b516f | |
|
06b6583131 | |
|
238eed184a | |
|
8272777045 | |
|
a252c950cc | |
|
544789248d | |
|
4493cfb731 | |
|
e877c549da | |
|
b28564b5f9 | |
|
079faa7e1a | |
|
035f820e40 | |
|
8ca6ac9b55 | |
|
7551b21b34 | |
|
57857fb612 | |
|
3821c30122 | |
|
17d931b61e | |
|
fa26e36d56 | |
|
f52b8d92df | |
|
83006cc64f | |
|
d1f22b3790 | |
|
f64ff84085 | |
|
cfff823ecf | |
|
5d6aee7649 | |
|
e538f5edf7 | |
|
153660dec2 | |
|
5472f1bbc6 | |
|
4025de8dce | |
|
ff55295249 | |
|
cb11f48e8e | |
|
fe0070b13f | |
|
fe109c46dd | |
|
00b62992a7 | |
|
a334604b28 | |
|
5dce23d2e4 | |
|
1090513e35 | |
|
5f7f747cef | |
|
2bb35d1cbb | |
|
bb5d1bac18 | |
|
db86607ba0 | |
|
ee3f64f570 | |
|
a741fb5554 | |
|
0b2cdfa3b5 | |
|
b12cdd714a | |
|
e8fbe86a2f | |
|
9a33953d0f | |
|
1efa1bf4e6 | |
|
c13f32f770 | |
|
b5c7509185 | |
|
2ee8ac271d | |
|
fe863a105a | |
|
0d165b270f | |
|
ea234546cb | |
|
78a82b9392 | |
|
360ce69631 | |
|
68283f612b | |
|
bc1afa531d | |
|
bebbd9ccd0 | |
|
0e3e59fa0c | |
|
e781e89416 | |
|
0851e6229e | |
|
0aad7b332f | |
|
aa9c732e54 | |
|
a11309dd84 | |
|
8b17b65820 | |
|
7fe4669a9d | |
|
1fece64356 | |
|
9efae17518 | |
|
51ccaa81ea | |
|
97c86e76ab | |
|
82821a787d | |
|
8eeed85719 | |
|
f84a51d264 | |
|
545fe62001 | |
|
b422aeb5c6 | |
|
bc8678b242 | |
|
ebfa749007 | |
|
d0b65cb871 |
|
@ -0,0 +1 @@
|
|||
* @buildpacks/implementation-maintainers
|
|
@ -0,0 +1,11 @@
|
|||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: gomod
|
||||
directory: /
|
||||
schedule:
|
||||
interval: daily
|
||||
ignore:
|
||||
- dependency-name: github.com/onsi/gomega
|
||||
labels:
|
||||
- semver:patch
|
||||
- type:dependency-upgrade
|
|
@ -0,0 +1,42 @@
|
|||
- name: semver:major
|
||||
description: A change requiring a major version bump
|
||||
color: f9d0c4
|
||||
- name: semver:minor
|
||||
description: A change requiring a minor version bump
|
||||
color: f9d0c4
|
||||
- name: semver:patch
|
||||
description: A change requiring a patch version bump
|
||||
color: f9d0c4
|
||||
- name: type:bug
|
||||
description: A general bug
|
||||
color: e3d9fc
|
||||
- name: type:dependency-upgrade
|
||||
description: A dependency upgrade
|
||||
color: e3d9fc
|
||||
- name: type:documentation
|
||||
description: A documentation update
|
||||
color: e3d9fc
|
||||
- name: type:enhancement
|
||||
description: A general enhancement
|
||||
color: e3d9fc
|
||||
- name: type:question
|
||||
description: A user question
|
||||
color: e3d9fc
|
||||
- name: type:task
|
||||
description: A general task
|
||||
color: e3d9fc
|
||||
- name: type:informational
|
||||
description: Provides information or notice to the community
|
||||
color: e3d9fc
|
||||
- name: type:poll
|
||||
description: Request for feedback from the community
|
||||
color: e3d9fc
|
||||
- name: note:ideal-for-contribution
|
||||
description: An issue that a contributor can help us with
|
||||
color: 54f7a8
|
||||
- name: note:on-hold
|
||||
description: We can't start working on this issue yet
|
||||
color: 54f7a8
|
||||
- name: note:good-first-issue
|
||||
description: A good first issue to get started with
|
||||
color: 54f7a8
|
|
@ -0,0 +1,38 @@
|
|||
github:
|
||||
username: ${{ secrets.IMPLEMENTATION_GITHUB_USERNAME }}
|
||||
token: ${{ secrets.IMPLEMENTATION_GITHUB_TOKEN }}
|
||||
|
||||
codeowners:
|
||||
- path: "*"
|
||||
owner: "@buildpacks/implementation-maintainers"
|
||||
|
||||
test:
|
||||
steps:
|
||||
- name: Install richgo
|
||||
run: |
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
echo "Installing richgo ${RICHGO_VERSION}"
|
||||
|
||||
mkdir -p "${HOME}"/bin
|
||||
echo "${HOME}/bin" >> "${GITHUB_PATH}"
|
||||
|
||||
curl \
|
||||
--location \
|
||||
--show-error \
|
||||
--silent \
|
||||
"https://github.com/kyoh86/richgo/releases/download/v${RICHGO_VERSION}/richgo_${RICHGO_VERSION}_linux_amd64.tar.gz" \
|
||||
| tar -C "${HOME}"/bin -xz richgo
|
||||
env:
|
||||
RICHGO_VERSION: 0.3.10
|
||||
- name: Run Tests
|
||||
run: |
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
GOCMD=richgo make
|
||||
env:
|
||||
RICHGO_FORCE_COLOR: "1"
|
|
@ -0,0 +1 @@
|
|||
1.42.0
|
|
@ -0,0 +1,32 @@
|
|||
template: $CHANGES
|
||||
name-template: $RESOLVED_VERSION
|
||||
tag-template: v$RESOLVED_VERSION
|
||||
categories:
|
||||
- title: ⭐️ Enhancements
|
||||
labels:
|
||||
- type:enhancement
|
||||
- title: "\U0001F41E Bug Fixes"
|
||||
labels:
|
||||
- type:bug
|
||||
- title: "\U0001F4D4 Documentation"
|
||||
labels:
|
||||
- type:documentation
|
||||
- title: ⛏ Dependency Upgrades
|
||||
labels:
|
||||
- type:dependency-upgrade
|
||||
- title: "\U0001F6A7 Tasks"
|
||||
labels:
|
||||
- type:task
|
||||
exclude-labels:
|
||||
- type:question
|
||||
version-resolver:
|
||||
major:
|
||||
labels:
|
||||
- semver:major
|
||||
minor:
|
||||
labels:
|
||||
- semver:minor
|
||||
patch:
|
||||
labels:
|
||||
- semver:patch
|
||||
default: patch
|
|
@ -0,0 +1,29 @@
|
|||
name: Minimal Labels
|
||||
"on":
|
||||
pull_request:
|
||||
types:
|
||||
- synchronize
|
||||
- reopened
|
||||
- labeled
|
||||
- unlabeled
|
||||
jobs:
|
||||
semver:
|
||||
name: Minimal Semver Labels
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
steps:
|
||||
- uses: mheap/github-action-required-labels@v5
|
||||
with:
|
||||
count: 1
|
||||
labels: semver:major, semver:minor, semver:patch
|
||||
mode: exactly
|
||||
type:
|
||||
name: Minimal Type Labels
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
steps:
|
||||
- uses: mheap/github-action-required-labels@v5
|
||||
with:
|
||||
count: 1
|
||||
labels: type:bug, type:dependency-upgrade, type:documentation, type:enhancement, type:question, type:task
|
||||
mode: exactly
|
|
@ -0,0 +1,17 @@
|
|||
name: Synchronize Labels
|
||||
"on":
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- .github/labels.yml
|
||||
jobs:
|
||||
synchronize:
|
||||
name: Synchronize Labels
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: micnncim/action-label-syncer@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.IMPLEMENTATION_GITHUB_TOKEN }}
|
|
@ -0,0 +1,54 @@
|
|||
name: Tests
|
||||
"on":
|
||||
merge_group:
|
||||
types:
|
||||
- checks_requested
|
||||
branches:
|
||||
- main
|
||||
pull_request: {}
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
jobs:
|
||||
unit:
|
||||
name: Unit Test
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/cache@v4
|
||||
with:
|
||||
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
|
||||
path: ${{ env.HOME }}/go/pkg/mod
|
||||
restore-keys: ${{ runner.os }}-go-
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.24"
|
||||
- name: Install richgo
|
||||
run: |
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
echo "Installing richgo ${RICHGO_VERSION}"
|
||||
|
||||
mkdir -p "${HOME}"/bin
|
||||
echo "${HOME}/bin" >> "${GITHUB_PATH}"
|
||||
|
||||
curl \
|
||||
--location \
|
||||
--show-error \
|
||||
--silent \
|
||||
"https://github.com/kyoh86/richgo/releases/download/v${RICHGO_VERSION}/richgo_${RICHGO_VERSION}_linux_amd64.tar.gz" \
|
||||
| tar -C "${HOME}"/bin -xz richgo
|
||||
env:
|
||||
RICHGO_VERSION: 0.3.10
|
||||
- name: Run Tests
|
||||
run: |
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
GOCMD=richgo make
|
||||
env:
|
||||
RICHGO_FORCE_COLOR: "1"
|
|
@ -0,0 +1,15 @@
|
|||
name: Update Draft Release
|
||||
"on":
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
jobs:
|
||||
update:
|
||||
name: Update Draft Release
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
steps:
|
||||
- id: release-drafter
|
||||
uses: release-drafter/release-drafter@v5
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.IMPLEMENTATION_GITHUB_TOKEN }}
|
|
@ -0,0 +1,72 @@
|
|||
name: Update Go
|
||||
"on":
|
||||
schedule:
|
||||
- cron: 17 2 * * 1
|
||||
workflow_dispatch: {}
|
||||
jobs:
|
||||
update:
|
||||
name: Update Go
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.24"
|
||||
- uses: actions/checkout@v4
|
||||
- name: Update Go Version & Modules
|
||||
id: update-go
|
||||
run: |
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if [ -z "${GO_VERSION:-}" ]; then
|
||||
echo "No go version set"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
OLD_GO_VERSION=$(grep -P '^go \d\.\d+' go.mod | cut -d ' ' -f 2 | cut -d '.' -f 1-2)
|
||||
|
||||
go mod edit -go="$GO_VERSION"
|
||||
go mod tidy
|
||||
go get -u -t ./...
|
||||
go mod tidy
|
||||
|
||||
git add go.mod go.sum
|
||||
git checkout -- .
|
||||
|
||||
if [ "$OLD_GO_VERSION" == "$GO_VERSION" ]; then
|
||||
COMMIT_TITLE="Bump Go Modules"
|
||||
COMMIT_BODY="Bumps Go modules used by the project. See the commit for details on what modules were updated."
|
||||
COMMIT_SEMVER="semver:patch"
|
||||
else
|
||||
COMMIT_TITLE="Bump Go from ${OLD_GO_VERSION} to ${GO_VERSION}"
|
||||
COMMIT_BODY="Bumps Go from ${OLD_GO_VERSION} to ${GO_VERSION} and update Go modules used by the project. See the commit for details on what modules were updated."
|
||||
COMMIT_SEMVER="semver:minor"
|
||||
fi
|
||||
|
||||
echo "commit-title=${COMMIT_TITLE}" >> "$GITHUB_OUTPUT"
|
||||
echo "commit-body=${COMMIT_BODY}" >> "$GITHUB_OUTPUT"
|
||||
echo "commit-semver=${COMMIT_SEMVER}" >> "$GITHUB_OUTPUT"
|
||||
env:
|
||||
GO_VERSION: "1.24"
|
||||
- uses: peter-evans/create-pull-request@v6
|
||||
with:
|
||||
author: ${{ secrets.IMPLEMENTATION_GITHUB_USERNAME }} <${{ secrets.IMPLEMENTATION_GITHUB_USERNAME }}@users.noreply.github.com>
|
||||
body: |-
|
||||
${{ steps.update-go.outputs.commit-body }}
|
||||
|
||||
<details>
|
||||
<summary>Release Notes</summary>
|
||||
${{ steps.pipeline.outputs.release-notes }}
|
||||
</details>
|
||||
branch: update/go
|
||||
commit-message: |-
|
||||
${{ steps.update-go.outputs.commit-title }}
|
||||
|
||||
${{ steps.update-go.outputs.commit-body }}
|
||||
delete-branch: true
|
||||
labels: ${{ steps.update-go.outputs.commit-semver }}, type:task
|
||||
signoff: true
|
||||
title: ${{ steps.update-go.outputs.commit-title }}
|
||||
token: ${{ secrets.IMPLEMENTATION_GITHUB_TOKEN }}
|
|
@ -0,0 +1,93 @@
|
|||
name: Update Pipeline
|
||||
"on":
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- .github/pipeline-descriptor.yml
|
||||
schedule:
|
||||
- cron: 0 5 * * 1-5
|
||||
workflow_dispatch: {}
|
||||
jobs:
|
||||
update:
|
||||
name: Update Pipeline
|
||||
runs-on:
|
||||
- ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/setup-go@v5
|
||||
with:
|
||||
go-version: "1.24"
|
||||
- name: Install octo
|
||||
run: |
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
go install -ldflags="-s -w" github.com/paketo-buildpacks/pipeline-builder/cmd/octo@latest
|
||||
- uses: actions/checkout@v4
|
||||
- name: Update Pipeline
|
||||
id: pipeline
|
||||
run: |
|
||||
#!/usr/bin/env bash
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
if [[ -f .github/pipeline-version ]]; then
|
||||
OLD_VERSION=$(cat .github/pipeline-version)
|
||||
else
|
||||
OLD_VERSION="0.0.0"
|
||||
fi
|
||||
|
||||
rm .github/workflows/pb-*.yml || true
|
||||
octo --descriptor "${DESCRIPTOR}"
|
||||
|
||||
PAYLOAD=$(gh api /repos/paketo-buildpacks/pipeline-builder/releases/latest)
|
||||
|
||||
NEW_VERSION=$(jq -n -r --argjson PAYLOAD "${PAYLOAD}" '$PAYLOAD.name')
|
||||
echo "${NEW_VERSION}" > .github/pipeline-version
|
||||
|
||||
RELEASE_NOTES=$(
|
||||
gh api \
|
||||
-F text="$(jq -n -r --argjson PAYLOAD "${PAYLOAD}" '$PAYLOAD.body')" \
|
||||
-F mode="gfm" \
|
||||
-F context="paketo-buildpacks/pipeline-builder" \
|
||||
-X POST /markdown
|
||||
)
|
||||
|
||||
git add .github/
|
||||
git add .gitignore
|
||||
|
||||
if [ -f scripts/build.sh ]; then
|
||||
git add scripts/build.sh
|
||||
fi
|
||||
|
||||
git checkout -- .
|
||||
|
||||
echo "old-version=${OLD_VERSION}" >> "$GITHUB_OUTPUT"
|
||||
echo "new-version=${NEW_VERSION}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
DELIMITER=$(openssl rand -hex 16) # roughly the same entropy as uuid v4 used in https://github.com/actions/toolkit/blob/b36e70495fbee083eb20f600eafa9091d832577d/packages/core/src/file-command.ts#L28
|
||||
printf "release-notes<<%s\n%s\n%s\n" "${DELIMITER}" "${RELEASE_NOTES}" "${DELIMITER}" >> "${GITHUB_OUTPUT}" # see https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#multiline-strings
|
||||
env:
|
||||
DESCRIPTOR: .github/pipeline-descriptor.yml
|
||||
GITHUB_TOKEN: ${{ secrets.IMPLEMENTATION_GITHUB_TOKEN }}
|
||||
- uses: peter-evans/create-pull-request@v6
|
||||
with:
|
||||
author: ${{ secrets.IMPLEMENTATION_GITHUB_USERNAME }} <${{ secrets.IMPLEMENTATION_GITHUB_USERNAME }}@users.noreply.github.com>
|
||||
body: |-
|
||||
Bumps pipeline from `${{ steps.pipeline.outputs.old-version }}` to `${{ steps.pipeline.outputs.new-version }}`.
|
||||
|
||||
<details>
|
||||
<summary>Release Notes</summary>
|
||||
${{ steps.pipeline.outputs.release-notes }}
|
||||
</details>
|
||||
branch: update/pipeline
|
||||
commit-message: |-
|
||||
Bump pipeline from ${{ steps.pipeline.outputs.old-version }} to ${{ steps.pipeline.outputs.new-version }}
|
||||
|
||||
Bumps pipeline from ${{ steps.pipeline.outputs.old-version }} to ${{ steps.pipeline.outputs.new-version }}.
|
||||
delete-branch: true
|
||||
labels: semver:patch, type:task
|
||||
signoff: true
|
||||
title: Bump pipeline from ${{ steps.pipeline.outputs.old-version }} to ${{ steps.pipeline.outputs.new-version }}
|
||||
token: ${{ secrets.IMPLEMENTATION_GITHUB_TOKEN }}
|
|
@ -11,3 +11,10 @@
|
|||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
bin/
|
||||
linux/
|
||||
dependencies/
|
||||
package/
|
||||
scratch/
|
||||
|
||||
|
|
|
@ -1 +0,0 @@
|
|||
* @buildpacks/implementation-maintainers
|
2
LICENSE
2
LICENSE
|
@ -187,7 +187,7 @@
|
|||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
Copyright 2018 The Cloud Native Buildpacks Authors
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
# Migration Guide
|
||||
|
||||
This guide highlights the major differences between libcnb v1 and v2 with a focus on what you as an author need to change to upgrade your buildpacks from v1 to v2.
|
||||
|
||||
## Buildpack API Support
|
||||
|
||||
With libcnb v1, you get support for buildpack API 0.5 to 0.8. With v2, you get support for 0.8 to 0.10. This should provide a seamless transition as you can continue to use buildpack API 0.8 with
|
||||
|
||||
## Removal of LayerContributor
|
||||
|
||||
The LayerContributor has been removed. Previously, libcnb would take a list of LayerContributors and execute them to retrieve the list of Layers to process. Now it just directly takes the list of Layers to process.
|
||||
|
||||
The path forward is to either update your buildpacks to return layers or to implement your own LayerContributor interface as a means to ease the transition to libcnb v2.
|
||||
|
||||
## Replace Builder and Detector with Functions
|
||||
|
||||
The Builder and Detector interfaces have been removed and replaced with functions, specifically BuildFunc and DetectFunc. They serve the same purpose, but simplify your implementation because you do not need to implement the single method interface, you can only need pass in a function that will be called back.
|
||||
|
||||
The path forward is to remove your Builder and Detector structs and pass the method directly into libcnb.
|
||||
|
||||
## Rename `poet` to `log`
|
||||
|
||||
We have renamed the `poet` module to be called `log`. This is a simple find & replace change in your code. We have also simplified the method names, so instead of needing to say `poet.NewLogger`, you can say `log.New` or `log.NewWithOptions`.
|
||||
|
||||
We have also introduced a new `Logger` interface which can be overridden to control how libcnb logs. A basic implementation has been provided, called `PlainLogger`. If you want to supply a custom implementation, it can be passed in through main/build/detect functions.
|
||||
|
||||
Lastly, libcnb has been modified such that it will only log at the `Debug` level. Anything problems that arise will instead return an error, which you as the buildpack author should handle.
|
||||
|
||||
## Remove Deprecated Pre-Buildpack API 0.7 BOM
|
||||
|
||||
The pre-buildpack API 0.7 BOM API was marked for deprecation in libcnb v1. It has been removed in v2. If you are still using it, you will need to migrate to use the new SBOM functionality provided by the [Buildpacks API](https://github.com/buildpacks/rfcs/blob/main/text/0095-sbom.md).
|
||||
|
||||
## Path to Source Code Changed
|
||||
|
||||
In libcnb v1, `BuildContext.Application.Path` points to the application source code. This was shortened to `BuildContext.ApplicationPath`. The same change was made for `DetectContext.ApplicationPath`.
|
||||
|
||||
## Remove Deprecated CNB Binding Support
|
||||
|
||||
The CNB Binding specification has long been replaced by the Service Binding Specification for Kubernetes. Support for the CNB Binding Support had remained, but it is removed in v2. This is unlikely to impact anyone.
|
||||
|
||||
## Remove Shell-Specific Logic & Overridable Process Arguments
|
||||
|
||||
To comply with [RFC #168](https://github.com/buildpacks/rfcs/pull/168), we remove the `Direct` field from the `Process` struct. We also change `Command` from a `string` to `string[]` on the `Process` struct, to support overridable process arguments. Both of these require at leats API 0.9.
|
||||
|
||||
In conjunction with this, we have also removed Profile & `profile.d` support. These should be replaced with the [Profile Buildpack](https://github.com/buildpacks/profile) and `exec.d`.
|
|
@ -0,0 +1,25 @@
|
|||
# Go parameters
|
||||
GOCMD?=go
|
||||
GO_VERSION=$(shell go list -m -f "{{.GoVersion}}")
|
||||
PACKAGE_BASE=github.com/buildpacks/libcnb/v2
|
||||
|
||||
all: test
|
||||
|
||||
install-goimports:
|
||||
@echo "> Installing goimports..."
|
||||
cd tools && $(GOCMD) install golang.org/x/tools/cmd/goimports
|
||||
|
||||
format: install-goimports
|
||||
@echo "> Formating code..."
|
||||
@goimports -l -w -local ${PACKAGE_BASE} .
|
||||
|
||||
install-golangci-lint:
|
||||
@echo "> Installing golangci-lint..."
|
||||
cd tools && $(GOCMD) install github.com/golangci/golangci-lint/cmd/golangci-lint
|
||||
|
||||
lint: install-golangci-lint
|
||||
@echo "> Linting code..."
|
||||
@golangci-lint run -c golangci.yaml
|
||||
|
||||
test: format lint
|
||||
$(GOCMD) test -parallel=1 -count=1 -v ./...
|
20
README.md
20
README.md
|
@ -1,11 +1,29 @@
|
|||
# `github.com/buildpacks/libcnb`
|
||||
`libcnb` is a Go language binding of the Cloud Native Buildpack API. It is a non-opinionated implementation adding language constructs and convenience methods for working with the API.
|
||||
|
||||
[](https://pkg.go.dev/github.com/buildpacks/libcnb)
|
||||
|
||||
`libcnb` is a Go language binding of the Cloud Native Buildpacks API. It is a non-opinionated implementation adding language constructs and convenience methods for working with the API.
|
||||
|
||||
> For operations such as building an app, creating a builder or packaging a buildpack, you may use [`pack`](https://github.com/buildpacks/pack) as a Go library.
|
||||
|
||||
## Usage
|
||||
|
||||
#### Installation
|
||||
|
||||
```
|
||||
go get github.com/buildpacks/libcnb
|
||||
```
|
||||
|
||||
or for the v2 alpha
|
||||
|
||||
```
|
||||
go get github.com/buildpacks/libcnb@v2.0.0-alpha.1
|
||||
```
|
||||
|
||||
#### Docs
|
||||
|
||||
https://pkg.go.dev/github.com/buildpacks/libcnb?tab=doc
|
||||
|
||||
## License
|
||||
This library is released under version 2.0 of the [Apache License][a].
|
||||
|
||||
|
|
|
@ -1,77 +0,0 @@
|
|||
/*
|
||||
* Copyright 2018-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package libcnb
|
||||
|
||||
// Application is the user contributed application to build.
|
||||
type Application struct {
|
||||
|
||||
// Path is the path to the application.
|
||||
Path string
|
||||
}
|
||||
|
||||
// Label represents an image label.
|
||||
type Label struct {
|
||||
|
||||
// Key is the key of the label.
|
||||
Key string `toml:"key"`
|
||||
|
||||
// Value is the value of the label.
|
||||
Value string `toml:"value"`
|
||||
}
|
||||
|
||||
// Process represents metadata about a type of command that can be run.
|
||||
type Process struct {
|
||||
|
||||
// Type is the type of the process.
|
||||
Type string `toml:"type"`
|
||||
|
||||
// Command is the command of the process.
|
||||
Command string `toml:"command"`
|
||||
|
||||
// Arguments are arguments to the command.
|
||||
Arguments []string `toml:"args"`
|
||||
|
||||
// Command is exec'd directly by the os (no profile.d scripts run)
|
||||
Direct bool `toml:"direct,omitempty"`
|
||||
}
|
||||
|
||||
// Slice represents metadata about a slice.
|
||||
type Slice struct {
|
||||
|
||||
// Paths are the contents of the slice.
|
||||
Paths []string `toml:"paths"`
|
||||
}
|
||||
|
||||
// Launch represents the contents of launch.toml.
|
||||
type Launch struct {
|
||||
|
||||
// Labels is the collection of image labels contributed by the buildpack.
|
||||
Labels []Label `toml:"labels"`
|
||||
|
||||
// Processes is the collection of process types contributed by the buildpack.
|
||||
Processes []Process `toml:"processes"`
|
||||
|
||||
// Slices is the collection of slices contributed by the buildpack.
|
||||
Slices []Slice `toml:"slices"`
|
||||
}
|
||||
|
||||
// Store represents the contents of store.toml
|
||||
type Store struct {
|
||||
|
||||
// Metadata represents the persistent metadata.
|
||||
Metadata map[string]interface{} `toml:"metadata"`
|
||||
}
|
303
build.go
303
build.go
|
@ -17,6 +17,7 @@
|
|||
package libcnb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
@ -24,15 +25,17 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/buildpacks/libcnb/internal"
|
||||
"github.com/buildpacks/libcnb/poet"
|
||||
"github.com/Masterminds/semver"
|
||||
|
||||
"github.com/buildpacks/libcnb/v2/internal"
|
||||
"github.com/buildpacks/libcnb/v2/log"
|
||||
)
|
||||
|
||||
// BuildContext contains the inputs to build.
|
||||
type BuildContext struct {
|
||||
|
||||
// Application is application to build.
|
||||
Application Application
|
||||
// ApplicationPath is the location of the application source code as provided by
|
||||
// the lifecycle.
|
||||
ApplicationPath string
|
||||
|
||||
// Buildpack is metadata about the buildpack, from buildpack.toml.
|
||||
Buildpack Buildpack
|
||||
|
@ -40,6 +43,9 @@ type BuildContext struct {
|
|||
// Layers is the layers available to the buildpack.
|
||||
Layers Layers
|
||||
|
||||
// Logger is the way to write messages to the end user
|
||||
Logger log.Logger
|
||||
|
||||
// PersistentMetadata is metadata that is persisted even across cache cleaning.
|
||||
PersistentMetadata map[string]interface{}
|
||||
|
||||
|
@ -49,37 +55,51 @@ type BuildContext struct {
|
|||
// Platform is the contents of the platform.
|
||||
Platform Platform
|
||||
|
||||
// StackID is the ID of the stack.
|
||||
// Deprecated: StackID is the ID of the stack.
|
||||
StackID string
|
||||
|
||||
// TargetInfo contains info of the target (os, arch, ...).
|
||||
TargetInfo TargetInfo
|
||||
|
||||
// TargetDistro is the target distribution (name, version).
|
||||
TargetDistro TargetDistro
|
||||
}
|
||||
|
||||
// BuildResult contains the results of detection.
|
||||
type BuildResult struct {
|
||||
|
||||
// Labels are the image labels contributed by the buildpack.
|
||||
Labels []Label
|
||||
|
||||
// Layers is the collection of LayerCreators contributed by the buildpack.
|
||||
Layers []LayerContributor
|
||||
Layers []Layer
|
||||
|
||||
// PersistentMetadata is metadata that is persisted even across cache cleaning.
|
||||
PersistentMetadata map[string]interface{}
|
||||
|
||||
// Plan is the buildpack plan contributed by the buildpack.
|
||||
Plan *BuildpackPlan
|
||||
|
||||
// Processes are the process types contributed by the buildpack.
|
||||
Processes []Process
|
||||
|
||||
// Slices are the application slices contributed by the buildpack.
|
||||
Slices []Slice
|
||||
|
||||
// Unmet contains buildpack plan entries that were not satisfied by the buildpack and therefore should be
|
||||
// passed to subsequent providers.
|
||||
Unmet []UnmetPlanEntry
|
||||
}
|
||||
|
||||
// Constants to track minimum and maximum supported Buildpack API versions
|
||||
const (
|
||||
// MinSupportedBPVersion indicates the minium supported version of the Buildpacks API
|
||||
MinSupportedBPVersion = "0.8"
|
||||
|
||||
// MaxSupportedBPVersion indicates the maximum supported version of the Buildpacks API
|
||||
MaxSupportedBPVersion = "0.10"
|
||||
)
|
||||
|
||||
// NewBuildResult creates a new BuildResult instance, initializing empty fields.
|
||||
func NewBuildResult() BuildResult {
|
||||
return BuildResult{
|
||||
PersistentMetadata: make(map[string]interface{}),
|
||||
Plan: &BuildpackPlan{},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -89,61 +109,47 @@ func (b BuildResult) String() string {
|
|||
l = append(l, reflect.TypeOf(c).Name())
|
||||
}
|
||||
|
||||
return fmt.Sprintf("{Labels:%+v Layers:%s PersistentMetadata:%+v Plan:%+v Processes:%+v Slices:%+v}",
|
||||
b.Labels, l, b.PersistentMetadata, b.Plan, b.PersistentMetadata, b.Slices)
|
||||
return fmt.Sprintf(
|
||||
"{Labels:%+v Layers:%s PersistentMetadata:%+v Processes:%+v Slices:%+v, Unmet:%+v}",
|
||||
b.Labels, l, b.PersistentMetadata, b.PersistentMetadata, b.Slices, b.Unmet,
|
||||
)
|
||||
}
|
||||
|
||||
//go:generate mockery -name Builder -case=underscore
|
||||
|
||||
// Builder describes an interface for types that can be used by the Build function.
|
||||
type Builder interface {
|
||||
|
||||
// Build takes a context and returns a result, performing buildpack build behaviors.
|
||||
Build(context BuildContext) (BuildResult, error)
|
||||
}
|
||||
// BuildFunc takes a context and returns a result, performing buildpack build behaviors.
|
||||
type BuildFunc func(context BuildContext) (BuildResult, error)
|
||||
|
||||
// Build is called by the main function of a buildpack, for build.
|
||||
func Build(builder Builder, options ...Option) {
|
||||
config := Config{
|
||||
arguments: os.Args,
|
||||
environmentWriter: internal.EnvironmentWriter{},
|
||||
exitHandler: internal.NewExitHandler(),
|
||||
tomlWriter: internal.TOMLWriter{},
|
||||
}
|
||||
|
||||
for _, option := range options {
|
||||
config = option(config)
|
||||
}
|
||||
|
||||
if len(config.arguments) != 4 {
|
||||
config.exitHandler.Error(fmt.Errorf("expected 3 arguments and received %d", len(config.arguments)-1))
|
||||
return
|
||||
}
|
||||
|
||||
func Build(build BuildFunc, config Config) {
|
||||
var (
|
||||
err error
|
||||
file string
|
||||
ok bool
|
||||
)
|
||||
ctx := BuildContext{}
|
||||
logger := poet.NewLogger(os.Stdout)
|
||||
ctx := BuildContext{Logger: config.logger}
|
||||
|
||||
ctx.Application.Path, err = os.Getwd()
|
||||
ctx.ApplicationPath, err = os.Getwd()
|
||||
if err != nil {
|
||||
config.exitHandler.Error(fmt.Errorf("unable to get working directory\n%w", err))
|
||||
return
|
||||
}
|
||||
if logger.IsDebugEnabled() {
|
||||
logger.Debug(ApplicationPathFormatter(ctx.Application.Path))
|
||||
|
||||
if config.logger.IsDebugEnabled() {
|
||||
if err := config.contentWriter.Write("Application contents", ctx.ApplicationPath); err != nil {
|
||||
config.logger.Debugf("unable to write application contents\n%w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if s, ok := os.LookupEnv("CNB_BUILDPACK_DIR"); ok {
|
||||
if s, ok := os.LookupEnv(EnvBuildpackDirectory); ok {
|
||||
ctx.Buildpack.Path = filepath.Clean(s)
|
||||
} else { // TODO: Remove branch once lifecycle has been updated to support this
|
||||
ctx.Buildpack.Path = filepath.Clean(strings.TrimSuffix(config.arguments[0], filepath.Join("bin", "build")))
|
||||
} else {
|
||||
config.exitHandler.Error(fmt.Errorf("unable to get CNB_BUILDPACK_DIR, not found"))
|
||||
return
|
||||
}
|
||||
if logger.IsDebugEnabled() {
|
||||
logger.Debug(BuildpackPathFormatter(ctx.Buildpack.Path))
|
||||
|
||||
if config.logger.IsDebugEnabled() {
|
||||
if err := config.contentWriter.Write("Buildpack contents", ctx.Buildpack.Path); err != nil {
|
||||
config.logger.Debugf("unable to write buildpack contents\n%w", err)
|
||||
}
|
||||
}
|
||||
|
||||
file = filepath.Join(ctx.Buildpack.Path, "buildpack.toml")
|
||||
|
@ -151,29 +157,64 @@ func Build(builder Builder, options ...Option) {
|
|||
config.exitHandler.Error(fmt.Errorf("unable to decode buildpack %s\n%w", file, err))
|
||||
return
|
||||
}
|
||||
logger.Debugf("Buildpack: %+v", ctx.Buildpack)
|
||||
config.logger.Debugf("Buildpack: %+v", ctx.Buildpack)
|
||||
|
||||
ctx.Layers = Layers{config.arguments[1]}
|
||||
logger.Debugf("Layers: %+v", ctx.Layers)
|
||||
|
||||
ctx.Platform.Path = config.arguments[2]
|
||||
if logger.IsDebugEnabled() {
|
||||
logger.Debug(PlatformFormatter(ctx.Platform))
|
||||
}
|
||||
|
||||
file = filepath.Join(ctx.Platform.Path, "bindings")
|
||||
if ctx.Platform.Bindings, err = NewBindingsFromPath(file); err != nil {
|
||||
config.exitHandler.Error(fmt.Errorf("unable to read platform bindings %s\n%w", file, err))
|
||||
API, err := semver.NewVersion(ctx.Buildpack.API)
|
||||
if err != nil {
|
||||
config.exitHandler.Error(errors.New("version cannot be parsed"))
|
||||
return
|
||||
}
|
||||
logger.Debugf("Platform Bindings: %+v", ctx.Platform.Bindings)
|
||||
|
||||
compatVersionCheck, _ := semver.NewConstraint(fmt.Sprintf(">= %s, <= %s", MinSupportedBPVersion, MaxSupportedBPVersion))
|
||||
if !compatVersionCheck.Check(API) {
|
||||
if MinSupportedBPVersion == MaxSupportedBPVersion {
|
||||
config.exitHandler.Error(fmt.Errorf("this version of libcnb is only compatible with buildpack API == %s", MinSupportedBPVersion))
|
||||
return
|
||||
}
|
||||
|
||||
config.exitHandler.Error(fmt.Errorf("this version of libcnb is only compatible with buildpack APIs >= %s, <= %s", MinSupportedBPVersion, MaxSupportedBPVersion))
|
||||
return
|
||||
}
|
||||
|
||||
layersDir, ok := os.LookupEnv(EnvLayersDirectory)
|
||||
if !ok {
|
||||
config.exitHandler.Error(fmt.Errorf("expected CNB_LAYERS_DIR to be set"))
|
||||
return
|
||||
}
|
||||
ctx.Layers = Layers{layersDir}
|
||||
|
||||
ctx.Platform.Path, ok = os.LookupEnv(EnvPlatformDirectory)
|
||||
if !ok {
|
||||
config.exitHandler.Error(fmt.Errorf("expected CNB_PLATFORM_DIR to be set"))
|
||||
return
|
||||
}
|
||||
|
||||
buildpackPlanPath, ok := os.LookupEnv(EnvBuildPlanPath)
|
||||
if !ok {
|
||||
config.exitHandler.Error(fmt.Errorf("expected CNB_BP_PLAN_PATH to be set"))
|
||||
return
|
||||
}
|
||||
|
||||
config.logger.Debugf("Layers: %+v", ctx.Layers)
|
||||
|
||||
if config.logger.IsDebugEnabled() {
|
||||
if err := config.contentWriter.Write("Platform contents", ctx.Platform.Path); err != nil {
|
||||
config.logger.Debugf("unable to write platform contents\n%w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if ctx.Platform.Bindings, err = NewBindings(ctx.Platform.Path); err != nil {
|
||||
config.exitHandler.Error(fmt.Errorf("unable to read platform bindings %s\n%w", ctx.Platform.Path, err))
|
||||
return
|
||||
}
|
||||
config.logger.Debugf("Platform Bindings: %+v", ctx.Platform.Bindings)
|
||||
|
||||
file = filepath.Join(ctx.Platform.Path, "env")
|
||||
if ctx.Platform.Environment, err = internal.NewConfigMapFromPath(file); err != nil {
|
||||
config.exitHandler.Error(fmt.Errorf("unable to read platform environment %s\n%w", file, err))
|
||||
return
|
||||
}
|
||||
logger.Debugf("Platform Environment: %s", ctx.Platform.Environment)
|
||||
config.logger.Debugf("Platform Environment: %s", ctx.Platform.Environment)
|
||||
|
||||
var store Store
|
||||
file = filepath.Join(ctx.Layers.Path, "store.toml")
|
||||
|
@ -182,27 +223,39 @@ func Build(builder Builder, options ...Option) {
|
|||
return
|
||||
}
|
||||
ctx.PersistentMetadata = store.Metadata
|
||||
logger.Debugf("Persistent Metadata: %+v", ctx.PersistentMetadata)
|
||||
config.logger.Debugf("Persistent Metadata: %+v", ctx.PersistentMetadata)
|
||||
|
||||
file = config.arguments[3]
|
||||
if _, err = toml.DecodeFile(file, &ctx.Plan); err != nil && !os.IsNotExist(err) {
|
||||
config.exitHandler.Error(fmt.Errorf("unable to decode buildpack plan %s\n%w", file, err))
|
||||
if _, err = toml.DecodeFile(buildpackPlanPath, &ctx.Plan); err != nil && !os.IsNotExist(err) {
|
||||
config.exitHandler.Error(fmt.Errorf("unable to decode buildpack plan %s\n%w", buildpackPlanPath, err))
|
||||
return
|
||||
}
|
||||
logger.Debugf("Buildpack Plan: %+v", ctx.Plan)
|
||||
config.logger.Debugf("Buildpack Plan: %+v", ctx.Plan)
|
||||
|
||||
if ctx.StackID, ok = os.LookupEnv("CNB_STACK_ID"); !ok {
|
||||
config.exitHandler.Error(fmt.Errorf("CNB_STACK_ID not set"))
|
||||
return
|
||||
if ctx.StackID, ok = os.LookupEnv(EnvStackID); !ok {
|
||||
config.logger.Debug("CNB_STACK_ID not set")
|
||||
} else {
|
||||
config.logger.Debugf("Stack: %s", ctx.StackID)
|
||||
}
|
||||
logger.Debugf("Stack: %s", ctx.StackID)
|
||||
|
||||
result, err := builder.Build(ctx)
|
||||
if API.GreaterThan(semver.MustParse("0.9")) {
|
||||
ctx.TargetInfo = TargetInfo{}
|
||||
ctx.TargetInfo.OS, _ = os.LookupEnv(EnvTargetOS)
|
||||
ctx.TargetInfo.Arch, _ = os.LookupEnv(EnvTargetArch)
|
||||
ctx.TargetInfo.Variant, _ = os.LookupEnv(EnvTargetArchVariant)
|
||||
config.logger.Debugf("System: %+v", ctx.TargetInfo)
|
||||
|
||||
ctx.TargetDistro = TargetDistro{}
|
||||
ctx.TargetDistro.Name, _ = os.LookupEnv(EnvTargetDistroName)
|
||||
ctx.TargetDistro.Version, _ = os.LookupEnv(EnvTargetDistroVersion)
|
||||
config.logger.Debugf("Distro: %+v", ctx.TargetDistro)
|
||||
}
|
||||
|
||||
result, err := build(ctx)
|
||||
if err != nil {
|
||||
config.exitHandler.Error(err)
|
||||
return
|
||||
}
|
||||
logger.Debugf("Result: %+v", result)
|
||||
config.logger.Debugf("Result: %+v", result)
|
||||
|
||||
file = filepath.Join(ctx.Layers.Path, "*.toml")
|
||||
existing, err := filepath.Glob(file)
|
||||
|
@ -212,50 +265,30 @@ func Build(builder Builder, options ...Option) {
|
|||
}
|
||||
var contributed []string
|
||||
|
||||
for _, creator := range result.Layers {
|
||||
name := creator.Name()
|
||||
layer, err := ctx.Layers.Layer(name)
|
||||
if err != nil {
|
||||
config.exitHandler.Error(fmt.Errorf("unable to create layer %s\n%w", name, err))
|
||||
return
|
||||
}
|
||||
|
||||
layer, err = creator.Contribute(layer)
|
||||
if err != nil {
|
||||
config.exitHandler.Error(fmt.Errorf("unable to invoke layer creator\n%w", err))
|
||||
return
|
||||
}
|
||||
|
||||
for _, layer := range result.Layers {
|
||||
file = filepath.Join(layer.Path, "env.build")
|
||||
logger.Debugf("Writing layer env.build: %s <= %+v", file, layer.BuildEnvironment)
|
||||
config.logger.Debugf("Writing layer env.build: %s <= %+v", file, layer.BuildEnvironment)
|
||||
if err = config.environmentWriter.Write(file, layer.BuildEnvironment); err != nil {
|
||||
config.exitHandler.Error(fmt.Errorf("unable to write layer env.build %s\n%w", file, err))
|
||||
return
|
||||
}
|
||||
|
||||
file = filepath.Join(layer.Path, "env.launch")
|
||||
logger.Debugf("Writing layer env.launch: %s <= %+v", file, layer.LaunchEnvironment)
|
||||
config.logger.Debugf("Writing layer env.launch: %s <= %+v", file, layer.LaunchEnvironment)
|
||||
if err = config.environmentWriter.Write(file, layer.LaunchEnvironment); err != nil {
|
||||
config.exitHandler.Error(fmt.Errorf("unable to write layer env.launch %s\n%w", file, err))
|
||||
return
|
||||
}
|
||||
|
||||
file = filepath.Join(layer.Path, "env")
|
||||
logger.Debugf("Writing layer env: %s <= %+v", file, layer.SharedEnvironment)
|
||||
config.logger.Debugf("Writing layer env: %s <= %+v", file, layer.SharedEnvironment)
|
||||
if err = config.environmentWriter.Write(file, layer.SharedEnvironment); err != nil {
|
||||
config.exitHandler.Error(fmt.Errorf("unable to write layer env %s\n%w", file, err))
|
||||
return
|
||||
}
|
||||
|
||||
file = filepath.Join(layer.Path, "profile.d")
|
||||
logger.Debugf("Writing layer profile.d: %s <= %+v", file, layer.Profile)
|
||||
if err = config.environmentWriter.Write(file, layer.Profile); err != nil {
|
||||
config.exitHandler.Error(fmt.Errorf("unable to write layer profile.d %s\n%w", file, err))
|
||||
return
|
||||
}
|
||||
|
||||
file = filepath.Join(ctx.Layers.Path, fmt.Sprintf("%s.toml", layer.Name))
|
||||
logger.Debugf("Writing layer metadata: %s <= %+v", file, layer)
|
||||
config.logger.Debugf("Writing layer metadata: %s <= %+v", file, layer)
|
||||
if err = config.tomlWriter.Write(file, layer); err != nil {
|
||||
config.exitHandler.Error(fmt.Errorf("unable to write layer metadata %s\n%w", file, err))
|
||||
return
|
||||
|
@ -268,7 +301,7 @@ func Build(builder Builder, options ...Option) {
|
|||
continue
|
||||
}
|
||||
|
||||
logger.Debugf("Removing %s", e)
|
||||
config.logger.Debugf("Removing %s", e)
|
||||
|
||||
if err := os.RemoveAll(e); err != nil {
|
||||
config.exitHandler.Error(fmt.Errorf("unable to remove %s\n%w", e, err))
|
||||
|
@ -276,40 +309,52 @@ func Build(builder Builder, options ...Option) {
|
|||
}
|
||||
}
|
||||
|
||||
if len(result.Labels) > 0 || len(result.Processes) > 0 || len(result.Slices) > 0 {
|
||||
launch := Launch{
|
||||
Labels: result.Labels,
|
||||
Processes: result.Processes,
|
||||
Slices: result.Slices,
|
||||
}
|
||||
if err := validateSBOMFormats(ctx.Layers.Path, ctx.Buildpack.Info.SBOMFormats); err != nil {
|
||||
config.exitHandler.Error(fmt.Errorf("unable to validate SBOM\n%w", err))
|
||||
return
|
||||
}
|
||||
|
||||
launch := LaunchTOML{
|
||||
Labels: result.Labels,
|
||||
Processes: result.Processes,
|
||||
Slices: result.Slices,
|
||||
}
|
||||
|
||||
if !launch.isEmpty() {
|
||||
file = filepath.Join(ctx.Layers.Path, "launch.toml")
|
||||
logger.Debugf("Writing application metadata: %s <= %+v", file, launch)
|
||||
config.logger.Debugf("Writing application metadata: %s <= %+v", file, launch)
|
||||
|
||||
if err = config.tomlWriter.Write(file, launch); err != nil {
|
||||
config.exitHandler.Error(fmt.Errorf("unable to write application metadata %s\n%w", file, err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
buildTOML := BuildTOML{
|
||||
Unmet: result.Unmet,
|
||||
}
|
||||
|
||||
if !buildTOML.isEmpty() {
|
||||
file = filepath.Join(ctx.Layers.Path, "build.toml")
|
||||
config.logger.Debugf("Writing build metadata: %s <= %+v", file, build)
|
||||
|
||||
if err = config.tomlWriter.Write(file, buildTOML); err != nil {
|
||||
config.exitHandler.Error(fmt.Errorf("unable to write build metadata %s\n%w", file, err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(result.PersistentMetadata) > 0 {
|
||||
store = Store{
|
||||
Metadata: result.PersistentMetadata,
|
||||
}
|
||||
file = filepath.Join(ctx.Layers.Path, "store.toml")
|
||||
logger.Debugf("Writing persistent metadata: %s <= %+v", file, store)
|
||||
config.logger.Debugf("Writing persistent metadata: %s <= %+v", file, store)
|
||||
if err = config.tomlWriter.Write(file, store); err != nil {
|
||||
config.exitHandler.Error(fmt.Errorf("unable to write persistent metadata %s\n%w", file, err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if result.Plan != nil && len(result.Plan.Entries) > 0 {
|
||||
file = config.arguments[3]
|
||||
logger.Debugf("Writing buildpack plan: %s <= %+v", file, result.Plan)
|
||||
if err = config.tomlWriter.Write(file, result.Plan); err != nil {
|
||||
config.exitHandler.Error(fmt.Errorf("unable to write buildpack plan %s\n%w", file, err))
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func contains(candidates []string, s string) bool {
|
||||
|
@ -321,3 +366,27 @@ func contains(candidates []string, s string) bool {
|
|||
|
||||
return false
|
||||
}
|
||||
|
||||
func validateSBOMFormats(layersPath string, acceptedSBOMFormats []string) error {
|
||||
sbomFiles, err := filepath.Glob(filepath.Join(layersPath, "*.sbom.*"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable find SBOM files\n%w", err)
|
||||
}
|
||||
|
||||
for _, sbomFile := range sbomFiles {
|
||||
parts := strings.Split(filepath.Base(sbomFile), ".")
|
||||
if len(parts) <= 2 {
|
||||
return fmt.Errorf("invalid format %s", filepath.Base(sbomFile))
|
||||
}
|
||||
sbomFormat, err := SBOMFormatFromString(strings.Join(parts[len(parts)-2:], "."))
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse SBOM %s\n%w", sbomFormat, err)
|
||||
}
|
||||
|
||||
if !contains(acceptedSBOMFormats, sbomFormat.MediaType()) {
|
||||
return fmt.Errorf("unable to find actual SBOM Type %s in list of supported SBOM types %s", sbomFormat.MediaType(), acceptedSBOMFormats)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -27,9 +27,6 @@ type BuildPlanRequire struct {
|
|||
// Name is the name of the dependency.
|
||||
Name string `toml:"name"`
|
||||
|
||||
// Version is the version of the dependency. Optional.
|
||||
Version string `toml:"version,omitempty"`
|
||||
|
||||
// Metadata is the metadata for the dependency. Optional.
|
||||
Metadata map[string]interface{} `toml:"metadata,omitempty"`
|
||||
}
|
||||
|
|
811
build_test.go
811
build_test.go
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,27 @@
|
|||
/*
|
||||
* Copyright 2018-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package libcnb
|
||||
|
||||
// BuildTOML represents the contents of build.toml.
|
||||
type BuildTOML struct {
|
||||
// Unmet is a collection of buildpack plan entries that should be passed through to subsequent providers.
|
||||
Unmet []UnmetPlanEntry
|
||||
}
|
||||
|
||||
func (b BuildTOML) isEmpty() bool {
|
||||
return len(b.Unmet) == 0
|
||||
}
|
76
buildpack.go
76
buildpack.go
|
@ -19,19 +19,43 @@ package libcnb
|
|||
// BuildpackInfo is information about the buildpack.
|
||||
type BuildpackInfo struct {
|
||||
// ID is the ID of the buildpack.
|
||||
ID string `mapstructure:"id" toml:"id"`
|
||||
ID string `toml:"id"`
|
||||
|
||||
// Name is the name of the buildpack.
|
||||
Name string `mapstructure:"name" toml:"name"`
|
||||
Name string `toml:"name"`
|
||||
|
||||
// Version is the version of the buildpack.
|
||||
Version string `mapstructure:"version" toml:"version"`
|
||||
Version string `toml:"version"`
|
||||
|
||||
// Homepage is the homepage of the buildpack.
|
||||
Homepage string `mapstructure:"homepage" toml:"homepage"`
|
||||
Homepage string `toml:"homepage"`
|
||||
|
||||
// ClearEnvironment is whether the environment should be clear of user-configured environment variables.
|
||||
ClearEnvironment bool `mapstructure:"clear-env" toml:"clear-env"`
|
||||
ClearEnvironment bool `toml:"clear-env"`
|
||||
|
||||
// Description is a string describing the buildpack.
|
||||
Description string `toml:"description"`
|
||||
|
||||
// Keywords is a list of words that are associated with the buildpack.
|
||||
Keywords []string `toml:"keywords"`
|
||||
|
||||
// Licenses a list of buildpack licenses.
|
||||
Licenses []License `toml:"licenses"`
|
||||
|
||||
// SBOM is the list of supported SBOM media types
|
||||
SBOMFormats []string `toml:"sbom-formats"`
|
||||
}
|
||||
|
||||
// License contains information about a Software License
|
||||
// governing the use or redistribution of a buildpack
|
||||
type License struct {
|
||||
// Type is the identifier for the license.
|
||||
// It MAY use the SPDX 2.1 license expression, but is not limited to identifiers in the SPDX Licenses List.
|
||||
Type string `toml:"type"`
|
||||
|
||||
// URI may be specified in lieu of or in addition to type to point to the license
|
||||
// if this buildpack is using a nonstandard license.
|
||||
URI string `toml:"uri"`
|
||||
}
|
||||
|
||||
// BuildpackOrderBuildpack is a buildpack within in a buildpack order group.
|
||||
|
@ -52,13 +76,39 @@ type BuildpackOrder struct {
|
|||
Groups []BuildpackOrderBuildpack `toml:"group"`
|
||||
}
|
||||
|
||||
// BuildpackStack is a stack supported by the buildpack.
|
||||
// Deprecated: BuildpackStack is a stack supported by the buildpack.
|
||||
type BuildpackStack struct {
|
||||
// ID is the id of the stack.
|
||||
ID string `toml:"id"`
|
||||
}
|
||||
|
||||
// Mixins is the collection of mixins associated with the stack.
|
||||
Mixins []string `toml:"mixins"`
|
||||
// TargetDistro is the supported target distro
|
||||
type TargetDistro struct {
|
||||
// Name is the name of the supported distro.
|
||||
Name string `toml:"name"`
|
||||
|
||||
// Version is the version of the supported distro.
|
||||
Version string `toml:"version"`
|
||||
}
|
||||
|
||||
// TargetInfo is the supported target
|
||||
type TargetInfo struct {
|
||||
// OS is the supported os.
|
||||
OS string `toml:"os"`
|
||||
|
||||
// Arch is the supported architecture.
|
||||
Arch string `toml:"arch"`
|
||||
|
||||
// Variant is the supported variant of the architecture.
|
||||
Variant string `toml:"variant"`
|
||||
}
|
||||
|
||||
// Target is a target supported by the buildpack.
|
||||
type Target struct {
|
||||
TargetInfo
|
||||
|
||||
// Distros is the collection of distros associated with the target.
|
||||
Distros []TargetDistro `toml:"distros"`
|
||||
}
|
||||
|
||||
// Buildpack is the contents of the buildpack.toml file.
|
||||
|
@ -69,15 +119,15 @@ type Buildpack struct {
|
|||
// Info is information about the buildpack.
|
||||
Info BuildpackInfo `toml:"buildpack"`
|
||||
|
||||
// Orders is buildpack collection of order definitions in the buildpack.
|
||||
Orders []BuildpackOrder `toml:"order"`
|
||||
|
||||
// Path is the path to the buildpack.
|
||||
Path string
|
||||
Path string `toml:"-"`
|
||||
|
||||
// Stacks is the collection of stacks supported by the buildpack.
|
||||
// Deprecated: Stacks is the collection of stacks supported by the buildpack.
|
||||
Stacks []BuildpackStack `toml:"stacks"`
|
||||
|
||||
// Targets is the collection of targets supported by the buildpack.
|
||||
Targets []Target `toml:"targets"`
|
||||
|
||||
// Metadata is arbitrary metadata attached to the buildpack.
|
||||
Metadata map[string]interface{} `toml:"metadata"`
|
||||
}
|
||||
|
|
|
@ -16,21 +16,26 @@
|
|||
|
||||
package libcnb
|
||||
|
||||
// BuildpackPlanEntry represents an entry in the buildpack plan.
|
||||
type BuildpackPlanEntry struct {
|
||||
// Name represents the name of the entry.
|
||||
Name string `toml:"name"`
|
||||
|
||||
// Version represents the version of the entry. Optional.
|
||||
Version string `toml:"version,omitempty"`
|
||||
|
||||
// Metadata is the metadata of the entry. Optional.
|
||||
Metadata map[string]interface{} `toml:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
// BuildpackPlan represents a buildpack plan.
|
||||
type BuildpackPlan struct {
|
||||
|
||||
// Entries represents all the buildpack plan entries.
|
||||
Entries []BuildpackPlanEntry `toml:"entries,omitempty"`
|
||||
}
|
||||
|
||||
// BuildpackPlanEntry represents an entry in the buildpack plan.
|
||||
type BuildpackPlanEntry struct {
|
||||
// Name represents the name of the entry.
|
||||
Name string `toml:"name"`
|
||||
|
||||
// Metadata is the metadata of the entry. Optional.
|
||||
Metadata map[string]interface{} `toml:"metadata,omitempty"`
|
||||
}
|
||||
|
||||
// UnmetPlanEntry denotes an unmet buildpack plan entry. When a buildpack returns an UnmetPlanEntry
|
||||
// in the BuildResult, any BuildpackPlanEntry with a matching Name will be provided to subsequent
|
||||
// providers.
|
||||
type UnmetPlanEntry struct {
|
||||
// Name represents the name of the entry.
|
||||
Name string `toml:"name"`
|
||||
}
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright 2018-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package libcnb_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/sclevine/spec"
|
||||
|
||||
"github.com/buildpacks/libcnb/v2"
|
||||
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func testBuildpackTOML(t *testing.T, _ spec.G, it spec.S) {
|
||||
var (
|
||||
Expect = NewWithT(t).Expect
|
||||
)
|
||||
|
||||
it("does not serialize the Path field", func() {
|
||||
bp := libcnb.Buildpack{
|
||||
API: "0.8",
|
||||
Info: libcnb.BuildpackInfo{
|
||||
ID: "test-buildpack/sample",
|
||||
Name: "sample",
|
||||
},
|
||||
Path: "../buildpack",
|
||||
}
|
||||
|
||||
output := &bytes.Buffer{}
|
||||
|
||||
Expect(toml.NewEncoder(output).Encode(bp)).To(Succeed())
|
||||
Expect(output.String()).NotTo(Or(ContainSubstring("Path = "), ContainSubstring("path = ")))
|
||||
})
|
||||
}
|
83
config.go
83
config.go
|
@ -16,7 +16,14 @@
|
|||
|
||||
package libcnb
|
||||
|
||||
//go:generate mockery -name EnvironmentWriter -case=underscore
|
||||
import (
|
||||
"os"
|
||||
|
||||
"github.com/buildpacks/libcnb/v2/internal"
|
||||
"github.com/buildpacks/libcnb/v2/log"
|
||||
)
|
||||
|
||||
//go:generate mockery --name EnvironmentWriter --case=underscore
|
||||
|
||||
// EnvironmentWriter is the interface implemented by a type that wants to serialize a map of environment variables to
|
||||
// the file system.
|
||||
|
@ -27,7 +34,7 @@ type EnvironmentWriter interface {
|
|||
Write(dir string, environment map[string]string) error
|
||||
}
|
||||
|
||||
//go:generate mockery -name ExitHandler -case=underscore
|
||||
//go:generate mockery --name ExitHandler --case=underscore
|
||||
|
||||
// ExitHandler is the interface implemented by a type that wants to handle exit behavior when a buildpack encounters an
|
||||
// error.
|
||||
|
@ -43,7 +50,7 @@ type ExitHandler interface {
|
|||
Pass()
|
||||
}
|
||||
|
||||
//go:generate mockery -name TOMLWriter -case=underscore
|
||||
//go:generate mockery --name TOMLWriter --case=underscore
|
||||
|
||||
// TOMLWriter is the interface implemented by a type that wants to serialize an object to a TOML file.
|
||||
type TOMLWriter interface {
|
||||
|
@ -52,17 +59,55 @@ type TOMLWriter interface {
|
|||
Write(path string, value interface{}) error
|
||||
}
|
||||
|
||||
//go:generate mockery --name ExecDWriter --case=underscore
|
||||
|
||||
// ExecDWriter is the interface implemented by a type that wants to write exec.d output to file descriptor 3.
|
||||
type ExecDWriter interface {
|
||||
|
||||
// Write is called with the map of environment value key value
|
||||
// pairs that will be written out
|
||||
Write(value map[string]string) error
|
||||
}
|
||||
|
||||
// Config is an object that contains configurable properties for execution.
|
||||
type Config struct {
|
||||
arguments []string
|
||||
environmentWriter EnvironmentWriter
|
||||
exitHandler ExitHandler
|
||||
tomlWriter TOMLWriter
|
||||
arguments []string
|
||||
dirContentFormatter log.DirectoryContentFormatter
|
||||
environmentWriter EnvironmentWriter
|
||||
execdWriter ExecDWriter
|
||||
exitHandler ExitHandler
|
||||
logger log.Logger
|
||||
tomlWriter TOMLWriter
|
||||
contentWriter internal.DirectoryContentsWriter
|
||||
extension bool
|
||||
}
|
||||
|
||||
// Option is a function for configuring a Config instance.
|
||||
type Option func(config Config) Config
|
||||
|
||||
// NewConfig will generate a config from the given set of options
|
||||
func NewConfig(options ...Option) Config {
|
||||
config := Config{}
|
||||
|
||||
// apply defaults
|
||||
options = append([]Option{
|
||||
WithArguments(os.Args),
|
||||
WithEnvironmentWriter(internal.EnvironmentWriter{}),
|
||||
WithExitHandler(internal.NewExitHandler()),
|
||||
WithLogger(log.New(os.Stdout)),
|
||||
WithTOMLWriter(internal.TOMLWriter{}),
|
||||
WithDirectoryContentFormatter(internal.NewPlainDirectoryContentFormatter()),
|
||||
}, options...)
|
||||
|
||||
for _, opt := range options {
|
||||
config = opt(config)
|
||||
}
|
||||
|
||||
config.contentWriter = internal.NewDirectoryContentsWriter(config.dirContentFormatter, config.logger.DebugWriter())
|
||||
|
||||
return config
|
||||
}
|
||||
|
||||
// WithArguments creates an Option that sets a collection of arguments.
|
||||
func WithArguments(arguments []string) Option {
|
||||
return func(config Config) Config {
|
||||
|
@ -94,3 +139,27 @@ func WithTOMLWriter(tomlWriter TOMLWriter) Option {
|
|||
return config
|
||||
}
|
||||
}
|
||||
|
||||
// WithExecDWriter creates an Option that sets a ExecDWriter implementation.
|
||||
func WithExecDWriter(execdWriter ExecDWriter) Option {
|
||||
return func(config Config) Config {
|
||||
config.execdWriter = execdWriter
|
||||
return config
|
||||
}
|
||||
}
|
||||
|
||||
// WithLogger creates an Option that sets a ExecDWriter implementation.
|
||||
func WithLogger(logger log.Logger) Option {
|
||||
return func(config Config) Config {
|
||||
config.logger = logger
|
||||
return config
|
||||
}
|
||||
}
|
||||
|
||||
// WithDirectoryContentFormatter creates an Option that sets a ExecDWriter implementation.
|
||||
func WithDirectoryContentFormatter(formatter log.DirectoryContentFormatter) Option {
|
||||
return func(config Config) Config {
|
||||
config.dirContentFormatter = formatter
|
||||
return config
|
||||
}
|
||||
}
|
||||
|
|
185
detect.go
185
detect.go
|
@ -17,25 +17,34 @@
|
|||
package libcnb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/buildpacks/libcnb/internal"
|
||||
"github.com/buildpacks/libcnb/poet"
|
||||
"github.com/Masterminds/semver"
|
||||
|
||||
"github.com/buildpacks/libcnb/v2/internal"
|
||||
"github.com/buildpacks/libcnb/v2/log"
|
||||
)
|
||||
|
||||
// DetectContext contains the inputs to detection.
|
||||
type DetectContext struct {
|
||||
|
||||
// Application is the application to build.
|
||||
Application Application
|
||||
// ApplicationPath is the location of the application source code as provided by
|
||||
// the lifecycle.
|
||||
ApplicationPath string
|
||||
|
||||
// Buildpack is metadata about the buildpack, from buildpack.toml.
|
||||
// Buildpack is metadata about the buildpack from buildpack.toml (empty when processing an extension)
|
||||
Buildpack Buildpack
|
||||
|
||||
// Extension is metadata about the extension from extension.toml (empty when processing a buildpack)
|
||||
Extension Extension
|
||||
|
||||
// Logger is the way to write messages to the end user
|
||||
Logger log.Logger
|
||||
|
||||
// Platform is the contents of the platform.
|
||||
Platform Platform
|
||||
|
||||
|
@ -53,97 +62,140 @@ type DetectResult struct {
|
|||
Plans []BuildPlan
|
||||
}
|
||||
|
||||
//go:generate mockery -name Detector -case=underscore
|
||||
|
||||
// Detector describes an interface for types that can be used by the Detect function.
|
||||
type Detector interface {
|
||||
|
||||
// Detect takes a context and returns a result, performing buildpack detect behaviors.
|
||||
Detect(context DetectContext) (DetectResult, error)
|
||||
}
|
||||
// DetectFunc takes a context and returns a result, performing buildpack detect behaviors.
|
||||
type DetectFunc func(context DetectContext) (DetectResult, error)
|
||||
|
||||
// Detect is called by the main function of a buildpack, for detection.
|
||||
func Detect(detector Detector, options ...Option) {
|
||||
config := Config{
|
||||
arguments: os.Args,
|
||||
environmentWriter: internal.EnvironmentWriter{},
|
||||
exitHandler: internal.NewExitHandler(),
|
||||
tomlWriter: internal.TOMLWriter{},
|
||||
}
|
||||
|
||||
for _, option := range options {
|
||||
config = option(config)
|
||||
}
|
||||
|
||||
if len(config.arguments) != 3 {
|
||||
config.exitHandler.Error(fmt.Errorf("expected 2 arguments and received %d", len(config.arguments)-1))
|
||||
return
|
||||
}
|
||||
|
||||
func Detect(detect DetectFunc, config Config) {
|
||||
var (
|
||||
err error
|
||||
file string
|
||||
ok bool
|
||||
err error
|
||||
file string
|
||||
ok bool
|
||||
api string
|
||||
path string
|
||||
destination interface{}
|
||||
)
|
||||
ctx := DetectContext{}
|
||||
logger := poet.NewLogger(os.Stdout)
|
||||
ctx := DetectContext{Logger: config.logger}
|
||||
|
||||
ctx.Application.Path, err = os.Getwd()
|
||||
var moduletype = "buildpack"
|
||||
if config.extension {
|
||||
moduletype = "extension"
|
||||
}
|
||||
|
||||
ctx.ApplicationPath, err = os.Getwd()
|
||||
if err != nil {
|
||||
config.exitHandler.Error(fmt.Errorf("unable to get working directory\n%w", err))
|
||||
return
|
||||
}
|
||||
if logger.IsDebugEnabled() {
|
||||
logger.Debug(ApplicationPathFormatter(ctx.Application.Path))
|
||||
|
||||
if config.logger.IsDebugEnabled() {
|
||||
if err := config.contentWriter.Write("Application contents", ctx.ApplicationPath); err != nil {
|
||||
config.logger.Debugf("unable to write application contents\n%w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if s, ok := os.LookupEnv("CNB_BUILDPACK_DIR"); ok {
|
||||
ctx.Buildpack.Path = filepath.Clean(s)
|
||||
} else { // TODO: Remove branch once lifecycle has been updated to support this
|
||||
ctx.Buildpack.Path = filepath.Clean(strings.TrimSuffix(config.arguments[0], filepath.Join("bin", "detect")))
|
||||
}
|
||||
if logger.IsDebugEnabled() {
|
||||
logger.Debug(BuildpackPathFormatter(ctx.Buildpack.Path))
|
||||
if !config.extension {
|
||||
if s, ok := os.LookupEnv(EnvBuildpackDirectory); ok {
|
||||
path = filepath.Clean(s)
|
||||
} else {
|
||||
config.exitHandler.Error(fmt.Errorf("unable to get CNB_BUILDPACK_DIR, not found"))
|
||||
return
|
||||
}
|
||||
ctx.Buildpack.Path = path
|
||||
destination = &ctx.Buildpack
|
||||
file = filepath.Join(ctx.Buildpack.Path, "buildpack.toml")
|
||||
} else {
|
||||
if s, ok := os.LookupEnv(EnvExtensionDirectory); ok {
|
||||
path = filepath.Clean(s)
|
||||
} else {
|
||||
config.exitHandler.Error(fmt.Errorf("unable to get CNB_EXTENSION_DIR, not found"))
|
||||
return
|
||||
}
|
||||
ctx.Extension.Path = path
|
||||
destination = &ctx.Extension
|
||||
file = filepath.Join(ctx.Extension.Path, "extension.toml")
|
||||
}
|
||||
|
||||
file = filepath.Join(ctx.Buildpack.Path, "buildpack.toml")
|
||||
if _, err = toml.DecodeFile(file, &ctx.Buildpack); err != nil && !os.IsNotExist(err) {
|
||||
config.exitHandler.Error(fmt.Errorf("unable to decode buildpack %s\n%w", file, err))
|
||||
if _, err = toml.DecodeFile(file, destination); err != nil && !os.IsNotExist(err) {
|
||||
config.exitHandler.Error(fmt.Errorf("unable to decode %s %s\n%w", moduletype, file, err))
|
||||
return
|
||||
}
|
||||
logger.Debugf("Buildpack: %+v", ctx.Buildpack)
|
||||
config.logger.Debugf("%s: %+v", moduletype, ctx.Buildpack)
|
||||
|
||||
ctx.Platform.Path = config.arguments[1]
|
||||
if logger.IsDebugEnabled() {
|
||||
logger.Debug(PlatformFormatter(ctx.Platform))
|
||||
if config.logger.IsDebugEnabled() {
|
||||
if err := config.contentWriter.Write(moduletype+" contents", path); err != nil {
|
||||
config.logger.Debugf("unable to write %s contents\n%w", moduletype, err)
|
||||
}
|
||||
}
|
||||
|
||||
if config.extension {
|
||||
api = ctx.Extension.API
|
||||
} else {
|
||||
api = ctx.Buildpack.API
|
||||
}
|
||||
API, err := semver.NewVersion(api)
|
||||
if err != nil {
|
||||
config.exitHandler.Error(errors.New("version cannot be parsed"))
|
||||
return
|
||||
}
|
||||
|
||||
compatVersionCheck, _ := semver.NewConstraint(fmt.Sprintf(">= %s, <= %s", MinSupportedBPVersion, MaxSupportedBPVersion))
|
||||
if !compatVersionCheck.Check(API) {
|
||||
if MinSupportedBPVersion == MaxSupportedBPVersion {
|
||||
config.exitHandler.Error(fmt.Errorf("this version of libcnb is only compatible with buildpack API == %s", MinSupportedBPVersion))
|
||||
return
|
||||
}
|
||||
|
||||
config.exitHandler.Error(fmt.Errorf("this version of libcnb is only compatible with buildpack APIs >= %s, <= %s", MinSupportedBPVersion, MaxSupportedBPVersion))
|
||||
return
|
||||
}
|
||||
|
||||
var buildPlanPath string
|
||||
|
||||
ctx.Platform.Path, ok = os.LookupEnv(EnvPlatformDirectory)
|
||||
if !ok {
|
||||
config.exitHandler.Error(fmt.Errorf("expected CNB_PLATFORM_DIR to be set"))
|
||||
return
|
||||
}
|
||||
|
||||
buildPlanPath, ok = os.LookupEnv(EnvDetectPlanPath)
|
||||
if !ok {
|
||||
config.exitHandler.Error(fmt.Errorf("expected CNB_BUILD_PLAN_PATH to be set"))
|
||||
return
|
||||
}
|
||||
|
||||
if config.logger.IsDebugEnabled() {
|
||||
if err := config.contentWriter.Write("Platform contents", ctx.Platform.Path); err != nil {
|
||||
config.logger.Debugf("unable to write platform contents\n%w", err)
|
||||
}
|
||||
}
|
||||
|
||||
file = filepath.Join(ctx.Platform.Path, "bindings")
|
||||
if ctx.Platform.Bindings, err = NewBindingsFromPath(file); err != nil {
|
||||
if ctx.Platform.Bindings, err = NewBindings(ctx.Platform.Path); err != nil {
|
||||
config.exitHandler.Error(fmt.Errorf("unable to read platform bindings %s\n%w", file, err))
|
||||
return
|
||||
}
|
||||
logger.Debugf("Platform Bindings: %+v", ctx.Platform.Bindings)
|
||||
config.logger.Debugf("Platform Bindings: %+v", ctx.Platform.Bindings)
|
||||
|
||||
file = filepath.Join(ctx.Platform.Path, "env")
|
||||
if ctx.Platform.Environment, err = internal.NewConfigMapFromPath(file); err != nil {
|
||||
config.exitHandler.Error(fmt.Errorf("unable to read platform environment %s\n%w", file, err))
|
||||
return
|
||||
}
|
||||
logger.Debugf("Platform Environment: %s", ctx.Platform.Environment)
|
||||
config.logger.Debugf("Platform Environment: %s", ctx.Platform.Environment)
|
||||
|
||||
if ctx.StackID, ok = os.LookupEnv("CNB_STACK_ID"); !ok {
|
||||
config.exitHandler.Error(fmt.Errorf("CNB_STACK_ID not set"))
|
||||
return
|
||||
if ctx.StackID, ok = os.LookupEnv(EnvStackID); !ok {
|
||||
config.logger.Debug("CNB_STACK_ID not set")
|
||||
} else {
|
||||
config.logger.Debugf("Stack: %s", ctx.StackID)
|
||||
}
|
||||
logger.Debugf("Stack: %s", ctx.StackID)
|
||||
|
||||
result, err := detector.Detect(ctx)
|
||||
result, err := detect(ctx)
|
||||
if err != nil {
|
||||
config.exitHandler.Error(err)
|
||||
return
|
||||
}
|
||||
logger.Debugf("Result: %+v", result)
|
||||
config.logger.Debugf("Result: %+v", result)
|
||||
|
||||
if !result.Pass {
|
||||
config.exitHandler.Fail()
|
||||
|
@ -159,11 +211,12 @@ func Detect(detector Detector, options ...Option) {
|
|||
plans.Or = result.Plans[1:]
|
||||
}
|
||||
|
||||
file = config.arguments[2]
|
||||
logger.Debugf("Writing build plans: %s <= %+v", file, plans)
|
||||
if err := config.tomlWriter.Write(file, plans); err != nil {
|
||||
config.exitHandler.Error(fmt.Errorf("unable to write buildplan %s\n%w", file, err))
|
||||
config.logger.Debugf("Writing build plans: %s <= %+v", buildPlanPath, plans)
|
||||
if err := config.tomlWriter.Write(buildPlanPath, plans); err != nil {
|
||||
config.exitHandler.Error(fmt.Errorf("unable to write buildplan %s\n%w", buildPlanPath, err))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
config.exitHandler.Pass()
|
||||
}
|
||||
|
|
382
detect_test.go
382
detect_test.go
|
@ -18,16 +18,17 @@ package libcnb_test
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/buildpacks/libcnb"
|
||||
"github.com/buildpacks/libcnb/mocks"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/sclevine/spec"
|
||||
"github.com/stretchr/testify/mock"
|
||||
|
||||
"github.com/buildpacks/libcnb/v2"
|
||||
"github.com/buildpacks/libcnb/v2/log"
|
||||
"github.com/buildpacks/libcnb/v2/mocks"
|
||||
)
|
||||
|
||||
func testDetect(t *testing.T, context spec.G, it spec.S) {
|
||||
|
@ -38,7 +39,7 @@ func testDetect(t *testing.T, context spec.G, it spec.S) {
|
|||
buildpackPath string
|
||||
buildPlanPath string
|
||||
commandPath string
|
||||
detector *mocks.Detector
|
||||
detectFunc libcnb.DetectFunc
|
||||
exitHandler *mocks.ExitHandler
|
||||
platformPath string
|
||||
tomlWriter *mocks.TOMLWriter
|
||||
|
@ -49,79 +50,77 @@ func testDetect(t *testing.T, context spec.G, it spec.S) {
|
|||
it.Before(func() {
|
||||
var err error
|
||||
|
||||
applicationPath, err = ioutil.TempDir("", "detect-application-path")
|
||||
applicationPath, err = os.MkdirTemp("", "detect-application-path")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
applicationPath, err = filepath.EvalSymlinks(applicationPath)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
buildpackPath, err = ioutil.TempDir("", "detect-buildpack-path")
|
||||
buildpackPath, err = os.MkdirTemp("", "detect-buildpack-path")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(os.Setenv("CNB_BUILDPACK_DIR", buildpackPath)).To(Succeed())
|
||||
|
||||
Expect(ioutil.WriteFile(filepath.Join(buildpackPath, "buildpack.toml"),
|
||||
Expect(os.WriteFile(filepath.Join(buildpackPath, "buildpack.toml"),
|
||||
[]byte(`
|
||||
api = "0.0.0"
|
||||
api = "0.8"
|
||||
|
||||
[buildpack]
|
||||
id = "test-id"
|
||||
name = "test-name"
|
||||
version = "1.1.1"
|
||||
clear-env = true
|
||||
description = "A test buildpack"
|
||||
keywords = ["test", "buildpack"]
|
||||
|
||||
[[order]]
|
||||
[[order.group]]
|
||||
id = "test-id"
|
||||
version = "2.2.2"
|
||||
optional = true
|
||||
[[buildpack.licenses]]
|
||||
type = "Apache-2.0"
|
||||
uri = "https://spdx.org/licenses/Apache-2.0.html"
|
||||
|
||||
[[buildpack.licenses]]
|
||||
type = "Apache-1.1"
|
||||
uri = "https://spdx.org/licenses/Apache-1.1.html"
|
||||
|
||||
[[stacks]]
|
||||
id = "test-id"
|
||||
mixins = ["test-name"]
|
||||
|
||||
[metadata]
|
||||
test-key = "test-value"
|
||||
`),
|
||||
0644),
|
||||
0600),
|
||||
).To(Succeed())
|
||||
|
||||
f, err := ioutil.TempFile("", "detect-buildplan-path")
|
||||
f, err := os.CreateTemp("", "detect-buildplan-path")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(f.Close()).NotTo(HaveOccurred())
|
||||
buildPlanPath = f.Name()
|
||||
|
||||
commandPath = filepath.Join("bin", "detect")
|
||||
|
||||
detector = &mocks.Detector{}
|
||||
detectFunc = func(libcnb.DetectContext) (libcnb.DetectResult, error) {
|
||||
return libcnb.DetectResult{}, nil
|
||||
}
|
||||
|
||||
exitHandler = &mocks.ExitHandler{}
|
||||
exitHandler.On("Error", mock.Anything)
|
||||
exitHandler.On("Fail")
|
||||
exitHandler.On("Pass")
|
||||
|
||||
platformPath, err = ioutil.TempDir("", "detect-platform-path")
|
||||
platformPath, err = os.MkdirTemp("", "detect-platform-path")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(os.MkdirAll(filepath.Join(platformPath, "bindings", "alpha", "metadata"), 0755)).To(Succeed())
|
||||
Expect(ioutil.WriteFile(
|
||||
filepath.Join(platformPath, "bindings", "alpha", "metadata", "test-metadata-key"),
|
||||
[]byte("test-metadata-value"),
|
||||
0644,
|
||||
)).To(Succeed())
|
||||
Expect(os.MkdirAll(filepath.Join(platformPath, "bindings", "alpha", "secret"), 0755)).To(Succeed())
|
||||
Expect(ioutil.WriteFile(
|
||||
filepath.Join(platformPath, "bindings", "alpha", "secret", "test-secret-key"),
|
||||
[]byte("test-secret-value"),
|
||||
0644,
|
||||
)).To(Succeed())
|
||||
Expect(os.MkdirAll(filepath.Join(platformPath, "bindings", "alpha"), 0755)).To(Succeed())
|
||||
Expect(os.WriteFile(filepath.Join(platformPath, "bindings", "alpha", "test-secret-key"),
|
||||
[]byte("test-secret-value"), 0600)).To(Succeed())
|
||||
|
||||
Expect(os.MkdirAll(filepath.Join(platformPath, "env"), 0755)).To(Succeed())
|
||||
Expect(ioutil.WriteFile(filepath.Join(platformPath, "env", "TEST_ENV"), []byte("test-value"), 0644)).
|
||||
Expect(os.WriteFile(filepath.Join(platformPath, "env", "TEST_ENV"), []byte("test-value"), 0600)).
|
||||
To(Succeed())
|
||||
|
||||
tomlWriter = &mocks.TOMLWriter{}
|
||||
tomlWriter.On("Write", mock.Anything, mock.Anything).Return(nil)
|
||||
|
||||
Expect(os.Setenv("CNB_STACK_ID", "test-stack-id")).To(Succeed())
|
||||
Expect(os.Setenv("CNB_PLATFORM_DIR", platformPath)).To(Succeed())
|
||||
Expect(os.Setenv("CNB_BUILD_PLAN_PATH", buildPlanPath)).To(Succeed())
|
||||
|
||||
workingDir, err = os.Getwd()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
@ -132,6 +131,8 @@ test-key = "test-value"
|
|||
Expect(os.Chdir(workingDir)).To(Succeed())
|
||||
Expect(os.Unsetenv("CNB_BUILDPACK_DIR")).To(Succeed())
|
||||
Expect(os.Unsetenv("CNB_STACK_ID")).To(Succeed())
|
||||
Expect(os.Unsetenv("CNB_PLATFORM_DIR")).To(Succeed())
|
||||
Expect(os.Unsetenv("CNB_BUILD_PLAN_PATH")).To(Succeed())
|
||||
|
||||
Expect(os.RemoveAll(applicationPath)).To(Succeed())
|
||||
Expect(os.RemoveAll(buildpackPath)).To(Succeed())
|
||||
|
@ -139,146 +140,200 @@ test-key = "test-value"
|
|||
Expect(os.RemoveAll(platformPath)).To(Succeed())
|
||||
})
|
||||
|
||||
it("encounters the wrong number of Arguments", func() {
|
||||
detector.On("Detect", mock.Anything).Return(libcnb.DetectResult{}, nil)
|
||||
context("buildpack API is not within the supported range", func() {
|
||||
it.Before(func() {
|
||||
Expect(os.WriteFile(filepath.Join(buildpackPath, "buildpack.toml"),
|
||||
[]byte(`
|
||||
api = "0.7"
|
||||
|
||||
libcnb.Detect(detector,
|
||||
libcnb.WithArguments([]string{commandPath}),
|
||||
libcnb.WithExitHandler(exitHandler),
|
||||
)
|
||||
[buildpack]
|
||||
id = "test-id"
|
||||
name = "test-name"
|
||||
version = "1.1.1"
|
||||
`),
|
||||
0600),
|
||||
).To(Succeed())
|
||||
})
|
||||
|
||||
Expect(exitHandler.Calls[0].Arguments.Get(0)).To(MatchError("expected 2 arguments and received 0"))
|
||||
it("fails", func() {
|
||||
libcnb.Detect(detectFunc,
|
||||
libcnb.NewConfig(
|
||||
libcnb.WithArguments([]string{commandPath, platformPath, buildPlanPath}),
|
||||
libcnb.WithExitHandler(exitHandler),
|
||||
libcnb.WithLogger(log.NewDiscard())),
|
||||
)
|
||||
|
||||
if libcnb.MinSupportedBPVersion == libcnb.MaxSupportedBPVersion {
|
||||
Expect(exitHandler.Calls[0].Arguments.Get(0)).To(MatchError(
|
||||
fmt.Sprintf("this version of libcnb is only compatible with buildpack API == %s", libcnb.MinSupportedBPVersion)))
|
||||
} else {
|
||||
Expect(exitHandler.Calls[0].Arguments.Get(0)).To(MatchError(
|
||||
fmt.Sprintf("this version of libcnb is only compatible with buildpack APIs >= %s, <= %s", libcnb.MinSupportedBPVersion, libcnb.MaxSupportedBPVersion),
|
||||
))
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
it("doesn't receive CNB_STACK_ID", func() {
|
||||
Expect(os.Unsetenv("CNB_STACK_ID")).To(Succeed())
|
||||
detector.On("Detect", mock.Anything).Return(libcnb.DetectResult{}, nil)
|
||||
context("errors if required env vars are not set", func() {
|
||||
for _, e := range []string{"CNB_PLATFORM_DIR", "CNB_BUILD_PLAN_PATH"} {
|
||||
// We need to do this assignment because of the way that spec binds variables
|
||||
envVar := e
|
||||
context(fmt.Sprintf("when %s is unset", envVar), func() {
|
||||
it.Before(func() {
|
||||
Expect(os.WriteFile(filepath.Join(buildpackPath, "buildpack.toml"),
|
||||
[]byte(`
|
||||
api = "0.8"
|
||||
|
||||
[buildpack]
|
||||
id = "test-id"
|
||||
name = "test-name"
|
||||
version = "1.1.1"
|
||||
`),
|
||||
0600),
|
||||
).To(Succeed())
|
||||
os.Unsetenv(envVar)
|
||||
})
|
||||
|
||||
libcnb.Detect(detector,
|
||||
libcnb.WithArguments([]string{commandPath, platformPath, buildPlanPath}),
|
||||
libcnb.WithExitHandler(exitHandler),
|
||||
)
|
||||
|
||||
Expect(exitHandler.Calls[0].Arguments.Get(0)).To(MatchError("CNB_STACK_ID not set"))
|
||||
it("fails", func() {
|
||||
libcnb.Detect(detectFunc,
|
||||
libcnb.NewConfig(
|
||||
libcnb.WithArguments([]string{commandPath}),
|
||||
libcnb.WithExitHandler(exitHandler)),
|
||||
)
|
||||
Expect(exitHandler.Calls[0].Arguments.Get(0)).To(MatchError(
|
||||
fmt.Sprintf("expected %s to be set", envVar),
|
||||
))
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
it("creates context", func() {
|
||||
detector.On("Detect", mock.Anything).Return(libcnb.DetectResult{}, nil)
|
||||
context("has a detect environment", func() {
|
||||
var ctx libcnb.DetectContext
|
||||
|
||||
libcnb.Detect(detector,
|
||||
libcnb.WithArguments([]string{commandPath, platformPath, buildPlanPath}),
|
||||
libcnb.WithExitHandler(exitHandler),
|
||||
)
|
||||
it.Before(func() {
|
||||
Expect(os.WriteFile(filepath.Join(buildpackPath, "buildpack.toml"),
|
||||
[]byte(`
|
||||
api = "0.8"
|
||||
|
||||
[buildpack]
|
||||
id = "test-id"
|
||||
name = "test-name"
|
||||
version = "1.1.1"
|
||||
`),
|
||||
0600),
|
||||
).To(Succeed())
|
||||
|
||||
ctx := detector.Calls[0].Arguments[0].(libcnb.DetectContext)
|
||||
Expect(ctx.Application).To(Equal(libcnb.Application{Path: applicationPath}))
|
||||
Expect(ctx.Buildpack).To(Equal(libcnb.Buildpack{
|
||||
API: "0.0.0",
|
||||
Info: libcnb.BuildpackInfo{
|
||||
ID: "test-id",
|
||||
Name: "test-name",
|
||||
Version: "1.1.1",
|
||||
ClearEnvironment: true,
|
||||
},
|
||||
Orders: []libcnb.BuildpackOrder{
|
||||
{
|
||||
Groups: []libcnb.BuildpackOrderBuildpack{
|
||||
{
|
||||
ID: "test-id",
|
||||
Version: "2.2.2",
|
||||
Optional: true,
|
||||
detectFunc = func(context libcnb.DetectContext) (libcnb.DetectResult, error) {
|
||||
ctx = context
|
||||
return libcnb.DetectResult{}, nil
|
||||
}
|
||||
})
|
||||
|
||||
it("creates context", func() {
|
||||
libcnb.Detect(detectFunc,
|
||||
libcnb.NewConfig(
|
||||
libcnb.WithArguments([]string{commandPath}),
|
||||
libcnb.WithExitHandler(exitHandler)),
|
||||
)
|
||||
|
||||
Expect(ctx.ApplicationPath).To(Equal(applicationPath))
|
||||
Expect(ctx.Buildpack).To(Equal(libcnb.Buildpack{
|
||||
API: "0.8",
|
||||
Info: libcnb.BuildpackInfo{
|
||||
ID: "test-id",
|
||||
Name: "test-name",
|
||||
Version: "1.1.1",
|
||||
},
|
||||
Path: buildpackPath,
|
||||
}))
|
||||
Expect(ctx.Platform).To(Equal(libcnb.Platform{
|
||||
Bindings: libcnb.Bindings{
|
||||
libcnb.Binding{
|
||||
Name: "alpha",
|
||||
Path: filepath.Join(platformPath, "bindings", "alpha"),
|
||||
Secret: map[string]string{
|
||||
"test-secret-key": "test-secret-value",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
Path: buildpackPath,
|
||||
Stacks: []libcnb.BuildpackStack{
|
||||
{
|
||||
ID: "test-id",
|
||||
Mixins: []string{"test-name"},
|
||||
},
|
||||
},
|
||||
Metadata: map[string]interface{}{"test-key": "test-value"},
|
||||
}))
|
||||
Expect(ctx.Platform).To(Equal(libcnb.Platform{
|
||||
Bindings: libcnb.Bindings{
|
||||
libcnb.Binding{
|
||||
Name: "alpha",
|
||||
Metadata: map[string]string{
|
||||
"test-metadata-key": "test-metadata-value",
|
||||
},
|
||||
Secret: map[string]string{
|
||||
"test-secret-key": "test-secret-value",
|
||||
},
|
||||
},
|
||||
},
|
||||
Environment: map[string]string{"TEST_ENV": "test-value"},
|
||||
Path: platformPath,
|
||||
}))
|
||||
Expect(ctx.StackID).To(Equal("test-stack-id"))
|
||||
Environment: map[string]string{"TEST_ENV": "test-value"},
|
||||
Path: platformPath,
|
||||
}))
|
||||
Expect(ctx.StackID).To(Equal("test-stack-id"))
|
||||
})
|
||||
})
|
||||
|
||||
it("extracts buildpack path from command path if CNB_BUILDPACK_PATH is not set", func() {
|
||||
it("fails if CNB_BUILDPACK_DIR is not set", func() {
|
||||
Expect(os.Unsetenv("CNB_BUILDPACK_DIR")).To(Succeed())
|
||||
|
||||
detector.On("Detect", mock.Anything).Return(libcnb.DetectResult{}, nil)
|
||||
|
||||
libcnb.Detect(detector,
|
||||
libcnb.WithArguments([]string{filepath.Join(buildpackPath, commandPath), platformPath, buildPlanPath}),
|
||||
libcnb.WithExitHandler(exitHandler),
|
||||
libcnb.Detect(detectFunc,
|
||||
libcnb.NewConfig(
|
||||
libcnb.WithArguments([]string{filepath.Join(buildpackPath, commandPath), platformPath, buildPlanPath}),
|
||||
libcnb.WithExitHandler(exitHandler),
|
||||
libcnb.WithLogger(log.NewDiscard())),
|
||||
)
|
||||
|
||||
ctx := detector.Calls[0].Arguments[0].(libcnb.DetectContext)
|
||||
|
||||
Expect(ctx.Buildpack.Path).To(Equal(buildpackPath))
|
||||
Expect(exitHandler.Calls[0].Arguments.Get(0)).To(MatchError("unable to get CNB_BUILDPACK_DIR, not found"))
|
||||
})
|
||||
|
||||
it("handles error from DetectFunc", func() {
|
||||
detector.On("Detect", mock.Anything).Return(libcnb.DetectResult{}, fmt.Errorf("test-error"))
|
||||
detectFunc = func(libcnb.DetectContext) (libcnb.DetectResult, error) {
|
||||
return libcnb.DetectResult{}, fmt.Errorf("test-error")
|
||||
}
|
||||
|
||||
libcnb.Detect(detector,
|
||||
libcnb.WithArguments([]string{commandPath, platformPath, buildPlanPath}),
|
||||
libcnb.WithExitHandler(exitHandler),
|
||||
libcnb.Detect(detectFunc,
|
||||
libcnb.NewConfig(
|
||||
libcnb.WithArguments([]string{commandPath, platformPath, buildPlanPath}),
|
||||
libcnb.WithExitHandler(exitHandler),
|
||||
libcnb.WithLogger(log.NewDiscard())),
|
||||
)
|
||||
|
||||
Expect(exitHandler.Calls[0].Arguments.Get(0)).To(MatchError("test-error"))
|
||||
})
|
||||
|
||||
it("does not write empty files", func() {
|
||||
detector.On("Detect", mock.Anything).Return(libcnb.DetectResult{Pass: true}, nil)
|
||||
detectFunc = func(libcnb.DetectContext) (libcnb.DetectResult, error) {
|
||||
return libcnb.DetectResult{Pass: true}, nil
|
||||
}
|
||||
|
||||
libcnb.Detect(detector,
|
||||
libcnb.WithArguments([]string{commandPath, platformPath, buildPlanPath}),
|
||||
libcnb.WithExitHandler(exitHandler),
|
||||
libcnb.WithTOMLWriter(tomlWriter),
|
||||
libcnb.Detect(detectFunc,
|
||||
libcnb.NewConfig(
|
||||
libcnb.WithArguments([]string{commandPath, platformPath, buildPlanPath}),
|
||||
libcnb.WithExitHandler(exitHandler),
|
||||
libcnb.WithTOMLWriter(tomlWriter),
|
||||
libcnb.WithLogger(log.NewDiscard())),
|
||||
)
|
||||
|
||||
Expect(tomlWriter.Calls).To(HaveLen(0))
|
||||
})
|
||||
|
||||
it("writes one build plan", func() {
|
||||
detector.On("Detect", mock.Anything).Return(libcnb.DetectResult{
|
||||
Pass: true,
|
||||
Plans: []libcnb.BuildPlan{
|
||||
{
|
||||
Provides: []libcnb.BuildPlanProvide{
|
||||
{Name: "test-name"},
|
||||
},
|
||||
Requires: []libcnb.BuildPlanRequire{
|
||||
{
|
||||
Name: "test-name",
|
||||
Version: "test-version",
|
||||
Metadata: map[string]interface{}{"test-key": "test-value"},
|
||||
detectFunc = func(libcnb.DetectContext) (libcnb.DetectResult, error) {
|
||||
return libcnb.DetectResult{
|
||||
Pass: true,
|
||||
Plans: []libcnb.BuildPlan{
|
||||
{
|
||||
Provides: []libcnb.BuildPlanProvide{
|
||||
{Name: "test-name"},
|
||||
},
|
||||
Requires: []libcnb.BuildPlanRequire{
|
||||
{
|
||||
Name: "test-name",
|
||||
Metadata: map[string]interface{}{"test-key": "test-value"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
}, nil
|
||||
}
|
||||
|
||||
libcnb.Detect(detector,
|
||||
libcnb.WithArguments([]string{commandPath, platformPath, buildPlanPath}),
|
||||
libcnb.WithExitHandler(exitHandler),
|
||||
libcnb.WithTOMLWriter(tomlWriter),
|
||||
libcnb.Detect(detectFunc,
|
||||
libcnb.NewConfig(
|
||||
libcnb.WithArguments([]string{commandPath, platformPath, buildPlanPath}),
|
||||
libcnb.WithExitHandler(exitHandler),
|
||||
libcnb.WithTOMLWriter(tomlWriter),
|
||||
libcnb.WithLogger(log.NewDiscard())),
|
||||
)
|
||||
|
||||
Expect(tomlWriter.Calls[0].Arguments.Get(0)).To(Equal(buildPlanPath))
|
||||
|
@ -290,7 +345,6 @@ test-key = "test-value"
|
|||
Requires: []libcnb.BuildPlanRequire{
|
||||
{
|
||||
Name: "test-name",
|
||||
Version: "test-version",
|
||||
Metadata: map[string]interface{}{"test-key": "test-value"},
|
||||
},
|
||||
},
|
||||
|
@ -299,40 +353,42 @@ test-key = "test-value"
|
|||
})
|
||||
|
||||
it("writes two build plans", func() {
|
||||
detector.On("Detect", mock.Anything).Return(libcnb.DetectResult{
|
||||
Pass: true,
|
||||
Plans: []libcnb.BuildPlan{
|
||||
{
|
||||
Provides: []libcnb.BuildPlanProvide{
|
||||
{Name: "test-name-1"},
|
||||
detectFunc = func(libcnb.DetectContext) (libcnb.DetectResult, error) {
|
||||
return libcnb.DetectResult{
|
||||
Pass: true,
|
||||
Plans: []libcnb.BuildPlan{
|
||||
{
|
||||
Provides: []libcnb.BuildPlanProvide{
|
||||
{Name: "test-name-1"},
|
||||
},
|
||||
Requires: []libcnb.BuildPlanRequire{
|
||||
{
|
||||
Name: "test-name-1",
|
||||
Metadata: map[string]interface{}{"test-key-1": "test-value-1"},
|
||||
},
|
||||
},
|
||||
},
|
||||
Requires: []libcnb.BuildPlanRequire{
|
||||
{
|
||||
Name: "test-name-1",
|
||||
Version: "test-version-1",
|
||||
Metadata: map[string]interface{}{"test-key-1": "test-value-1"},
|
||||
{
|
||||
Provides: []libcnb.BuildPlanProvide{
|
||||
{Name: "test-name-2"},
|
||||
},
|
||||
Requires: []libcnb.BuildPlanRequire{
|
||||
{
|
||||
Name: "test-name-2",
|
||||
Metadata: map[string]interface{}{"test-key-2": "test-value-2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
Provides: []libcnb.BuildPlanProvide{
|
||||
{Name: "test-name-2"},
|
||||
},
|
||||
Requires: []libcnb.BuildPlanRequire{
|
||||
{
|
||||
Name: "test-name-2",
|
||||
Version: "test-version-2",
|
||||
Metadata: map[string]interface{}{"test-key-2": "test-value-2"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil)
|
||||
}, nil
|
||||
}
|
||||
|
||||
libcnb.Detect(detector,
|
||||
libcnb.WithArguments([]string{commandPath, platformPath, buildPlanPath}),
|
||||
libcnb.WithExitHandler(exitHandler),
|
||||
libcnb.WithTOMLWriter(tomlWriter),
|
||||
libcnb.Detect(detectFunc,
|
||||
libcnb.NewConfig(
|
||||
libcnb.WithArguments([]string{commandPath, platformPath, buildPlanPath}),
|
||||
libcnb.WithExitHandler(exitHandler),
|
||||
libcnb.WithTOMLWriter(tomlWriter),
|
||||
libcnb.WithLogger(log.NewDiscard())),
|
||||
)
|
||||
|
||||
Expect(tomlWriter.Calls[0].Arguments.Get(0)).To(Equal(buildPlanPath))
|
||||
|
@ -344,7 +400,6 @@ test-key = "test-value"
|
|||
Requires: []libcnb.BuildPlanRequire{
|
||||
{
|
||||
Name: "test-name-1",
|
||||
Version: "test-version-1",
|
||||
Metadata: map[string]interface{}{"test-key-1": "test-value-1"},
|
||||
},
|
||||
},
|
||||
|
@ -357,7 +412,6 @@ test-key = "test-value"
|
|||
Requires: []libcnb.BuildPlanRequire{
|
||||
{
|
||||
Name: "test-name-2",
|
||||
Version: "test-version-2",
|
||||
Metadata: map[string]interface{}{"test-key-2": "test-value-2"},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -18,6 +18,7 @@ package libcnb
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
// Environment represents the file-based environment variable specification.
|
||||
|
@ -26,14 +27,16 @@ type Environment map[string]string
|
|||
// Append formats using the default formats for its operands and appends the value of this environment variable to any
|
||||
// previous declarations of the value without any delimitation. Spaces are added between operands when neither is a
|
||||
// string. If delimitation is important during concatenation, callers are required to add it.
|
||||
func (e Environment) Append(name string, a ...interface{}) {
|
||||
func (e Environment) Append(name string, delimiter string, a ...interface{}) {
|
||||
e.delimiter(name, delimiter)
|
||||
e[fmt.Sprintf("%s.append", name)] = fmt.Sprint(a...)
|
||||
}
|
||||
|
||||
// Appendf formats according to a format specifier and appends the value of this environment variable to any previous
|
||||
// declarations of the value without any delimitation. If delimitation is important during concatenation, callers are
|
||||
// required to add it.
|
||||
func (e Environment) Appendf(name string, format string, a ...interface{}) {
|
||||
func (e Environment) Appendf(name string, delimiter string, format string, a ...interface{}) {
|
||||
e.delimiter(name, delimiter)
|
||||
e[fmt.Sprintf("%s.append", name)] = fmt.Sprintf(format, a...)
|
||||
}
|
||||
|
||||
|
@ -48,11 +51,6 @@ func (e Environment) Defaultf(name string, format string, a ...interface{}) {
|
|||
e[fmt.Sprintf("%s.default", name)] = fmt.Sprintf(format, a...)
|
||||
}
|
||||
|
||||
// Delimiter sets a delimiter for an environment variable with this value.
|
||||
func (e Environment) Delimiter(name string, delimiter string) {
|
||||
e[fmt.Sprintf("%s.delim", name)] = delimiter
|
||||
}
|
||||
|
||||
// Override formats using the default formats for its operands and overrides any existing value for an environment
|
||||
// variable with this value. Spaces are added between operands when neither is a string.
|
||||
func (e Environment) Override(name string, a ...interface{}) {
|
||||
|
@ -68,26 +66,71 @@ func (e Environment) Overridef(name string, format string, a ...interface{}) {
|
|||
// Prepend formats using the default formats for its operands and prepends the value of this environment variable to any
|
||||
// previous declarations of the value without any delimitation. Spaces are added between operands when neither is a
|
||||
// string. If delimitation is important during concatenation, callers are required to add it.
|
||||
func (e Environment) Prepend(name string, a ...interface{}) {
|
||||
func (e Environment) Prepend(name string, delimiter string, a ...interface{}) {
|
||||
e.delimiter(name, delimiter)
|
||||
e[fmt.Sprintf("%s.prepend", name)] = fmt.Sprint(a...)
|
||||
}
|
||||
|
||||
// Prependf formats using the default formats for its operands and prepends the value of this environment variable to
|
||||
// any previous declarations of the value without any delimitation. If delimitation is important during concatenation,
|
||||
// callers are required to add it.
|
||||
func (e Environment) Prependf(name string, format string, a ...interface{}) {
|
||||
func (e Environment) Prependf(name string, delimiter string, format string, a ...interface{}) {
|
||||
e.delimiter(name, delimiter)
|
||||
e[fmt.Sprintf("%s.prepend", name)] = fmt.Sprintf(format, a...)
|
||||
}
|
||||
|
||||
// PrependPath formats using the default formats for its operands and prepends the value of this environment variable to
|
||||
// any previous declarations of the value using the OS path delimiter. Spaces are added between operands when neither is
|
||||
// a string.
|
||||
func (e Environment) PrependPath(name string, a ...interface{}) {
|
||||
e[name] = fmt.Sprint(a...)
|
||||
// ProcessAppend formats using the default formats for its operands and appends the value of this environment variable
|
||||
// to any previous declarations of the value without any delimitation. Spaces are added between operands when neither is
|
||||
// a string. If delimitation is important during concatenation, callers are required to add it.
|
||||
func (e Environment) ProcessAppend(processType string, name string, delimiter string, a ...interface{}) {
|
||||
e.Append(filepath.Join(processType, name), delimiter, a...)
|
||||
}
|
||||
|
||||
// PrependPathf formats according to a format specifier and prepends the value of this environment variable to any
|
||||
// previous declarations of the value using the OS path delimiter.
|
||||
func (e Environment) PrependPathf(name string, format string, a ...interface{}) {
|
||||
e[name] = fmt.Sprintf(format, a...)
|
||||
// ProcessAppendf formats according to a format specifier and appends the value of this environment variable to any
|
||||
// previous declarations of the value without any delimitation. If delimitation is important during concatenation,
|
||||
// callers are required to add it.
|
||||
func (e Environment) ProcessAppendf(processType string, name string, delimiter string, format string, a ...interface{}) {
|
||||
e.Appendf(filepath.Join(processType, name), delimiter, format, a...)
|
||||
}
|
||||
|
||||
// ProcessDefault formats using the default formats for its operands and sets a default for an environment variable with
|
||||
// this value. Spaces are added between operands when neither is a string.
|
||||
func (e Environment) ProcessDefault(processType string, name string, a ...interface{}) {
|
||||
e.Default(filepath.Join(processType, name), a...)
|
||||
}
|
||||
|
||||
// ProcessDefaultf formats according to a format specifier and sets a default for an environment variable with this
|
||||
// value.
|
||||
func (e Environment) ProcessDefaultf(processType string, name string, format string, a ...interface{}) {
|
||||
e.Defaultf(filepath.Join(processType, name), format, a...)
|
||||
}
|
||||
|
||||
// ProcessOverride formats using the default formats for its operands and overrides any existing value for an
|
||||
// environment variable with this value. Spaces are added between operands when neither is a string.
|
||||
func (e Environment) ProcessOverride(processType string, name string, a ...interface{}) {
|
||||
e.Override(filepath.Join(processType, name), a...)
|
||||
}
|
||||
|
||||
// ProcessOverridef formats according to a format specifier and overrides any existing value for an environment variable
|
||||
// with this value.
|
||||
func (e Environment) ProcessOverridef(processType string, name string, format string, a ...interface{}) {
|
||||
e.Overridef(filepath.Join(processType, name), format, a...)
|
||||
}
|
||||
|
||||
// ProcessPrepend formats using the default formats for its operands and prepends the value of this environment variable
|
||||
// to any previous declarations of the value without any delimitation. Spaces are added between operands when neither
|
||||
// is a string. If delimitation is important during concatenation, callers are required to add it.
|
||||
func (e Environment) ProcessPrepend(processType string, name string, delimiter string, a ...interface{}) {
|
||||
e.Prepend(filepath.Join(processType, name), delimiter, a...)
|
||||
}
|
||||
|
||||
// ProcessPrependf formats using the default formats for its operands and prepends the value of this environment
|
||||
// variable to any previous declarations of the value without any delimitation. If delimitation is important during
|
||||
// concatenation, callers are required to add it.
|
||||
func (e Environment) ProcessPrependf(processType string, name string, delimiter string, format string, a ...interface{}) {
|
||||
e.Prependf(filepath.Join(processType, name), delimiter, format, a...)
|
||||
}
|
||||
|
||||
func (e Environment) delimiter(name string, delimiter string) {
|
||||
e[fmt.Sprintf("%s.delim", name)] = delimiter
|
||||
}
|
||||
|
|
|
@ -17,14 +17,16 @@
|
|||
package libcnb_test
|
||||
|
||||
import (
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/buildpacks/libcnb"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/sclevine/spec"
|
||||
|
||||
"github.com/buildpacks/libcnb/v2"
|
||||
)
|
||||
|
||||
func testEnvironment(t *testing.T, context spec.G, it spec.S) {
|
||||
func testEnvironment(t *testing.T, _ spec.G, it spec.S) {
|
||||
var (
|
||||
Expect = NewWithT(t).Expect
|
||||
|
||||
|
@ -36,13 +38,19 @@ func testEnvironment(t *testing.T, context spec.G, it spec.S) {
|
|||
})
|
||||
|
||||
it("adds append value", func() {
|
||||
environment.Append("TEST_NAME", "test-value")
|
||||
Expect(environment).To(Equal(libcnb.Environment{"TEST_NAME.append": "test-value"}))
|
||||
environment.Append("TEST_NAME", "test-delimiter", "test-value")
|
||||
Expect(environment).To(Equal(libcnb.Environment{
|
||||
"TEST_NAME.delim": "test-delimiter",
|
||||
"TEST_NAME.append": "test-value",
|
||||
}))
|
||||
})
|
||||
|
||||
it("adds append formatted value", func() {
|
||||
environment.Appendf("TEST_NAME", "test-%s", "value")
|
||||
Expect(environment).To(Equal(libcnb.Environment{"TEST_NAME.append": "test-value"}))
|
||||
environment.Appendf("TEST_NAME", "test-delimiter", "test-%s", "value")
|
||||
Expect(environment).To(Equal(libcnb.Environment{
|
||||
"TEST_NAME.delim": "test-delimiter",
|
||||
"TEST_NAME.append": "test-value",
|
||||
}))
|
||||
})
|
||||
|
||||
it("adds default value", func() {
|
||||
|
@ -55,11 +63,6 @@ func testEnvironment(t *testing.T, context spec.G, it spec.S) {
|
|||
Expect(environment).To(Equal(libcnb.Environment{"TEST_NAME.default": "test-value"}))
|
||||
})
|
||||
|
||||
it("adds delimiter value", func() {
|
||||
environment.Delimiter("TEST_NAME", "test-value")
|
||||
Expect(environment).To(Equal(libcnb.Environment{"TEST_NAME.delim": "test-value"}))
|
||||
})
|
||||
|
||||
it("adds override value", func() {
|
||||
environment.Override("TEST_NAME", "test-value")
|
||||
Expect(environment).To(Equal(libcnb.Environment{"TEST_NAME.override": "test-value"}))
|
||||
|
@ -71,23 +74,70 @@ func testEnvironment(t *testing.T, context spec.G, it spec.S) {
|
|||
})
|
||||
|
||||
it("adds prepend value", func() {
|
||||
environment.Prepend("TEST_NAME", "test-value")
|
||||
Expect(environment).To(Equal(libcnb.Environment{"TEST_NAME.prepend": "test-value"}))
|
||||
environment.Prepend("TEST_NAME", "test-delimiter", "test-value")
|
||||
Expect(environment).To(Equal(libcnb.Environment{
|
||||
"TEST_NAME.delim": "test-delimiter",
|
||||
"TEST_NAME.prepend": "test-value",
|
||||
}))
|
||||
})
|
||||
|
||||
it("adds prepend formatted value", func() {
|
||||
environment.Prependf("TEST_NAME", "test-%s", "value")
|
||||
Expect(environment).To(Equal(libcnb.Environment{"TEST_NAME.prepend": "test-value"}))
|
||||
environment.Prependf("TEST_NAME", "test-delimiter", "test-%s", "value")
|
||||
Expect(environment).To(Equal(libcnb.Environment{
|
||||
"TEST_NAME.delim": "test-delimiter",
|
||||
"TEST_NAME.prepend": "test-value",
|
||||
}))
|
||||
})
|
||||
|
||||
it("adds prepend path value", func() {
|
||||
environment.PrependPath("TEST_NAME", "test-value")
|
||||
Expect(environment).To(Equal(libcnb.Environment{"TEST_NAME": "test-value"}))
|
||||
it("adds process-specific append value", func() {
|
||||
environment.ProcessAppend("test-process", "TEST_NAME", "test-delimiter", "test-value")
|
||||
Expect(environment).To(Equal(libcnb.Environment{
|
||||
filepath.Join("test-process", "TEST_NAME.delim"): "test-delimiter",
|
||||
filepath.Join("test-process", "TEST_NAME.append"): "test-value",
|
||||
}))
|
||||
})
|
||||
|
||||
it("adds prepend path formatted value", func() {
|
||||
environment.PrependPathf("TEST_NAME", "test-%s", "value")
|
||||
Expect(environment).To(Equal(libcnb.Environment{"TEST_NAME": "test-value"}))
|
||||
it("adds process-specific append formatted value", func() {
|
||||
environment.ProcessAppendf("test-process", "TEST_NAME", "test-delimiter", "test-%s", "value")
|
||||
Expect(environment).To(Equal(libcnb.Environment{
|
||||
filepath.Join("test-process", "TEST_NAME.delim"): "test-delimiter",
|
||||
filepath.Join("test-process", "TEST_NAME.append"): "test-value",
|
||||
}))
|
||||
})
|
||||
|
||||
it("adds process-specific default value", func() {
|
||||
environment.ProcessDefault("test-process", "TEST_NAME", "test-value")
|
||||
Expect(environment).To(Equal(libcnb.Environment{filepath.Join("test-process", "TEST_NAME.default"): "test-value"}))
|
||||
})
|
||||
|
||||
it("adds process-specific default formatted value", func() {
|
||||
environment.ProcessDefaultf("test-process", "TEST_NAME", "test-%s", "value")
|
||||
Expect(environment).To(Equal(libcnb.Environment{filepath.Join("test-process", "TEST_NAME.default"): "test-value"}))
|
||||
})
|
||||
|
||||
it("adds process-specific override value", func() {
|
||||
environment.ProcessOverride("test-process", "TEST_NAME", "test-value")
|
||||
Expect(environment).To(Equal(libcnb.Environment{filepath.Join("test-process", "TEST_NAME.override"): "test-value"}))
|
||||
})
|
||||
|
||||
it("adds process-specific override formatted value", func() {
|
||||
environment.ProcessOverridef("test-process", "TEST_NAME", "test-%s", "value")
|
||||
Expect(environment).To(Equal(libcnb.Environment{filepath.Join("test-process", "TEST_NAME.override"): "test-value"}))
|
||||
})
|
||||
|
||||
it("adds process-specific prepend value", func() {
|
||||
environment.ProcessPrepend("test-process", "TEST_NAME", "test-delimiter", "test-value")
|
||||
Expect(environment).To(Equal(libcnb.Environment{
|
||||
filepath.Join("test-process", "TEST_NAME.delim"): "test-delimiter",
|
||||
filepath.Join("test-process", "TEST_NAME.prepend"): "test-value",
|
||||
}))
|
||||
})
|
||||
|
||||
it("adds process-specific prepend formatted value", func() {
|
||||
environment.ProcessPrependf("test-process", "TEST_NAME", "test-delimiter", "test-%s", "value")
|
||||
Expect(environment).To(Equal(libcnb.Environment{
|
||||
filepath.Join("test-process", "TEST_NAME.delim"): "test-delimiter",
|
||||
filepath.Join("test-process", "TEST_NAME.prepend"): "test-value",
|
||||
}))
|
||||
})
|
||||
}
|
||||
|
|
|
@ -0,0 +1,103 @@
|
|||
package examples
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
cdx "github.com/CycloneDX/cyclonedx-go"
|
||||
|
||||
"github.com/buildpacks/libcnb/v2"
|
||||
"github.com/buildpacks/libcnb/v2/log"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultVersion = "0.1"
|
||||
)
|
||||
|
||||
type Builder struct {
|
||||
Logger log.Logger
|
||||
}
|
||||
|
||||
// BuildpackPlan may contain multiple entries for a single buildpack, resolve
|
||||
// into a single entry.
|
||||
func resolve(plan libcnb.BuildpackPlan, name string) libcnb.BuildpackPlanEntry {
|
||||
entry := libcnb.BuildpackPlanEntry{
|
||||
Name: name,
|
||||
Metadata: map[string]interface{}{},
|
||||
}
|
||||
for _, e := range plan.Entries {
|
||||
for k, v := range e.Metadata {
|
||||
entry.Metadata[k] = v
|
||||
}
|
||||
}
|
||||
return entry
|
||||
}
|
||||
|
||||
func populateLayer(layer libcnb.Layer, version string) (libcnb.Layer, error) {
|
||||
exampleFile := filepath.Join(layer.Path, "example.txt")
|
||||
if err := os.WriteFile(exampleFile, []byte(version), 0600); err != nil {
|
||||
return libcnb.Layer{}, fmt.Errorf("unable to write example file: %w", err)
|
||||
}
|
||||
|
||||
layer.SharedEnvironment.Default("EXAMPLE_FILE", exampleFile)
|
||||
|
||||
// Provide an SBOM
|
||||
bom := cdx.NewBOM()
|
||||
bom.Metadata = &cdx.Metadata{
|
||||
Component: &cdx.Component{
|
||||
Type: cdx.ComponentTypeFile,
|
||||
Name: "example",
|
||||
Version: version,
|
||||
},
|
||||
}
|
||||
sbomPath := layer.SBOMPath(libcnb.CycloneDXJSON)
|
||||
sbomFile, err := os.OpenFile(sbomPath, os.O_CREATE|os.O_WRONLY, 0600)
|
||||
if err != nil {
|
||||
return layer, err
|
||||
}
|
||||
defer sbomFile.Close()
|
||||
encoder := cdx.NewBOMEncoder(sbomFile, cdx.BOMFileFormatJSON)
|
||||
if err := encoder.Encode(bom); err != nil {
|
||||
return layer, err
|
||||
}
|
||||
return layer, nil
|
||||
}
|
||||
|
||||
func (b Builder) Build(context libcnb.BuildContext) (libcnb.BuildResult, error) {
|
||||
// Reduce possible multiple buildpack plan entries to a single entry
|
||||
entry := resolve(context.Plan, Provides)
|
||||
result := libcnb.NewBuildResult()
|
||||
|
||||
// Read metadata from the buildpack plan, often contributed by libcnb.Requires
|
||||
// of the Detect phase
|
||||
version := DefaultVersion
|
||||
if v, ok := entry.Metadata["version"].(string); ok {
|
||||
version = v
|
||||
}
|
||||
|
||||
// Create a layer
|
||||
layer, err := context.Layers.Layer("example")
|
||||
if err != nil {
|
||||
return result, err
|
||||
}
|
||||
layer.LayerTypes = libcnb.LayerTypes{
|
||||
Launch: true,
|
||||
Build: true,
|
||||
Cache: true,
|
||||
}
|
||||
|
||||
layer, err = populateLayer(layer, version)
|
||||
if err != nil {
|
||||
return result, nil
|
||||
}
|
||||
|
||||
result.Layers = append(result.Layers, layer)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func ExampleBuild() {
|
||||
detector := Detector{log.New(os.Stdout)}
|
||||
builder := Builder{log.New(os.Stdout)}
|
||||
libcnb.BuildpackMain(detector.Detect, builder.Build)
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
package examples
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/buildpacks/libcnb/v2"
|
||||
"github.com/buildpacks/libcnb/v2/log"
|
||||
)
|
||||
|
||||
const (
|
||||
Provides = "example"
|
||||
BpExampleVersion = "BP_EXAMPLE_VERSION"
|
||||
)
|
||||
|
||||
type Detector struct {
|
||||
Logger log.Logger
|
||||
}
|
||||
|
||||
func (Detector) Detect(context libcnb.DetectContext) (libcnb.DetectResult, error) {
|
||||
version := "1.0"
|
||||
// Scan the application source folder to see if the example buildpack is
|
||||
// required. If `version.toml` does not exist we return a failed DetectResult
|
||||
// but no runtime error has occurred, so we return an empty error.
|
||||
versionPath := filepath.Join(context.ApplicationPath, "version.toml")
|
||||
if _, err := os.Open(versionPath); errors.Is(err, os.ErrNotExist) {
|
||||
return libcnb.DetectResult{}, nil
|
||||
}
|
||||
// Read the version number from the buildpack definition
|
||||
if exampleVersion, exists := context.Buildpack.Metadata["version"]; exists {
|
||||
version = exampleVersion.(string)
|
||||
}
|
||||
// Accept version number from the environment if the user provides it
|
||||
if exampleVersion, exists := context.Platform.Environment[BpExampleVersion]; exists {
|
||||
version = exampleVersion
|
||||
}
|
||||
metadata := map[string]interface{}{
|
||||
"version": version,
|
||||
}
|
||||
return libcnb.DetectResult{
|
||||
Pass: true,
|
||||
Plans: []libcnb.BuildPlan{
|
||||
{
|
||||
// Let the system know that if other buildpacks Require "example"
|
||||
// then this buildpack Provides the implementation logic.
|
||||
Provides: []libcnb.BuildPlanProvide{
|
||||
{Name: Provides},
|
||||
},
|
||||
// It is common for a buildpack to Require itself if the build phase
|
||||
// needs information from the detect phase. Here we pass the version number
|
||||
// as metadata to the build phase.
|
||||
Requires: []libcnb.BuildPlanRequire{
|
||||
{
|
||||
Name: Provides,
|
||||
Metadata: metadata,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}, nil
|
||||
}
|
||||
|
||||
func ExampleDetect() {
|
||||
detector := Detector{log.New(os.Stdout)}
|
||||
libcnb.BuildpackMain(detector.Detect, nil)
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
package examples
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/buildpacks/libcnb/v2"
|
||||
"github.com/buildpacks/libcnb/v2/log"
|
||||
)
|
||||
|
||||
type Generator struct {
|
||||
Logger log.Logger
|
||||
}
|
||||
|
||||
func (Generator) Generate(context libcnb.GenerateContext) (libcnb.GenerateResult, error) {
|
||||
// here you can read the context.ApplicationPath folder
|
||||
// and create run.Dockerfile and build.Dockerfile in the context.OutputPath folder
|
||||
// and read metadata from the context.Extension struct
|
||||
|
||||
// Just to use context to keep compiler happy =)
|
||||
fmt.Println(context.Extension.Info.ID)
|
||||
|
||||
result := libcnb.NewGenerateResult()
|
||||
return result, nil
|
||||
}
|
||||
|
||||
func ExampleGenerate() {
|
||||
generator := Generator{log.New(os.Stdout)}
|
||||
libcnb.ExtensionMain(nil, generator.Generate)
|
||||
}
|
|
@ -0,0 +1,71 @@
|
|||
/*
|
||||
* Copyright 2018-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package libcnb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/buildpacks/libcnb/v2/internal"
|
||||
)
|
||||
|
||||
//go:generate mockery --name ExecD --case=underscore
|
||||
|
||||
// ExecD describes an interface for types that follow the Exec.d specification.
|
||||
// It should return a map of environment variables and their values as output.
|
||||
type ExecD interface {
|
||||
Execute() (map[string]string, error)
|
||||
}
|
||||
|
||||
// RunExecD is called by the main function of a buildpack's execd binary, encompassing multiple execd
|
||||
// executors in one binary.
|
||||
func RunExecD(execDMap map[string]ExecD, options ...Option) {
|
||||
config := Config{
|
||||
arguments: os.Args,
|
||||
execdWriter: internal.NewExecDWriter(),
|
||||
exitHandler: internal.NewExitHandler(),
|
||||
}
|
||||
|
||||
for _, option := range options {
|
||||
config = option(config)
|
||||
}
|
||||
|
||||
if len(config.arguments) == 0 {
|
||||
config.exitHandler.Error(fmt.Errorf("expected command name"))
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
c := filepath.Base(config.arguments[0])
|
||||
e, ok := execDMap[c]
|
||||
if !ok {
|
||||
config.exitHandler.Error(fmt.Errorf("unsupported command %s", c))
|
||||
return
|
||||
}
|
||||
|
||||
r, err := e.Execute()
|
||||
if err != nil {
|
||||
config.exitHandler.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := config.execdWriter.Write(r); err != nil {
|
||||
config.exitHandler.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
|
@ -0,0 +1,130 @@
|
|||
/*
|
||||
* Copyright 2018-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package libcnb_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/sclevine/spec"
|
||||
"github.com/stretchr/testify/mock"
|
||||
|
||||
"github.com/buildpacks/libcnb/v2"
|
||||
"github.com/buildpacks/libcnb/v2/mocks"
|
||||
)
|
||||
|
||||
func testExecD(t *testing.T, _ spec.G, it spec.S) {
|
||||
var (
|
||||
Expect = NewWithT(t).Expect
|
||||
|
||||
exitHandler *mocks.ExitHandler
|
||||
execdWriter *mocks.ExecDWriter
|
||||
)
|
||||
|
||||
it.Before(func() {
|
||||
execdWriter = &mocks.ExecDWriter{}
|
||||
execdWriter.On("Write", mock.Anything).Return(nil)
|
||||
exitHandler = &mocks.ExitHandler{}
|
||||
exitHandler.On("Error", mock.Anything)
|
||||
exitHandler.On("Pass", mock.Anything)
|
||||
exitHandler.On("Fail", mock.Anything)
|
||||
})
|
||||
|
||||
it("encounters the wrong number of arguments", func() {
|
||||
libcnb.RunExecD(map[string]libcnb.ExecD{},
|
||||
libcnb.WithArguments([]string{}),
|
||||
libcnb.WithExitHandler(exitHandler),
|
||||
)
|
||||
|
||||
Expect(exitHandler.Calls[0].Arguments.Get(0)).To(MatchError("expected command name"))
|
||||
})
|
||||
|
||||
it("encounters an unsupported execd binary name", func() {
|
||||
libcnb.RunExecD(map[string]libcnb.ExecD{},
|
||||
libcnb.WithArguments([]string{"/dne"}),
|
||||
libcnb.WithExitHandler(exitHandler),
|
||||
)
|
||||
|
||||
Expect(exitHandler.Calls[0].Arguments.Get(0)).To(MatchError("unsupported command dne"))
|
||||
})
|
||||
|
||||
it("calls the appropriate execd for a given execd invoker binary", func() {
|
||||
execd1 := &mocks.ExecD{}
|
||||
execd2 := &mocks.ExecD{}
|
||||
execd1.On("Execute", mock.Anything).Return(map[string]string{}, nil)
|
||||
|
||||
libcnb.RunExecD(map[string]libcnb.ExecD{"execd1": execd1, "execd2": execd2},
|
||||
libcnb.WithArguments([]string{"execd1"}),
|
||||
libcnb.WithExitHandler(exitHandler),
|
||||
libcnb.WithExecDWriter(execdWriter),
|
||||
)
|
||||
|
||||
Expect(execd1.Calls).To(HaveLen(1))
|
||||
Expect(execd2.Calls).To(BeEmpty())
|
||||
})
|
||||
|
||||
it("calls exitHandler with the error from the execd", func() {
|
||||
e := &mocks.ExecD{}
|
||||
err := fmt.Errorf("example error")
|
||||
e.On("Execute", mock.Anything).Return(nil, err)
|
||||
|
||||
libcnb.RunExecD(map[string]libcnb.ExecD{"e": e},
|
||||
libcnb.WithArguments([]string{"/bin/e"}),
|
||||
libcnb.WithExitHandler(exitHandler),
|
||||
libcnb.WithExecDWriter(execdWriter),
|
||||
)
|
||||
|
||||
Expect(e.Calls).To(HaveLen(1))
|
||||
Expect(execdWriter.Calls).To(HaveLen(0))
|
||||
Expect(exitHandler.Calls[0].Arguments.Get(0)).To(MatchError(err))
|
||||
})
|
||||
|
||||
it("calls execdWriter.write with the appropriate input", func() {
|
||||
e := &mocks.ExecD{}
|
||||
o := map[string]string{"test": "test"}
|
||||
e.On("Execute", mock.Anything).Return(o, nil)
|
||||
|
||||
libcnb.RunExecD(map[string]libcnb.ExecD{"e": e},
|
||||
libcnb.WithArguments([]string{"/bin/e"}),
|
||||
libcnb.WithExitHandler(exitHandler),
|
||||
libcnb.WithExecDWriter(execdWriter),
|
||||
)
|
||||
|
||||
Expect(e.Calls).To(HaveLen(1))
|
||||
Expect(execdWriter.Calls).To(HaveLen(1))
|
||||
Expect(execdWriter.Calls[0].Method).To(BeIdenticalTo("Write"))
|
||||
Expect(execdWriter.Calls[0].Arguments).To(HaveLen(1))
|
||||
Expect(execdWriter.Calls[0].Arguments[0]).To(Equal(o))
|
||||
})
|
||||
|
||||
it("calls exitHandler with the error from the execd", func() {
|
||||
e := &mocks.ExecD{}
|
||||
err := fmt.Errorf("example error")
|
||||
e.On("Execute", mock.Anything).Return(nil, err)
|
||||
|
||||
libcnb.RunExecD(map[string]libcnb.ExecD{"e": e},
|
||||
libcnb.WithArguments([]string{"/bin/e"}),
|
||||
libcnb.WithExitHandler(exitHandler),
|
||||
libcnb.WithExecDWriter(execdWriter),
|
||||
)
|
||||
|
||||
Expect(e.Calls).To(HaveLen(1))
|
||||
Expect(execdWriter.Calls).To(HaveLen(0))
|
||||
Expect(exitHandler.Calls[0].Arguments.Get(0)).To(MatchError(err))
|
||||
})
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright 2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package libcnb
|
||||
|
||||
// ExtensionInfo is information about the extension.
|
||||
type ExtensionInfo struct {
|
||||
// ID is the ID of the extension.
|
||||
ID string `toml:"id"`
|
||||
|
||||
// Name is the name of the extension.
|
||||
Name string `toml:"name"`
|
||||
|
||||
// Version is the version of the extension.
|
||||
Version string `toml:"version"`
|
||||
|
||||
// Homepage is the homepage of the extension.
|
||||
Homepage string `toml:"homepage"`
|
||||
|
||||
// Description is a string describing the extension.
|
||||
Description string `toml:"description"`
|
||||
|
||||
// Keywords is a list of words that are associated with the extension.
|
||||
Keywords []string `toml:"keywords"`
|
||||
|
||||
// Licenses a list of extension licenses.
|
||||
Licenses []License `toml:"licenses"`
|
||||
}
|
||||
|
||||
// Extension is the contents of the extension.toml file.
|
||||
type Extension struct {
|
||||
// API is the api version expected by the extension.
|
||||
API string `toml:"api"`
|
||||
|
||||
// Info is information about the extension.
|
||||
Info ExtensionInfo `toml:"extension"`
|
||||
|
||||
// Path is the path to the extension.
|
||||
Path string `toml:"-"`
|
||||
|
||||
// Targets is the collection of targets supported by the buildpack.
|
||||
Targets []Target `toml:"targets"`
|
||||
|
||||
// Metadata is arbitrary metadata attached to the extension.
|
||||
Metadata map[string]interface{} `toml:"metadata"`
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright 2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package libcnb_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/sclevine/spec"
|
||||
|
||||
"github.com/buildpacks/libcnb/v2"
|
||||
|
||||
. "github.com/onsi/gomega"
|
||||
)
|
||||
|
||||
func testExtensionTOML(t *testing.T, _ spec.G, it spec.S) {
|
||||
var (
|
||||
Expect = NewWithT(t).Expect
|
||||
)
|
||||
|
||||
it("does not serialize the Path field", func() {
|
||||
extn := libcnb.Extension{
|
||||
API: "0.8",
|
||||
Info: libcnb.ExtensionInfo{
|
||||
ID: "test-buildpack/sample",
|
||||
Name: "sample",
|
||||
},
|
||||
Path: "../buildpack",
|
||||
}
|
||||
|
||||
output := &bytes.Buffer{}
|
||||
|
||||
Expect(toml.NewEncoder(output).Encode(extn)).To(Succeed())
|
||||
Expect(output.String()).NotTo(Or(ContainSubstring("Path = "), ContainSubstring("path = ")))
|
||||
})
|
||||
}
|
59
formatter.go
59
formatter.go
|
@ -1,59 +0,0 @@
|
|||
/*
|
||||
* Copyright 2018-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package libcnb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/buildpacks/libcnb/internal"
|
||||
)
|
||||
|
||||
// ApplicationPathFormatter is the formatter for an ApplicationPath.
|
||||
type ApplicationPathFormatter string
|
||||
|
||||
func (a ApplicationPathFormatter) String() string {
|
||||
contents, err := internal.DirectoryContents{Path: string(a)}.Get()
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Application contents: %s", err)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("Application contents: %s", contents)
|
||||
}
|
||||
|
||||
// BuildpackPathFormatter is the formatter for a BuildpackPath.
|
||||
type BuildpackPathFormatter string
|
||||
|
||||
func (b BuildpackPathFormatter) String() string {
|
||||
contents, err := internal.DirectoryContents{Path: string(b)}.Get()
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Buildpack contents: %s", err)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("Buildpack contents: %s", contents)
|
||||
}
|
||||
|
||||
// PlatformFormatter is the formatter for a Platform.
|
||||
type PlatformFormatter Platform
|
||||
|
||||
func (p PlatformFormatter) String() string {
|
||||
contents, err := internal.DirectoryContents{Path: p.Path}.Get()
|
||||
if err != nil {
|
||||
return fmt.Sprintf("Platform contents: %s", err)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("Platform contents: %s", contents)
|
||||
}
|
|
@ -1,120 +0,0 @@
|
|||
/*
|
||||
* Copyright 2018-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package libcnb_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/buildpacks/libcnb"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/sclevine/spec"
|
||||
)
|
||||
|
||||
func testFormatter(t *testing.T, context spec.G, it spec.S) {
|
||||
var (
|
||||
Expect = NewWithT(t).Expect
|
||||
)
|
||||
|
||||
context("ApplicationPathFormatter", func() {
|
||||
var (
|
||||
app string
|
||||
)
|
||||
|
||||
it.Before(func() {
|
||||
var err error
|
||||
app, err = ioutil.TempDir("", "application-path-formatter")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
it.After(func() {
|
||||
Expect(os.RemoveAll(app)).To(Succeed())
|
||||
})
|
||||
|
||||
it("lists empty directory contents", func() {
|
||||
Expect(libcnb.ApplicationPathFormatter(app).String()).To(Equal("Application contents: [.]"))
|
||||
})
|
||||
|
||||
it("lists directory contents", func() {
|
||||
f, err := os.Create(filepath.Join(app, "test-file"))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
defer f.Close()
|
||||
|
||||
Expect(libcnb.ApplicationPathFormatter(app).String()).To(Equal("Application contents: [. test-file]"))
|
||||
})
|
||||
})
|
||||
|
||||
context("BuildpackPathFormatter", func() {
|
||||
var (
|
||||
bp string
|
||||
)
|
||||
|
||||
it.Before(func() {
|
||||
var err error
|
||||
bp, err = ioutil.TempDir("", "buildpack-path-formatter")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
it.After(func() {
|
||||
Expect(os.RemoveAll(bp)).To(Succeed())
|
||||
})
|
||||
|
||||
it("lists empty directory contents", func() {
|
||||
Expect(libcnb.BuildpackPathFormatter(bp).String()).To(Equal("Buildpack contents: [.]"))
|
||||
})
|
||||
|
||||
it("lists directory contents", func() {
|
||||
f, err := os.Create(filepath.Join(bp, "test-file"))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
defer f.Close()
|
||||
|
||||
Expect(libcnb.BuildpackPathFormatter(bp).String()).To(Equal("Buildpack contents: [. test-file]"))
|
||||
})
|
||||
})
|
||||
|
||||
context("PlatformFormatter", func() {
|
||||
var (
|
||||
plat libcnb.Platform
|
||||
)
|
||||
|
||||
it.Before(func() {
|
||||
var err error
|
||||
plat.Path, err = ioutil.TempDir("", "platform-formatter")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
it.After(func() {
|
||||
Expect(os.RemoveAll(plat.Path)).To(Succeed())
|
||||
})
|
||||
|
||||
it("lists empty directory contents", func() {
|
||||
Expect(libcnb.PlatformFormatter(plat).String()).To(Equal("Platform contents: [.]"))
|
||||
})
|
||||
|
||||
it("lists directory contents", func() {
|
||||
f, err := os.Create(filepath.Join(plat.Path, "test-file"))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
defer f.Close()
|
||||
|
||||
Expect(libcnb.PlatformFormatter(plat).String()).To(Equal("Platform contents: [. test-file]"))
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
}
|
|
@ -0,0 +1,261 @@
|
|||
/*
|
||||
* Copyright 2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package libcnb
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
"github.com/Masterminds/semver"
|
||||
|
||||
"github.com/buildpacks/libcnb/v2/internal"
|
||||
"github.com/buildpacks/libcnb/v2/log"
|
||||
)
|
||||
|
||||
// GenerateContext contains the inputs to generate.
|
||||
type GenerateContext struct {
|
||||
// ApplicationPath is the location of the application source code as provided by
|
||||
// the lifecycle.
|
||||
ApplicationPath string
|
||||
|
||||
// Extension is metadata about the extension, from extension.toml.
|
||||
Extension Extension
|
||||
|
||||
// OutputDirectory is the location Dockerfiles should be written to.
|
||||
OutputDirectory string
|
||||
|
||||
// Logger is the way to write messages to the end user
|
||||
Logger log.Logger
|
||||
|
||||
// Plan is the buildpack plan provided to the buildpack.
|
||||
Plan BuildpackPlan
|
||||
|
||||
// Platform is the contents of the platform.
|
||||
Platform Platform
|
||||
|
||||
// TargetInfo contains info of the target (os, arch, ...).
|
||||
TargetInfo TargetInfo
|
||||
|
||||
// TargetDistro is the target distribution (name, version).
|
||||
TargetDistro TargetDistro
|
||||
|
||||
// Deprecated: StackID is the ID of the stack.
|
||||
StackID string
|
||||
}
|
||||
|
||||
// GenerateResult contains the results of detection.
|
||||
type GenerateResult struct {
|
||||
// Unmet contains buildpack plan entries that were not satisfied by the buildpack and therefore should be
|
||||
// passed to subsequent providers.
|
||||
Unmet []UnmetPlanEntry
|
||||
RunDockerfile []byte
|
||||
BuildDockerfile []byte
|
||||
Config *ExtendConfig
|
||||
}
|
||||
|
||||
// DockerfileArg is a Dockerfile argument
|
||||
type DockerfileArg struct {
|
||||
Name string `toml:"name"`
|
||||
Value string `toml:"value"`
|
||||
}
|
||||
|
||||
// BuildConfig contains additional arguments passed to the generated Dockerfiles
|
||||
type BuildConfig struct {
|
||||
Args []DockerfileArg `toml:"args"`
|
||||
}
|
||||
|
||||
// ExtendConfig contains additional configuration for the Dockerfiles
|
||||
type ExtendConfig struct {
|
||||
Build BuildConfig `toml:"build"`
|
||||
Run BuildConfig `toml:"run"`
|
||||
}
|
||||
|
||||
// NewGenerateResult creates a new BuildResult instance, initializing empty fields.
|
||||
func NewGenerateResult() GenerateResult {
|
||||
return GenerateResult{}
|
||||
}
|
||||
|
||||
func (b GenerateResult) String() string {
|
||||
return fmt.Sprintf(
|
||||
"{Unmet:%+v}",
|
||||
b.Unmet,
|
||||
)
|
||||
}
|
||||
|
||||
// GenerateFunc takes a context and returns a result, performing extension generate behaviors.
|
||||
type GenerateFunc func(context GenerateContext) (GenerateResult, error)
|
||||
|
||||
// Generate is called by the main function of a extension, for generate phase
|
||||
func Generate(generate GenerateFunc, config Config) {
|
||||
var (
|
||||
err error
|
||||
file string
|
||||
ok bool
|
||||
)
|
||||
ctx := GenerateContext{Logger: config.logger}
|
||||
|
||||
ctx.ApplicationPath, err = os.Getwd()
|
||||
if err != nil {
|
||||
config.exitHandler.Error(fmt.Errorf("unable to get working directory\n%w", err))
|
||||
return
|
||||
}
|
||||
|
||||
if config.logger.IsDebugEnabled() {
|
||||
if err := config.contentWriter.Write("Application contents", ctx.ApplicationPath); err != nil {
|
||||
config.logger.Debugf("unable to write application contents\n%w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if s, ok := os.LookupEnv(EnvExtensionDirectory); ok {
|
||||
ctx.Extension.Path = filepath.Clean(s)
|
||||
} else {
|
||||
config.exitHandler.Error(fmt.Errorf("unable to get CNB_EXTENSION_DIR, not found"))
|
||||
return
|
||||
}
|
||||
|
||||
if config.logger.IsDebugEnabled() {
|
||||
if err := config.contentWriter.Write("Extension contents", ctx.Extension.Path); err != nil {
|
||||
config.logger.Debugf("unable to write extension contents\n%w", err)
|
||||
}
|
||||
}
|
||||
|
||||
file = filepath.Join(ctx.Extension.Path, "extension.toml")
|
||||
if _, err = toml.DecodeFile(file, &ctx.Extension); err != nil && !os.IsNotExist(err) {
|
||||
config.exitHandler.Error(fmt.Errorf("unable to decode extension %s\n%w", file, err))
|
||||
return
|
||||
}
|
||||
config.logger.Debugf("Extension: %+v", ctx.Extension)
|
||||
|
||||
API, err := semver.NewVersion(ctx.Extension.API)
|
||||
if err != nil {
|
||||
config.exitHandler.Error(errors.New("version cannot be parsed"))
|
||||
return
|
||||
}
|
||||
|
||||
compatVersionCheck, _ := semver.NewConstraint(fmt.Sprintf(">= %s, <= %s", MinSupportedBPVersion, MaxSupportedBPVersion))
|
||||
if !compatVersionCheck.Check(API) {
|
||||
if MinSupportedBPVersion == MaxSupportedBPVersion {
|
||||
config.exitHandler.Error(fmt.Errorf("this version of libcnb is only compatible with buildpack API == %s", MinSupportedBPVersion))
|
||||
return
|
||||
}
|
||||
|
||||
config.exitHandler.Error(fmt.Errorf("this version of libcnb is only compatible with buildpack APIs >= %s, <= %s", MinSupportedBPVersion, MaxSupportedBPVersion))
|
||||
return
|
||||
}
|
||||
|
||||
outputDir, ok := os.LookupEnv(EnvOutputDirectory)
|
||||
if !ok {
|
||||
config.exitHandler.Error(fmt.Errorf("expected CNB_OUTPUT_DIR to be set"))
|
||||
return
|
||||
}
|
||||
ctx.OutputDirectory = outputDir
|
||||
|
||||
ctx.Platform.Path, ok = os.LookupEnv(EnvPlatformDirectory)
|
||||
if !ok {
|
||||
config.exitHandler.Error(fmt.Errorf("expected CNB_PLATFORM_DIR to be set"))
|
||||
return
|
||||
}
|
||||
|
||||
buildpackPlanPath, ok := os.LookupEnv(EnvBuildPlanPath)
|
||||
if !ok {
|
||||
config.exitHandler.Error(fmt.Errorf("expected CNB_BP_PLAN_PATH to be set"))
|
||||
return
|
||||
}
|
||||
|
||||
if config.logger.IsDebugEnabled() {
|
||||
if err := config.contentWriter.Write("Platform contents", ctx.Platform.Path); err != nil {
|
||||
config.logger.Debugf("unable to write platform contents\n%w", err)
|
||||
}
|
||||
}
|
||||
|
||||
if ctx.Platform.Bindings, err = NewBindings(ctx.Platform.Path); err != nil {
|
||||
config.exitHandler.Error(fmt.Errorf("unable to read platform bindings %s\n%w", ctx.Platform.Path, err))
|
||||
return
|
||||
}
|
||||
config.logger.Debugf("Platform Bindings: %+v", ctx.Platform.Bindings)
|
||||
|
||||
file = filepath.Join(ctx.Platform.Path, "env")
|
||||
if ctx.Platform.Environment, err = internal.NewConfigMapFromPath(file); err != nil {
|
||||
config.exitHandler.Error(fmt.Errorf("unable to read platform environment %s\n%w", file, err))
|
||||
return
|
||||
}
|
||||
config.logger.Debugf("Platform Environment: %s", ctx.Platform.Environment)
|
||||
|
||||
if _, err = toml.DecodeFile(buildpackPlanPath, &ctx.Plan); err != nil && !os.IsNotExist(err) {
|
||||
config.exitHandler.Error(fmt.Errorf("unable to decode buildpack plan %s\n%w", buildpackPlanPath, err))
|
||||
return
|
||||
}
|
||||
config.logger.Debugf("Buildpack Plan: %+v", ctx.Plan)
|
||||
|
||||
if ctx.StackID, ok = os.LookupEnv(EnvStackID); !ok {
|
||||
config.logger.Debug("CNB_STACK_ID not set")
|
||||
} else {
|
||||
config.logger.Debugf("Stack: %s", ctx.StackID)
|
||||
}
|
||||
|
||||
if API.GreaterThan(semver.MustParse("0.9")) {
|
||||
ctx.TargetInfo = TargetInfo{}
|
||||
ctx.TargetInfo.OS, _ = os.LookupEnv(EnvTargetOS)
|
||||
ctx.TargetInfo.Arch, _ = os.LookupEnv(EnvTargetArch)
|
||||
ctx.TargetInfo.Variant, _ = os.LookupEnv(EnvTargetArchVariant)
|
||||
config.logger.Debugf("System: %+v", ctx.TargetInfo)
|
||||
|
||||
ctx.TargetDistro = TargetDistro{}
|
||||
ctx.TargetDistro.Name, _ = os.LookupEnv(EnvTargetDistroName)
|
||||
ctx.TargetDistro.Version, _ = os.LookupEnv(EnvTargetDistroVersion)
|
||||
config.logger.Debugf("Distro: %+v", ctx.TargetDistro)
|
||||
}
|
||||
|
||||
result, err := generate(ctx)
|
||||
if err != nil {
|
||||
config.exitHandler.Error(err)
|
||||
return
|
||||
}
|
||||
config.logger.Debugf("Result: %+v", result)
|
||||
|
||||
if len(result.RunDockerfile) > 0 {
|
||||
//nolint:gosec
|
||||
if err := os.WriteFile(filepath.Join(ctx.OutputDirectory, "run.Dockerfile"), result.RunDockerfile, 0644); err != nil {
|
||||
config.exitHandler.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(result.BuildDockerfile) > 0 {
|
||||
//nolint:gosec
|
||||
if err := os.WriteFile(filepath.Join(ctx.OutputDirectory, "build.Dockerfile"), result.BuildDockerfile, 0644); err != nil {
|
||||
config.exitHandler.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if result.Config != nil {
|
||||
configFile, err := os.Create(filepath.Join(ctx.OutputDirectory, "extend-config.toml"))
|
||||
if err != nil {
|
||||
config.exitHandler.Error(err)
|
||||
return
|
||||
}
|
||||
|
||||
if err := toml.NewEncoder(configFile).Encode(result.Config); err != nil {
|
||||
config.exitHandler.Error(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,458 @@
|
|||
/*
|
||||
* Copyright 2018-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package libcnb_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
"text/template"
|
||||
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/sclevine/spec"
|
||||
"github.com/stretchr/testify/mock"
|
||||
|
||||
"github.com/buildpacks/libcnb/v2"
|
||||
"github.com/buildpacks/libcnb/v2/log"
|
||||
"github.com/buildpacks/libcnb/v2/mocks"
|
||||
)
|
||||
|
||||
func testGenerate(t *testing.T, context spec.G, it spec.S) {
|
||||
var (
|
||||
Expect = NewWithT(t).Expect
|
||||
|
||||
generateFunc libcnb.GenerateFunc
|
||||
applicationPath string
|
||||
extensionPath string
|
||||
outputPath string
|
||||
buildpackPlanPath string
|
||||
extnTOMLContents string
|
||||
commandPath string
|
||||
environmentWriter *mocks.EnvironmentWriter
|
||||
exitHandler *mocks.ExitHandler
|
||||
platformPath string
|
||||
tomlWriter *mocks.TOMLWriter
|
||||
extensionTOML *template.Template
|
||||
|
||||
workingDir string
|
||||
)
|
||||
|
||||
it.Before(func() {
|
||||
generateFunc = func(libcnb.GenerateContext) (libcnb.GenerateResult, error) {
|
||||
return libcnb.NewGenerateResult(), nil
|
||||
}
|
||||
|
||||
var err error
|
||||
applicationPath, err = os.MkdirTemp("", "generate-application-path")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
applicationPath, err = filepath.EvalSymlinks(applicationPath)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
extensionPath, err = os.MkdirTemp("", "generate-extension-path")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(os.Setenv("CNB_EXTENSION_DIR", extensionPath)).To(Succeed())
|
||||
|
||||
extnTOMLContents = `
|
||||
api = "{{.APIVersion}}"
|
||||
|
||||
[extension]
|
||||
id = "test-id"
|
||||
name = "test-name"
|
||||
version = "1.1.1"
|
||||
description = "A test extension"
|
||||
keywords = ["test", "extension"]
|
||||
|
||||
[[extension.licenses]]
|
||||
type = "Apache-2.0"
|
||||
uri = "https://spdx.org/licenses/Apache-2.0.html"
|
||||
|
||||
[[extension.licenses]]
|
||||
type = "Apache-1.1"
|
||||
uri = "https://spdx.org/licenses/Apache-1.1.html"
|
||||
|
||||
[metadata]
|
||||
test-key = "test-value"
|
||||
`
|
||||
extensionTOML, err = template.New("extension.toml").Parse(extnTOMLContents)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
var b bytes.Buffer
|
||||
err = extensionTOML.Execute(&b, map[string]string{"APIVersion": "0.8"})
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
Expect(os.WriteFile(filepath.Join(extensionPath, "extension.toml"), b.Bytes(), 0600)).To(Succeed())
|
||||
|
||||
f, err := os.CreateTemp("", "generate-buildpackplan-path")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(f.Close()).NotTo(HaveOccurred())
|
||||
buildpackPlanPath = f.Name()
|
||||
|
||||
Expect(os.WriteFile(buildpackPlanPath,
|
||||
[]byte(`
|
||||
[[entries]]
|
||||
name = "test-name"
|
||||
version = "test-version"
|
||||
|
||||
[entries.metadata]
|
||||
test-key = "test-value"
|
||||
`),
|
||||
0600),
|
||||
).To(Succeed())
|
||||
|
||||
commandPath = filepath.Join("bin", "generate")
|
||||
|
||||
environmentWriter = &mocks.EnvironmentWriter{}
|
||||
environmentWriter.On("Write", mock.Anything, mock.Anything).Return(nil)
|
||||
|
||||
exitHandler = &mocks.ExitHandler{}
|
||||
exitHandler.On("Error", mock.Anything)
|
||||
|
||||
platformPath, err = os.MkdirTemp("", "generate-platform-path")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(os.MkdirAll(filepath.Join(platformPath, "bindings", "alpha"), 0755)).To(Succeed())
|
||||
Expect(os.WriteFile(filepath.Join(platformPath, "bindings", "alpha", "test-secret-key"),
|
||||
[]byte("test-secret-value"), 0600)).To(Succeed())
|
||||
|
||||
Expect(os.MkdirAll(filepath.Join(platformPath, "env"), 0755)).To(Succeed())
|
||||
Expect(os.WriteFile(filepath.Join(platformPath, "env", "TEST_ENV"), []byte("test-value"), 0600)).
|
||||
To(Succeed())
|
||||
|
||||
tomlWriter = &mocks.TOMLWriter{}
|
||||
tomlWriter.On("Write", mock.Anything, mock.Anything).Return(nil)
|
||||
|
||||
outputPath, err = os.MkdirTemp("", "generate-output-path")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(os.Setenv("CNB_OUTPUT_DIR", outputPath)).To(Succeed())
|
||||
|
||||
Expect(os.Setenv("CNB_STACK_ID", "test-stack-id")).To(Succeed())
|
||||
|
||||
Expect(os.Setenv("CNB_PLATFORM_DIR", platformPath)).To(Succeed())
|
||||
Expect(os.Setenv("CNB_BP_PLAN_PATH", buildpackPlanPath)).To(Succeed())
|
||||
|
||||
Expect(os.Setenv("CNB_TARGET_OS", "linux")).To(Succeed())
|
||||
Expect(os.Setenv("CNB_TARGET_ARCH", "arm")).To(Succeed())
|
||||
Expect(os.Setenv("CNB_TARGET_ARCH_VARIANT", "v6")).To(Succeed())
|
||||
Expect(os.Setenv("CNB_TARGET_DISTRO_NAME", "ubuntu")).To(Succeed())
|
||||
Expect(os.Setenv("CNB_TARGET_DISTRO_VERSION", "24.04")).To(Succeed())
|
||||
|
||||
workingDir, err = os.Getwd()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(os.Chdir(applicationPath)).To(Succeed())
|
||||
})
|
||||
|
||||
it.After(func() {
|
||||
Expect(os.Chdir(workingDir)).To(Succeed())
|
||||
Expect(os.Unsetenv("CNB_EXTENSION_DIR")).To(Succeed())
|
||||
Expect(os.Unsetenv("CNB_STACK_ID")).To(Succeed())
|
||||
Expect(os.Unsetenv("CNB_PLATFORM_DIR")).To(Succeed())
|
||||
Expect(os.Unsetenv("CNB_BP_PLAN_PATH")).To(Succeed())
|
||||
Expect(os.Unsetenv("CNB_OUTPUT_DIR")).To(Succeed())
|
||||
|
||||
Expect(os.Unsetenv("CNB_TARGET_OS"))
|
||||
Expect(os.Unsetenv("CNB_TARGET_ARCH"))
|
||||
Expect(os.Unsetenv("CNB_TARGET_ARCH_VARIANT"))
|
||||
Expect(os.Unsetenv("CNB_TARGET_DISTRO_NAME"))
|
||||
Expect(os.Unsetenv("CNB_TARGET_DISTRO_VERSION"))
|
||||
|
||||
Expect(os.RemoveAll(applicationPath)).To(Succeed())
|
||||
Expect(os.RemoveAll(extensionPath)).To(Succeed())
|
||||
Expect(os.RemoveAll(buildpackPlanPath)).To(Succeed())
|
||||
Expect(os.RemoveAll(outputPath)).To(Succeed())
|
||||
Expect(os.RemoveAll(platformPath)).To(Succeed())
|
||||
})
|
||||
|
||||
context("buildpack API is not within the supported range", func() {
|
||||
it.Before(func() {
|
||||
Expect(os.WriteFile(filepath.Join(extensionPath, "extension.toml"),
|
||||
[]byte(`
|
||||
api = "0.7"
|
||||
|
||||
[extension]
|
||||
id = "test-id"
|
||||
name = "test-name"
|
||||
version = "1.1.1"
|
||||
`),
|
||||
0600),
|
||||
).To(Succeed())
|
||||
})
|
||||
|
||||
it("fails", func() {
|
||||
libcnb.Generate(generateFunc,
|
||||
libcnb.NewConfig(
|
||||
libcnb.WithArguments([]string{commandPath, outputPath, platformPath, buildpackPlanPath}),
|
||||
libcnb.WithExitHandler(exitHandler),
|
||||
libcnb.WithLogger(log.NewDiscard())),
|
||||
)
|
||||
|
||||
if libcnb.MinSupportedBPVersion == libcnb.MaxSupportedBPVersion {
|
||||
Expect(exitHandler.Calls[0].Arguments.Get(0)).To(MatchError(
|
||||
fmt.Sprintf("this version of libcnb is only compatible with buildpack API == %s", libcnb.MinSupportedBPVersion)))
|
||||
} else {
|
||||
Expect(exitHandler.Calls[0].Arguments.Get(0)).To(MatchError(
|
||||
fmt.Sprintf("this version of libcnb is only compatible with buildpack APIs >= %s, <= %s", libcnb.MinSupportedBPVersion, libcnb.MaxSupportedBPVersion),
|
||||
))
|
||||
}
|
||||
})
|
||||
})
|
||||
|
||||
context("errors if required env vars are not set", func() {
|
||||
for _, e := range []string{"CNB_OUTPUT_DIR", "CNB_PLATFORM_DIR", "CNB_BP_PLAN_PATH"} {
|
||||
// We need to do this assignment because of the way that spec binds variables
|
||||
envVar := e
|
||||
context(fmt.Sprintf("when %s is unset", envVar), func() {
|
||||
it.Before(func() {
|
||||
Expect(os.WriteFile(filepath.Join(extensionPath, "extension.toml"),
|
||||
[]byte(`
|
||||
api = "0.8"
|
||||
|
||||
[extension]
|
||||
id = "test-id"
|
||||
name = "test-name"
|
||||
version = "1.1.1"
|
||||
`),
|
||||
0600),
|
||||
).To(Succeed())
|
||||
os.Unsetenv(envVar)
|
||||
})
|
||||
|
||||
it("fails", func() {
|
||||
libcnb.Generate(generateFunc,
|
||||
libcnb.NewConfig(
|
||||
libcnb.WithArguments([]string{commandPath}),
|
||||
libcnb.WithExitHandler(exitHandler)),
|
||||
)
|
||||
Expect(exitHandler.Calls[0].Arguments.Get(0)).To(MatchError(
|
||||
fmt.Sprintf("expected %s to be set", envVar),
|
||||
))
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
context("has a build environment", func() {
|
||||
var ctx libcnb.GenerateContext
|
||||
|
||||
it.Before(func() {
|
||||
Expect(os.WriteFile(filepath.Join(extensionPath, "extension.toml"),
|
||||
[]byte(`
|
||||
api = "0.8"
|
||||
|
||||
[extension]
|
||||
id = "test-id"
|
||||
name = "test-name"
|
||||
version = "1.1.1"
|
||||
`),
|
||||
0600),
|
||||
).To(Succeed())
|
||||
|
||||
generateFunc = func(context libcnb.GenerateContext) (libcnb.GenerateResult, error) {
|
||||
ctx = context
|
||||
return libcnb.NewGenerateResult(), nil
|
||||
}
|
||||
})
|
||||
|
||||
it("creates context", func() {
|
||||
libcnb.Generate(generateFunc,
|
||||
libcnb.NewConfig(
|
||||
libcnb.WithArguments([]string{commandPath})),
|
||||
)
|
||||
Expect(ctx.ApplicationPath).To(Equal(applicationPath))
|
||||
Expect(ctx.Extension).To(Equal(libcnb.Extension{
|
||||
API: "0.8",
|
||||
Info: libcnb.ExtensionInfo{
|
||||
ID: "test-id",
|
||||
Name: "test-name",
|
||||
Version: "1.1.1",
|
||||
},
|
||||
Path: extensionPath,
|
||||
}))
|
||||
Expect(ctx.OutputDirectory).To(Equal(outputPath))
|
||||
Expect(ctx.Plan).To(Equal(libcnb.BuildpackPlan{
|
||||
Entries: []libcnb.BuildpackPlanEntry{
|
||||
{
|
||||
Name: "test-name",
|
||||
Metadata: map[string]interface{}{
|
||||
"test-key": "test-value",
|
||||
},
|
||||
},
|
||||
},
|
||||
}))
|
||||
Expect(ctx.Platform).To(Equal(libcnb.Platform{
|
||||
Bindings: libcnb.Bindings{
|
||||
libcnb.Binding{
|
||||
Name: "alpha",
|
||||
Path: filepath.Join(platformPath, "bindings", "alpha"),
|
||||
Secret: map[string]string{
|
||||
"test-secret-key": "test-secret-value",
|
||||
},
|
||||
},
|
||||
},
|
||||
Environment: map[string]string{"TEST_ENV": "test-value"},
|
||||
Path: platformPath,
|
||||
}))
|
||||
Expect(ctx.StackID).To(Equal("test-stack-id"))
|
||||
})
|
||||
})
|
||||
|
||||
context("has a build environment specifying target metadata", func() {
|
||||
var ctx libcnb.GenerateContext
|
||||
|
||||
it.Before(func() {
|
||||
Expect(os.WriteFile(filepath.Join(extensionPath, "extension.toml"),
|
||||
[]byte(`
|
||||
api = "0.10"
|
||||
|
||||
[extension]
|
||||
id = "test-id"
|
||||
name = "test-name"
|
||||
version = "1.1.1"
|
||||
|
||||
[[targets]]
|
||||
os = "linux"
|
||||
arch = "amd64"
|
||||
|
||||
[[targets.distros]]
|
||||
name = "ubuntu"
|
||||
version = "18.04"
|
||||
|
||||
[[targets.distros]]
|
||||
name = "debian"
|
||||
|
||||
[[targets]]
|
||||
os = "linux"
|
||||
arch = "arm"
|
||||
variant = "v6"
|
||||
`), 0600),
|
||||
).To(Succeed())
|
||||
|
||||
generateFunc = func(context libcnb.GenerateContext) (libcnb.GenerateResult, error) {
|
||||
ctx = context
|
||||
return libcnb.NewGenerateResult(), nil
|
||||
}
|
||||
})
|
||||
|
||||
it("provides target information", func() {
|
||||
libcnb.Generate(generateFunc,
|
||||
libcnb.NewConfig(
|
||||
libcnb.WithArguments([]string{commandPath}),
|
||||
libcnb.WithLogger(log.New(os.Stdout)),
|
||||
),
|
||||
)
|
||||
|
||||
Expect(ctx.Extension.Targets).To(HaveLen(2))
|
||||
Expect(ctx.Extension.Targets[0].OS).To(Equal("linux"))
|
||||
Expect(ctx.Extension.Targets[0].Arch).To(Equal("amd64"))
|
||||
Expect(ctx.Extension.Targets[0].Distros).To(HaveLen(2))
|
||||
Expect(ctx.Extension.Targets[0].Distros[0].Name).To(Equal("ubuntu"))
|
||||
Expect(ctx.Extension.Targets[0].Distros[0].Version).To(Equal("18.04"))
|
||||
Expect(ctx.Extension.Targets[0].Distros[1].Name).To(Equal("debian"))
|
||||
|
||||
Expect(ctx.Extension.Targets[1].Variant).To(Equal("v6"))
|
||||
|
||||
Expect(ctx.TargetInfo.OS).To(Equal("linux"))
|
||||
Expect(ctx.TargetInfo.Arch).To(Equal("arm"))
|
||||
Expect(ctx.TargetInfo.Variant).To(Equal("v6"))
|
||||
Expect(ctx.TargetDistro.Name).To(Equal("ubuntu"))
|
||||
Expect(ctx.TargetDistro.Version).To(Equal("24.04"))
|
||||
})
|
||||
})
|
||||
|
||||
it("fails if CNB_EXTENSION_DIR is not set", func() {
|
||||
Expect(os.Unsetenv("CNB_EXTENSION_DIR")).To(Succeed())
|
||||
|
||||
libcnb.Generate(generateFunc,
|
||||
libcnb.NewConfig(
|
||||
libcnb.WithArguments([]string{filepath.Join(extensionPath, commandPath), outputPath, platformPath, buildpackPlanPath}),
|
||||
libcnb.WithExitHandler(exitHandler),
|
||||
libcnb.WithLogger(log.NewDiscard())),
|
||||
)
|
||||
|
||||
Expect(exitHandler.Calls[0].Arguments.Get(0)).To(MatchError("unable to get CNB_EXTENSION_DIR, not found"))
|
||||
})
|
||||
|
||||
it("handles error from GenerateFunc", func() {
|
||||
generateFunc = func(libcnb.GenerateContext) (libcnb.GenerateResult, error) {
|
||||
return libcnb.NewGenerateResult(), errors.New("test-error")
|
||||
}
|
||||
|
||||
libcnb.Generate(generateFunc,
|
||||
libcnb.NewConfig(
|
||||
libcnb.WithArguments([]string{commandPath, outputPath, platformPath, buildpackPlanPath}),
|
||||
libcnb.WithExitHandler(exitHandler),
|
||||
libcnb.WithLogger(log.NewDiscard())),
|
||||
)
|
||||
|
||||
Expect(exitHandler.Calls[0].Arguments.Get(0)).To(MatchError("test-error"))
|
||||
})
|
||||
|
||||
it("writes Dockerfiles", func() {
|
||||
generateFunc = func(_ libcnb.GenerateContext) (libcnb.GenerateResult, error) {
|
||||
result := libcnb.NewGenerateResult()
|
||||
result.BuildDockerfile = []byte(`FROM foo:latest`)
|
||||
result.RunDockerfile = []byte(`FROM bar:latest`)
|
||||
return result, nil
|
||||
}
|
||||
|
||||
libcnb.Generate(generateFunc,
|
||||
libcnb.NewConfig(
|
||||
libcnb.WithArguments([]string{commandPath, outputPath, platformPath, buildpackPlanPath}),
|
||||
libcnb.WithTOMLWriter(tomlWriter),
|
||||
libcnb.WithLogger(log.NewDiscard())),
|
||||
)
|
||||
|
||||
Expect(filepath.Join(outputPath, "build.Dockerfile")).To(BeARegularFile())
|
||||
Expect(filepath.Join(outputPath, "run.Dockerfile")).To(BeARegularFile())
|
||||
})
|
||||
|
||||
it("writes extend-config.toml", func() {
|
||||
generateFunc = func(_ libcnb.GenerateContext) (libcnb.GenerateResult, error) {
|
||||
result := libcnb.NewGenerateResult()
|
||||
result.Config = &libcnb.ExtendConfig{
|
||||
Build: libcnb.BuildConfig{
|
||||
Args: []libcnb.DockerfileArg{
|
||||
{
|
||||
Name: "foo",
|
||||
Value: "bar",
|
||||
},
|
||||
},
|
||||
},
|
||||
Run: libcnb.BuildConfig{
|
||||
Args: []libcnb.DockerfileArg{
|
||||
{
|
||||
Name: "bar",
|
||||
Value: "bazz",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
return result, nil
|
||||
}
|
||||
|
||||
libcnb.Generate(generateFunc,
|
||||
libcnb.NewConfig(
|
||||
libcnb.WithArguments([]string{commandPath, outputPath, platformPath, buildpackPlanPath}),
|
||||
libcnb.WithTOMLWriter(tomlWriter),
|
||||
libcnb.WithLogger(log.NewDiscard())),
|
||||
)
|
||||
|
||||
Expect(filepath.Join(outputPath, "extend-config.toml")).To(BeARegularFile())
|
||||
})
|
||||
}
|
35
go.mod
35
go.mod
|
@ -1,21 +1,24 @@
|
|||
module github.com/buildpacks/libcnb
|
||||
module github.com/buildpacks/libcnb/v2
|
||||
|
||||
go 1.14
|
||||
go 1.24
|
||||
|
||||
require (
|
||||
github.com/BurntSushi/toml v0.3.1
|
||||
github.com/golang/protobuf v1.3.4 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e // indirect
|
||||
github.com/onsi/ginkgo v1.12.0 // indirect
|
||||
github.com/onsi/gomega v1.9.0
|
||||
github.com/BurntSushi/toml v1.5.0
|
||||
github.com/CycloneDX/cyclonedx-go v0.9.2
|
||||
github.com/Masterminds/semver v1.5.0
|
||||
github.com/onsi/gomega v1.37.0
|
||||
github.com/sclevine/spec v1.4.0
|
||||
github.com/stretchr/objx v0.2.0 // indirect
|
||||
github.com/stretchr/testify v1.5.1
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a // indirect
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 // indirect
|
||||
golang.org/x/text v0.3.2 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f // indirect
|
||||
gopkg.in/yaml.v2 v2.2.8 // indirect
|
||||
github.com/stretchr/testify v1.10.0
|
||||
)
|
||||
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/kr/text v0.2.0 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
golang.org/x/net v0.41.0 // indirect
|
||||
golang.org/x/text v0.26.0 // indirect
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
|
97
go.sum
97
go.sum
|
@ -1,63 +1,58 @@
|
|||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
|
||||
github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/CycloneDX/cyclonedx-go v0.9.2 h1:688QHn2X/5nRezKe2ueIVCt+NRqf7fl3AVQk+vaFcIo=
|
||||
github.com/CycloneDX/cyclonedx-go v0.9.2/go.mod h1:vcK6pKgO1WanCdd61qx4bFnSsDJQ6SbM2ZuMIgq86Jg=
|
||||
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
|
||||
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
||||
github.com/bradleyjkemp/cupaloy/v2 v2.8.0 h1:any4BmKE+jGIaMpnU8YgH/I2LPiLBufr6oMMlVBbn9M=
|
||||
github.com/bradleyjkemp/cupaloy/v2 v2.8.0/go.mod h1:bm7JXdkRd4BHJk9HpwqAI8BoAY1lps46Enkdqw6aRX0=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.4 h1:87PNWwrRvUSnqS4dlcBU/ftvOIBep4sYuBLlh6rX2wk=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad h1:a6HEuzUHeKH6hwfN/ZoQgRgVIWFJljSWa/zetS2WTvg=
|
||||
github.com/google/pprof v0.0.0-20241210010833-40e02aabc2ad/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI=
|
||||
github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/ginkgo v1.12.0 h1:Iw5WCbBcaAAd0fpRb1c9r5YCylv4XDoCSigm1zLevwU=
|
||||
github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg=
|
||||
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
|
||||
github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg=
|
||||
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
|
||||
github.com/onsi/ginkgo/v2 v2.23.3 h1:edHxnszytJ4lD9D5Jjc4tiDkPBZ3siDeJJkUZJJVkp0=
|
||||
github.com/onsi/ginkgo/v2 v2.23.3/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM=
|
||||
github.com/onsi/gomega v1.37.0 h1:CdEG8g0S133B4OswTDC/5XPSzE1OeP29QOioj2PID2Y=
|
||||
github.com/onsi/gomega v1.37.0/go.mod h1:8D9+Txp43QWKhM24yyOBEdpkzN8FvJyAwecBgsU4KU0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8=
|
||||
github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.2.0 h1:Hbg2NidpLE8veEBkEZTL3CvlkUIVzuU9jDplZO54c48=
|
||||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a h1:GuSPYbZzB5/dcLNCwLQLsg3obCJtX9IJhpXkvY7kzk0=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527 h1:uYVVQ9WP/Ds2ROhcaGPeIdVq0RIXVLwsHlnvJ+cT1So=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/terminalstatic/go-xsd-validate v0.1.6 h1:TenYeQ3eY631qNi1/cTmLH/s2slHPRKTTHT+XSHkepo=
|
||||
github.com/terminalstatic/go-xsd-validate v0.1.6/go.mod h1:18lsvYFofBflqCrvo1umpABZ99+GneNTw2kEEc8UPJw=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw=
|
||||
golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M=
|
||||
golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA=
|
||||
golang.org/x/tools v0.33.0 h1:4qz2S3zmRxbGIhDIAgjxvFutSvH5EfnsYrRBj0UI0bc=
|
||||
golang.org/x/tools v0.33.0/go.mod h1:CIJMaWEY88juyUfo7UbgPqbC8rU2OqfAV1h2Qp0oMYI=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=
|
||||
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
run:
|
||||
timeout: 6m
|
||||
|
||||
linters:
|
||||
disable-all: true
|
||||
enable:
|
||||
- bodyclose
|
||||
- dogsled
|
||||
- errcheck
|
||||
- copyloopvar
|
||||
- gocritic
|
||||
- goimports
|
||||
- gosec
|
||||
- gosimple
|
||||
- govet
|
||||
- ineffassign
|
||||
- misspell
|
||||
- nakedret
|
||||
- revive
|
||||
- staticcheck
|
||||
- stylecheck
|
||||
- typecheck
|
||||
- unconvert
|
||||
- unused
|
||||
- whitespace
|
||||
|
||||
linters-settings:
|
||||
revive:
|
||||
rules:
|
||||
- name: dot-imports
|
||||
disabled: true
|
||||
goimports:
|
||||
local-prefixes: github.com/buildpacks/libcnb/v2
|
|
@ -27,9 +27,13 @@ func TestUnit(t *testing.T) {
|
|||
suite := spec.New("libcnb", spec.Report(report.Terminal{}))
|
||||
suite("Build", testBuild)
|
||||
suite("Detect", testDetect)
|
||||
suite("Generate", testGenerate)
|
||||
suite("Environment", testEnvironment)
|
||||
suite("Formatter", testFormatter)
|
||||
suite("Layer", testLayer)
|
||||
suite("Main", testMain)
|
||||
suite("Platform", testPlatform)
|
||||
suite("ExecD", testExecD)
|
||||
suite("BuildpackTOML", testBuildpackTOML)
|
||||
suite("ExtensionTOML", testExtensionTOML)
|
||||
suite.Run(t)
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ package internal
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
@ -35,12 +35,21 @@ func NewConfigMapFromPath(path string) (ConfigMap, error) {
|
|||
|
||||
configMap := ConfigMap{}
|
||||
for _, file := range files {
|
||||
contents, err := ioutil.ReadFile(file)
|
||||
if strings.HasPrefix(filepath.Base(file), ".") {
|
||||
// ignore hidden files
|
||||
continue
|
||||
}
|
||||
if stat, err := os.Stat(file); err != nil {
|
||||
return nil, fmt.Errorf("failed to stat file %s\n%w", file, err)
|
||||
} else if stat.IsDir() {
|
||||
continue
|
||||
}
|
||||
contents, err := os.ReadFile(file)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to read file %s\n%w", file, err)
|
||||
}
|
||||
|
||||
configMap[filepath.Base(file)] = strings.TrimSpace(string(contents))
|
||||
configMap[filepath.Base(file)] = string(contents)
|
||||
}
|
||||
|
||||
return configMap, nil
|
||||
|
|
|
@ -17,17 +17,17 @@
|
|||
package internal_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/buildpacks/libcnb/internal"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/sclevine/spec"
|
||||
|
||||
"github.com/buildpacks/libcnb/v2/internal"
|
||||
)
|
||||
|
||||
func testConfigMap(t *testing.T, context spec.G, it spec.S) {
|
||||
func testConfigMap(t *testing.T, _ spec.G, it spec.S) {
|
||||
var (
|
||||
Expect = NewWithT(t).Expect
|
||||
|
||||
|
@ -36,7 +36,7 @@ func testConfigMap(t *testing.T, context spec.G, it spec.S) {
|
|||
|
||||
it.Before(func() {
|
||||
var err error
|
||||
path, err = ioutil.TempDir("", "config-map")
|
||||
path, err = os.MkdirTemp("", "config-map")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
|
@ -47,18 +47,45 @@ func testConfigMap(t *testing.T, context spec.G, it spec.S) {
|
|||
it("returns an empty ConfigMap when directory does not exist", func() {
|
||||
Expect(os.RemoveAll(path)).To(Succeed())
|
||||
|
||||
c, err := internal.NewConfigMapFromPath(path)
|
||||
cm, err := internal.NewConfigMapFromPath(path)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(c).To(Equal(internal.ConfigMap{}))
|
||||
Expect(cm).To(Equal(internal.ConfigMap{}))
|
||||
})
|
||||
|
||||
it("loads the ConfigMap from a directory", func() {
|
||||
Expect(ioutil.WriteFile(filepath.Join(path, "test-key"), []byte("test-value"), 0644)).To(Succeed())
|
||||
Expect(os.WriteFile(filepath.Join(path, "test-key"), []byte("test-value"), 0600)).To(Succeed())
|
||||
|
||||
c, err := internal.NewConfigMapFromPath(path)
|
||||
cm, err := internal.NewConfigMapFromPath(path)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(c).To(Equal(internal.ConfigMap{"test-key": "test-value"}))
|
||||
Expect(cm).To(Equal(internal.ConfigMap{"test-key": "test-value"}))
|
||||
})
|
||||
|
||||
it("ignores dirs and follows symlinks", func() {
|
||||
// this is necessary to support bindings mounted as k8s config maps & secrets
|
||||
Expect(os.MkdirAll(filepath.Join(path, ".hidden"), 0755)).To(Succeed())
|
||||
Expect(os.WriteFile(
|
||||
filepath.Join(path, ".hidden", "test-key"),
|
||||
[]byte("test-value"),
|
||||
0600,
|
||||
)).To(Succeed())
|
||||
Expect(os.Symlink(
|
||||
filepath.Join(".hidden", "test-key"),
|
||||
filepath.Join(path, "test-key"),
|
||||
)).To(Succeed())
|
||||
|
||||
cm, err := internal.NewConfigMapFromPath(path)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(cm).To(Equal(internal.ConfigMap{"test-key": "test-value"}))
|
||||
})
|
||||
|
||||
it("ignores hidden files", func() {
|
||||
Expect(os.WriteFile(filepath.Join(path, ".hidden-key"), []byte("hidden-value"), 0600)).To(Succeed())
|
||||
|
||||
cm, err := internal.NewConfigMapFromPath(path)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(cm).To(BeEmpty())
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2018-2020 the original author or authors.
|
||||
* Copyright 2018-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -18,36 +18,53 @@ package internal
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
|
||||
"github.com/buildpacks/libcnb/v2/log"
|
||||
)
|
||||
|
||||
// DirectoryContents is used to generate a collection of the names of all files within a directory.
|
||||
type DirectoryContents struct {
|
||||
Path string
|
||||
// DirectoryContentsWriter is used write the contents of a directory to the given io.Writer
|
||||
type DirectoryContentsWriter struct {
|
||||
format log.DirectoryContentFormatter
|
||||
writer io.Writer
|
||||
}
|
||||
|
||||
// Get returns the names of all files within a directory
|
||||
func (d DirectoryContents) Get() ([]string, error) {
|
||||
var contents []string
|
||||
// NewDirectoryContentsWriter returns a new DirectoryContentsWriter initialized and ready to be used
|
||||
func NewDirectoryContentsWriter(format log.DirectoryContentFormatter, writer io.Writer) DirectoryContentsWriter {
|
||||
return DirectoryContentsWriter{
|
||||
format: format,
|
||||
writer: writer,
|
||||
}
|
||||
}
|
||||
|
||||
if err := filepath.Walk(d.Path, func(path string, info os.FileInfo, err error) error {
|
||||
// Write all the file contents to the writer
|
||||
func (d DirectoryContentsWriter) Write(title, path string) error {
|
||||
d.format.RootPath(path)
|
||||
|
||||
if _, err := d.writer.Write([]byte(d.format.Title(title))); err != nil {
|
||||
return fmt.Errorf("unable to write title\n%w", err)
|
||||
}
|
||||
|
||||
if err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rel, err := filepath.Rel(d.Path, path)
|
||||
msg, err := d.format.File(path, info)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to calculate relative path %s -> %s\n%w", d.Path, path, err)
|
||||
return fmt.Errorf("unable to format\n%w", err)
|
||||
}
|
||||
|
||||
if _, err := d.writer.Write([]byte(msg)); err != nil {
|
||||
return fmt.Errorf("unable to write\n%w", err)
|
||||
}
|
||||
|
||||
contents = append(contents, rel)
|
||||
return nil
|
||||
}); err != nil {
|
||||
return nil, fmt.Errorf("error walking path %s\n%w", d.Path, err)
|
||||
return fmt.Errorf("error walking path %s\n%w", path, err)
|
||||
}
|
||||
|
||||
sort.Strings(contents)
|
||||
return contents, nil
|
||||
return nil
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2018-2020 the original author or authors.
|
||||
* Copyright 2018-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -17,26 +17,29 @@
|
|||
package internal_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/buildpacks/libcnb/internal"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/sclevine/spec"
|
||||
|
||||
"github.com/buildpacks/libcnb/v2/internal"
|
||||
)
|
||||
|
||||
func testDirectoryContents(t *testing.T, context spec.G, it spec.S) {
|
||||
func testDirectoryContentsWriter(t *testing.T, context spec.G, it spec.S) {
|
||||
var (
|
||||
Expect = NewWithT(t).Expect
|
||||
|
||||
path string
|
||||
buf bytes.Buffer
|
||||
)
|
||||
|
||||
it.Before(func() {
|
||||
var err error
|
||||
path, err = ioutil.TempDir("", "directory-contents")
|
||||
path, err = os.MkdirTemp("", "directory-contents")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
|
@ -44,8 +47,32 @@ func testDirectoryContents(t *testing.T, context spec.G, it spec.S) {
|
|||
Expect(os.RemoveAll(path)).To(Succeed())
|
||||
})
|
||||
|
||||
context("directory content formats", func() {
|
||||
fm := internal.NewPlainDirectoryContentFormatter()
|
||||
|
||||
it("formats title", func() {
|
||||
Expect(fm.Title("foo")).To(Equal("foo:\n"))
|
||||
})
|
||||
|
||||
it("formats a file", func() {
|
||||
cwd, err := os.Getwd()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
info, err := os.Stat(cwd)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
fm.RootPath(filepath.Dir(cwd))
|
||||
|
||||
Expect(fm.File(cwd, info)).To(Equal(fmt.Sprintf("%s\n", filepath.Base(cwd))))
|
||||
})
|
||||
})
|
||||
|
||||
it("lists empty directory contents", func() {
|
||||
Expect(internal.DirectoryContents{path}.Get()).To(Equal([]string{"."}))
|
||||
fm := internal.NewPlainDirectoryContentFormatter()
|
||||
dc := internal.NewDirectoryContentsWriter(fm, &buf)
|
||||
|
||||
Expect(dc.Write("title", path)).To(Succeed())
|
||||
Expect(buf.String()).To(Equal("title:\n.\n"))
|
||||
})
|
||||
|
||||
it("lists directory contents", func() {
|
||||
|
@ -53,6 +80,10 @@ func testDirectoryContents(t *testing.T, context spec.G, it spec.S) {
|
|||
Expect(err).NotTo(HaveOccurred())
|
||||
defer f.Close()
|
||||
|
||||
Expect(internal.DirectoryContents{path}.Get()).To(Equal([]string{".", "test-file"}))
|
||||
fm := internal.NewPlainDirectoryContentFormatter()
|
||||
dc := internal.NewDirectoryContentsWriter(fm, &buf)
|
||||
|
||||
Expect(dc.Write("title", path)).To(Succeed())
|
||||
Expect(buf.String()).To(Equal("title:\n.\ntest-file\n"))
|
||||
})
|
||||
}
|
||||
|
|
|
@ -18,7 +18,6 @@ package internal
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
@ -38,7 +37,14 @@ func (w EnvironmentWriter) Write(path string, environment map[string]string) err
|
|||
|
||||
for key, value := range environment {
|
||||
f := filepath.Join(path, key)
|
||||
if err := ioutil.WriteFile(f, []byte(value), 0644); err != nil {
|
||||
|
||||
// required to support process-specific environment variables
|
||||
if err := os.MkdirAll(filepath.Dir(f), 0755); err != nil {
|
||||
return fmt.Errorf("unable to mkdir from key %s\n%w", filepath.Dir(f), err)
|
||||
}
|
||||
|
||||
//nolint:gosec
|
||||
if err := os.WriteFile(f, []byte(value), 0644); err != nil {
|
||||
return fmt.Errorf("unable to write file %s\n%w", f, err)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,17 +17,17 @@
|
|||
package internal_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/buildpacks/libcnb/internal"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/sclevine/spec"
|
||||
|
||||
"github.com/buildpacks/libcnb/v2/internal"
|
||||
)
|
||||
|
||||
func testEnvironmentWriter(t *testing.T, context spec.G, it spec.S) {
|
||||
func testEnvironmentWriter(t *testing.T, _ spec.G, it spec.S) {
|
||||
var (
|
||||
Expect = NewWithT(t).Expect
|
||||
|
||||
|
@ -37,11 +37,9 @@ func testEnvironmentWriter(t *testing.T, context spec.G, it spec.S) {
|
|||
|
||||
it.Before(func() {
|
||||
var err error
|
||||
path, err = ioutil.TempDir("", "environment-writer")
|
||||
path, err = os.MkdirTemp("", "environment-writer")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(os.RemoveAll(path)).To(Succeed())
|
||||
|
||||
writer = internal.EnvironmentWriter{}
|
||||
})
|
||||
|
||||
it.After(func() {
|
||||
|
@ -55,15 +53,26 @@ func testEnvironmentWriter(t *testing.T, context spec.G, it spec.S) {
|
|||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
content, err := ioutil.ReadFile(filepath.Join(path, "some-name"))
|
||||
content, err := os.ReadFile(filepath.Join(path, "some-name"))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(string(content)).To(Equal("some-content"))
|
||||
|
||||
content, err = ioutil.ReadFile(filepath.Join(path, "other-name"))
|
||||
content, err = os.ReadFile(filepath.Join(path, "other-name"))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(string(content)).To(Equal("other-content"))
|
||||
})
|
||||
|
||||
it("writes the given environment with process specific envs to a directory", func() {
|
||||
err := writer.Write(path, map[string]string{
|
||||
"some-proc/some-name": "some-content",
|
||||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
content, err := os.ReadFile(filepath.Join(path, "some-proc", "some-name"))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(string(content)).To(Equal("some-content"))
|
||||
})
|
||||
|
||||
it("writes does not create a directory of the env map is empty", func() {
|
||||
err := writer.Write(path, map[string]string{})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
@ -71,4 +80,3 @@ func testEnvironmentWriter(t *testing.T, context spec.G, it spec.S) {
|
|||
Expect(path).NotTo(BeAnExistingFile())
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright 2018-2021 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package internal
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/BurntSushi/toml"
|
||||
)
|
||||
|
||||
// ExecDWriter is a type used to write TOML files to fd3.
|
||||
type ExecDWriter struct {
|
||||
outputWriter io.Writer
|
||||
}
|
||||
|
||||
// Option is a function for configuring an ExitHandler instance.
|
||||
type ExecDOption func(handler ExecDWriter) ExecDWriter
|
||||
|
||||
// WithExecDOutputWriter creates an Option that configures the writer.
|
||||
func WithExecDOutputWriter(writer io.Writer) ExecDOption {
|
||||
return func(execdWriter ExecDWriter) ExecDWriter {
|
||||
execdWriter.outputWriter = writer
|
||||
return execdWriter
|
||||
}
|
||||
}
|
||||
|
||||
// NewExitHandler creates a new instance that calls os.Exit and writes to os.stderr.
|
||||
func NewExecDWriter(options ...ExecDOption) ExecDWriter {
|
||||
h := ExecDWriter{
|
||||
outputWriter: os.NewFile(3, "/dev/fd/3"),
|
||||
}
|
||||
|
||||
for _, option := range options {
|
||||
h = option(h)
|
||||
}
|
||||
|
||||
return h
|
||||
}
|
||||
|
||||
// Write outputs the value serialized in TOML format to the appropriate writer.
|
||||
func (e ExecDWriter) Write(value map[string]string) error {
|
||||
if value == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return toml.NewEncoder(e.outputWriter).Encode(value)
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright 2018-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package internal_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/sclevine/spec"
|
||||
|
||||
"github.com/buildpacks/libcnb/v2/internal"
|
||||
)
|
||||
|
||||
func testExecDWriter(t *testing.T, _ spec.G, it spec.S) {
|
||||
var (
|
||||
Expect = NewWithT(t).Expect
|
||||
|
||||
b *bytes.Buffer
|
||||
writer internal.ExecDWriter
|
||||
)
|
||||
|
||||
it.Before(func() {
|
||||
b = bytes.NewBuffer([]byte{})
|
||||
|
||||
writer = internal.NewExecDWriter(
|
||||
internal.WithExecDOutputWriter(b),
|
||||
)
|
||||
})
|
||||
|
||||
it("writes the correct set of values", func() {
|
||||
env := map[string]string{
|
||||
"test": "test",
|
||||
"test2": "te∆t",
|
||||
}
|
||||
Expect(writer.Write(env)).To(BeNil())
|
||||
Expect(b.String()).To(internal.MatchTOML(`
|
||||
test = "test"
|
||||
test2 = "te∆t"`))
|
||||
})
|
||||
}
|
|
@ -21,12 +21,13 @@ import (
|
|||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/buildpacks/libcnb/internal"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/sclevine/spec"
|
||||
|
||||
"github.com/buildpacks/libcnb/v2/internal"
|
||||
)
|
||||
|
||||
func testExitHandler(t *testing.T, context spec.G, it spec.S) {
|
||||
func testExitHandler(t *testing.T, _ spec.G, it spec.S) {
|
||||
var (
|
||||
Expect = NewWithT(t).Expect
|
||||
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
type PlainDirectoryContentFormatter struct {
|
||||
rootPath string
|
||||
}
|
||||
|
||||
// NewPlainDirectoryContentFormatter returns a formatter applies no formatting
|
||||
//
|
||||
// The returned formatter operates as such:
|
||||
//
|
||||
// Title -> returns string followed by `:\n`
|
||||
// File -> returns file name relative to the root followed by `\n`
|
||||
func NewPlainDirectoryContentFormatter() *PlainDirectoryContentFormatter {
|
||||
return &PlainDirectoryContentFormatter{}
|
||||
}
|
||||
|
||||
func (p *PlainDirectoryContentFormatter) File(path string, _ os.FileInfo) (string, error) {
|
||||
rel, err := filepath.Rel(p.rootPath, path)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("unable to calculate relative path %s -> %s\n%w", p.rootPath, path, err)
|
||||
}
|
||||
|
||||
return fmt.Sprintf("%s\n", rel), nil
|
||||
}
|
||||
|
||||
func (p *PlainDirectoryContentFormatter) RootPath(path string) {
|
||||
p.rootPath = path
|
||||
}
|
||||
|
||||
func (p *PlainDirectoryContentFormatter) Title(title string) string {
|
||||
return fmt.Sprintf("%s:\n", title)
|
||||
}
|
|
@ -0,0 +1,67 @@
|
|||
/*
|
||||
* Copyright 2018-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package internal_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/sclevine/spec"
|
||||
|
||||
"github.com/buildpacks/libcnb/v2/internal"
|
||||
)
|
||||
|
||||
func testFormatters(t *testing.T, context spec.G, it spec.S) {
|
||||
var (
|
||||
Expect = NewWithT(t).Expect
|
||||
|
||||
path string
|
||||
)
|
||||
|
||||
it.Before(func() {
|
||||
var err error
|
||||
path, err = os.MkdirTemp("", "directory-contents")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
it.After(func() {
|
||||
Expect(os.RemoveAll(path)).To(Succeed())
|
||||
})
|
||||
|
||||
context("directory content formats", func() {
|
||||
fm := internal.NewPlainDirectoryContentFormatter()
|
||||
|
||||
it("formats title", func() {
|
||||
Expect(fm.Title("foo")).To(Equal("foo:\n"))
|
||||
})
|
||||
|
||||
it("formats a file", func() {
|
||||
cwd, err := os.Getwd()
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
info, err := os.Stat(cwd)
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
|
||||
fm.RootPath(filepath.Dir(cwd))
|
||||
|
||||
Expect(fm.File(cwd, info)).To(Equal(fmt.Sprintf("%s\n", filepath.Base(cwd))))
|
||||
})
|
||||
})
|
||||
}
|
|
@ -26,9 +26,11 @@ import (
|
|||
func TestUnit(t *testing.T) {
|
||||
suite := spec.New("libcnb/internal", spec.Report(report.Terminal{}))
|
||||
suite("ConfigMap", testConfigMap)
|
||||
suite("DirectoryContents", testDirectoryContents)
|
||||
suite("DirectoryContents", testDirectoryContentsWriter)
|
||||
suite("EnvironmentWriter", testEnvironmentWriter)
|
||||
suite("ExitHandler", testExitHandler)
|
||||
suite("TOMLWriter", testTOMLWriter)
|
||||
suite("ExecDWriter", testExecDWriter)
|
||||
suite("Formatters", testFormatters)
|
||||
suite.Run(t)
|
||||
}
|
||||
|
|
|
@ -17,17 +17,17 @@
|
|||
package internal_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/buildpacks/libcnb/internal"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/sclevine/spec"
|
||||
|
||||
"github.com/buildpacks/libcnb/v2/internal"
|
||||
)
|
||||
|
||||
func testTOMLWriter(t *testing.T, context spec.G, it spec.S) {
|
||||
func testTOMLWriter(t *testing.T, _ spec.G, it spec.S) {
|
||||
var (
|
||||
Expect = NewWithT(t).Expect
|
||||
|
||||
|
@ -38,7 +38,7 @@ func testTOMLWriter(t *testing.T, context spec.G, it spec.S) {
|
|||
|
||||
it.Before(func() {
|
||||
var err error
|
||||
parent, err = ioutil.TempDir("", "toml-writer")
|
||||
parent, err = os.MkdirTemp("", "toml-writer")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
path = filepath.Join(parent, "text.toml")
|
||||
|
@ -55,7 +55,7 @@ func testTOMLWriter(t *testing.T, context spec.G, it spec.S) {
|
|||
})
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(ioutil.ReadFile(path)).To(internal.MatchTOML(`
|
||||
Expect(os.ReadFile(path)).To(internal.MatchTOML(`
|
||||
some-field = "some-value"
|
||||
other-field = "other-value"`))
|
||||
})
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* Copyright 2018-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package libcnb
|
||||
|
||||
// Label represents an image label.
|
||||
type Label struct {
|
||||
// Key is the key of the label.
|
||||
Key string `toml:"key"`
|
||||
|
||||
// Value is the value of the label.
|
||||
Value string `toml:"value"`
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
/*
|
||||
* Copyright 2018-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package libcnb
|
||||
|
||||
// LaunchTOML represents the contents of launch.toml.
|
||||
type LaunchTOML struct {
|
||||
// Labels is the collection of image labels contributed by the buildpack.
|
||||
Labels []Label `toml:"labels"`
|
||||
|
||||
// Processes is the collection of process types contributed by the buildpack.
|
||||
Processes []Process `toml:"processes"`
|
||||
|
||||
// Slices is the collection of slices contributed by the buildpack.
|
||||
Slices []Slice `toml:"slices"`
|
||||
}
|
||||
|
||||
func (l LaunchTOML) isEmpty() bool {
|
||||
return len(l.Labels) == 0 && len(l.Processes) == 0 && len(l.Slices) == 0
|
||||
}
|
144
layer.go
144
layer.go
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2018-2020 the original author or authors.
|
||||
* Copyright 2018-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -24,31 +24,75 @@ import (
|
|||
"github.com/BurntSushi/toml"
|
||||
)
|
||||
|
||||
// Profile is the collection of values to be written into profile.d
|
||||
type Profile map[string]string
|
||||
const (
|
||||
BOMFormatCycloneDXExtension = "cdx.json"
|
||||
BOMFormatSPDXExtension = "spdx.json"
|
||||
BOMFormatSyftExtension = "syft.json"
|
||||
BOMMediaTypeCycloneDX = "application/vnd.cyclonedx+json"
|
||||
BOMMediaTypeSPDX = "application/spdx+json"
|
||||
BOMMediaTypeSyft = "application/vnd.syft+json"
|
||||
BOMUnknown = "unknown"
|
||||
)
|
||||
|
||||
// Add formats using the default formats for its operands and adds an entry for a .profile.d file. Spaces are added
|
||||
// between operands when neither is a string.
|
||||
func (p Profile) Add(name string, a ...interface{}) {
|
||||
p[name] = fmt.Sprint(a...)
|
||||
// Exec represents the exec.d layer location
|
||||
type Exec struct {
|
||||
// Path is the path to the exec.d directory.
|
||||
Path string
|
||||
}
|
||||
|
||||
// Addf formats according to a format specifier and adds an entry for a .profile.d file.
|
||||
func (p Profile) Addf(name string, format string, a ...interface{}) {
|
||||
p[name] = fmt.Sprintf(format, a...)
|
||||
// FilePath returns the fully qualified file path for a given name.
|
||||
func (e Exec) FilePath(name string) string {
|
||||
return filepath.Join(e.Path, name)
|
||||
}
|
||||
|
||||
// ProcessFilePath returns the fully qualified file path for a given name.
|
||||
func (e Exec) ProcessFilePath(processType string, name string) string {
|
||||
return filepath.Join(e.Path, processType, name)
|
||||
}
|
||||
|
||||
// BOMFormat indicates the format of the SBOM entry
|
||||
type SBOMFormat int
|
||||
|
||||
const (
|
||||
CycloneDXJSON SBOMFormat = iota
|
||||
SPDXJSON
|
||||
SyftJSON
|
||||
UnknownFormat
|
||||
)
|
||||
|
||||
func (b SBOMFormat) String() string {
|
||||
return []string{
|
||||
BOMFormatCycloneDXExtension,
|
||||
BOMFormatSPDXExtension,
|
||||
BOMFormatSyftExtension,
|
||||
BOMUnknown}[b]
|
||||
}
|
||||
|
||||
func (b SBOMFormat) MediaType() string {
|
||||
return []string{
|
||||
BOMMediaTypeCycloneDX,
|
||||
BOMMediaTypeSPDX,
|
||||
BOMMediaTypeSyft,
|
||||
BOMUnknown}[b]
|
||||
}
|
||||
|
||||
func SBOMFormatFromString(from string) (SBOMFormat, error) {
|
||||
switch from {
|
||||
case CycloneDXJSON.String():
|
||||
return CycloneDXJSON, nil
|
||||
case SPDXJSON.String():
|
||||
return SPDXJSON, nil
|
||||
case SyftJSON.String():
|
||||
return SyftJSON, nil
|
||||
}
|
||||
|
||||
return UnknownFormat, fmt.Errorf("unable to translate from %s to SBOMFormat", from)
|
||||
}
|
||||
|
||||
// Contribute represents a layer managed by the buildpack.
|
||||
type Layer struct {
|
||||
|
||||
// Build indicates that a layer should be used for builds.
|
||||
Build bool `toml:"build"`
|
||||
|
||||
// Cache indicates that a layer should be cached.
|
||||
Cache bool `toml:"cache"`
|
||||
|
||||
// Launch indicates that a layer should be used for launch.
|
||||
Launch bool `toml:"launch"`
|
||||
// LayerTypes indicates the type of layer
|
||||
LayerTypes `toml:"types"`
|
||||
|
||||
// Metadata is the metadata associated with the layer.
|
||||
Metadata map[string]interface{} `toml:"metadata"`
|
||||
|
@ -68,25 +112,55 @@ type Layer struct {
|
|||
// SharedEnvironment are the environment variables set at both build and launch times.
|
||||
SharedEnvironment Environment `toml:"-"`
|
||||
|
||||
// Profile is the profile.d scripts set in the layer.
|
||||
Profile Profile `toml:"-"`
|
||||
// Exec is the exec.d executables set in the layer.
|
||||
Exec Exec `toml:"-"`
|
||||
}
|
||||
|
||||
//go:generate mockery -name LayerContributor -case=underscore
|
||||
func (l Layer) Reset() (Layer, error) {
|
||||
l.LayerTypes = LayerTypes{
|
||||
Build: false,
|
||||
Launch: false,
|
||||
Cache: false,
|
||||
}
|
||||
|
||||
// LayerContributor is an interface for types that create layers.
|
||||
type LayerContributor interface {
|
||||
l.SharedEnvironment = Environment{}
|
||||
l.BuildEnvironment = Environment{}
|
||||
l.LaunchEnvironment = Environment{}
|
||||
l.Metadata = nil
|
||||
|
||||
// Contribute accepts a layer and transforms it, returning a layer.
|
||||
Contribute(layer Layer) (Layer, error)
|
||||
err := os.RemoveAll(l.Path)
|
||||
if err != nil {
|
||||
return Layer{}, fmt.Errorf("error could not remove file: %s", err)
|
||||
}
|
||||
|
||||
// Name is the name of the layer.
|
||||
Name() string
|
||||
err = os.MkdirAll(l.Path, os.ModePerm)
|
||||
if err != nil {
|
||||
return Layer{}, fmt.Errorf("error could not create directory: %s", err)
|
||||
}
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
// SBOMPath returns the path to the layer specific SBOM File
|
||||
func (l Layer) SBOMPath(bt SBOMFormat) string {
|
||||
return filepath.Join(filepath.Dir(l.Path), fmt.Sprintf("%s.sbom.%s", l.Name, bt))
|
||||
}
|
||||
|
||||
// LayerTypes describes which types apply to a given layer. A layer may have any combination of Launch, Build, and
|
||||
// Cache types.
|
||||
type LayerTypes struct {
|
||||
// Build indicates that a layer should be used for builds.
|
||||
Build bool `toml:"build"`
|
||||
|
||||
// Cache indicates that a layer should be cached.
|
||||
Cache bool `toml:"cache"`
|
||||
|
||||
// Launch indicates that a layer should be used for launch.
|
||||
Launch bool `toml:"launch"`
|
||||
}
|
||||
|
||||
// Layers represents the layers part of the specification.
|
||||
type Layers struct {
|
||||
|
||||
// Path is the layers filesystem location.
|
||||
Path string
|
||||
}
|
||||
|
@ -99,7 +173,7 @@ func (l *Layers) Layer(name string) (Layer, error) {
|
|||
BuildEnvironment: Environment{},
|
||||
LaunchEnvironment: Environment{},
|
||||
SharedEnvironment: Environment{},
|
||||
Profile: Profile{},
|
||||
Exec: Exec{Path: filepath.Join(l.Path, name, "exec.d")},
|
||||
}
|
||||
|
||||
f := filepath.Join(l.Path, fmt.Sprintf("%s.toml", name))
|
||||
|
@ -109,3 +183,13 @@ func (l *Layers) Layer(name string) (Layer, error) {
|
|||
|
||||
return layer, nil
|
||||
}
|
||||
|
||||
// BOMBuildPath returns the full path to the build SBoM file for the buildpack
|
||||
func (l Layers) BuildSBOMPath(bt SBOMFormat) string {
|
||||
return filepath.Join(l.Path, fmt.Sprintf("build.sbom.%s", bt))
|
||||
}
|
||||
|
||||
// BOMLaunchPath returns the full path to the launch SBoM file for the buildpack
|
||||
func (l Layers) LaunchSBOMPath(bt SBOMFormat) string {
|
||||
return filepath.Join(l.Path, fmt.Sprintf("launch.sbom.%s", bt))
|
||||
}
|
||||
|
|
239
layer_test.go
239
layer_test.go
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2018-2020 the original author or authors.
|
||||
* Copyright 2018-2024 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -17,14 +17,14 @@
|
|||
package libcnb_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/buildpacks/libcnb"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/sclevine/spec"
|
||||
|
||||
"github.com/buildpacks/libcnb/v2"
|
||||
)
|
||||
|
||||
func testLayer(t *testing.T, context spec.G, it spec.S) {
|
||||
|
@ -35,29 +35,169 @@ func testLayer(t *testing.T, context spec.G, it spec.S) {
|
|||
path string
|
||||
)
|
||||
|
||||
context("Profile", func() {
|
||||
var profile libcnb.Profile
|
||||
context("Exec", func() {
|
||||
var exec libcnb.Exec
|
||||
|
||||
it.Before(func() {
|
||||
profile = libcnb.Profile{}
|
||||
exec = libcnb.Exec{Path: "test-path"}
|
||||
})
|
||||
|
||||
it("adds content", func() {
|
||||
profile.Add("test-name", "test-value")
|
||||
Expect(profile).To(Equal(libcnb.Profile{"test-name": "test-value"}))
|
||||
it("returns filename", func() {
|
||||
Expect(exec.FilePath("test-name")).To(Equal(filepath.Join("test-path", "test-name")))
|
||||
})
|
||||
|
||||
it("adds formatted content", func() {
|
||||
profile.Addf("test-name", "test-%s", "value")
|
||||
Expect(profile).To(Equal(libcnb.Profile{"test-name": "test-value"}))
|
||||
it("returns process-specific filename", func() {
|
||||
Expect(exec.ProcessFilePath("test-process", "test-name")).
|
||||
To(Equal(filepath.Join("test-path", "test-process", "test-name")))
|
||||
})
|
||||
})
|
||||
|
||||
context("Reset", func() {
|
||||
var layer libcnb.Layer
|
||||
|
||||
it.Before(func() {
|
||||
layers = libcnb.Layers{Path: t.TempDir()}
|
||||
})
|
||||
|
||||
context("when there is no previous build", func() {
|
||||
it.Before(func() {
|
||||
layer = libcnb.Layer{
|
||||
Name: "test-name",
|
||||
Path: filepath.Join(layers.Path, "test-name"),
|
||||
LayerTypes: libcnb.LayerTypes{
|
||||
Launch: true,
|
||||
Build: true,
|
||||
Cache: true,
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
it("initializes an empty layer", func() {
|
||||
var err error
|
||||
layer, err = layer.Reset()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(layer).To(Equal(libcnb.Layer{
|
||||
Name: "test-name",
|
||||
Path: filepath.Join(layers.Path, "test-name"),
|
||||
LayerTypes: libcnb.LayerTypes{
|
||||
Launch: false,
|
||||
Build: false,
|
||||
Cache: false,
|
||||
},
|
||||
SharedEnvironment: libcnb.Environment{},
|
||||
BuildEnvironment: libcnb.Environment{},
|
||||
LaunchEnvironment: libcnb.Environment{},
|
||||
}))
|
||||
|
||||
Expect(filepath.Join(layers.Path, "test-name")).To(BeADirectory())
|
||||
})
|
||||
})
|
||||
|
||||
context("when cache is retrieved from previous build", func() {
|
||||
it.Before(func() {
|
||||
sharedEnvDir := filepath.Join(layers.Path, "test-name", "env")
|
||||
Expect(os.MkdirAll(sharedEnvDir, os.ModePerm)).To(Succeed())
|
||||
|
||||
err := os.WriteFile(filepath.Join(sharedEnvDir, "OVERRIDE_VAR.override"), []byte("override-value"), 0600)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
buildEnvDir := filepath.Join(layers.Path, "test-name", "env.build")
|
||||
Expect(os.MkdirAll(buildEnvDir, os.ModePerm)).To(Succeed())
|
||||
|
||||
err = os.WriteFile(filepath.Join(buildEnvDir, "DEFAULT_VAR.default"), []byte("default-value"), 0600)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = os.WriteFile(filepath.Join(buildEnvDir, "INVALID_VAR.invalid"), []byte("invalid-value"), 0600)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
launchEnvDir := filepath.Join(layers.Path, "test-name", "env.launch")
|
||||
Expect(os.MkdirAll(launchEnvDir, os.ModePerm)).To(Succeed())
|
||||
|
||||
err = os.WriteFile(filepath.Join(launchEnvDir, "APPEND_VAR.append"), []byte("append-value"), 0600)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
err = os.WriteFile(filepath.Join(launchEnvDir, "APPEND_VAR.delim"), []byte("!"), 0600)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
layer = libcnb.Layer{
|
||||
Name: "test-name",
|
||||
Path: filepath.Join(layers.Path, "test-name"),
|
||||
LayerTypes: libcnb.LayerTypes{
|
||||
Launch: true,
|
||||
Build: true,
|
||||
Cache: true,
|
||||
},
|
||||
SharedEnvironment: libcnb.Environment{
|
||||
"OVERRIDE_VAR.override": "override-value",
|
||||
},
|
||||
BuildEnvironment: libcnb.Environment{
|
||||
"DEFAULT_VAR.default": "default-value",
|
||||
},
|
||||
LaunchEnvironment: libcnb.Environment{
|
||||
"APPEND_VAR.append": "append-value",
|
||||
"APPEND_VAR.delim": "!",
|
||||
},
|
||||
Metadata: map[string]interface{}{
|
||||
"some-key": "some-value",
|
||||
},
|
||||
}
|
||||
})
|
||||
|
||||
context("when Reset is called on a layer", func() {
|
||||
it("resets all of the layer data and clears the directory", func() {
|
||||
layer, err := layer.Reset()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(layer).To(Equal(libcnb.Layer{
|
||||
Name: "test-name",
|
||||
Path: filepath.Join(layers.Path, "test-name"),
|
||||
LayerTypes: libcnb.LayerTypes{
|
||||
Launch: false,
|
||||
Build: false,
|
||||
Cache: false,
|
||||
},
|
||||
SharedEnvironment: libcnb.Environment{},
|
||||
BuildEnvironment: libcnb.Environment{},
|
||||
LaunchEnvironment: libcnb.Environment{},
|
||||
}))
|
||||
|
||||
Expect(filepath.Join(layers.Path, "test-name")).To(BeADirectory())
|
||||
|
||||
files, err := filepath.Glob(filepath.Join(layers.Path, "test-name", "*"))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(files).To(BeEmpty())
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context("could not remove files in layer", func() {
|
||||
it.Before(func() {
|
||||
Expect(os.Chmod(layers.Path, 0000)).To(Succeed())
|
||||
})
|
||||
|
||||
it.After(func() {
|
||||
Expect(os.Chmod(layers.Path, 0777)).To(Succeed())
|
||||
})
|
||||
|
||||
it("return an error", func() {
|
||||
layer := libcnb.Layer{
|
||||
Name: "some-layer",
|
||||
Path: filepath.Join(layers.Path, "some-layer"),
|
||||
}
|
||||
|
||||
_, err := layer.Reset()
|
||||
Expect(err).To(MatchError(ContainSubstring("error could not remove file: ")))
|
||||
Expect(err).To(MatchError(ContainSubstring("permission denied")))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
context("Layers", func() {
|
||||
|
||||
it.Before(func() {
|
||||
var err error
|
||||
path, err = ioutil.TempDir("", "layers")
|
||||
path, err = os.MkdirTemp("", "layers")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
layers = libcnb.Layers{Path: path}
|
||||
|
@ -71,32 +211,91 @@ func testLayer(t *testing.T, context spec.G, it spec.S) {
|
|||
l, err := layers.Layer("test-name")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(l.Build).To(BeFalse())
|
||||
Expect(l.Cache).To(BeFalse())
|
||||
Expect(l.Launch).To(BeFalse())
|
||||
Expect(l.LayerTypes.Build).To(BeFalse())
|
||||
Expect(l.LayerTypes.Cache).To(BeFalse())
|
||||
Expect(l.LayerTypes.Launch).To(BeFalse())
|
||||
Expect(l.Metadata).To(BeNil())
|
||||
Expect(l.Name).To(Equal("test-name"))
|
||||
Expect(l.Path).To(Equal(filepath.Join(path, "test-name")))
|
||||
Expect(l.BuildEnvironment).To(Equal(libcnb.Environment{}))
|
||||
Expect(l.LaunchEnvironment).To(Equal(libcnb.Environment{}))
|
||||
Expect(l.SharedEnvironment).To(Equal(libcnb.Environment{}))
|
||||
Expect(l.Profile).To(Equal(libcnb.Profile{}))
|
||||
})
|
||||
|
||||
it("generates SBOM paths", func() {
|
||||
l, err := layers.Layer("test-name")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(l.Path).To(Equal(filepath.Join(path, "test-name")))
|
||||
Expect(layers.BuildSBOMPath(libcnb.CycloneDXJSON)).To(Equal(filepath.Join(path, "build.sbom.cdx.json")))
|
||||
Expect(layers.BuildSBOMPath(libcnb.SPDXJSON)).To(Equal(filepath.Join(path, "build.sbom.spdx.json")))
|
||||
Expect(layers.BuildSBOMPath(libcnb.SyftJSON)).To(Equal(filepath.Join(path, "build.sbom.syft.json")))
|
||||
Expect(layers.LaunchSBOMPath(libcnb.SyftJSON)).To(Equal(filepath.Join(path, "launch.sbom.syft.json")))
|
||||
Expect(l.SBOMPath(libcnb.SyftJSON)).To(Equal(filepath.Join(path, "test-name.sbom.syft.json")))
|
||||
})
|
||||
|
||||
it("maps from string to SBOM Format", func() {
|
||||
fmt, err := libcnb.SBOMFormatFromString("cdx.json")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(fmt).To(Equal(libcnb.CycloneDXJSON))
|
||||
|
||||
fmt, err = libcnb.SBOMFormatFromString("spdx.json")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(fmt).To(Equal(libcnb.SPDXJSON))
|
||||
|
||||
fmt, err = libcnb.SBOMFormatFromString("syft.json")
|
||||
Expect(err).ToNot(HaveOccurred())
|
||||
Expect(fmt).To(Equal(libcnb.SyftJSON))
|
||||
|
||||
fmt, err = libcnb.SBOMFormatFromString("foobar.json")
|
||||
Expect(err).To(MatchError("unable to translate from foobar.json to SBOMFormat"))
|
||||
Expect(fmt).To(Equal(libcnb.UnknownFormat))
|
||||
})
|
||||
|
||||
it("reads existing metadata", func() {
|
||||
Expect(ioutil.WriteFile(
|
||||
Expect(os.WriteFile(
|
||||
filepath.Join(path, "test-name.toml"),
|
||||
[]byte(`
|
||||
[types]
|
||||
launch = true
|
||||
build = false
|
||||
|
||||
[metadata]
|
||||
test-key = "test-value"
|
||||
`),
|
||||
0644),
|
||||
0600),
|
||||
).To(Succeed())
|
||||
|
||||
l, err := layers.Layer("test-name")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(l.Metadata).To(Equal(map[string]interface{}{"test-key": "test-value"}))
|
||||
Expect(l.Launch).To(BeTrue())
|
||||
Expect(l.Build).To(BeFalse())
|
||||
})
|
||||
|
||||
it("reads existing metadata with launch, build and cache all false", func() {
|
||||
Expect(os.WriteFile(
|
||||
filepath.Join(path, "test-name.toml"),
|
||||
[]byte(`
|
||||
[types]
|
||||
launch = false
|
||||
build = false
|
||||
cache = false
|
||||
|
||||
[metadata]
|
||||
test-key = "test-value"
|
||||
`),
|
||||
0600),
|
||||
).To(Succeed())
|
||||
|
||||
l, err := layers.Layer("test-name")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(l.Metadata).To(Equal(map[string]interface{}{"test-key": "test-value"}))
|
||||
Expect(l.Launch).To(BeFalse())
|
||||
Expect(l.Build).To(BeFalse())
|
||||
Expect(l.Cache).To(BeFalse())
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
|
@ -0,0 +1,59 @@
|
|||
/*
|
||||
* Copyright 2018-2022 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package log
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
//go:generate mockery --name DirectoryContentFormatter --case=underscore
|
||||
|
||||
// DirectoryContentFormatter allows customization of logged directory output
|
||||
//
|
||||
// When libcnb logs the contents of a directory, each item in the directory
|
||||
// is passed through a DirectoryContentFormatter.
|
||||
//
|
||||
// DirectoryContentsWriter implements this workflow:
|
||||
// - call RootPath(string) with the root path that's being walked
|
||||
// - call Title(string) with the given title, the output is logged
|
||||
// - for each file in the directory:
|
||||
// - call File(string, os.FileInfo), the output is logged
|
||||
//
|
||||
// # A default implementation is provided that returns a formatter applies no formatting
|
||||
//
|
||||
// The returned formatter operates as such:
|
||||
//
|
||||
// Title -> returns string followed by `:\n`
|
||||
// File -> returns file name relative to the root followed by `\n`
|
||||
//
|
||||
// A buildpack author could provide their own implementation through
|
||||
// WithDirectoryContentFormatter when calling Detect or Build.
|
||||
//
|
||||
// A custom implementation might log in color or might log additional
|
||||
// information about each file, like permissions. The implementation can
|
||||
// also control line endings to force all of the files to be logged on a
|
||||
// single line, or as multiple lines.
|
||||
type DirectoryContentFormatter interface {
|
||||
// File takes the full path and os.FileInfo and returns a display string
|
||||
File(path string, info os.FileInfo) (string, error)
|
||||
|
||||
// RootPath provides the root path being iterated
|
||||
RootPath(path string)
|
||||
|
||||
// Title provides a plain string title which can be embellished
|
||||
Title(title string) string
|
||||
}
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package poet_test
|
||||
package log_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
@ -24,7 +24,7 @@ import (
|
|||
)
|
||||
|
||||
func TestUnit(t *testing.T) {
|
||||
suite := spec.New("libcnb/poet", spec.Report(report.Terminal{}))
|
||||
suite("Logger", testLogger)
|
||||
suite := spec.New("libcnb/log", spec.Report(report.Terminal{}))
|
||||
suite("PlainLogger", testLogger)
|
||||
suite.Run(t)
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
/*
|
||||
* Copyright 2018-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
//go:generate mockery --name Logger --case=underscore
|
||||
|
||||
// Logger is the interface implement by a type that wishes to write log messages generated by libcnb
|
||||
type Logger interface {
|
||||
// Debug formats using the default formats for its operands
|
||||
Debug(a ...interface{})
|
||||
|
||||
// Debugf formats according to a format specifier
|
||||
Debugf(format string, a ...interface{})
|
||||
|
||||
// DebugWriter returns the configured debug writer
|
||||
DebugWriter() io.Writer
|
||||
|
||||
// IsDebugEnabled indicates whether debug logging is enabled
|
||||
IsDebugEnabled() bool
|
||||
}
|
||||
|
||||
// PlainLogger implements Logger and logs messages to a writer.
|
||||
type PlainLogger struct {
|
||||
debug io.Writer
|
||||
}
|
||||
|
||||
// New creates a new instance of PlainLogger. It configures debug logging if $BP_DEBUG or $BP_LOG_LEVEL are set.
|
||||
func New(debug io.Writer) PlainLogger {
|
||||
if strings.ToLower(os.Getenv("BP_LOG_LEVEL")) == "debug" || os.Getenv("BP_DEBUG") != "" {
|
||||
return PlainLogger{debug: debug}
|
||||
}
|
||||
|
||||
return PlainLogger{}
|
||||
}
|
||||
|
||||
// NewDiscard creates a new instance of PlainLogger that discards all log messages. Useful in testing.
|
||||
func NewDiscard() PlainLogger {
|
||||
return PlainLogger{debug: io.Discard}
|
||||
}
|
||||
|
||||
// Debug formats using the default formats for its operands and writes to the configured debug writer. Spaces are added
|
||||
// between operands when neither is a string.
|
||||
func (l PlainLogger) Debug(a ...interface{}) {
|
||||
if !l.IsDebugEnabled() {
|
||||
return
|
||||
}
|
||||
|
||||
s := fmt.Sprint(a...)
|
||||
|
||||
if !strings.HasSuffix(s, "\n") {
|
||||
s += "\n"
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprint(l.debug, s)
|
||||
}
|
||||
|
||||
// Debugf formats according to a format specifier and writes to the configured debug writer.
|
||||
func (l PlainLogger) Debugf(format string, a ...interface{}) {
|
||||
if !l.IsDebugEnabled() {
|
||||
return
|
||||
}
|
||||
|
||||
if !strings.HasSuffix(format, "\n") {
|
||||
format += "\n"
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintf(l.debug, format, a...)
|
||||
}
|
||||
|
||||
// DebugWriter returns the configured debug writer.
|
||||
func (l PlainLogger) DebugWriter() io.Writer {
|
||||
if l.IsDebugEnabled() {
|
||||
return l.debug
|
||||
}
|
||||
return io.Discard
|
||||
}
|
||||
|
||||
// IsDebugEnabled indicates whether debug logging is enabled.
|
||||
func (l PlainLogger) IsDebugEnabled() bool {
|
||||
return l.debug != nil
|
||||
}
|
|
@ -14,16 +14,18 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package poet_test
|
||||
package log_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/buildpacks/libcnb/poet"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/sclevine/spec"
|
||||
|
||||
"github.com/buildpacks/libcnb/v2/log"
|
||||
)
|
||||
|
||||
func testLogger(t *testing.T, context spec.G, it spec.S) {
|
||||
|
@ -31,7 +33,7 @@ func testLogger(t *testing.T, context spec.G, it spec.S) {
|
|||
Expect = NewWithT(t).Expect
|
||||
|
||||
b *bytes.Buffer
|
||||
l poet.Logger
|
||||
l log.PlainLogger
|
||||
)
|
||||
|
||||
it.Before(func() {
|
||||
|
@ -40,18 +42,26 @@ func testLogger(t *testing.T, context spec.G, it spec.S) {
|
|||
|
||||
context("without BP_DEBUG", func() {
|
||||
it.Before(func() {
|
||||
l = poet.NewLogger(b)
|
||||
l = log.New(b)
|
||||
})
|
||||
|
||||
it("does not configure debug", func() {
|
||||
Expect(l.IsDebugEnabled()).To(BeFalse())
|
||||
})
|
||||
|
||||
it("does not return nil debug writer", func() {
|
||||
Expect(l.DebugWriter()).To(Not(BeNil()))
|
||||
})
|
||||
|
||||
it("does not return non-discard writer", func() {
|
||||
Expect(l.DebugWriter()).To(Equal(io.Discard))
|
||||
})
|
||||
})
|
||||
|
||||
context("with BP_DEBUG", func() {
|
||||
it.Before(func() {
|
||||
Expect(os.Setenv("BP_DEBUG", "")).To(Succeed())
|
||||
l = poet.NewLogger(b)
|
||||
l = log.New(b)
|
||||
})
|
||||
|
||||
it.After(func() {
|
||||
|
@ -59,55 +69,33 @@ func testLogger(t *testing.T, context spec.G, it spec.S) {
|
|||
})
|
||||
|
||||
it("configures debug", func() {
|
||||
Expect(l.IsDebugEnabled()).To(BeTrue())
|
||||
Expect(l.IsDebugEnabled()).To(BeFalse())
|
||||
})
|
||||
})
|
||||
|
||||
context("with debug disabled", func() {
|
||||
context("with BP_LOG_LEVEL set to DEBUG", func() {
|
||||
it.Before(func() {
|
||||
l = poet.NewLoggerWithOptions(b)
|
||||
Expect(os.Setenv("BP_LOG_LEVEL", "DEBUG")).To(Succeed())
|
||||
l = log.New(b)
|
||||
})
|
||||
|
||||
it("does not write debug log", func() {
|
||||
l.Debug("test-message")
|
||||
Expect(b.String()).To(Equal(""))
|
||||
it.After(func() {
|
||||
Expect(os.Unsetenv("BP_LOG_LEVEL")).To(Succeed())
|
||||
})
|
||||
|
||||
it("does not write debugf log", func() {
|
||||
l.Debugf("test-%s", "message")
|
||||
Expect(b.String()).To(Equal(""))
|
||||
})
|
||||
|
||||
it("does not return debug writer", func() {
|
||||
Expect(l.DebugWriter()).To(BeNil())
|
||||
})
|
||||
|
||||
it("indicates that debug is not enabled", func() {
|
||||
Expect(l.IsDebugEnabled()).To(BeFalse())
|
||||
})
|
||||
|
||||
it("writes info log", func() {
|
||||
l.Info("test-message")
|
||||
Expect(b.String()).To(Equal("test-message\n"))
|
||||
})
|
||||
|
||||
it("writes infof log", func() {
|
||||
l.Infof("test-%s", "message")
|
||||
Expect(b.String()).To(Equal("test-message\n"))
|
||||
})
|
||||
|
||||
it("returns info writer", func() {
|
||||
Expect(l.InfoWriter()).NotTo(BeNil())
|
||||
})
|
||||
|
||||
it("indicates that info is enabled", func() {
|
||||
Expect(l.IsInfoEnabled()).To(BeTrue())
|
||||
it("configures debug", func() {
|
||||
Expect(l.IsDebugEnabled()).To(BeTrue())
|
||||
})
|
||||
})
|
||||
|
||||
context("with debug enabled", func() {
|
||||
it.Before(func() {
|
||||
l = poet.NewLoggerWithOptions(b, poet.WithDebug(b))
|
||||
Expect(os.Setenv("BP_LOG_LEVEL", "DEBUG")).To(Succeed())
|
||||
l = log.New(b)
|
||||
})
|
||||
|
||||
it.After(func() {
|
||||
Expect(os.Unsetenv("BP_LOG_LEVEL")).To(Succeed())
|
||||
})
|
||||
|
||||
it("writes debug log", func() {
|
||||
|
@ -120,30 +108,14 @@ func testLogger(t *testing.T, context spec.G, it spec.S) {
|
|||
Expect(b.String()).To(Equal("test-message\n"))
|
||||
})
|
||||
|
||||
it("returns debug writer", func() {
|
||||
Expect(l.DebugWriter()).NotTo(BeNil())
|
||||
it("writes debug directly", func() {
|
||||
_, err := l.DebugWriter().Write([]byte("test-message\n"))
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(b.String()).To(Equal("test-message\n"))
|
||||
})
|
||||
|
||||
it("indicates that debug is enabled", func() {
|
||||
Expect(l.IsDebugEnabled()).To(BeTrue())
|
||||
})
|
||||
|
||||
it("writes info log", func() {
|
||||
l.Info("test-message")
|
||||
Expect(b.String()).To(Equal("test-message\n"))
|
||||
})
|
||||
|
||||
it("writes infof log", func() {
|
||||
l.Infof("test-%s", "message")
|
||||
Expect(b.String()).To(Equal("test-message\n"))
|
||||
})
|
||||
|
||||
it("returns info writer", func() {
|
||||
Expect(l.InfoWriter()).NotTo(BeNil())
|
||||
})
|
||||
|
||||
it("indicates that info is enabled", func() {
|
||||
Expect(l.IsInfoEnabled()).To(BeTrue())
|
||||
})
|
||||
})
|
||||
}
|
|
@ -0,0 +1,79 @@
|
|||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
fs "io/fs"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// DirectoryContentFormatter is an autogenerated mock type for the DirectoryContentFormatter type
|
||||
type DirectoryContentFormatter struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// File provides a mock function with given fields: path, info
|
||||
func (_m *DirectoryContentFormatter) File(path string, info fs.FileInfo) (string, error) {
|
||||
ret := _m.Called(path, info)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for File")
|
||||
}
|
||||
|
||||
var r0 string
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func(string, fs.FileInfo) (string, error)); ok {
|
||||
return rf(path, info)
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func(string, fs.FileInfo) string); ok {
|
||||
r0 = rf(path, info)
|
||||
} else {
|
||||
r0 = ret.Get(0).(string)
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func(string, fs.FileInfo) error); ok {
|
||||
r1 = rf(path, info)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// RootPath provides a mock function with given fields: path
|
||||
func (_m *DirectoryContentFormatter) RootPath(path string) {
|
||||
_m.Called(path)
|
||||
}
|
||||
|
||||
// Title provides a mock function with given fields: title
|
||||
func (_m *DirectoryContentFormatter) Title(title string) string {
|
||||
ret := _m.Called(title)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Title")
|
||||
}
|
||||
|
||||
var r0 string
|
||||
if rf, ok := ret.Get(0).(func(string) string); ok {
|
||||
r0 = rf(title)
|
||||
} else {
|
||||
r0 = ret.Get(0).(string)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// NewDirectoryContentFormatter creates a new instance of DirectoryContentFormatter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewDirectoryContentFormatter(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *DirectoryContentFormatter {
|
||||
mock := &DirectoryContentFormatter{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
|
@ -0,0 +1,81 @@
|
|||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
io "io"
|
||||
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// Logger is an autogenerated mock type for the Logger type
|
||||
type Logger struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Debug provides a mock function with given fields: a
|
||||
func (_m *Logger) Debug(a ...interface{}) {
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, a...)
|
||||
_m.Called(_ca...)
|
||||
}
|
||||
|
||||
// DebugWriter provides a mock function with given fields:
|
||||
func (_m *Logger) DebugWriter() io.Writer {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for DebugWriter")
|
||||
}
|
||||
|
||||
var r0 io.Writer
|
||||
if rf, ok := ret.Get(0).(func() io.Writer); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(io.Writer)
|
||||
}
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// Debugf provides a mock function with given fields: format, a
|
||||
func (_m *Logger) Debugf(format string, a ...interface{}) {
|
||||
var _ca []interface{}
|
||||
_ca = append(_ca, format)
|
||||
_ca = append(_ca, a...)
|
||||
_m.Called(_ca...)
|
||||
}
|
||||
|
||||
// IsDebugEnabled provides a mock function with given fields:
|
||||
func (_m *Logger) IsDebugEnabled() bool {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for IsDebugEnabled")
|
||||
}
|
||||
|
||||
var r0 bool
|
||||
if rf, ok := ret.Get(0).(func() bool); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Get(0).(bool)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// NewLogger creates a new instance of Logger. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewLogger(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *Logger {
|
||||
mock := &Logger{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright 2018-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package libcnb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func main(detect DetectFunc, build BuildFunc, generate GenerateFunc, options ...Option) {
|
||||
config := NewConfig(options...)
|
||||
|
||||
if len(config.arguments) == 0 {
|
||||
config.exitHandler.Error(fmt.Errorf("expected command name"))
|
||||
return
|
||||
}
|
||||
|
||||
config.extension = build == nil && generate != nil
|
||||
|
||||
switch c := filepath.Base(config.arguments[0]); c {
|
||||
case "build":
|
||||
Build(build, config)
|
||||
case "detect":
|
||||
Detect(detect, config)
|
||||
case "generate":
|
||||
Generate(generate, config)
|
||||
default:
|
||||
config.exitHandler.Error(fmt.Errorf("unsupported command %s", c))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// BuildpackMain is called by the main function of a buildpack, encapsulating both detection and build in the same binary.
|
||||
func BuildpackMain(detect DetectFunc, build BuildFunc, options ...Option) {
|
||||
main(detect, build, nil, options...)
|
||||
}
|
||||
|
||||
// ExtensionMain is called by the main function of a extension, encapsulating both detection and generation in the same binary.
|
||||
func ExtensionMain(detect DetectFunc, generate GenerateFunc, options ...Option) {
|
||||
main(detect, nil, generate, options...)
|
||||
}
|
|
@ -0,0 +1,276 @@
|
|||
/*
|
||||
* Copyright 2018-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package libcnb_test
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/sclevine/spec"
|
||||
"github.com/stretchr/testify/mock"
|
||||
|
||||
"github.com/buildpacks/libcnb/v2"
|
||||
"github.com/buildpacks/libcnb/v2/log"
|
||||
"github.com/buildpacks/libcnb/v2/mocks"
|
||||
)
|
||||
|
||||
func testMain(t *testing.T, _ spec.G, it spec.S) {
|
||||
var (
|
||||
Expect = NewWithT(t).Expect
|
||||
|
||||
applicationPath string
|
||||
buildFunc libcnb.BuildFunc
|
||||
buildpackPath string
|
||||
buildpackPlanPath string
|
||||
detectFunc libcnb.DetectFunc
|
||||
environmentWriter *mocks.EnvironmentWriter
|
||||
exitHandler *mocks.ExitHandler
|
||||
generateFunc libcnb.GenerateFunc
|
||||
layersPath string
|
||||
platformPath string
|
||||
tomlWriter *mocks.TOMLWriter
|
||||
|
||||
workingDir string
|
||||
)
|
||||
|
||||
it.Before(func() {
|
||||
var err error
|
||||
|
||||
applicationPath, err = os.MkdirTemp("", "main-application-path")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
applicationPath, err = filepath.EvalSymlinks(applicationPath)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
buildFunc = func(libcnb.BuildContext) (libcnb.BuildResult, error) {
|
||||
return libcnb.NewBuildResult(), nil
|
||||
}
|
||||
|
||||
buildpackPath, err = os.MkdirTemp("", "main-buildpack-path")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(os.Setenv("CNB_BUILDPACK_DIR", buildpackPath)).To(Succeed())
|
||||
|
||||
Expect(os.WriteFile(filepath.Join(buildpackPath, "buildpack.toml"),
|
||||
[]byte(`
|
||||
api = "0.8"
|
||||
|
||||
[buildpack]
|
||||
id = "test-id"
|
||||
name = "test-name"
|
||||
version = "1.1.1"
|
||||
clear-env = true
|
||||
|
||||
[[order]]
|
||||
[[order.group]]
|
||||
id = "test-id"
|
||||
version = "2.2.2"
|
||||
optional = true
|
||||
|
||||
[[stacks]]
|
||||
id = "test-id"
|
||||
|
||||
[metadata]
|
||||
test-key = "test-value"
|
||||
`),
|
||||
0600),
|
||||
).To(Succeed())
|
||||
|
||||
f, err := os.CreateTemp("", "main-buildpackplan-path")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(f.Close()).NotTo(HaveOccurred())
|
||||
buildpackPlanPath = f.Name()
|
||||
|
||||
Expect(os.WriteFile(buildpackPlanPath,
|
||||
[]byte(`
|
||||
[[entries]]
|
||||
name = "test-name"
|
||||
version = "test-version"
|
||||
|
||||
[entries.metadata]
|
||||
test-key = "test-value"
|
||||
`),
|
||||
0600),
|
||||
).To(Succeed())
|
||||
|
||||
detectFunc = func(libcnb.DetectContext) (libcnb.DetectResult, error) {
|
||||
return libcnb.DetectResult{}, nil
|
||||
}
|
||||
|
||||
generateFunc = func(libcnb.GenerateContext) (libcnb.GenerateResult, error) {
|
||||
return libcnb.GenerateResult{}, nil
|
||||
}
|
||||
|
||||
environmentWriter = &mocks.EnvironmentWriter{}
|
||||
environmentWriter.On("Write", mock.Anything, mock.Anything).Return(nil)
|
||||
|
||||
exitHandler = &mocks.ExitHandler{}
|
||||
exitHandler.On("Error", mock.Anything)
|
||||
exitHandler.On("Pass", mock.Anything)
|
||||
exitHandler.On("Fail", mock.Anything)
|
||||
|
||||
layersPath, err = os.MkdirTemp("", "main-layers-path")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(os.WriteFile(filepath.Join(layersPath, "store.toml"),
|
||||
[]byte(`
|
||||
[metadata]
|
||||
test-key = "test-value"
|
||||
`),
|
||||
0600),
|
||||
).To(Succeed())
|
||||
|
||||
platformPath, err = os.MkdirTemp("", "main-platform-path")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(os.MkdirAll(filepath.Join(platformPath, "bindings", "alpha", "metadata"), 0755)).To(Succeed())
|
||||
Expect(os.WriteFile(
|
||||
filepath.Join(platformPath, "bindings", "alpha", "metadata", "test-metadata-key"),
|
||||
[]byte("test-metadata-value"),
|
||||
0600,
|
||||
)).To(Succeed())
|
||||
Expect(os.MkdirAll(filepath.Join(platformPath, "bindings", "alpha", "secret"), 0755)).To(Succeed())
|
||||
Expect(os.WriteFile(
|
||||
filepath.Join(platformPath, "bindings", "alpha", "secret", "test-secret-key"),
|
||||
[]byte("test-secret-value"),
|
||||
0600,
|
||||
)).To(Succeed())
|
||||
|
||||
Expect(os.MkdirAll(filepath.Join(platformPath, "env"), 0755)).To(Succeed())
|
||||
Expect(os.WriteFile(filepath.Join(platformPath, "env", "TEST_ENV"), []byte("test-value"), 0600)).
|
||||
To(Succeed())
|
||||
|
||||
tomlWriter = &mocks.TOMLWriter{}
|
||||
tomlWriter.On("Write", mock.Anything, mock.Anything).Return(nil)
|
||||
|
||||
Expect(os.Setenv("CNB_STACK_ID", "test-stack-id")).To(Succeed())
|
||||
Expect(os.Setenv("CNB_LAYERS_DIR", layersPath)).To(Succeed())
|
||||
Expect(os.Setenv("CNB_PLATFORM_DIR", platformPath)).To(Succeed())
|
||||
Expect(os.Setenv("CNB_BP_PLAN_PATH", buildpackPlanPath)).To(Succeed())
|
||||
Expect(os.Setenv("CNB_BUILD_PLAN_PATH", buildpackPlanPath)).To(Succeed())
|
||||
|
||||
workingDir, err = os.Getwd()
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
Expect(os.Chdir(applicationPath)).To(Succeed())
|
||||
})
|
||||
|
||||
it.After(func() {
|
||||
Expect(os.Chdir(workingDir)).To(Succeed())
|
||||
Expect(os.Unsetenv("CNB_BUILDPACK_DIR")).To(Succeed())
|
||||
Expect(os.Unsetenv("CNB_STACK_ID")).To(Succeed())
|
||||
Expect(os.Unsetenv("CNB_PLATFORM_DIR")).To(Succeed())
|
||||
Expect(os.Unsetenv("CNB_BP_PLAN_PATH")).To(Succeed())
|
||||
Expect(os.Unsetenv("CNB_LAYERS_DIR")).To(Succeed())
|
||||
Expect(os.Unsetenv("CNB_BUILD_PLAN_PATH")).To(Succeed())
|
||||
|
||||
Expect(os.RemoveAll(applicationPath)).To(Succeed())
|
||||
Expect(os.RemoveAll(buildpackPath)).To(Succeed())
|
||||
Expect(os.RemoveAll(buildpackPlanPath)).To(Succeed())
|
||||
Expect(os.RemoveAll(layersPath)).To(Succeed())
|
||||
Expect(os.RemoveAll(platformPath)).To(Succeed())
|
||||
})
|
||||
|
||||
it("encounters the wrong number of arguments", func() {
|
||||
libcnb.BuildpackMain(detectFunc, buildFunc,
|
||||
libcnb.WithArguments([]string{}),
|
||||
libcnb.WithExitHandler(exitHandler),
|
||||
libcnb.WithLogger(log.NewDiscard()),
|
||||
)
|
||||
|
||||
Expect(exitHandler.Calls[0].Arguments.Get(0)).To(MatchError("expected command name"))
|
||||
})
|
||||
|
||||
it("calls builder for build command", func() {
|
||||
commandPath := filepath.Join("bin", "build")
|
||||
|
||||
libcnb.BuildpackMain(detectFunc, buildFunc,
|
||||
libcnb.WithArguments([]string{commandPath}),
|
||||
libcnb.WithExitHandler(exitHandler),
|
||||
libcnb.WithLogger(log.NewDiscard()),
|
||||
)
|
||||
|
||||
Expect(exitHandler.Calls).To(BeEmpty())
|
||||
})
|
||||
|
||||
it("calls detector for detect command", func() {
|
||||
detectFunc = func(libcnb.DetectContext) (libcnb.DetectResult, error) {
|
||||
return libcnb.DetectResult{Pass: true}, nil
|
||||
}
|
||||
commandPath := filepath.Join("bin", "detect")
|
||||
|
||||
libcnb.BuildpackMain(detectFunc, buildFunc,
|
||||
libcnb.WithArguments([]string{commandPath}),
|
||||
libcnb.WithExitHandler(exitHandler),
|
||||
libcnb.WithLogger(log.NewDiscard()),
|
||||
)
|
||||
})
|
||||
|
||||
it("calls generator for generate command", func() {
|
||||
generateFunc = func(libcnb.GenerateContext) (libcnb.GenerateResult, error) {
|
||||
return libcnb.GenerateResult{}, nil
|
||||
}
|
||||
commandPath := filepath.Join("bin", "generate")
|
||||
|
||||
libcnb.ExtensionMain(nil, generateFunc,
|
||||
libcnb.WithArguments([]string{commandPath}),
|
||||
libcnb.WithExitHandler(exitHandler),
|
||||
libcnb.WithLogger(log.NewDiscard()),
|
||||
)
|
||||
})
|
||||
|
||||
it("calls exitHandler.Pass() on detection pass", func() {
|
||||
detectFunc = func(libcnb.DetectContext) (libcnb.DetectResult, error) {
|
||||
return libcnb.DetectResult{Pass: true}, nil
|
||||
}
|
||||
commandPath := filepath.Join("bin", "detect")
|
||||
|
||||
libcnb.BuildpackMain(detectFunc, buildFunc,
|
||||
libcnb.WithArguments([]string{commandPath}),
|
||||
libcnb.WithExitHandler(exitHandler),
|
||||
libcnb.WithLogger(log.NewDiscard()),
|
||||
)
|
||||
|
||||
Expect(exitHandler.Calls[0].Method).To(BeIdenticalTo("Pass"))
|
||||
})
|
||||
|
||||
it("calls exitHandler.Fail() on detection fail", func() {
|
||||
detectFunc = func(libcnb.DetectContext) (libcnb.DetectResult, error) {
|
||||
return libcnb.DetectResult{Pass: false}, nil
|
||||
}
|
||||
commandPath := filepath.Join("bin", "detect")
|
||||
|
||||
libcnb.BuildpackMain(detectFunc, buildFunc,
|
||||
libcnb.WithArguments([]string{commandPath}),
|
||||
libcnb.WithExitHandler(exitHandler),
|
||||
libcnb.WithLogger(log.NewDiscard()),
|
||||
)
|
||||
|
||||
Expect(exitHandler.Calls[0].Method).To(BeIdenticalTo("Fail"))
|
||||
})
|
||||
|
||||
it("encounters an unknown command", func() {
|
||||
commandPath := filepath.Join("bin", "test-command")
|
||||
|
||||
libcnb.BuildpackMain(detectFunc, buildFunc,
|
||||
libcnb.WithArguments([]string{commandPath}),
|
||||
libcnb.WithExitHandler(exitHandler),
|
||||
libcnb.WithLogger(log.NewDiscard()),
|
||||
)
|
||||
|
||||
Expect(exitHandler.Calls[0].Arguments.Get(0)).To(MatchError("unsupported command test-command"))
|
||||
})
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
libcnb "github.com/buildpacks/libcnb"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// Builder is an autogenerated mock type for the Builder type
|
||||
type Builder struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Build provides a mock function with given fields: context
|
||||
func (_m *Builder) Build(context libcnb.BuildContext) (libcnb.BuildResult, error) {
|
||||
ret := _m.Called(context)
|
||||
|
||||
var r0 libcnb.BuildResult
|
||||
if rf, ok := ret.Get(0).(func(libcnb.BuildContext) libcnb.BuildResult); ok {
|
||||
r0 = rf(context)
|
||||
} else {
|
||||
r0 = ret.Get(0).(libcnb.BuildResult)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(libcnb.BuildContext) error); ok {
|
||||
r1 = rf(context)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
|
@ -1,34 +0,0 @@
|
|||
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
libcnb "github.com/buildpacks/libcnb"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// Detector is an autogenerated mock type for the Detector type
|
||||
type Detector struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Detect provides a mock function with given fields: context
|
||||
func (_m *Detector) Detect(context libcnb.DetectContext) (libcnb.DetectResult, error) {
|
||||
ret := _m.Called(context)
|
||||
|
||||
var r0 libcnb.DetectResult
|
||||
if rf, ok := ret.Get(0).(func(libcnb.DetectContext) libcnb.DetectResult); ok {
|
||||
r0 = rf(context)
|
||||
} else {
|
||||
r0 = ret.Get(0).(libcnb.DetectResult)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(libcnb.DetectContext) error); ok {
|
||||
r1 = rf(context)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
|
@ -13,6 +13,10 @@ type EnvironmentWriter struct {
|
|||
func (_m *EnvironmentWriter) Write(dir string, environment map[string]string) error {
|
||||
ret := _m.Called(dir, environment)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Write")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string, map[string]string) error); ok {
|
||||
r0 = rf(dir, environment)
|
||||
|
@ -22,3 +26,17 @@ func (_m *EnvironmentWriter) Write(dir string, environment map[string]string) er
|
|||
|
||||
return r0
|
||||
}
|
||||
|
||||
// NewEnvironmentWriter creates a new instance of EnvironmentWriter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewEnvironmentWriter(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *EnvironmentWriter {
|
||||
mock := &EnvironmentWriter{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import mock "github.com/stretchr/testify/mock"
|
||||
|
||||
// ExecD is an autogenerated mock type for the ExecD type
|
||||
type ExecD struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Execute provides a mock function with given fields:
|
||||
func (_m *ExecD) Execute() (map[string]string, error) {
|
||||
ret := _m.Called()
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Execute")
|
||||
}
|
||||
|
||||
var r0 map[string]string
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(0).(func() (map[string]string, error)); ok {
|
||||
return rf()
|
||||
}
|
||||
if rf, ok := ret.Get(0).(func() map[string]string); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
if ret.Get(0) != nil {
|
||||
r0 = ret.Get(0).(map[string]string)
|
||||
}
|
||||
}
|
||||
|
||||
if rf, ok := ret.Get(1).(func() error); ok {
|
||||
r1 = rf()
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// NewExecD creates a new instance of ExecD. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewExecD(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *ExecD {
|
||||
mock := &ExecD{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import mock "github.com/stretchr/testify/mock"
|
||||
|
||||
// ExecDWriter is an autogenerated mock type for the ExecDWriter type
|
||||
type ExecDWriter struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Write provides a mock function with given fields: value
|
||||
func (_m *ExecDWriter) Write(value map[string]string) error {
|
||||
ret := _m.Called(value)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Write")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(map[string]string) error); ok {
|
||||
r0 = rf(value)
|
||||
} else {
|
||||
r0 = ret.Error(0)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
||||
|
||||
// NewExecDWriter creates a new instance of ExecDWriter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewExecDWriter(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *ExecDWriter {
|
||||
mock := &ExecDWriter{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
|
@ -23,3 +23,17 @@ func (_m *ExitHandler) Fail() {
|
|||
func (_m *ExitHandler) Pass() {
|
||||
_m.Called()
|
||||
}
|
||||
|
||||
// NewExitHandler creates a new instance of ExitHandler. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewExitHandler(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *ExitHandler {
|
||||
mock := &ExitHandler{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
|
|
|
@ -1,48 +0,0 @@
|
|||
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
import (
|
||||
libcnb "github.com/buildpacks/libcnb"
|
||||
mock "github.com/stretchr/testify/mock"
|
||||
)
|
||||
|
||||
// LayerContributor is an autogenerated mock type for the LayerContributor type
|
||||
type LayerContributor struct {
|
||||
mock.Mock
|
||||
}
|
||||
|
||||
// Contribute provides a mock function with given fields: _a0
|
||||
func (_m *LayerContributor) Contribute(_a0 libcnb.Layer) (libcnb.Layer, error) {
|
||||
ret := _m.Called(_a0)
|
||||
|
||||
var r0 libcnb.Layer
|
||||
if rf, ok := ret.Get(0).(func(libcnb.Layer) libcnb.Layer); ok {
|
||||
r0 = rf(_a0)
|
||||
} else {
|
||||
r0 = ret.Get(0).(libcnb.Layer)
|
||||
}
|
||||
|
||||
var r1 error
|
||||
if rf, ok := ret.Get(1).(func(libcnb.Layer) error); ok {
|
||||
r1 = rf(_a0)
|
||||
} else {
|
||||
r1 = ret.Error(1)
|
||||
}
|
||||
|
||||
return r0, r1
|
||||
}
|
||||
|
||||
// Name provides a mock function with given fields:
|
||||
func (_m *LayerContributor) Name() string {
|
||||
ret := _m.Called()
|
||||
|
||||
var r0 string
|
||||
if rf, ok := ret.Get(0).(func() string); ok {
|
||||
r0 = rf()
|
||||
} else {
|
||||
r0 = ret.Get(0).(string)
|
||||
}
|
||||
|
||||
return r0
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
// Code generated by mockery v1.0.0. DO NOT EDIT.
|
||||
// Code generated by mockery v2.43.2. DO NOT EDIT.
|
||||
|
||||
package mocks
|
||||
|
||||
|
@ -13,6 +13,10 @@ type TOMLWriter struct {
|
|||
func (_m *TOMLWriter) Write(path string, value interface{}) error {
|
||||
ret := _m.Called(path, value)
|
||||
|
||||
if len(ret) == 0 {
|
||||
panic("no return value specified for Write")
|
||||
}
|
||||
|
||||
var r0 error
|
||||
if rf, ok := ret.Get(0).(func(string, interface{}) error); ok {
|
||||
r0 = rf(path, value)
|
||||
|
@ -22,3 +26,17 @@ func (_m *TOMLWriter) Write(path string, value interface{}) error {
|
|||
|
||||
return r0
|
||||
}
|
||||
|
||||
// NewTOMLWriter creates a new instance of TOMLWriter. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
|
||||
// The first argument is typically a *testing.T value.
|
||||
func NewTOMLWriter(t interface {
|
||||
mock.TestingT
|
||||
Cleanup(func())
|
||||
}) *TOMLWriter {
|
||||
mock := &TOMLWriter{}
|
||||
mock.Mock.Test(t)
|
||||
|
||||
t.Cleanup(func() { mock.AssertExpectations(t) })
|
||||
|
||||
return mock
|
||||
}
|
||||
|
|
253
platform.go
253
platform.go
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
* Copyright 2018-2020 the original author or authors.
|
||||
* Copyright 2018-2023 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -17,23 +17,76 @@
|
|||
package libcnb
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/fs"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/buildpacks/libcnb/internal"
|
||||
"github.com/buildpacks/libcnb/v2/internal"
|
||||
)
|
||||
|
||||
const (
|
||||
// BindingKind is the metadata key for a binding's kind.
|
||||
BindingKind = "kind"
|
||||
|
||||
// BindingProvider is the metadata key for a binding's provider.
|
||||
// BindingProvider is the key for a binding's provider.
|
||||
BindingProvider = "provider"
|
||||
|
||||
// BindingTags is the metadata key for a binding's tags.
|
||||
BindingTags = "tags"
|
||||
// BindingType is the key for a binding's type.
|
||||
BindingType = "type"
|
||||
|
||||
// EnvServiceBindings is the name of the environment variable that contains the path to service bindings directory.
|
||||
//
|
||||
// See the Service Binding Specification for Kubernetes for more details - https://k8s-service-bindings.github.io/spec/
|
||||
EnvServiceBindings = "SERVICE_BINDING_ROOT"
|
||||
|
||||
// EnvBuildpackDirectory is the name of the environment variable that contains the path to the buildpack
|
||||
EnvBuildpackDirectory = "CNB_BUILDPACK_DIR"
|
||||
|
||||
// EnvExtensionDirectory is the name of the environment variable that contains the path to the extension
|
||||
EnvExtensionDirectory = "CNB_EXTENSION_DIR"
|
||||
|
||||
// EnvVcapServices is the name of the environment variable that contains the bindings in cloudfoundry
|
||||
EnvVcapServices = "VCAP_SERVICES"
|
||||
|
||||
// EnvLayersDirectory is the name of the environment variable that contains the root path to all buildpack layers
|
||||
EnvLayersDirectory = "CNB_LAYERS_DIR"
|
||||
|
||||
// EnvOutputDirectory is the name of the environment variable that contains the path to the output directory
|
||||
EnvOutputDirectory = "CNB_OUTPUT_DIR"
|
||||
|
||||
// EnvPlatformDirectory is the name of the environment variable that contains the path to the platform directory
|
||||
EnvPlatformDirectory = "CNB_PLATFORM_DIR"
|
||||
|
||||
// EnvDetectBuildPlanPath is the name of the environment variable that contains the path to the build plan
|
||||
EnvDetectPlanPath = "CNB_BUILD_PLAN_PATH"
|
||||
|
||||
// EnvBuildPlanPath is the name of the environment variable that contains the path to the build plan
|
||||
EnvBuildPlanPath = "CNB_BP_PLAN_PATH"
|
||||
|
||||
// Deprecated: EnvStackID is the name of the environment variable that contains the stack id
|
||||
EnvStackID = "CNB_STACK_ID"
|
||||
|
||||
// EnvTargetOS contains the name of the os
|
||||
EnvTargetOS = "CNB_TARGET_OS"
|
||||
|
||||
// EnvTargetArch contains the architecture
|
||||
EnvTargetArch = "CNB_TARGET_ARCH"
|
||||
|
||||
// EnvTargetOS contains the variant of the architecture
|
||||
EnvTargetArchVariant = "CNB_TARGET_ARCH_VARIANT"
|
||||
|
||||
// EnvTargetDistroName contains the name of the ditro
|
||||
EnvTargetDistroName = "CNB_TARGET_DISTRO_NAME"
|
||||
|
||||
// EnvTargetDistroVersion contains the version of the distro
|
||||
EnvTargetDistroVersion = "CNB_TARGET_DISTRO_VERSION"
|
||||
|
||||
// DefaultPlatformBindingsLocation is the typical location for bindings, which exists under the platform directory
|
||||
//
|
||||
// Not guaranteed to exist, but often does. This should only be used as a fallback if EnvServiceBindings and EnvPlatformDirectory are not set
|
||||
DefaultPlatformBindingsLocation = "/platform/bindings"
|
||||
)
|
||||
|
||||
// Binding is a projection of metadata about an external entity to be bound to.
|
||||
|
@ -42,94 +95,92 @@ type Binding struct {
|
|||
// Name is the name of the binding
|
||||
Name string
|
||||
|
||||
// Metadata is the metadata of the binding.
|
||||
Metadata map[string]string
|
||||
// Path is the path to the binding directory.
|
||||
Path string
|
||||
|
||||
// Secrete is the secret of the binding.
|
||||
// Type is the type of the binding.
|
||||
Type string
|
||||
|
||||
// Provider is the optional provider of the binding.
|
||||
Provider string
|
||||
|
||||
// Secret is the secret of the binding.
|
||||
Secret map[string]string
|
||||
}
|
||||
|
||||
// NewBinding creates a new Binding initialized with no metadata or secret.
|
||||
func NewBinding(name string) Binding {
|
||||
return Binding{
|
||||
Name: name,
|
||||
Metadata: map[string]string{},
|
||||
Secret: map[string]string{},
|
||||
// NewBinding creates a new Binding initialized with a secret.
|
||||
func NewBinding(name string, path string, secret map[string]string) Binding {
|
||||
b := Binding{
|
||||
Name: name,
|
||||
Path: path,
|
||||
Secret: make(map[string]string),
|
||||
}
|
||||
|
||||
for k, v := range secret {
|
||||
switch k {
|
||||
case BindingType:
|
||||
b.Type = strings.TrimSpace(v)
|
||||
case BindingProvider:
|
||||
b.Provider = strings.TrimSpace(v)
|
||||
default:
|
||||
b.Secret[k] = strings.TrimSpace(v)
|
||||
}
|
||||
}
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// NewBindingFromPath creates a new binding from the files located at a path.
|
||||
func NewBindingFromPath(path string) (Binding, error) {
|
||||
var f string
|
||||
|
||||
f = filepath.Join(path, "metadata")
|
||||
metadata, err := internal.NewConfigMapFromPath(f)
|
||||
secret, err := internal.NewConfigMapFromPath(path)
|
||||
if err != nil {
|
||||
return Binding{}, fmt.Errorf("unable to create new config map from %s\n%w", f, err)
|
||||
return Binding{}, fmt.Errorf("unable to create new config map from %s\n%w", path, err)
|
||||
}
|
||||
|
||||
f = filepath.Join(path, "secret")
|
||||
secret, err := internal.NewConfigMapFromPath(f)
|
||||
if err != nil {
|
||||
return Binding{}, fmt.Errorf("unable to create new config map from %s\n%w", f, err)
|
||||
}
|
||||
|
||||
return Binding{Name: filepath.Base(path), Metadata: metadata, Secret: secret}, nil
|
||||
}
|
||||
|
||||
// Kind returns the kind of the binding.
|
||||
func (b Binding) Kind() string {
|
||||
return b.Metadata[BindingKind]
|
||||
}
|
||||
|
||||
// Provider returns the provider of the binding.
|
||||
func (b Binding) Provider() string {
|
||||
return b.Metadata[BindingProvider]
|
||||
}
|
||||
|
||||
// Tags returns the tags of the binding.
|
||||
func (b Binding) Tags() []string {
|
||||
return strings.Split(b.Metadata[BindingTags], "\n")
|
||||
return NewBinding(filepath.Base(path), path, secret), nil
|
||||
}
|
||||
|
||||
func (b Binding) String() string {
|
||||
m := make(map[string]string, len(b.Metadata))
|
||||
for k, v := range b.Metadata {
|
||||
m[k] = strings.ReplaceAll(v, "\n", " ")
|
||||
}
|
||||
|
||||
var s []string
|
||||
for k, _ := range b.Secret {
|
||||
for k := range b.Secret {
|
||||
s = append(s, k)
|
||||
}
|
||||
sort.Strings(s)
|
||||
|
||||
return fmt.Sprintf("{Metadata: %s Secret: %s}", m, s)
|
||||
return fmt.Sprintf("{Name: %s Path: %s Type: %s Provider: %s Secret: %s}",
|
||||
b.Name, b.Path, b.Type, b.Provider, s)
|
||||
}
|
||||
|
||||
// SecretFilePath return the path to a secret file with the given name.
|
||||
func (b Binding) SecretFilePath(name string) (string, bool) {
|
||||
if _, ok := b.Secret[name]; !ok {
|
||||
return "", false
|
||||
}
|
||||
|
||||
return filepath.Join(b.Path, name), true
|
||||
}
|
||||
|
||||
// Bindings is a collection of bindings keyed by their name.
|
||||
type Bindings []Binding
|
||||
|
||||
// NewBindingsFromEnvironment creates a new bindings from all the bindings at the path defined by $CNB_BINDINGS. If
|
||||
// $CNB_BINDINGS is not defined, returns an empty collection of Bindings.
|
||||
func NewBindingsFromEnvironment() (Bindings, error) {
|
||||
path, ok := os.LookupEnv("CNB_BINDINGS")
|
||||
if !ok {
|
||||
return Bindings{}, nil
|
||||
}
|
||||
|
||||
return NewBindingsFromPath(path)
|
||||
}
|
||||
|
||||
// NewBindingsFromPath creates a new instance from all the bindings at a given path.
|
||||
func NewBindingsFromPath(path string) (Bindings, error) {
|
||||
files, err := filepath.Glob(filepath.Join(path, "*"))
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to glob %s\n%w", path, err)
|
||||
files, err := os.ReadDir(path)
|
||||
if err != nil && errors.Is(err, fs.ErrNotExist) {
|
||||
return Bindings{}, nil
|
||||
} else if err != nil {
|
||||
return nil, fmt.Errorf("unable to list directory %s\n%w", path, err)
|
||||
}
|
||||
|
||||
bindings := Bindings{}
|
||||
for _, file := range files {
|
||||
binding, err := NewBindingFromPath(file)
|
||||
bindingPath := filepath.Join(path, file.Name())
|
||||
|
||||
if strings.HasPrefix(filepath.Base(bindingPath), ".") {
|
||||
// ignore hidden files
|
||||
continue
|
||||
}
|
||||
binding, err := NewBindingFromPath(bindingPath)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("unable to create new binding from %s\n%w", file, err)
|
||||
}
|
||||
|
@ -140,6 +191,76 @@ func NewBindingsFromPath(path string) (Bindings, error) {
|
|||
return bindings, nil
|
||||
}
|
||||
|
||||
type vcapServicesBinding struct {
|
||||
Name string `json:"name"`
|
||||
Label string `json:"label"`
|
||||
Credentials map[string]interface{} `json:"credentials"`
|
||||
}
|
||||
|
||||
func toJSONString(input interface{}) (string, error) {
|
||||
switch in := input.(type) {
|
||||
case string:
|
||||
return in, nil
|
||||
default:
|
||||
jsonProperty, err := json.Marshal(in)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(jsonProperty), nil
|
||||
}
|
||||
}
|
||||
|
||||
// NewBindingsFromVcapServicesEnv creates a new instance from all the bindings given from the VCAP_SERVICES.
|
||||
func NewBindingsFromVcapServicesEnv(content string) (Bindings, error) {
|
||||
var contentTyped map[string][]vcapServicesBinding
|
||||
|
||||
err := json.Unmarshal([]byte(content), &contentTyped)
|
||||
if err != nil {
|
||||
return Bindings{}, err
|
||||
}
|
||||
|
||||
bindings := Bindings{}
|
||||
for p, bArray := range contentTyped {
|
||||
for _, b := range bArray {
|
||||
secret := map[string]string{}
|
||||
for k, v := range b.Credentials {
|
||||
secret[k], err = toJSONString(v)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
bindings = append(bindings, Binding{
|
||||
Name: b.Name,
|
||||
Type: b.Label,
|
||||
Provider: p,
|
||||
Secret: secret,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return bindings, nil
|
||||
}
|
||||
|
||||
// NewBindings creates a new bindings from all the bindings at the path defined by $SERVICE_BINDING_ROOT.
|
||||
// If that isn't defined, bindings are read from <platform>/bindings.
|
||||
// If that isn't defined, bindings are read from $VCAP_SERVICES.
|
||||
// If that isn't defined, the specified platform path will be used
|
||||
func NewBindings(platformDir string) (Bindings, error) {
|
||||
if path, ok := os.LookupEnv(EnvServiceBindings); ok {
|
||||
return NewBindingsFromPath(path)
|
||||
}
|
||||
|
||||
if path, ok := os.LookupEnv(EnvPlatformDirectory); ok {
|
||||
return NewBindingsFromPath(filepath.Join(path, "bindings"))
|
||||
}
|
||||
|
||||
if content, ok := os.LookupEnv(EnvVcapServices); ok {
|
||||
return NewBindingsFromVcapServicesEnv(content)
|
||||
}
|
||||
|
||||
return NewBindingsFromPath(filepath.Join(platformDir, "bindings"))
|
||||
}
|
||||
|
||||
// Platform is the contents of the platform directory.
|
||||
type Platform struct {
|
||||
|
||||
|
|
257
platform_test.go
257
platform_test.go
|
@ -17,26 +17,28 @@
|
|||
package libcnb_test
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"testing"
|
||||
|
||||
"github.com/buildpacks/libcnb"
|
||||
. "github.com/onsi/gomega"
|
||||
"github.com/sclevine/spec"
|
||||
|
||||
"github.com/buildpacks/libcnb/v2"
|
||||
)
|
||||
|
||||
func testPlatform(t *testing.T, context spec.G, it spec.S) {
|
||||
var (
|
||||
Expect = NewWithT(t).Expect
|
||||
|
||||
path string
|
||||
path, platformPath string
|
||||
)
|
||||
|
||||
it.Before(func() {
|
||||
var err error
|
||||
path, err = ioutil.TempDir("", "bindings")
|
||||
platformPath, err = os.MkdirTemp("", "platform")
|
||||
path = filepath.Join(platformPath, "bindings")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
})
|
||||
|
||||
|
@ -44,96 +46,197 @@ func testPlatform(t *testing.T, context spec.G, it spec.S) {
|
|||
Expect(os.RemoveAll(path)).To(Succeed())
|
||||
})
|
||||
|
||||
context("Binding", func() {
|
||||
it("creates an empty binding", func() {
|
||||
Expect(libcnb.NewBinding("test-name")).To(Equal(libcnb.Binding{
|
||||
Name: "test-name",
|
||||
Metadata: map[string]string{},
|
||||
Secret: map[string]string{},
|
||||
}))
|
||||
})
|
||||
context("Cloudfoundry VCAP_SERVICES", func() {
|
||||
it("creates a bindings from VCAP_SERVICES", func() {
|
||||
content, err := os.ReadFile("testdata/vcap_services.json")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
t.Setenv(libcnb.EnvVcapServices, string(content))
|
||||
|
||||
it("creates a binding from a path", func() {
|
||||
Expect(os.MkdirAll(filepath.Join(path, "metadata"), 0755)).To(Succeed())
|
||||
Expect(ioutil.WriteFile(filepath.Join(path, "metadata", "test-metadata-key"), []byte("test-metadata-value"), 0644)).To(Succeed())
|
||||
Expect(os.MkdirAll(filepath.Join(path, "secret"), 0755)).To(Succeed())
|
||||
Expect(ioutil.WriteFile(filepath.Join(path, "secret", "test-secret-key"), []byte("test-secret-value"), 0644)).To(Succeed())
|
||||
|
||||
Expect(libcnb.NewBindingFromPath(path)).To(Equal(libcnb.Binding{
|
||||
Name: filepath.Base(path),
|
||||
Metadata: map[string]string{
|
||||
"test-metadata-key": "test-metadata-value",
|
||||
},
|
||||
Secret: map[string]string{
|
||||
"test-secret-key": "test-secret-value",
|
||||
},
|
||||
}))
|
||||
})
|
||||
|
||||
it("sanitizes secrets", func() {
|
||||
Expect(os.MkdirAll(filepath.Join(path, "metadata"), 0755)).To(Succeed())
|
||||
Expect(ioutil.WriteFile(filepath.Join(path, "metadata", "test-metadata-key"), []byte("test-metadata-value"), 0644)).To(Succeed())
|
||||
Expect(os.MkdirAll(filepath.Join(path, "secret"), 0755)).To(Succeed())
|
||||
Expect(ioutil.WriteFile(filepath.Join(path, "secret", "test-secret-key"), []byte("test-secret-value"), 0644)).To(Succeed())
|
||||
|
||||
b, err := libcnb.NewBindingFromPath(path)
|
||||
bindings, err := libcnb.NewBindings("")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(b.String()).To(Equal("{Metadata: map[test-metadata-key:test-metadata-value] Secret: [test-secret-key]}"))
|
||||
Expect(bindings).To(ConsistOf(libcnb.Bindings{
|
||||
{
|
||||
Name: "elephantsql-binding-c6c60",
|
||||
Type: "elephantsql-type",
|
||||
Provider: "elephantsql-provider",
|
||||
Secret: map[string]string{
|
||||
"bool": "true",
|
||||
"int": "1",
|
||||
"uri": "postgres://exampleuser:examplepass@postgres.example.com:5432/exampleuser",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "mysendgrid",
|
||||
Type: "sendgrid-type",
|
||||
Provider: "sendgrid-provider",
|
||||
Secret: map[string]string{
|
||||
"username": "QvsXMbJ3rK",
|
||||
"password": "HCHMOYluTv",
|
||||
"hostname": "smtp.example.com",
|
||||
},
|
||||
},
|
||||
{
|
||||
Name: "postgres",
|
||||
Type: "postgres",
|
||||
Provider: "postgres",
|
||||
Secret: map[string]string{
|
||||
"urls": "{\"example\":\"http://example.com\"}",
|
||||
"username": "foo",
|
||||
"password": "bar",
|
||||
},
|
||||
},
|
||||
}))
|
||||
})
|
||||
|
||||
it("returns kind", func() {
|
||||
b := libcnb.Binding{Metadata: map[string]string{libcnb.BindingKind: "test-kind"}}
|
||||
it("creates empty bindings from empty VCAP_SERVICES", func() {
|
||||
t.Setenv(libcnb.EnvVcapServices, "{}")
|
||||
|
||||
Expect(b.Kind()).To(Equal("test-kind"))
|
||||
})
|
||||
bindings, err := libcnb.NewBindings("")
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
it("returns provider", func() {
|
||||
b := libcnb.Binding{Metadata: map[string]string{libcnb.BindingProvider: "test-provider"}}
|
||||
|
||||
Expect(b.Provider()).To(Equal("test-provider"))
|
||||
})
|
||||
|
||||
it("returns tags", func() {
|
||||
b := libcnb.Binding{Metadata: map[string]string{libcnb.BindingTags: "test-tag-1\ntest-tag-2"}}
|
||||
|
||||
Expect(b.Tags()).To(Equal([]string{"test-tag-1", "test-tag-2"}))
|
||||
Expect(bindings).To(HaveLen(0))
|
||||
})
|
||||
})
|
||||
|
||||
context("Bindings", func() {
|
||||
it("creates a bindings from a path", func() {
|
||||
context("Kubernetes Service Bindings", func() {
|
||||
it.Before(func() {
|
||||
Expect(os.MkdirAll(filepath.Join(path, "alpha"), 0755)).To(Succeed())
|
||||
Expect(os.WriteFile(filepath.Join(path, "alpha", "type"), []byte("test-type"), 0600)).To(Succeed())
|
||||
Expect(os.WriteFile(filepath.Join(path, "alpha", "provider"), []byte("test-provider"), 0600)).To(Succeed())
|
||||
Expect(os.WriteFile(filepath.Join(path, "alpha", "test-secret-key"), []byte("test-secret-value"), 0600)).To(Succeed())
|
||||
|
||||
Expect(os.MkdirAll(filepath.Join(path, "bravo"), 0755)).To(Succeed())
|
||||
Expect(os.WriteFile(filepath.Join(path, "bravo", "type"), []byte("test-type"), 0600)).To(Succeed())
|
||||
Expect(os.WriteFile(filepath.Join(path, "bravo", "provider"), []byte("test-provider"), 0600)).To(Succeed())
|
||||
Expect(os.WriteFile(filepath.Join(path, "bravo", "test-secret-key"), []byte("test-secret-value"), 0600)).To(Succeed())
|
||||
|
||||
Expect(libcnb.NewBindingsFromPath(path)).To(Equal(libcnb.Bindings{
|
||||
libcnb.NewBinding("alpha"),
|
||||
libcnb.NewBinding("bravo"),
|
||||
}))
|
||||
Expect(os.MkdirAll(filepath.Join(path, ".hidden", "metadata"), 0755)).To(Succeed())
|
||||
Expect(os.WriteFile(filepath.Join(path, ".hidden", "metadata", "kind"), []byte("test-kind"), 0600)).To(Succeed())
|
||||
Expect(os.WriteFile(filepath.Join(path, ".hiddenFile"), []byte("test-kind"), 0600)).To(Succeed())
|
||||
})
|
||||
|
||||
it("returns empty bindings if environment variable is not set", func() {
|
||||
Expect(libcnb.NewBindingsFromEnvironment()).To(Equal(libcnb.Bindings{}))
|
||||
})
|
||||
|
||||
context("from environment", func() {
|
||||
it.Before(func() {
|
||||
Expect(os.MkdirAll(filepath.Join(path, "alpha"), 0755)).To(Succeed())
|
||||
Expect(os.MkdirAll(filepath.Join(path, "bravo"), 0755)).To(Succeed())
|
||||
|
||||
Expect(os.Setenv("CNB_BINDINGS", path))
|
||||
})
|
||||
|
||||
it.After(func() {
|
||||
Expect(os.Unsetenv("CNB_BINDINGS"))
|
||||
})
|
||||
|
||||
it("creates bindings from path in $CNB_BINDINGS", func() {
|
||||
Expect(libcnb.NewBindingsFromEnvironment()).To(Equal(libcnb.Bindings{
|
||||
libcnb.NewBinding("alpha"),
|
||||
libcnb.NewBinding("bravo"),
|
||||
context("Binding", func() {
|
||||
it("creates an empty binding", func() {
|
||||
Expect(libcnb.NewBinding("test-name", "test-path", map[string]string{
|
||||
libcnb.BindingType: "test-type",
|
||||
libcnb.BindingProvider: "test-provider",
|
||||
"test-key": "test-value",
|
||||
})).To(Equal(libcnb.Binding{
|
||||
Name: "test-name",
|
||||
Path: "test-path",
|
||||
Type: "test-type",
|
||||
Provider: "test-provider",
|
||||
Secret: map[string]string{
|
||||
"test-key": "test-value",
|
||||
},
|
||||
}))
|
||||
})
|
||||
|
||||
it("creates a binding from a path", func() {
|
||||
path := filepath.Join(path, "alpha")
|
||||
|
||||
binding, err := libcnb.NewBindingFromPath(path)
|
||||
Expect(binding, err).To(Equal(libcnb.Binding{
|
||||
Name: filepath.Base(path),
|
||||
Path: path,
|
||||
Type: "test-type",
|
||||
Provider: "test-provider",
|
||||
Secret: map[string]string{"test-secret-key": "test-secret-value"},
|
||||
}))
|
||||
|
||||
secretFilePath, ok := binding.SecretFilePath("test-secret-key")
|
||||
Expect(ok).To(BeTrue())
|
||||
Expect(secretFilePath).To(Equal(filepath.Join(path, "test-secret-key")))
|
||||
})
|
||||
|
||||
it("sanitizes secrets", func() {
|
||||
path := filepath.Join(path, "alpha")
|
||||
|
||||
b, err := libcnb.NewBindingFromPath(path)
|
||||
Expect(err).NotTo(HaveOccurred())
|
||||
|
||||
Expect(b.String()).To(Equal(fmt.Sprintf("{Name: alpha Path: %s Type: test-type Provider: test-provider Secret: [test-secret-key]}", path)))
|
||||
})
|
||||
})
|
||||
|
||||
context("Bindings", func() {
|
||||
it("creates a bindings from a path", func() {
|
||||
Expect(libcnb.NewBindingsFromPath(path)).To(Equal(libcnb.Bindings{
|
||||
libcnb.Binding{
|
||||
Name: "alpha",
|
||||
Path: filepath.Join(path, "alpha"),
|
||||
Type: "test-type",
|
||||
Provider: "test-provider",
|
||||
Secret: map[string]string{"test-secret-key": "test-secret-value"},
|
||||
},
|
||||
libcnb.Binding{
|
||||
Name: "bravo",
|
||||
Path: filepath.Join(path, "bravo"),
|
||||
Type: "test-type",
|
||||
Provider: "test-provider",
|
||||
Secret: map[string]string{"test-secret-key": "test-secret-value"},
|
||||
},
|
||||
}))
|
||||
})
|
||||
|
||||
it("creates an empty binding if path does not exist", func() {
|
||||
Expect(libcnb.NewBindingsFromPath("/path/doesnt/exist")).To(Equal(libcnb.Bindings{}))
|
||||
})
|
||||
|
||||
it("returns empty bindings if SERVICE_BINDING_ROOT and CNB_PLATFORM_DIR are not set and /platform/bindings does not exist", func() {
|
||||
Expect(libcnb.NewBindings(libcnb.DefaultPlatformBindingsLocation)).To(Equal(libcnb.Bindings{}))
|
||||
})
|
||||
|
||||
context("from environment", func() {
|
||||
it.After(func() {
|
||||
Expect(os.Unsetenv(libcnb.EnvServiceBindings))
|
||||
Expect(os.Unsetenv("CNB_PLATFORM_DIR"))
|
||||
})
|
||||
|
||||
it("creates bindings from path in SERVICE_BINDING_ROOT if both set", func() {
|
||||
Expect(os.Setenv(libcnb.EnvServiceBindings, path))
|
||||
Expect(os.Setenv("CNB_PLATFORM_DIR", "/does/not/exist"))
|
||||
|
||||
Expect(libcnb.NewBindings(libcnb.DefaultPlatformBindingsLocation)).To(Equal(libcnb.Bindings{
|
||||
libcnb.Binding{
|
||||
Name: "alpha",
|
||||
Path: filepath.Join(path, "alpha"),
|
||||
Type: "test-type",
|
||||
Provider: "test-provider",
|
||||
Secret: map[string]string{"test-secret-key": "test-secret-value"},
|
||||
},
|
||||
libcnb.Binding{
|
||||
Name: "bravo",
|
||||
Path: filepath.Join(path, "bravo"),
|
||||
Type: "test-type",
|
||||
Provider: "test-provider",
|
||||
Secret: map[string]string{"test-secret-key": "test-secret-value"},
|
||||
},
|
||||
}))
|
||||
})
|
||||
|
||||
it("creates bindings from path in SERVICE_BINDING_ROOT if SERVICE_BINDING_ROOT not set", func() {
|
||||
Expect(os.Setenv("CNB_PLATFORM_DIR", filepath.Dir(path)))
|
||||
|
||||
Expect(libcnb.NewBindings(libcnb.DefaultPlatformBindingsLocation)).To(Equal(libcnb.Bindings{
|
||||
libcnb.Binding{
|
||||
Name: "alpha",
|
||||
Path: filepath.Join(path, "alpha"),
|
||||
Type: "test-type",
|
||||
Provider: "test-provider",
|
||||
Secret: map[string]string{"test-secret-key": "test-secret-value"},
|
||||
},
|
||||
libcnb.Binding{
|
||||
Name: "bravo",
|
||||
Path: filepath.Join(path, "bravo"),
|
||||
Type: "test-type",
|
||||
Provider: "test-provider",
|
||||
Secret: map[string]string{"test-secret-key": "test-secret-value"},
|
||||
},
|
||||
}))
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
|
141
poet/logger.go
141
poet/logger.go
|
@ -1,141 +0,0 @@
|
|||
/*
|
||||
* Copyright 2018-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package poet
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Logger logs messages to a writer.
|
||||
type Logger struct {
|
||||
debug io.Writer
|
||||
info io.Writer
|
||||
}
|
||||
|
||||
// Option is a function that configures a Logger.
|
||||
type Option func(Logger) Logger
|
||||
|
||||
// WithDebug configures the debug Writer.
|
||||
func WithDebug(writer io.Writer) Option {
|
||||
return func(logger Logger) Logger {
|
||||
logger.debug = writer
|
||||
return logger
|
||||
}
|
||||
}
|
||||
|
||||
// NewLoggerWithOptions create a new instance of Logger. It configures the Logger with options.
|
||||
func NewLoggerWithOptions(writer io.Writer, options ...Option) Logger {
|
||||
l := Logger{
|
||||
info: writer,
|
||||
}
|
||||
|
||||
for _, option := range options {
|
||||
l = option(l)
|
||||
}
|
||||
|
||||
return l
|
||||
}
|
||||
|
||||
// NewLogger creates a new instance of Logger. It configures debug logging if $BP_DEBUG is set.
|
||||
func NewLogger(writer io.Writer) Logger {
|
||||
var options []Option
|
||||
|
||||
if _, ok := os.LookupEnv("BP_DEBUG"); ok {
|
||||
options = append(options, WithDebug(writer))
|
||||
}
|
||||
|
||||
return NewLoggerWithOptions(writer, options...)
|
||||
}
|
||||
|
||||
// Debug formats using the default formats for its operands and writes to the configured debug writer. Spaces are added
|
||||
// between operands when neither is a string.
|
||||
func (l Logger) Debug(a ...interface{}) {
|
||||
if !l.IsDebugEnabled() {
|
||||
return
|
||||
}
|
||||
|
||||
l.print(l.debug, a...)
|
||||
}
|
||||
|
||||
// Debugf formats according to a format specifier and writes to the configured debug writer.
|
||||
func (l Logger) Debugf(format string, a ...interface{}) {
|
||||
if !l.IsDebugEnabled() {
|
||||
return
|
||||
}
|
||||
|
||||
l.printf(l.debug, format, a...)
|
||||
}
|
||||
|
||||
// DebugWriter returns the configured debug writer.
|
||||
func (l Logger) DebugWriter() io.Writer {
|
||||
return l.debug
|
||||
}
|
||||
|
||||
// IsDebugEnabled indicates whether debug logging is enabled.
|
||||
func (l Logger) IsDebugEnabled() bool {
|
||||
return l.debug != nil
|
||||
}
|
||||
|
||||
// Info formats using the default formats for its operands and writes to the configured info writer. Spaces are added
|
||||
// between operands when neither is a string.
|
||||
func (l Logger) Info(a ...interface{}) {
|
||||
if !l.IsInfoEnabled() {
|
||||
return
|
||||
}
|
||||
|
||||
l.print(l.info, a...)
|
||||
}
|
||||
|
||||
// Infof formats according to a format specifier and writes to the configured info writer.
|
||||
func (l Logger) Infof(format string, a ...interface{}) {
|
||||
if !l.IsInfoEnabled() {
|
||||
return
|
||||
}
|
||||
|
||||
l.printf(l.info, format, a...)
|
||||
}
|
||||
|
||||
// InfoWriter returns the configured info writer.
|
||||
func (l Logger) InfoWriter() io.Writer {
|
||||
return l.info
|
||||
}
|
||||
|
||||
// IsInfoEnabled indicates whether info logging is enabled.
|
||||
func (l Logger) IsInfoEnabled() bool {
|
||||
return l.info != nil
|
||||
}
|
||||
|
||||
func (Logger) print(writer io.Writer, a ...interface{}) {
|
||||
s := fmt.Sprint(a...)
|
||||
|
||||
if !strings.HasSuffix(s, "\n") {
|
||||
s = s + "\n"
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprint(writer, s)
|
||||
}
|
||||
|
||||
func (Logger) printf(writer io.Writer, format string, a ...interface{}) {
|
||||
if !strings.HasSuffix(format, "\n") {
|
||||
format = format + "\n"
|
||||
}
|
||||
|
||||
_, _ = fmt.Fprintf(writer, format, a...)
|
||||
}
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright 2018-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package libcnb
|
||||
|
||||
// Process represents metadata about a type of command that can be run.
|
||||
type Process struct {
|
||||
// Type is the type of the process.
|
||||
Type string `toml:"type"`
|
||||
|
||||
// Command is the command of the process.
|
||||
Command []string `toml:"command"`
|
||||
|
||||
// Arguments are arguments to the command.
|
||||
Arguments []string `toml:"args"`
|
||||
|
||||
// WorkingDirectory is a directory to execute the command in, removes the need to use a shell environment to CD into working directory
|
||||
WorkingDirectory string `toml:"working-dir,omitempty"`
|
||||
|
||||
// Default can be set to true to indicate that the process
|
||||
// type being defined should be the default process type for the app image.
|
||||
Default bool `toml:"default,omitempty"`
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright 2018-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package libcnb
|
||||
|
||||
// Slice represents metadata about a slice.
|
||||
type Slice struct {
|
||||
|
||||
// Paths are the contents of the slice.
|
||||
Paths []string `toml:"paths"`
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright 2018-2020 the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package libcnb
|
||||
|
||||
// Store represents the contents of store.toml
|
||||
type Store struct {
|
||||
|
||||
// Metadata represents the persistent metadata.
|
||||
Metadata map[string]interface{} `toml:"metadata"`
|
||||
}
|
|
@ -0,0 +1,68 @@
|
|||
{
|
||||
"elephantsql-provider": [
|
||||
{
|
||||
"name": "elephantsql-binding-c6c60",
|
||||
"binding_guid": "44ceb72f-100b-4f50-87a2-7809c8b42b8d",
|
||||
"binding_name": "elephantsql-binding-c6c60",
|
||||
"instance_guid": "391308e8-8586-4c42-b464-c7831aa2ad22",
|
||||
"instance_name": "elephantsql-c6c60",
|
||||
"label": "elephantsql-type",
|
||||
"tags": [
|
||||
"postgres",
|
||||
"postgresql",
|
||||
"relational"
|
||||
],
|
||||
"plan": "turtle",
|
||||
"credentials": {
|
||||
"uri": "postgres://exampleuser:examplepass@postgres.example.com:5432/exampleuser",
|
||||
"int": 1,
|
||||
"bool": true
|
||||
},
|
||||
"syslog_drain_url": null,
|
||||
"volume_mounts": []
|
||||
}
|
||||
],
|
||||
"sendgrid-provider": [
|
||||
{
|
||||
"name": "mysendgrid",
|
||||
"binding_guid": "6533b1b6-7916-488d-b286-ca33d3fa0081",
|
||||
"binding_name": null,
|
||||
"instance_guid": "8c907d0f-ec0f-44e4-87cf-e23c9ba3925d",
|
||||
"instance_name": "mysendgrid",
|
||||
"label": "sendgrid-type",
|
||||
"tags": [
|
||||
"smtp"
|
||||
],
|
||||
"plan": "free",
|
||||
"credentials": {
|
||||
"hostname": "smtp.example.com",
|
||||
"username": "QvsXMbJ3rK",
|
||||
"password": "HCHMOYluTv"
|
||||
},
|
||||
"syslog_drain_url": null,
|
||||
"volume_mounts": []
|
||||
}
|
||||
],
|
||||
"postgres": [
|
||||
{
|
||||
"name": "postgres",
|
||||
"label": "postgres",
|
||||
"plan": "default",
|
||||
"tags": [
|
||||
"postgres"
|
||||
],
|
||||
"binding_guid": "6533b1b6-7916-488d-b286-ca33d3fa0081",
|
||||
"binding_name": null,
|
||||
"instance_guid": "8c907d0f-ec0f-44e4-87cf-e23c9ba3925d",
|
||||
"credentials": {
|
||||
"username": "foo",
|
||||
"password": "bar",
|
||||
"urls": {
|
||||
"example": "http://example.com"
|
||||
}
|
||||
},
|
||||
"syslog_drain_url": null,
|
||||
"volume_mounts": []
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,190 @@
|
|||
module github.com/buildpacks/libcnb/tools/v2
|
||||
|
||||
go 1.22.1
|
||||
|
||||
toolchain go1.23.2
|
||||
|
||||
require golang.org/x/tools v0.26.0
|
||||
|
||||
require github.com/golangci/golangci-lint v1.61.0
|
||||
|
||||
require (
|
||||
4d63.com/gocheckcompilerdirectives v1.2.1 // indirect
|
||||
4d63.com/gochecknoglobals v0.2.1 // indirect
|
||||
github.com/4meepo/tagalign v1.3.4 // indirect
|
||||
github.com/Abirdcfly/dupword v0.1.1 // indirect
|
||||
github.com/Antonboom/errname v0.1.13 // indirect
|
||||
github.com/Antonboom/nilnil v0.1.9 // indirect
|
||||
github.com/Antonboom/testifylint v1.4.3 // indirect
|
||||
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c // indirect
|
||||
github.com/Crocmagnon/fatcontext v0.5.2 // indirect
|
||||
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 // indirect
|
||||
github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.3.0 // indirect
|
||||
github.com/OpenPeeDeeP/depguard/v2 v2.2.0 // indirect
|
||||
github.com/alecthomas/go-check-sumtype v0.1.4 // indirect
|
||||
github.com/alexkohler/nakedret/v2 v2.0.4 // indirect
|
||||
github.com/alexkohler/prealloc v1.0.0 // indirect
|
||||
github.com/alingse/asasalint v0.0.11 // indirect
|
||||
github.com/ashanbrown/forbidigo v1.6.0 // indirect
|
||||
github.com/ashanbrown/makezero v1.1.1 // indirect
|
||||
github.com/beorn7/perks v1.0.1 // indirect
|
||||
github.com/bkielbasa/cyclop v1.2.1 // indirect
|
||||
github.com/blizzy78/varnamelen v0.8.0 // indirect
|
||||
github.com/bombsimon/wsl/v4 v4.4.1 // indirect
|
||||
github.com/breml/bidichk v0.2.7 // indirect
|
||||
github.com/breml/errchkjson v0.3.6 // indirect
|
||||
github.com/butuzov/ireturn v0.3.0 // indirect
|
||||
github.com/butuzov/mirror v1.2.0 // indirect
|
||||
github.com/catenacyber/perfsprint v0.7.1 // indirect
|
||||
github.com/ccojocar/zxcvbn-go v1.0.2 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.1.2 // indirect
|
||||
github.com/charithe/durationcheck v0.0.10 // indirect
|
||||
github.com/chavacava/garif v0.1.0 // indirect
|
||||
github.com/ckaznocha/intrange v0.2.0 // indirect
|
||||
github.com/curioswitch/go-reassign v0.2.0 // indirect
|
||||
github.com/daixiang0/gci v0.13.5 // indirect
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/denis-tingaikin/go-header v0.5.0 // indirect
|
||||
github.com/ettle/strcase v0.2.0 // indirect
|
||||
github.com/fatih/color v1.17.0 // indirect
|
||||
github.com/fatih/structtag v1.2.0 // indirect
|
||||
github.com/firefart/nonamedreturns v1.0.5 // indirect
|
||||
github.com/fsnotify/fsnotify v1.5.4 // indirect
|
||||
github.com/fzipp/gocyclo v0.6.0 // indirect
|
||||
github.com/ghostiam/protogetter v0.3.6 // indirect
|
||||
github.com/go-critic/go-critic v0.11.4 // indirect
|
||||
github.com/go-toolsmith/astcast v1.1.0 // indirect
|
||||
github.com/go-toolsmith/astcopy v1.1.0 // indirect
|
||||
github.com/go-toolsmith/astequal v1.2.0 // indirect
|
||||
github.com/go-toolsmith/astfmt v1.1.0 // indirect
|
||||
github.com/go-toolsmith/astp v1.1.0 // indirect
|
||||
github.com/go-toolsmith/strparse v1.1.0 // indirect
|
||||
github.com/go-toolsmith/typep v1.1.0 // indirect
|
||||
github.com/go-viper/mapstructure/v2 v2.1.0 // indirect
|
||||
github.com/go-xmlfmt/xmlfmt v1.1.2 // indirect
|
||||
github.com/gobwas/glob v0.2.3 // indirect
|
||||
github.com/gofrs/flock v0.12.1 // indirect
|
||||
github.com/golang/protobuf v1.5.3 // indirect
|
||||
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a // indirect
|
||||
github.com/golangci/gofmt v0.0.0-20240816233607-d8596aa466a9 // indirect
|
||||
github.com/golangci/misspell v0.6.0 // indirect
|
||||
github.com/golangci/modinfo v0.3.4 // indirect
|
||||
github.com/golangci/plugin-module-register v0.1.1 // indirect
|
||||
github.com/golangci/revgrep v0.5.3 // indirect
|
||||
github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/gordonklaus/ineffassign v0.1.0 // indirect
|
||||
github.com/gostaticanalysis/analysisutil v0.7.1 // indirect
|
||||
github.com/gostaticanalysis/comment v1.4.2 // indirect
|
||||
github.com/gostaticanalysis/forcetypeassert v0.1.0 // indirect
|
||||
github.com/gostaticanalysis/nilerr v0.1.1 // indirect
|
||||
github.com/hashicorp/go-version v1.7.0 // indirect
|
||||
github.com/hashicorp/hcl v1.0.0 // indirect
|
||||
github.com/hexops/gotextdiff v1.0.3 // indirect
|
||||
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||
github.com/jgautheron/goconst v1.7.1 // indirect
|
||||
github.com/jingyugao/rowserrcheck v1.1.1 // indirect
|
||||
github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af // indirect
|
||||
github.com/jjti/go-spancheck v0.6.2 // indirect
|
||||
github.com/julz/importas v0.1.0 // indirect
|
||||
github.com/karamaru-alpha/copyloopvar v1.1.0 // indirect
|
||||
github.com/kisielk/errcheck v1.7.0 // indirect
|
||||
github.com/kkHAIKE/contextcheck v1.1.5 // indirect
|
||||
github.com/kulti/thelper v0.6.3 // indirect
|
||||
github.com/kunwardeep/paralleltest v1.0.10 // indirect
|
||||
github.com/kyoh86/exportloopref v0.1.11 // indirect
|
||||
github.com/lasiar/canonicalheader v1.1.1 // indirect
|
||||
github.com/ldez/gomoddirectives v0.2.4 // indirect
|
||||
github.com/ldez/tagliatelle v0.5.0 // indirect
|
||||
github.com/leonklingele/grouper v1.1.2 // indirect
|
||||
github.com/lufeee/execinquery v1.2.1 // indirect
|
||||
github.com/macabu/inamedparam v0.1.3 // indirect
|
||||
github.com/magiconair/properties v1.8.6 // indirect
|
||||
github.com/maratori/testableexamples v1.0.0 // indirect
|
||||
github.com/maratori/testpackage v1.1.1 // indirect
|
||||
github.com/matoous/godox v0.0.0-20230222163458-006bad1f9d26 // indirect
|
||||
github.com/mattn/go-colorable v0.1.13 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.9 // indirect
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 // indirect
|
||||
github.com/mgechev/revive v1.3.9 // indirect
|
||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||
github.com/mitchellh/mapstructure v1.5.0 // indirect
|
||||
github.com/moricho/tparallel v0.3.2 // indirect
|
||||
github.com/nakabonne/nestif v0.3.1 // indirect
|
||||
github.com/nishanths/exhaustive v0.12.0 // indirect
|
||||
github.com/nishanths/predeclared v0.2.2 // indirect
|
||||
github.com/nunnatsa/ginkgolinter v0.16.2 // indirect
|
||||
github.com/olekukonko/tablewriter v0.0.5 // indirect
|
||||
github.com/pelletier/go-toml v1.9.5 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/polyfloyd/go-errorlint v1.6.0 // indirect
|
||||
github.com/prometheus/client_golang v1.12.1 // indirect
|
||||
github.com/prometheus/client_model v0.2.0 // indirect
|
||||
github.com/prometheus/common v0.32.1 // indirect
|
||||
github.com/prometheus/procfs v0.7.3 // indirect
|
||||
github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1 // indirect
|
||||
github.com/quasilyte/go-ruleguard/dsl v0.3.22 // indirect
|
||||
github.com/quasilyte/gogrep v0.5.0 // indirect
|
||||
github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 // indirect
|
||||
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 // indirect
|
||||
github.com/ryancurrah/gomodguard v1.3.5 // indirect
|
||||
github.com/ryanrolds/sqlclosecheck v0.5.1 // indirect
|
||||
github.com/sanposhiho/wastedassign/v2 v2.0.7 // indirect
|
||||
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 // indirect
|
||||
github.com/sashamelentyev/interfacebloat v1.1.0 // indirect
|
||||
github.com/sashamelentyev/usestdlibvars v1.27.0 // indirect
|
||||
github.com/securego/gosec/v2 v2.21.2 // indirect
|
||||
github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c // indirect
|
||||
github.com/sirupsen/logrus v1.9.3 // indirect
|
||||
github.com/sivchari/containedctx v1.0.3 // indirect
|
||||
github.com/sivchari/tenv v1.10.0 // indirect
|
||||
github.com/sonatard/noctx v0.0.2 // indirect
|
||||
github.com/sourcegraph/go-diff v0.7.0 // indirect
|
||||
github.com/spf13/afero v1.11.0 // indirect
|
||||
github.com/spf13/cast v1.5.0 // indirect
|
||||
github.com/spf13/cobra v1.8.1 // indirect
|
||||
github.com/spf13/jwalterweatherman v1.1.0 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/spf13/viper v1.12.0 // indirect
|
||||
github.com/ssgreg/nlreturn/v2 v2.2.1 // indirect
|
||||
github.com/stbenjam/no-sprintf-host-port v0.1.1 // indirect
|
||||
github.com/stretchr/objx v0.5.2 // indirect
|
||||
github.com/stretchr/testify v1.9.0 // indirect
|
||||
github.com/subosito/gotenv v1.4.1 // indirect
|
||||
github.com/tdakkota/asciicheck v0.2.0 // indirect
|
||||
github.com/tetafro/godot v1.4.17 // indirect
|
||||
github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966 // indirect
|
||||
github.com/timonwong/loggercheck v0.9.4 // indirect
|
||||
github.com/tomarrell/wrapcheck/v2 v2.9.0 // indirect
|
||||
github.com/tommy-muehle/go-mnd/v2 v2.5.1 // indirect
|
||||
github.com/ultraware/funlen v0.1.0 // indirect
|
||||
github.com/ultraware/whitespace v0.1.1 // indirect
|
||||
github.com/uudashr/gocognit v1.1.3 // indirect
|
||||
github.com/xen0n/gosmopolitan v1.2.2 // indirect
|
||||
github.com/yagipy/maintidx v1.0.0 // indirect
|
||||
github.com/yeya24/promlinter v0.3.0 // indirect
|
||||
github.com/ykadowak/zerologlint v0.1.5 // indirect
|
||||
gitlab.com/bosi/decorder v0.4.2 // indirect
|
||||
go-simpler.org/musttag v0.12.2 // indirect
|
||||
go-simpler.org/sloglint v0.7.2 // indirect
|
||||
go.uber.org/atomic v1.7.0 // indirect
|
||||
go.uber.org/automaxprocs v1.5.3 // indirect
|
||||
go.uber.org/multierr v1.6.0 // indirect
|
||||
go.uber.org/zap v1.24.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e // indirect
|
||||
golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f // indirect
|
||||
golang.org/x/mod v0.21.0 // indirect
|
||||
golang.org/x/sync v0.8.0 // indirect
|
||||
golang.org/x/sys v0.26.0 // indirect
|
||||
golang.org/x/text v0.18.0 // indirect
|
||||
google.golang.org/protobuf v1.34.2 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
honnef.co/go/tools v0.5.1 // indirect
|
||||
mvdan.cc/gofumpt v0.7.0 // indirect
|
||||
mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f // indirect
|
||||
)
|
|
@ -0,0 +1,952 @@
|
|||
4d63.com/gocheckcompilerdirectives v1.2.1 h1:AHcMYuw56NPjq/2y615IGg2kYkBdTvOaojYCBcRE7MA=
|
||||
4d63.com/gocheckcompilerdirectives v1.2.1/go.mod h1:yjDJSxmDTtIHHCqX0ufRYZDL6vQtMG7tJdKVeWwsqvs=
|
||||
4d63.com/gochecknoglobals v0.2.1 h1:1eiorGsgHOFOuoOiJDy2psSrQbRdIHrlge0IJIkUgDc=
|
||||
4d63.com/gochecknoglobals v0.2.1/go.mod h1:KRE8wtJB3CXCsb1xy421JfTHIIbmT3U5ruxw2Qu8fSU=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To=
|
||||
cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4=
|
||||
cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M=
|
||||
cloud.google.com/go v0.54.0/go.mod h1:1rq2OEkV3YMf6n/9ZvGWI3GWw0VoqH/1x2nd8Is/bPc=
|
||||
cloud.google.com/go v0.56.0/go.mod h1:jr7tqZxxKOVYizybht9+26Z/gUq7tiRzu+ACVAMbKVk=
|
||||
cloud.google.com/go v0.57.0/go.mod h1:oXiQ6Rzq3RAkkY7N6t3TcE6jE+CIBBbA36lwQ1JyzZs=
|
||||
cloud.google.com/go v0.62.0/go.mod h1:jmCYTdRCQuc1PHIIJ/maLInMho30T/Y0M4hTdTShOYc=
|
||||
cloud.google.com/go v0.65.0/go.mod h1:O5N8zS7uWy9vkA9vayVHs65eM1ubvY4h553ofrNHObY=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/bigquery v1.3.0/go.mod h1:PjpwJnslEMmckchkHFfq+HTD2DmtT67aNFKH1/VBDHE=
|
||||
cloud.google.com/go/bigquery v1.4.0/go.mod h1:S8dzgnTigyfTmLBfrtrhyYhwRxG72rYxvftPBK2Dvzc=
|
||||
cloud.google.com/go/bigquery v1.5.0/go.mod h1:snEHRnqQbz117VIFhE8bmtwIDY80NLUZUMb4Nv6dBIg=
|
||||
cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4gLoIoXIAPc=
|
||||
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/datastore v1.1.0/go.mod h1:umbIZjpQpHh4hmRpGhH4tLFup+FVzqBi1b3c64qFpCk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/pubsub v1.1.0/go.mod h1:EwwdRX2sKPjnvnqCa270oGRyludottCI76h+R3AArQw=
|
||||
cloud.google.com/go/pubsub v1.2.0/go.mod h1:jhfEVHT8odbXTkndysNHCcx0awwzvfOlguIAii9o8iA=
|
||||
cloud.google.com/go/pubsub v1.3.1/go.mod h1:i+ucay31+CNRpDW4Lu78I4xXG+O1r/MAHgjpRVR+TSU=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
cloud.google.com/go/storage v1.5.0/go.mod h1:tpKbwo567HUNpVclU5sGELwQWBDZ8gh0ZeosJ0Rtdos=
|
||||
cloud.google.com/go/storage v1.6.0/go.mod h1:N7U0C8pVQ/+NIKOBQyamJIeKQKkZ+mxpohlUTyfDhBk=
|
||||
cloud.google.com/go/storage v1.8.0/go.mod h1:Wv1Oy7z6Yz3DshWRJFhqM/UCfaWIRTdp0RXyy7KQOVs=
|
||||
cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9ullr3+Kg0=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/4meepo/tagalign v1.3.4 h1:P51VcvBnf04YkHzjfclN6BbsopfJR5rxs1n+5zHt+w8=
|
||||
github.com/4meepo/tagalign v1.3.4/go.mod h1:M+pnkHH2vG8+qhE5bVc/zeP7HS/j910Fwa9TUSyZVI0=
|
||||
github.com/Abirdcfly/dupword v0.1.1 h1:Bsxe0fIw6OwBtXMIncaTxCLHYO5BB+3mcsR5E8VXloY=
|
||||
github.com/Abirdcfly/dupword v0.1.1/go.mod h1:B49AcJdTYYkpd4HjgAcutNGG9HZ2JWwKunH9Y2BA6sM=
|
||||
github.com/Antonboom/errname v0.1.13 h1:JHICqsewj/fNckzrfVSe+T33svwQxmjC+1ntDsHOVvM=
|
||||
github.com/Antonboom/errname v0.1.13/go.mod h1:uWyefRYRN54lBg6HseYCFhs6Qjcy41Y3Jl/dVhA87Ns=
|
||||
github.com/Antonboom/nilnil v0.1.9 h1:eKFMejSxPSA9eLSensFmjW2XTgTwJMjZ8hUHtV4s/SQ=
|
||||
github.com/Antonboom/nilnil v0.1.9/go.mod h1:iGe2rYwCq5/Me1khrysB4nwI7swQvjclR8/YRPl5ihQ=
|
||||
github.com/Antonboom/testifylint v1.4.3 h1:ohMt6AHuHgttaQ1xb6SSnxCeK4/rnK7KKzbvs7DmEck=
|
||||
github.com/Antonboom/testifylint v1.4.3/go.mod h1:+8Q9+AOLsz5ZiQiiYujJKs9mNz398+M6UgslP4qgJLA=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c h1:pxW6RcqyfI9/kWtOwnv/G+AzdKuy2ZrqINhenH4HyNs=
|
||||
github.com/BurntSushi/toml v1.4.1-0.20240526193622-a339e1f7089c/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/Crocmagnon/fatcontext v0.5.2 h1:vhSEg8Gqng8awhPju2w7MKHqMlg4/NI+gSDHtR3xgwA=
|
||||
github.com/Crocmagnon/fatcontext v0.5.2/go.mod h1:87XhRMaInHP44Q7Tlc7jkgKKB7kZAOPiDkFMdKCC+74=
|
||||
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 h1:sHglBQTwgx+rWPdisA5ynNEsoARbiCBOyGcJM4/OzsM=
|
||||
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs=
|
||||
github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0 h1:/fTUt5vmbkAcMBt4YQiuC23cV0kEsN1MVMNqeOW43cU=
|
||||
github.com/GaijinEntertainment/go-exhaustruct/v3 v3.3.0/go.mod h1:ONJg5sxcbsdQQ4pOW8TGdTidT2TMAUy/2Xhr8mrYaao=
|
||||
github.com/Masterminds/semver/v3 v3.3.0 h1:B8LGeaivUe71a5qox1ICM/JLl0NqZSW5CHyL+hmvYS0=
|
||||
github.com/Masterminds/semver/v3 v3.3.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
|
||||
github.com/OpenPeeDeeP/depguard/v2 v2.2.0 h1:vDfG60vDtIuf0MEOhmLlLLSzqaRM8EMcgJPdp74zmpA=
|
||||
github.com/OpenPeeDeeP/depguard/v2 v2.2.0/go.mod h1:CIzddKRvLBC4Au5aYP/i3nyaWQ+ClszLIuVocRiCYFQ=
|
||||
github.com/alecthomas/assert/v2 v2.2.2 h1:Z/iVC0xZfWTaFNE6bA3z07T86hd45Xe2eLt6WVy2bbk=
|
||||
github.com/alecthomas/assert/v2 v2.2.2/go.mod h1:pXcQ2Asjp247dahGEmsZ6ru0UVwnkhktn7S0bBDLxvQ=
|
||||
github.com/alecthomas/go-check-sumtype v0.1.4 h1:WCvlB3l5Vq5dZQTFmodqL2g68uHiSwwlWcT5a2FGK0c=
|
||||
github.com/alecthomas/go-check-sumtype v0.1.4/go.mod h1:WyYPfhfkdhyrdaligV6svFopZV8Lqdzn5pyVBaV6jhQ=
|
||||
github.com/alecthomas/repr v0.2.0 h1:HAzS41CIzNW5syS8Mf9UwXhNH1J9aix/BvDRf1Ml2Yk=
|
||||
github.com/alecthomas/repr v0.2.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190717042225-c3de453c63f4/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d/go.mod h1:rBZYJk541a8SKzHPHnH3zbiI+7dagKZ0cgpgrD7Fyho=
|
||||
github.com/alexkohler/nakedret/v2 v2.0.4 h1:yZuKmjqGi0pSmjGpOC016LtPJysIL0WEUiaXW5SUnNg=
|
||||
github.com/alexkohler/nakedret/v2 v2.0.4/go.mod h1:bF5i0zF2Wo2o4X4USt9ntUWve6JbFv02Ff4vlkmS/VU=
|
||||
github.com/alexkohler/prealloc v1.0.0 h1:Hbq0/3fJPQhNkN0dR95AVrr6R7tou91y0uHG5pOcUuw=
|
||||
github.com/alexkohler/prealloc v1.0.0/go.mod h1:VetnK3dIgFBBKmg0YnD9F9x6Icjd+9cvfHR56wJVlKE=
|
||||
github.com/alingse/asasalint v0.0.11 h1:SFwnQXJ49Kx/1GghOFz1XGqHYKp21Kq1nHad/0WQRnw=
|
||||
github.com/alingse/asasalint v0.0.11/go.mod h1:nCaoMhw7a9kSJObvQyVzNTPBDbNpdocqrSP7t/cW5+I=
|
||||
github.com/ashanbrown/forbidigo v1.6.0 h1:D3aewfM37Yb3pxHujIPSpTf6oQk9sc9WZi8gerOIVIY=
|
||||
github.com/ashanbrown/forbidigo v1.6.0/go.mod h1:Y8j9jy9ZYAEHXdu723cUlraTqbzjKF1MUyfOKL+AjcU=
|
||||
github.com/ashanbrown/makezero v1.1.1 h1:iCQ87C0V0vSyO+M9E/FZYbu65auqH0lnsOkf5FcB28s=
|
||||
github.com/ashanbrown/makezero v1.1.1/go.mod h1:i1bJLCRSCHOcOa9Y6MyF2FTfMZMFdHvxKHxgO5Z1axI=
|
||||
github.com/benbjohnson/clock v1.1.0 h1:Q92kusRqC1XV2MjkWETPvjJVqKetz1OzxZB7mHJLju8=
|
||||
github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
|
||||
github.com/bkielbasa/cyclop v1.2.1 h1:AeF71HZDob1P2/pRm1so9cd1alZnrpyc4q2uP2l0gJY=
|
||||
github.com/bkielbasa/cyclop v1.2.1/go.mod h1:K/dT/M0FPAiYjBgQGau7tz+3TMh4FWAEqlMhzFWCrgM=
|
||||
github.com/blizzy78/varnamelen v0.8.0 h1:oqSblyuQvFsW1hbBHh1zfwrKe3kcSj0rnXkKzsQ089M=
|
||||
github.com/blizzy78/varnamelen v0.8.0/go.mod h1:V9TzQZ4fLJ1DSrjVDfl89H7aMnTvKkApdHeyESmyR7k=
|
||||
github.com/bombsimon/wsl/v4 v4.4.1 h1:jfUaCkN+aUpobrMO24zwyAMwMAV5eSziCkOKEauOLdw=
|
||||
github.com/bombsimon/wsl/v4 v4.4.1/go.mod h1:Xu/kDxGZTofQcDGCtQe9KCzhHphIe0fDuyWTxER9Feo=
|
||||
github.com/breml/bidichk v0.2.7 h1:dAkKQPLl/Qrk7hnP6P+E0xOodrq8Us7+U0o4UBOAlQY=
|
||||
github.com/breml/bidichk v0.2.7/go.mod h1:YodjipAGI9fGcYM7II6wFvGhdMYsC5pHDlGzqvEW3tQ=
|
||||
github.com/breml/errchkjson v0.3.6 h1:VLhVkqSBH96AvXEyclMR37rZslRrY2kcyq+31HCsVrA=
|
||||
github.com/breml/errchkjson v0.3.6/go.mod h1:jhSDoFheAF2RSDOlCfhHO9KqhZgAYLyvHe7bRCX8f/U=
|
||||
github.com/butuzov/ireturn v0.3.0 h1:hTjMqWw3y5JC3kpnC5vXmFJAWI/m31jaCYQqzkS6PL0=
|
||||
github.com/butuzov/ireturn v0.3.0/go.mod h1:A09nIiwiqzN/IoVo9ogpa0Hzi9fex1kd9PSD6edP5ZA=
|
||||
github.com/butuzov/mirror v1.2.0 h1:9YVK1qIjNspaqWutSv8gsge2e/Xpq1eqEkslEUHy5cs=
|
||||
github.com/butuzov/mirror v1.2.0/go.mod h1:DqZZDtzm42wIAIyHXeN8W/qb1EPlb9Qn/if9icBOpdQ=
|
||||
github.com/catenacyber/perfsprint v0.7.1 h1:PGW5G/Kxn+YrN04cRAZKC+ZuvlVwolYMrIyyTJ/rMmc=
|
||||
github.com/catenacyber/perfsprint v0.7.1/go.mod h1:/wclWYompEyjUD2FuIIDVKNkqz7IgBIWXIH3V0Zol50=
|
||||
github.com/ccojocar/zxcvbn-go v1.0.2 h1:na/czXU8RrhXO4EZme6eQJLR4PzcGsahsBOAwU6I3Vg=
|
||||
github.com/ccojocar/zxcvbn-go v1.0.2/go.mod h1:g1qkXtUSvHP8lhHp5GrSmTz6uWALGRMQdw6Qnz/hi60=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/cespare/xxhash/v2 v2.1.2 h1:YRXhKfTDauu4ajMg1TPgFO5jnlC2HCbmLXMcTG5cbYE=
|
||||
github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/charithe/durationcheck v0.0.10 h1:wgw73BiocdBDQPik+zcEoBG/ob8uyBHf2iyoHGPf5w4=
|
||||
github.com/charithe/durationcheck v0.0.10/go.mod h1:bCWXb7gYRysD1CU3C+u4ceO49LoGOY1C1L6uouGNreQ=
|
||||
github.com/chavacava/garif v0.1.0 h1:2JHa3hbYf5D9dsgseMKAmc/MZ109otzgNFk5s87H9Pc=
|
||||
github.com/chavacava/garif v0.1.0/go.mod h1:XMyYCkEL58DF0oyW4qDjjnPWONs2HBqYKI+UIPD+Gww=
|
||||
github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
|
||||
github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
|
||||
github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
|
||||
github.com/ckaznocha/intrange v0.2.0 h1:FykcZuJ8BD7oX93YbO1UY9oZtkRbp+1/kJcDjkefYLs=
|
||||
github.com/ckaznocha/intrange v0.2.0/go.mod h1:r5I7nUlAAG56xmkOpw4XVr16BXhwYTUdcuRFeevn1oE=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
|
||||
github.com/curioswitch/go-reassign v0.2.0 h1:G9UZyOcpk/d7Gd6mqYgd8XYWFMw/znxwGDUstnC9DIo=
|
||||
github.com/curioswitch/go-reassign v0.2.0/go.mod h1:x6OpXuWvgfQaMGks2BZybTngWjT84hqJfKoO8Tt/Roc=
|
||||
github.com/daixiang0/gci v0.13.5 h1:kThgmH1yBmZSBCh1EJVxQ7JsHpm5Oms0AMed/0LaH4c=
|
||||
github.com/daixiang0/gci v0.13.5/go.mod h1:12etP2OniiIdP4q+kjUGrC/rUagga7ODbqsom5Eo5Yk=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/denis-tingaikin/go-header v0.5.0 h1:SRdnP5ZKvcO9KKRP1KJrhFR3RrlGuD+42t4429eC9k8=
|
||||
github.com/denis-tingaikin/go-header v0.5.0/go.mod h1:mMenU5bWrok6Wl2UsZjy+1okegmwQ3UgWl4V1D8gjlY=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/ettle/strcase v0.2.0 h1:fGNiVF21fHXpX1niBgk0aROov1LagYsOwV/xqKDKR/Q=
|
||||
github.com/ettle/strcase v0.2.0/go.mod h1:DajmHElDSaX76ITe3/VHVyMin4LWSJN5Z909Wp+ED1A=
|
||||
github.com/fatih/color v1.17.0 h1:GlRw1BRJxkpqUCBKzKOw098ed57fEsKeNjpTe3cSjK4=
|
||||
github.com/fatih/color v1.17.0/go.mod h1:YZ7TlrGPkiz6ku9fK3TLD/pl3CpsiFyu8N92HLgmosI=
|
||||
github.com/fatih/structtag v1.2.0 h1:/OdNE99OxoI/PqaW/SuSK9uxxT3f/tcSZgon/ssNSx4=
|
||||
github.com/fatih/structtag v1.2.0/go.mod h1:mBJUNpUnHmRKrKlQQlmCrh5PuhftFbNv8Ys4/aAZl94=
|
||||
github.com/firefart/nonamedreturns v1.0.5 h1:tM+Me2ZaXs8tfdDw3X6DOX++wMCOqzYUho6tUTYIdRA=
|
||||
github.com/firefart/nonamedreturns v1.0.5/go.mod h1:gHJjDqhGM4WyPt639SOZs+G89Ko7QKH5R5BhnO6xJhw=
|
||||
github.com/frankban/quicktest v1.14.3 h1:FJKSZTDHjyhriyC81FLQ0LY93eSai0ZyR/ZIkd3ZUKE=
|
||||
github.com/frankban/quicktest v1.14.3/go.mod h1:mgiwOwqx65TmIk1wJ6Q7wvnVMocbUorkibMOrVTHZps=
|
||||
github.com/fsnotify/fsnotify v1.5.4 h1:jRbGcIw6P2Meqdwuo0H1p6JVLbL5DHKAKlYndzMwVZI=
|
||||
github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU=
|
||||
github.com/fzipp/gocyclo v0.6.0 h1:lsblElZG7d3ALtGMx9fmxeTKZaLLpU8mET09yN4BBLo=
|
||||
github.com/fzipp/gocyclo v0.6.0/go.mod h1:rXPyn8fnlpa0R2csP/31uerbiVBugk5whMdlyaLkLoA=
|
||||
github.com/ghostiam/protogetter v0.3.6 h1:R7qEWaSgFCsy20yYHNIJsU9ZOb8TziSRRxuAOTVKeOk=
|
||||
github.com/ghostiam/protogetter v0.3.6/go.mod h1:7lpeDnEJ1ZjL/YtyoN99ljO4z0pd3H0d18/t2dPBxHw=
|
||||
github.com/go-critic/go-critic v0.11.4 h1:O7kGOCx0NDIni4czrkRIXTnit0mkyKOCePh3My6OyEU=
|
||||
github.com/go-critic/go-critic v0.11.4/go.mod h1:2QAdo4iuLik5S9YG0rT4wcZ8QxwHYkrr6/2MWAiv/vc=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-quicktest/qt v1.101.0 h1:O1K29Txy5P2OK0dGo59b7b0LR6wKfIhttaAhHUyn7eI=
|
||||
github.com/go-quicktest/qt v1.101.0/go.mod h1:14Bz/f7NwaXPtdYEgzsx46kqSxVwTbzVZsDC26tQJow=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
github.com/go-toolsmith/astcast v1.1.0 h1:+JN9xZV1A+Re+95pgnMgDboWNVnIMMQXwfBwLRPgSC8=
|
||||
github.com/go-toolsmith/astcast v1.1.0/go.mod h1:qdcuFWeGGS2xX5bLM/c3U9lewg7+Zu4mr+xPwZIB4ZU=
|
||||
github.com/go-toolsmith/astcopy v1.1.0 h1:YGwBN0WM+ekI/6SS6+52zLDEf8Yvp3n2seZITCUBt5s=
|
||||
github.com/go-toolsmith/astcopy v1.1.0/go.mod h1:hXM6gan18VA1T/daUEHCFcYiW8Ai1tIwIzHY6srfEAw=
|
||||
github.com/go-toolsmith/astequal v1.0.3/go.mod h1:9Ai4UglvtR+4up+bAD4+hCj7iTo4m/OXVTSLnCyTAx4=
|
||||
github.com/go-toolsmith/astequal v1.1.0/go.mod h1:sedf7VIdCL22LD8qIvv7Nn9MuWJruQA/ysswh64lffQ=
|
||||
github.com/go-toolsmith/astequal v1.2.0 h1:3Fs3CYZ1k9Vo4FzFhwwewC3CHISHDnVUPC4x0bI2+Cw=
|
||||
github.com/go-toolsmith/astequal v1.2.0/go.mod h1:c8NZ3+kSFtFY/8lPso4v8LuJjdJiUFVnSuU3s0qrrDY=
|
||||
github.com/go-toolsmith/astfmt v1.1.0 h1:iJVPDPp6/7AaeLJEruMsBUlOYCmvg0MoCfJprsOmcco=
|
||||
github.com/go-toolsmith/astfmt v1.1.0/go.mod h1:OrcLlRwu0CuiIBp/8b5PYF9ktGVZUjlNMV634mhwuQ4=
|
||||
github.com/go-toolsmith/astp v1.1.0 h1:dXPuCl6u2llURjdPLLDxJeZInAeZ0/eZwFJmqZMnpQA=
|
||||
github.com/go-toolsmith/astp v1.1.0/go.mod h1:0T1xFGz9hicKs8Z5MfAqSUitoUYS30pDMsRVIDHs8CA=
|
||||
github.com/go-toolsmith/pkgload v1.2.2 h1:0CtmHq/02QhxcF7E9N5LIFcYFsMR5rdovfqTtRKkgIk=
|
||||
github.com/go-toolsmith/pkgload v1.2.2/go.mod h1:R2hxLNRKuAsiXCo2i5J6ZQPhnPMOVtU+f0arbFPWCus=
|
||||
github.com/go-toolsmith/strparse v1.0.0/go.mod h1:YI2nUKP9YGZnL/L1/DLFBfixrcjslWct4wyljWhSRy8=
|
||||
github.com/go-toolsmith/strparse v1.1.0 h1:GAioeZUK9TGxnLS+qfdqNbA4z0SSm5zVNtCQiyP2Bvw=
|
||||
github.com/go-toolsmith/strparse v1.1.0/go.mod h1:7ksGy58fsaQkGQlY8WVoBFNyEPMGuJin1rfoPS4lBSQ=
|
||||
github.com/go-toolsmith/typep v1.1.0 h1:fIRYDyF+JywLfqzyhdiHzRop/GQDxxNhLGQ6gFUNHus=
|
||||
github.com/go-toolsmith/typep v1.1.0/go.mod h1:fVIw+7zjdsMxDA3ITWnH1yOiw1rnTQKCsF/sk2H/qig=
|
||||
github.com/go-viper/mapstructure/v2 v2.1.0 h1:gHnMa2Y/pIxElCH2GlZZ1lZSsn6XMtufpGyP1XxdC/w=
|
||||
github.com/go-viper/mapstructure/v2 v2.1.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
|
||||
github.com/go-xmlfmt/xmlfmt v1.1.2 h1:Nea7b4icn8s57fTx1M5AI4qQT5HEM3rVUO8MuE6g80U=
|
||||
github.com/go-xmlfmt/xmlfmt v1.1.2/go.mod h1:aUCEOzzezBEjDBbFBoSiya/gduyIiWYRP6CnSFIV8AM=
|
||||
github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y=
|
||||
github.com/gobwas/glob v0.2.3/go.mod h1:d3Ez4x06l9bZtSvzIay5+Yzi0fmZzPgnTbPcKjJAkT8=
|
||||
github.com/gofrs/flock v0.12.1 h1:MTLVXXHf8ekldpJk3AKicLij9MdwOWkZ+a/jHHZby9E=
|
||||
github.com/gofrs/flock v0.12.1/go.mod h1:9zxTsyu5xtJ9DK+1tFZyibEV7y3uwDxPPfbxeeHCoD0=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.1/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.4/go.mod h1:l3mdAwkq5BuhzHwde/uurv3sEJeZMXNpwsxVWU71h+4=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=
|
||||
github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a h1:w8hkcTqaFpzKqonE9uMCefW1WDie15eSP/4MssdenaM=
|
||||
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a/go.mod h1:ryS0uhF+x9jgbj/N71xsEqODy9BN81/GonCZiOzirOk=
|
||||
github.com/golangci/gofmt v0.0.0-20240816233607-d8596aa466a9 h1:/1322Qns6BtQxUZDTAT4SdcoxknUki7IAoK4SAXr8ME=
|
||||
github.com/golangci/gofmt v0.0.0-20240816233607-d8596aa466a9/go.mod h1:Oesb/0uFAyWoaw1U1qS5zyjCg5NP9C9iwjnI4tIsXEE=
|
||||
github.com/golangci/golangci-lint v1.61.0 h1:VvbOLaRVWmyxCnUIMTbf1kDsaJbTzH20FAMXTAlQGu8=
|
||||
github.com/golangci/golangci-lint v1.61.0/go.mod h1:e4lztIrJJgLPhWvFPDkhiMwEFRrWlmFbrZea3FsJyN8=
|
||||
github.com/golangci/misspell v0.6.0 h1:JCle2HUTNWirNlDIAUO44hUsKhOFqGPoC4LZxlaSXDs=
|
||||
github.com/golangci/misspell v0.6.0/go.mod h1:keMNyY6R9isGaSAu+4Q8NMBwMPkh15Gtc8UCVoDtAWo=
|
||||
github.com/golangci/modinfo v0.3.4 h1:oU5huX3fbxqQXdfspamej74DFX0kyGLkw1ppvXoJ8GA=
|
||||
github.com/golangci/modinfo v0.3.4/go.mod h1:wytF1M5xl9u0ij8YSvhkEVPP3M5Mc7XLl1pxH3B2aUM=
|
||||
github.com/golangci/plugin-module-register v0.1.1 h1:TCmesur25LnyJkpsVrupv1Cdzo+2f7zX0H6Jkw1Ol6c=
|
||||
github.com/golangci/plugin-module-register v0.1.1/go.mod h1:TTpqoB6KkwOJMV8u7+NyXMrkwwESJLOkfl9TxR1DGFc=
|
||||
github.com/golangci/revgrep v0.5.3 h1:3tL7c1XBMtWHHqVpS5ChmiAAoe4PF/d5+ULzV9sLAzs=
|
||||
github.com/golangci/revgrep v0.5.3/go.mod h1:U4R/s9dlXZsg8uJmaR1GrloUr14D7qDl8gi2iPXJH8k=
|
||||
github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed h1:IURFTjxeTfNFP0hTEi1YKjB/ub8zkpaOqFFMApi2EAs=
|
||||
github.com/golangci/unconvert v0.0.0-20240309020433-c5143eacb3ed/go.mod h1:XLXN8bNw4CGRPaqgl3bv/lhz7bsGPh4/xSaMTbo2vkQ=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20191218002539-d4f498aebedc/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200212024743-f11f1df84d12/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200229191704-1ebb73c60ed3/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200430221834-fc25d7d30c6d/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20200708004538-1a94d8640e99/go.mod h1:ZgVRPoUq/hfqzAqh7sHMqb3I9Rq5C59dIz2SbBwJ4eM=
|
||||
github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 h1:5iH8iuqE5apketRbSFBy+X1V0o+l+8NF1avt4HWl7cA=
|
||||
github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gordonklaus/ineffassign v0.1.0 h1:y2Gd/9I7MdY1oEIt+n+rowjBNDcLQq3RsH5hwJd0f9s=
|
||||
github.com/gordonklaus/ineffassign v0.1.0/go.mod h1:Qcp2HIAYhR7mNUVSIxZww3Guk4it82ghYcEXIAk+QT0=
|
||||
github.com/gostaticanalysis/analysisutil v0.7.1 h1:ZMCjoue3DtDWQ5WyU16YbjbQEQ3VuzwxALrpYd+HeKk=
|
||||
github.com/gostaticanalysis/analysisutil v0.7.1/go.mod h1:v21E3hY37WKMGSnbsw2S/ojApNWb6C1//mXO48CXbVc=
|
||||
github.com/gostaticanalysis/comment v1.4.1/go.mod h1:ih6ZxzTHLdadaiSnF5WY3dxUoXfXAlTaRzuaNDlSado=
|
||||
github.com/gostaticanalysis/comment v1.4.2 h1:hlnx5+S2fY9Zo9ePo4AhgYsYHbM2+eAv8m/s1JiCd6Q=
|
||||
github.com/gostaticanalysis/comment v1.4.2/go.mod h1:KLUTGDv6HOCotCH8h2erHKmpci2ZoR8VPu34YA2uzdM=
|
||||
github.com/gostaticanalysis/forcetypeassert v0.1.0 h1:6eUflI3DiGusXGK6X7cCcIgVCpZ2CiZ1Q7jl6ZxNV70=
|
||||
github.com/gostaticanalysis/forcetypeassert v0.1.0/go.mod h1:qZEedyP/sY1lTGV1uJ3VhWZ2mqag3IkWsDHVbplHXak=
|
||||
github.com/gostaticanalysis/nilerr v0.1.1 h1:ThE+hJP0fEp4zWLkWHWcRyI2Od0p7DlgYG3Uqrmrcpk=
|
||||
github.com/gostaticanalysis/nilerr v0.1.1/go.mod h1:wZYb6YI5YAxxq0i1+VJbY0s2YONW0HU0GPE3+5PWN4A=
|
||||
github.com/gostaticanalysis/testutil v0.3.1-0.20210208050101-bfb5c8eec0e4/go.mod h1:D+FIZ+7OahH3ePw/izIEeH5I06eKs1IKI4Xr64/Am3M=
|
||||
github.com/gostaticanalysis/testutil v0.4.0 h1:nhdCmubdmDF6VEatUNjgUZBJKWRqugoISdUv3PPQgHY=
|
||||
github.com/gostaticanalysis/testutil v0.4.0/go.mod h1:bLIoPefWXrRi/ssLFWX1dx7Repi5x3CuviD3dgAZaBU=
|
||||
github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY=
|
||||
github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hexops/gotextdiff v1.0.3 h1:gitA9+qJrrTCsiCl7+kh75nPqQt1cx4ZkudSTLoUqJM=
|
||||
github.com/hexops/gotextdiff v1.0.3/go.mod h1:pSWU5MAI3yDq+fZBTazCSJysOMbxWL1BSow5/V2vxeg=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
|
||||
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
|
||||
github.com/jgautheron/goconst v1.7.1 h1:VpdAG7Ca7yvvJk5n8dMwQhfEZJh95kl/Hl9S1OI5Jkk=
|
||||
github.com/jgautheron/goconst v1.7.1/go.mod h1:aAosetZ5zaeC/2EfMeRswtxUFBpe2Hr7HzkgX4fanO4=
|
||||
github.com/jingyugao/rowserrcheck v1.1.1 h1:zibz55j/MJtLsjP1OF4bSdgXxwL1b+Vn7Tjzq7gFzUs=
|
||||
github.com/jingyugao/rowserrcheck v1.1.1/go.mod h1:4yvlZSDb3IyDTUZJUmpZfm2Hwok+Dtp+nu2qOq+er9c=
|
||||
github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af h1:KA9BjwUk7KlCh6S9EAGWBt1oExIUv9WyNCiRz5amv48=
|
||||
github.com/jirfag/go-printf-func-name v0.0.0-20200119135958-7558a9eaa5af/go.mod h1:HEWGJkRDzjJY2sqdDwxccsGicWEf9BQOZsq2tV+xzM0=
|
||||
github.com/jjti/go-spancheck v0.6.2 h1:iYtoxqPMzHUPp7St+5yA8+cONdyXD3ug6KK15n7Pklk=
|
||||
github.com/jjti/go-spancheck v0.6.2/go.mod h1:+X7lvIrR5ZdUTkxFYqzJ0abr8Sb5LOo80uOhWNqIrYA=
|
||||
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.10/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.11/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
|
||||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/XSXhF0NWZEnDohbsk=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
|
||||
github.com/julz/importas v0.1.0 h1:F78HnrsjY3cR7j0etXy5+TU1Zuy7Xt08X/1aJnH5xXY=
|
||||
github.com/julz/importas v0.1.0/go.mod h1:oSFU2R4XK/P7kNBrnL/FEQlDGN1/6WoxXEjSSXO0DV0=
|
||||
github.com/karamaru-alpha/copyloopvar v1.1.0 h1:x7gNyKcC2vRBO1H2Mks5u1VxQtYvFiym7fCjIP8RPos=
|
||||
github.com/karamaru-alpha/copyloopvar v1.1.0/go.mod h1:u7CIfztblY0jZLOQZgH3oYsJzpC2A7S6u/lfgSXHy0k=
|
||||
github.com/kisielk/errcheck v1.7.0 h1:+SbscKmWJ5mOK/bO1zS60F5I9WwZDWOfRsC4RwfwRV0=
|
||||
github.com/kisielk/errcheck v1.7.0/go.mod h1:1kLL+jV4e+CFfueBmI1dSK2ADDyQnlrnrY/FqKluHJQ=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/kkHAIKE/contextcheck v1.1.5 h1:CdnJh63tcDe53vG+RebdpdXJTc9atMgGqdx8LXxiilg=
|
||||
github.com/kkHAIKE/contextcheck v1.1.5/go.mod h1:O930cpht4xb1YQpK+1+AgoM3mFsvxr7uyFptcnWTYUA=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kulti/thelper v0.6.3 h1:ElhKf+AlItIu+xGnI990no4cE2+XaSu1ULymV2Yulxs=
|
||||
github.com/kulti/thelper v0.6.3/go.mod h1:DsqKShOvP40epevkFrvIwkCMNYxMeTNjdWL4dqWHZ6I=
|
||||
github.com/kunwardeep/paralleltest v1.0.10 h1:wrodoaKYzS2mdNVnc4/w31YaXFtsc21PCTdvWJ/lDDs=
|
||||
github.com/kunwardeep/paralleltest v1.0.10/go.mod h1:2C7s65hONVqY7Q5Efj5aLzRCNLjw2h4eMc9EcypGjcY=
|
||||
github.com/kyoh86/exportloopref v0.1.11 h1:1Z0bcmTypkL3Q4k+IDHMWTcnCliEZcaPiIe0/ymEyhQ=
|
||||
github.com/kyoh86/exportloopref v0.1.11/go.mod h1:qkV4UF1zGl6EkF1ox8L5t9SwyeBAZ3qLMd6up458uqA=
|
||||
github.com/lasiar/canonicalheader v1.1.1 h1:wC+dY9ZfiqiPwAexUApFush/csSPXeIi4QqyxXmng8I=
|
||||
github.com/lasiar/canonicalheader v1.1.1/go.mod h1:cXkb3Dlk6XXy+8MVQnF23CYKWlyA7kfQhSw2CcZtZb0=
|
||||
github.com/ldez/gomoddirectives v0.2.4 h1:j3YjBIjEBbqZ0NKtBNzr8rtMHTOrLPeiwTkfUJZ3alg=
|
||||
github.com/ldez/gomoddirectives v0.2.4/go.mod h1:oWu9i62VcQDYp9EQ0ONTfqLNh+mDLWWDO+SO0qSQw5g=
|
||||
github.com/ldez/tagliatelle v0.5.0 h1:epgfuYt9v0CG3fms0pEgIMNPuFf/LpPIfjk4kyqSioo=
|
||||
github.com/ldez/tagliatelle v0.5.0/go.mod h1:rj1HmWiL1MiKQuOONhd09iySTEkUuE/8+5jtPYz9xa4=
|
||||
github.com/leonklingele/grouper v1.1.2 h1:o1ARBDLOmmasUaNDesWqWCIFH3u7hoFlM84YrjT3mIY=
|
||||
github.com/leonklingele/grouper v1.1.2/go.mod h1:6D0M/HVkhs2yRKRFZUoGjeDy7EZTfFBE9gl4kjmIGkA=
|
||||
github.com/lufeee/execinquery v1.2.1 h1:hf0Ems4SHcUGBxpGN7Jz78z1ppVkP/837ZlETPCEtOM=
|
||||
github.com/lufeee/execinquery v1.2.1/go.mod h1:EC7DrEKView09ocscGHC+apXMIaorh4xqSxS/dy8SbM=
|
||||
github.com/macabu/inamedparam v0.1.3 h1:2tk/phHkMlEL/1GNe/Yf6kkR/hkcUdAEY3L0hjYV1Mk=
|
||||
github.com/macabu/inamedparam v0.1.3/go.mod h1:93FLICAIk/quk7eaPPQvbzihUdn/QkGDwIZEoLtpH6I=
|
||||
github.com/magiconair/properties v1.8.6 h1:5ibWZ6iY0NctNGWo87LalDlEZ6R41TqbbDamhfG/Qzo=
|
||||
github.com/magiconair/properties v1.8.6/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60=
|
||||
github.com/maratori/testableexamples v1.0.0 h1:dU5alXRrD8WKSjOUnmJZuzdxWOEQ57+7s93SLMxb2vI=
|
||||
github.com/maratori/testableexamples v1.0.0/go.mod h1:4rhjL1n20TUTT4vdh3RDqSizKLyXp7K2u6HgraZCGzE=
|
||||
github.com/maratori/testpackage v1.1.1 h1:S58XVV5AD7HADMmD0fNnziNHqKvSdDuEKdPD1rNTU04=
|
||||
github.com/maratori/testpackage v1.1.1/go.mod h1:s4gRK/ym6AMrqpOa/kEbQTV4Q4jb7WeLZzVhVVVOQMc=
|
||||
github.com/matoous/godox v0.0.0-20230222163458-006bad1f9d26 h1:gWg6ZQ4JhDfJPqlo2srm/LN17lpybq15AryXIRcWYLE=
|
||||
github.com/matoous/godox v0.0.0-20230222163458-006bad1f9d26/go.mod h1:1BELzlh859Sh1c6+90blK8lbYy0kwQf1bYlBhBysy1s=
|
||||
github.com/matryer/is v1.4.0 h1:sosSmIWwkYITGrxZ25ULNDeKiMNzFSr4V/eqBQP0PeE=
|
||||
github.com/matryer/is v1.4.0/go.mod h1:8I/i5uYgLzgsgEloJE1U6xx5HkBQpAZvepWuujKwMRU=
|
||||
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
|
||||
github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg=
|
||||
github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0=
|
||||
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/mgechev/revive v1.3.9 h1:18Y3R4a2USSBF+QZKFQwVkBROUda7uoBlkEuBD+YD1A=
|
||||
github.com/mgechev/revive v1.3.9/go.mod h1:+uxEIr5UH0TjXWHTno3xh4u7eg6jDpXKzQccA9UGhHU=
|
||||
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY=
|
||||
github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
|
||||
github.com/moricho/tparallel v0.3.2 h1:odr8aZVFA3NZrNybggMkYO3rgPRcqjeQUlBBFVxKHTI=
|
||||
github.com/moricho/tparallel v0.3.2/go.mod h1:OQ+K3b4Ln3l2TZveGCywybl68glfLEwFGqvnjok8b+U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nakabonne/nestif v0.3.1 h1:wm28nZjhQY5HyYPx+weN3Q65k6ilSBxDb8v5S81B81U=
|
||||
github.com/nakabonne/nestif v0.3.1/go.mod h1:9EtoZochLn5iUprVDmDjqGKPofoUEBL8U4Ngq6aY7OE=
|
||||
github.com/nishanths/exhaustive v0.12.0 h1:vIY9sALmw6T/yxiASewa4TQcFsVYZQQRUQJhKRf3Swg=
|
||||
github.com/nishanths/exhaustive v0.12.0/go.mod h1:mEZ95wPIZW+x8kC4TgC+9YCUgiST7ecevsVDTgc2obs=
|
||||
github.com/nishanths/predeclared v0.2.2 h1:V2EPdZPliZymNAn79T8RkNApBjMmVKh5XRpLm/w98Vk=
|
||||
github.com/nishanths/predeclared v0.2.2/go.mod h1:RROzoN6TnGQupbC+lqggsOlcgysk3LMK/HI84Mp280c=
|
||||
github.com/nunnatsa/ginkgolinter v0.16.2 h1:8iLqHIZvN4fTLDC0Ke9tbSZVcyVHoBs0HIbnVSxfHJk=
|
||||
github.com/nunnatsa/ginkgolinter v0.16.2/go.mod h1:4tWRinDN1FeJgU+iJANW/kz7xKN5nYRAOfJDQUS9dOQ=
|
||||
github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec=
|
||||
github.com/olekukonko/tablewriter v0.0.5/go.mod h1:hPp6KlRPjbx+hW8ykQs1w3UBbZlj6HuIJcUGPhkA7kY=
|
||||
github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4=
|
||||
github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag=
|
||||
github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8=
|
||||
github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc=
|
||||
github.com/otiai10/copy v1.2.0/go.mod h1:rrF5dJ5F0t/EWSYODDu4j9/vEeYHMkc8jt0zJChqQWw=
|
||||
github.com/otiai10/copy v1.14.0 h1:dCI/t1iTdYGtkvCuBG2BgR6KZa83PTclw4U5n2wAllU=
|
||||
github.com/otiai10/copy v1.14.0/go.mod h1:ECfuL02W+/FkTWZWgQqXPWZgW9oeKCSQ5qVfSc4qc4w=
|
||||
github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJG+0mI8eUu6xqkFDYS2kb2saOteoSB3cE=
|
||||
github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs=
|
||||
github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo=
|
||||
github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc=
|
||||
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
|
||||
github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
|
||||
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/polyfloyd/go-errorlint v1.6.0 h1:tftWV9DE7txiFzPpztTAwyoRLKNj9gpVm2cg8/OwcYY=
|
||||
github.com/polyfloyd/go-errorlint v1.6.0/go.mod h1:HR7u8wuP1kb1NeN1zqTd1ZMlqUKPPHF+Id4vIPvDqVw=
|
||||
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
|
||||
github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.7.1/go.mod h1:PY5Wy2awLA44sXw4AOSfFBetzPP4j5+D6mVACh+pe2M=
|
||||
github.com/prometheus/client_golang v1.11.0/go.mod h1:Z6t4BnS23TR94PD6BsDNk8yVqroYurpAkEiz0P2BEV0=
|
||||
github.com/prometheus/client_golang v1.12.1 h1:ZiaPsmm9uiBeaSMRznKsCDNtPCS0T3JVDGF+06gjBzk=
|
||||
github.com/prometheus/client_golang v1.12.1/go.mod h1:3Z9XVyYiZYEO+YQWt3RD2R3jrbd179Rt297l4aS6nDY=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.2.0 h1:uq5h0d+GuxiXLJLNABMgp2qUWDPiLvgCzz2dUR+/W/M=
|
||||
github.com/prometheus/client_model v0.2.0/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.10.0/go.mod h1:Tlit/dnDKsSWFlCLTWaA1cyBgKHSMdTB80sz/V91rCo=
|
||||
github.com/prometheus/common v0.26.0/go.mod h1:M7rCNAaPfAosfx8veZJCuw84e35h3Cfd9VFqTh1DIvc=
|
||||
github.com/prometheus/common v0.32.1 h1:hWIdL3N2HoUx3B8j3YN9mWor0qhY/NlEKZEaXxuIRh4=
|
||||
github.com/prometheus/common v0.32.1/go.mod h1:vu+V0TpY+O6vW9J44gczi3Ap/oXXR10b+M/gUGO4Hls=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.1.3/go.mod h1:lV6e/gmhEcM9IjHGsFOCxxuZ+z1YqCvr4OA4YeYWdaU=
|
||||
github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/prometheus/procfs v0.7.3 h1:4jVXhlkAyzOScmCkXBTOLRLTz8EeU+eyjrwB/EPq0VU=
|
||||
github.com/prometheus/procfs v0.7.3/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1xBZuNvfVA=
|
||||
github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1 h1:+Wl/0aFp0hpuHM3H//KMft64WQ1yX9LdJY64Qm/gFCo=
|
||||
github.com/quasilyte/go-ruleguard v0.4.3-0.20240823090925-0fe6f58b47b1/go.mod h1:GJLgqsLeo4qgavUoL8JeGFNS7qcisx3awV/w9eWTmNI=
|
||||
github.com/quasilyte/go-ruleguard/dsl v0.3.22 h1:wd8zkOhSNr+I+8Qeciml08ivDt1pSXe60+5DqOpCjPE=
|
||||
github.com/quasilyte/go-ruleguard/dsl v0.3.22/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
|
||||
github.com/quasilyte/gogrep v0.5.0 h1:eTKODPXbI8ffJMN+W2aE0+oL0z/nh8/5eNdiO34SOAo=
|
||||
github.com/quasilyte/gogrep v0.5.0/go.mod h1:Cm9lpz9NZjEoL1tgZ2OgeUKPIxL1meE7eo60Z6Sk+Ng=
|
||||
github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727 h1:TCg2WBOl980XxGFEZSS6KlBGIV0diGdySzxATTWoqaU=
|
||||
github.com/quasilyte/regex/syntax v0.0.0-20210819130434-b3f0c404a727/go.mod h1:rlzQ04UMyJXu/aOvhd8qT+hvDrFpiwqp8MRXDY9szc0=
|
||||
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567 h1:M8mH9eK4OUR4lu7Gd+PU1fV2/qnDNfzT635KRSObncs=
|
||||
github.com/quasilyte/stdinfo v0.0.0-20220114132959-f7386bf02567/go.mod h1:DWNGW8A4Y+GyBgPuaQJuWiy0XYftx4Xm/y5Jqk9I6VQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
|
||||
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
|
||||
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryancurrah/gomodguard v1.3.5 h1:cShyguSwUEeC0jS7ylOiG/idnd1TpJ1LfHGpV3oJmPU=
|
||||
github.com/ryancurrah/gomodguard v1.3.5/go.mod h1:MXlEPQRxgfPQa62O8wzK3Ozbkv9Rkqr+wKjSxTdsNJE=
|
||||
github.com/ryanrolds/sqlclosecheck v0.5.1 h1:dibWW826u0P8jNLsLN+En7+RqWWTYrjCB9fJfSfdyCU=
|
||||
github.com/ryanrolds/sqlclosecheck v0.5.1/go.mod h1:2g3dUjoS6AL4huFdv6wn55WpLIDjY7ZgUR4J8HOO/XQ=
|
||||
github.com/sanposhiho/wastedassign/v2 v2.0.7 h1:J+6nrY4VW+gC9xFzUc+XjPD3g3wF3je/NsJFwFK7Uxc=
|
||||
github.com/sanposhiho/wastedassign/v2 v2.0.7/go.mod h1:KyZ0MWTwxxBmfwn33zh3k1dmsbF2ud9pAAGfoLfjhtI=
|
||||
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1 h1:lZUw3E0/J3roVtGQ+SCrUrg3ON6NgVqpn3+iol9aGu4=
|
||||
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY=
|
||||
github.com/sashamelentyev/interfacebloat v1.1.0 h1:xdRdJp0irL086OyW1H/RTZTr1h/tMEOsumirXcOJqAw=
|
||||
github.com/sashamelentyev/interfacebloat v1.1.0/go.mod h1:+Y9yU5YdTkrNvoX0xHc84dxiN1iBi9+G8zZIhPVoNjQ=
|
||||
github.com/sashamelentyev/usestdlibvars v1.27.0 h1:t/3jZpSXtRPRf2xr0m63i32ZrusyurIGT9E5wAvXQnI=
|
||||
github.com/sashamelentyev/usestdlibvars v1.27.0/go.mod h1:9nl0jgOfHKWNFS43Ojw0i7aRoS4j6EBye3YBhmAIRF8=
|
||||
github.com/securego/gosec/v2 v2.21.2 h1:deZp5zmYf3TWwU7A7cR2+SolbTpZ3HQiwFqnzQyEl3M=
|
||||
github.com/securego/gosec/v2 v2.21.2/go.mod h1:au33kg78rNseF5PwPnTWhuYBFf534bvJRvOrgZ/bFzU=
|
||||
github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c h1:W65qqJCIOVP4jpqPQ0YvHYKwcMEMVWIzWC5iNQQfBTU=
|
||||
github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c/go.mod h1:/PevMnwAxekIXwN8qQyfc5gl2NlkB3CQlkizAbOkeBs=
|
||||
github.com/shurcooL/go v0.0.0-20180423040247-9e1955d9fb6e/go.mod h1:TDJrrUr11Vxrven61rcy3hJMUqaf/CLWYhHNPmT14Lk=
|
||||
github.com/shurcooL/go-goon v0.0.0-20170922171312-37c2f522c041/go.mod h1:N5mDOmsrJOB+vfqUK+7DmDyjhSLIIBnXo9lvZJj3MWQ=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
|
||||
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
|
||||
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
|
||||
github.com/sivchari/containedctx v1.0.3 h1:x+etemjbsh2fB5ewm5FeLNi5bUjK0V8n0RB+Wwfd0XE=
|
||||
github.com/sivchari/containedctx v1.0.3/go.mod h1:c1RDvCbnJLtH4lLcYD/GqwiBSSf4F5Qk0xld2rBqzJ4=
|
||||
github.com/sivchari/tenv v1.10.0 h1:g/hzMA+dBCKqGXgW8AV/1xIWhAvDrx0zFKNR48NFMg0=
|
||||
github.com/sivchari/tenv v1.10.0/go.mod h1:tdY24masnVoZFxYrHv/nD6Tc8FbkEtAQEEziXpyMgqY=
|
||||
github.com/sonatard/noctx v0.0.2 h1:L7Dz4De2zDQhW8S0t+KUjY0MAQJd6SgVwhzNIc4ok00=
|
||||
github.com/sonatard/noctx v0.0.2/go.mod h1:kzFz+CzWSjQ2OzIm46uJZoXuBpa2+0y3T36U18dWqIo=
|
||||
github.com/sourcegraph/go-diff v0.7.0 h1:9uLlrd5T46OXs5qpp8L/MTltk0zikUGi0sNNyCpA8G0=
|
||||
github.com/sourcegraph/go-diff v0.7.0/go.mod h1:iBszgVvyxdc8SFZ7gm69go2KDdt3ag071iBaWPF6cjs=
|
||||
github.com/spf13/afero v1.11.0 h1:WJQKhtpdm3v2IzqG8VMqrr6Rf3UYpEF239Jy9wNepM8=
|
||||
github.com/spf13/afero v1.11.0/go.mod h1:GH9Y3pIexgf1MTIWtNGyogA5MwRIDXGUr+hbWNoBjkY=
|
||||
github.com/spf13/cast v1.5.0 h1:rj3WzYc11XZaIZMPKmwP96zkFEnnAmV8s6XbB2aY32w=
|
||||
github.com/spf13/cast v1.5.0/go.mod h1:SpXXQ5YoyJw6s3/6cMTQuxvgRl3PCJiyaX9p6b155UU=
|
||||
github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM=
|
||||
github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y=
|
||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.12.0 h1:CZ7eSOd3kZoaYDLbXnmzgQI5RlciuXBMA+18HwHRfZQ=
|
||||
github.com/spf13/viper v1.12.0/go.mod h1:b6COn30jlNxbm/V2IqWiNWkJ+vZNiMNksliPCiuKtSI=
|
||||
github.com/ssgreg/nlreturn/v2 v2.2.1 h1:X4XDI7jstt3ySqGU86YGAURbxw3oTDPK9sPEi6YEwQ0=
|
||||
github.com/ssgreg/nlreturn/v2 v2.2.1/go.mod h1:E/iiPB78hV7Szg2YfRgyIrk1AD6JVMTRkkxBiELzh2I=
|
||||
github.com/stbenjam/no-sprintf-host-port v0.1.1 h1:tYugd/yrm1O0dV+ThCbaKZh195Dfm07ysF0U6JQXczc=
|
||||
github.com/stbenjam/no-sprintf-host-port v0.1.1/go.mod h1:TLhvtIvONRzdmkFiio4O8LHsN9N74I+PhRquPsxpL0I=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
|
||||
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
|
||||
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
|
||||
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
|
||||
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
github.com/subosito/gotenv v1.4.1 h1:jyEFiXpy21Wm81FBN71l9VoMMV8H8jG+qIK3GCpY6Qs=
|
||||
github.com/subosito/gotenv v1.4.1/go.mod h1:ayKnFf/c6rvx/2iiLrJUk1e6plDbT3edrFNGqEflhK0=
|
||||
github.com/tdakkota/asciicheck v0.2.0 h1:o8jvnUANo0qXtnslk2d3nMKTFNlOnJjRrNcj0j9qkHM=
|
||||
github.com/tdakkota/asciicheck v0.2.0/go.mod h1:Qb7Y9EgjCLJGup51gDHFzbI08/gbGhL/UVhYIPWG2rg=
|
||||
github.com/tenntenn/modver v1.0.1 h1:2klLppGhDgzJrScMpkj9Ujy3rXPUspSjAcev9tSEBgA=
|
||||
github.com/tenntenn/modver v1.0.1/go.mod h1:bePIyQPb7UeioSRkw3Q0XeMhYZSMx9B8ePqg6SAMGH0=
|
||||
github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3 h1:f+jULpRQGxTSkNYKJ51yaw6ChIqO+Je8UqsTKN/cDag=
|
||||
github.com/tenntenn/text/transform v0.0.0-20200319021203-7eef512accb3/go.mod h1:ON8b8w4BN/kE1EOhwT0o+d62W65a6aPw1nouo9LMgyY=
|
||||
github.com/tetafro/godot v1.4.17 h1:pGzu+Ye7ZUEFx7LHU0dAKmCOXWsPjl7qA6iMGndsjPs=
|
||||
github.com/tetafro/godot v1.4.17/go.mod h1:2oVxTBSftRTh4+MVfUaUXR6bn2GDXCaMcOG4Dk3rfio=
|
||||
github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966 h1:quvGphlmUVU+nhpFa4gg4yJyTRJ13reZMDHrKwYw53M=
|
||||
github.com/timakin/bodyclose v0.0.0-20230421092635-574207250966/go.mod h1:27bSVNWSBOHm+qRp1T9qzaIpsWEP6TbUnei/43HK+PQ=
|
||||
github.com/timonwong/loggercheck v0.9.4 h1:HKKhqrjcVj8sxL7K77beXh0adEm6DLjV/QOGeMXEVi4=
|
||||
github.com/timonwong/loggercheck v0.9.4/go.mod h1:caz4zlPcgvpEkXgVnAJGowHAMW2NwHaNlpS8xDbVhTg=
|
||||
github.com/tomarrell/wrapcheck/v2 v2.9.0 h1:801U2YCAjLhdN8zhZ/7tdjB3EnAoRlJHt/s+9hijLQ4=
|
||||
github.com/tomarrell/wrapcheck/v2 v2.9.0/go.mod h1:g9vNIyhb5/9TQgumxQyOEqDHsmGYcGsVMOx/xGkqdMo=
|
||||
github.com/tommy-muehle/go-mnd/v2 v2.5.1 h1:NowYhSdyE/1zwK9QCLeRb6USWdoif80Ie+v+yU8u1Zw=
|
||||
github.com/tommy-muehle/go-mnd/v2 v2.5.1/go.mod h1:WsUAkMJMYww6l/ufffCD3m+P7LEvr8TnZn9lwVDlgzw=
|
||||
github.com/ultraware/funlen v0.1.0 h1:BuqclbkY6pO+cvxoq7OsktIXZpgBSkYTQtmwhAK81vI=
|
||||
github.com/ultraware/funlen v0.1.0/go.mod h1:XJqmOQja6DpxarLj6Jj1U7JuoS8PvL4nEqDaQhy22p4=
|
||||
github.com/ultraware/whitespace v0.1.1 h1:bTPOGejYFulW3PkcrqkeQwOd6NKOOXvmGD9bo/Gk8VQ=
|
||||
github.com/ultraware/whitespace v0.1.1/go.mod h1:XcP1RLD81eV4BW8UhQlpaR+SDc2givTvyI8a586WjW8=
|
||||
github.com/uudashr/gocognit v1.1.3 h1:l+a111VcDbKfynh+airAy/DJQKaXh2m9vkoysMPSZyM=
|
||||
github.com/uudashr/gocognit v1.1.3/go.mod h1:aKH8/e8xbTRBwjbCkwZ8qt4l2EpKXl31KMHgSS+lZ2U=
|
||||
github.com/xen0n/gosmopolitan v1.2.2 h1:/p2KTnMzwRexIW8GlKawsTWOxn7UHA+jCMF/V8HHtvU=
|
||||
github.com/xen0n/gosmopolitan v1.2.2/go.mod h1:7XX7Mj61uLYrj0qmeN0zi7XDon9JRAEhYQqAPLVNTeg=
|
||||
github.com/yagipy/maintidx v1.0.0 h1:h5NvIsCz+nRDapQ0exNv4aJ0yXSI0420omVANTv3GJM=
|
||||
github.com/yagipy/maintidx v1.0.0/go.mod h1:0qNf/I/CCZXSMhsRsrEPDZ+DkekpKLXAJfsTACwgXLk=
|
||||
github.com/yeya24/promlinter v0.3.0 h1:JVDbMp08lVCP7Y6NP3qHroGAO6z2yGKQtS5JsjqtoFs=
|
||||
github.com/yeya24/promlinter v0.3.0/go.mod h1:cDfJQQYv9uYciW60QT0eeHlFodotkYZlL+YcPQN+mW4=
|
||||
github.com/ykadowak/zerologlint v0.1.5 h1:Gy/fMz1dFQN9JZTPjv1hxEk+sRWm05row04Yoolgdiw=
|
||||
github.com/ykadowak/zerologlint v0.1.5/go.mod h1:KaUskqF3e/v59oPmdq1U1DnKcuHokl2/K1U4pmIELKg=
|
||||
github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
|
||||
gitlab.com/bosi/decorder v0.4.2 h1:qbQaV3zgwnBZ4zPMhGLW4KZe7A7NwxEhJx39R3shffo=
|
||||
gitlab.com/bosi/decorder v0.4.2/go.mod h1:muuhHoaJkA9QLcYHq4Mj8FJUwDZ+EirSHRiaTcTf6T8=
|
||||
go-simpler.org/assert v0.9.0 h1:PfpmcSvL7yAnWyChSjOz6Sp6m9j5lyK8Ok9pEL31YkQ=
|
||||
go-simpler.org/assert v0.9.0/go.mod h1:74Eqh5eI6vCK6Y5l3PI8ZYFXG4Sa+tkr70OIPJAUr28=
|
||||
go-simpler.org/musttag v0.12.2 h1:J7lRc2ysXOq7eM8rwaTYnNrHd5JwjppzB6mScysB2Cs=
|
||||
go-simpler.org/musttag v0.12.2/go.mod h1:uN1DVIasMTQKk6XSik7yrJoEysGtR2GRqvWnI9S7TYM=
|
||||
go-simpler.org/sloglint v0.7.2 h1:Wc9Em/Zeuu7JYpl+oKoYOsQSy2X560aVueCW/m6IijY=
|
||||
go-simpler.org/sloglint v0.7.2/go.mod h1:US+9C80ppl7VsThQclkM7BkCHQAzuz8kHLsW3ppuluo=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw=
|
||||
go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw=
|
||||
go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc=
|
||||
go.uber.org/automaxprocs v1.5.3 h1:kWazyxZUrS3Gs4qUpbwo5kEIMGe/DAvi5Z4tl2NW4j8=
|
||||
go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnwa1WM0=
|
||||
go.uber.org/goleak v1.1.11 h1:wy28qYRKZgnJTxGxvye5/wgWr1EKjmUDGYox5mGlRlI=
|
||||
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
|
||||
go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4=
|
||||
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
|
||||
go.uber.org/zap v1.24.0 h1:FiJd5l1UOLj0wCgbSE0rwwXHzEdAZS6hiiSnxJN/D60=
|
||||
go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
|
||||
golang.org/x/crypto v0.1.0/go.mod h1:RecgLatLF4+eUMCP1PoPZQb+cVrJcOPbHkTkbkB9sbw=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4=
|
||||
golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM=
|
||||
golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU=
|
||||
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk=
|
||||
golang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=
|
||||
golang.org/x/exp/typeparams v0.0.0-20220428152302-39d4317da171/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||
golang.org/x/exp/typeparams v0.0.0-20230203172020-98cc5a0785f9/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||
golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f h1:phY1HzDcf18Aq9A8KkmRtY9WvOFIxN8wgfvy6Zm1DV8=
|
||||
golang.org/x/exp/typeparams v0.0.0-20240314144324-c7f7c6466f7f/go.mod h1:AbB0pIl9nAr9wVwH+Z2ZpaocVmF5I4GyWCDIsVjR0bk=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs=
|
||||
golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.1.1-0.20191107180719-034126e5016b/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.5.1/go.mod h1:5OXOZSfqPIIbmVBIIKWRFfZjPR0E5r58TLhUjH0a2Ro=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220106191415-9b9b3d81d5e3/go.mod h1:3p9vT2HGsQu2K1YbXdKPJLVgG5VJdoTa1poYQBtP1AY=
|
||||
golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
|
||||
golang.org/x/mod v0.6.0/go.mod h1:4mET923SAdbXp2ki8ey+zGs1SLqsuM2Y0uvdZR/fUNI=
|
||||
golang.org/x/mod v0.7.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
|
||||
golang.org/x/mod v0.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0=
|
||||
golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20191209160850-c0dbc17a3553/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200222125558-5a598a2470a0/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200506145744-7e3656a0809f/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
|
||||
golang.org/x/net v0.0.0-20200625001655-4c5254603344/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
|
||||
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
|
||||
golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY=
|
||||
golang.org/x/net v0.5.0/go.mod h1:DivGGAXEgPSlEBzxGzZI+ZLohi+xUj054jfeKui00ws=
|
||||
golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
|
||||
golang.org/x/net v0.30.0 h1:AcW1SDZMkb8IpzCdQUaIq2sP4sZ4zw+55h6ynffypl4=
|
||||
golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20210514164344-f6687ab2804c/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.8.0 h1:3NFvSEYkUoMifnESzZl15y791HH1qU2xm6eCJU5ZPXQ=
|
||||
golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190726091711-fc99dfbffb4e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191001151750-bb3f8db39f24/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191228213918-04cbcbbfeed8/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200106162015-b016eb3dc98e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200113162924-86b910548bc1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200122134326-e047566fdf82/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200212091648-12a6c2dcc1e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200302150141-5c8b2ff67527/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200501052902-10377860bb8e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200511232937-7e40ca221e25/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200515095857-1151b9dac4a9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200523222454-059865788121/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200615200032-f1bc736245b1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200625212154-ddb9806d33ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200803210538-64077c9b5642/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210603081109-ebe580a85c40/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211019181941-9d821ace8654/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211105183446-c75c47738b0c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220114195835-da31bd327af9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.26.0 h1:KHjCJyddX0LoSTb3J+vWpupP9p0oznkqVk/IfjymZbo=
|
||||
golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
|
||||
golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc=
|
||||
golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
|
||||
golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
golang.org/x/text v0.18.0 h1:XvMDiNzPAl0jr17s6W9lcaIhGUfUORdGCNsuLmPG224=
|
||||
golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190321232350-e250d351ecad/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190910044552-dd2b5c81c578/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191108193012-7d206e10da11/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191113191852-77e3bb0ad9e7/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191115202509-3a792d9c32b2/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191125144606-a911d9008d1f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191130070609-6e064ea0cf2d/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191216173652-a0e659d51361/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20191227053925-7b8e75db28f4/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200117161641-43d50277825c/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200122220014-bf1340f18c4a/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200204074204-1cc6d1ef6c74/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200207183749-b753a1ba74fa/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200212150539-ea181f53ac56/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200224181240-023911ca70b2/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200227222343-706bc42d1f0d/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=
|
||||
golang.org/x/tools v0.0.0-20200304193943-95d2e580d8eb/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200312045724-11d5b4c81c7d/go.mod h1:o4KQGtdN14AW+yjsvvwRTJJuXz8XRtIHtEnmAXLyFUw=
|
||||
golang.org/x/tools v0.0.0-20200324003944-a576cf524670/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200329025819-fd4102a86c65/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200331025713-a30bf2db82d4/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8=
|
||||
golang.org/x/tools v0.0.0-20200501065659-ab2804fb9c9d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200512131952-2bc93b1c0c88/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200515010526-7d3b6ebf133d/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200618134242-20370b0cb4b2/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20200724022722-7017fd6b1305/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200729194436-6467de6f59a7/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200804011535-6c149bb5ef0d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200820010801-b793a1359eac/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20200825202427-b303f430e36d/go.mod h1:njjCfa9FT2d7l9Bc6FUM5FLjQPp3cFF28FI3qnDFljA=
|
||||
golang.org/x/tools v0.0.0-20201023174141-c8cfbd0f21e6/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/tools v0.1.1-0.20210205202024-ef80cdb6ec6d/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU=
|
||||
golang.org/x/tools v0.1.1-0.20210302220138-2ac05c832e1a/go.mod h1:9bzcO0MWcOuT0tm1iBGzDVPshzfwoVvREIui8C+MHqU=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
golang.org/x/tools v0.1.9/go.mod h1:nABZi5QlRsZVlzPpHl034qft6wpY4eDcsTt5AaioBiU=
|
||||
golang.org/x/tools v0.1.10/go.mod h1:Uh6Zz+xoGYZom868N8YTex3t7RhtHDBrE8Gzo9bV56E=
|
||||
golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
|
||||
golang.org/x/tools v0.2.0/go.mod h1:y4OqIKeOV/fWJetJ8bXPU1sEVniLMIyDAZWeHdV+NTA=
|
||||
golang.org/x/tools v0.3.0/go.mod h1:/rWhSS2+zyEVwoJf8YAX6L2f0ntZ7Kn/mGgAWcipA5k=
|
||||
golang.org/x/tools v0.5.0/go.mod h1:N+Kgy78s5I24c24dU8OfWNEotWjutIs8SnJvn5IDq+k=
|
||||
golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
|
||||
golang.org/x/tools v0.26.0 h1:v/60pFQmzmT9ExmjDv2gGIfi3OqfKoEP6I5+umXlbnQ=
|
||||
golang.org/x/tools v0.26.0/go.mod h1:TPVVj70c7JJ3WCazhD8OdXcZg/og+b9+tH/KxylGwH0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.15.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/api v0.17.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.18.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.19.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.20.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.22.0/go.mod h1:BwFmGc8tA3vsd7r/7kR8DY7iEEGSU04BFxCo5jP/sfE=
|
||||
google.golang.org/api v0.24.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.28.0/go.mod h1:lIXQywCXRcnZPGlsd8NbLnOjtAoL6em04bJ9+z0MncE=
|
||||
google.golang.org/api v0.29.0/go.mod h1:Lcubydp8VUV7KeIHD9z2Bys/sm/vGKnG1UHuDBSrHWM=
|
||||
google.golang.org/api v0.30.0/go.mod h1:QGmEvQ87FHZNiUVJkT14jQNYJ4ZJjdRF23ZXz5138Fc=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/appengine v1.6.6/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191115194625-c23dd37a84c9/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191216164720-4f79533eabd1/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20191230161307-f3c370f40bfb/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200115191322-ca5a22157cba/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200122232147-0452cf42e150/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200204135345-fa8e72b47b90/go.mod h1:GmwEX6Z4W5gMy59cAlVYjN9JhxgbQH6Gn+gFDQe2lzA=
|
||||
google.golang.org/genproto v0.0.0-20200212174721-66ed5ce911ce/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200224152610-e50cd9704f63/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200228133532-8c2c7df3a383/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200305110556-506484158171/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200312145019-da6875a35672/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200331122359-1ee6d9798940/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200430143042-b979b6f78d84/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200511104702-f5ebc3bea380/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200515170657-fc4c6c6a6587/go.mod h1:YsZOwe1myG/8QRHRsmBRE1LrgQY60beZKjly0O1fX9U=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200618031413-b414f8b61790/go.mod h1:jDfRM7FcilCzHH/e9qn6dsT145K34l5v+OpcnNgKAAA=
|
||||
google.golang.org/genproto v0.0.0-20200729003335-053ba62fc06f/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200804131852-c06518451d9c/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/genproto v0.0.0-20200825200019-8632dd797987/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.27.1/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.28.0/go.mod h1:rpkK4SK4GF4Ach/+MFLZUBavHOvF2JJB5uozKKal+60=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=
|
||||
google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA=
|
||||
gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
|
||||
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k=
|
||||
honnef.co/go/tools v0.5.1 h1:4bH5o3b5ZULQ4UrBmP+63W9r7qIkqJClEA9ko5YKx+I=
|
||||
honnef.co/go/tools v0.5.1/go.mod h1:e9irvo83WDG9/irijV44wr3tbhcFeRnfpVlRqVwpzMs=
|
||||
mvdan.cc/gofumpt v0.7.0 h1:bg91ttqXmi9y2xawvkuMXyvAA/1ZGJqYAEGjXuP0JXU=
|
||||
mvdan.cc/gofumpt v0.7.0/go.mod h1:txVFJy/Sc/mvaycET54pV8SW8gWxTlUuGHVEcncmNUo=
|
||||
mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f h1:lMpcwN6GxNbWtbpI1+xzFLSW8XzX0u72NttUGVFjO3U=
|
||||
mvdan.cc/unparam v0.0.0-20240528143540-8a5130ca722f/go.mod h1:RSLa7mKKCNeTTMHBw5Hsy2rfJmd6O2ivt9Dw9ZqCQpQ=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0=
|
||||
rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA=
|
|
@ -0,0 +1,9 @@
|
|||
//go:build tools
|
||||
// +build tools
|
||||
|
||||
package tools
|
||||
|
||||
import (
|
||||
_ "github.com/golangci/golangci-lint/cmd/golangci-lint"
|
||||
_ "golang.org/x/tools/cmd/goimports"
|
||||
)
|
Loading…
Reference in New Issue