Compare commits

...

85 Commits

Author SHA1 Message Date
Buildpacks Robot f521f92a09
Bump pipeline from 1.33.0 to 1.37.5 (#288)
* Bump pipeline from 1.33.0 to 1.37.5

Bumps pipeline from 1.33.0 to 1.37.5.

Signed-off-by: GitHub <noreply@github.com>

* Apply suggestions from code review

Signed-off-by: Daniel Mikusa <dan@mikusa.com>

---------

Signed-off-by: GitHub <noreply@github.com>
Signed-off-by: Daniel Mikusa <dan@mikusa.com>
Co-authored-by: buildpack-bot <buildpack-bot@users.noreply.github.com>
Co-authored-by: Daniel Mikusa <dan@mikusa.com>
2024-04-24 22:51:23 -04:00
Buildpacks Robot e20ec30ee2
Bump Go Modules (#287)
Bumps Go modules used by the project. See the commit for details on what modules were updated.

Signed-off-by: GitHub <noreply@github.com>
Co-authored-by: buildpack-bot <buildpack-bot@users.noreply.github.com>
2024-04-24 22:47:07 -04:00
Ralf Pannemans 18b4f48b23
Do not enforce presence of CNB_STACK_ID (#284)
Signed-off-by: Johannes Dillmann <j.dillmann@sap.com>
Co-authored-by: Johannes Dillmann <j.dillmann@sap.com>
2024-04-19 08:48:41 -04:00
Philipp Stehle 7f587a123f
align toml tag of `working-dir` with spec (#268)
See https://github.com/buildpacks/spec/blob/buildpack/v0.10/buildpack.md#launchtoml-toml

Signed-off-by: Ralf Pannemans <ralf.pannemans@sap.com>
Co-authored-by: Ralf Pannemans <ralf.pannemans@sap.com>
2023-11-28 11:53:48 -05:00
Buildpacks Robot 77105e2978
Bump Go Modules (#265)
Bumps Go modules used by the project. See the commit for details on what modules were updated.

Signed-off-by: GitHub <noreply@github.com>
Co-authored-by: buildpack-bot <buildpack-bot@users.noreply.github.com>
2023-10-22 23:13:19 -04:00
Ralf Pannemans d17194071c
Convert nested binding credentials to JSON (#264)
Convert nested binding credentials to JSON (#256)

Signed-off-by: Johannes Dillmann <j.dillmann@sap.com>
Signed-off-by: Pavel Busko <pavel.busko@sap.com>
Signed-off-by: Ralf Pannemans <ralf.pannemans@sap.com>
Co-authored-by: Johannes Dillmann <modulo11@users.noreply.github.com>
Co-authored-by: Pavel Busko <pavel.busko@sap.com>
Co-authored-by: Johannes Dillmann <j.dillmann@sap.com>
2023-10-22 22:21:05 -04:00
Aidan Delaney 9021dcf494
Add a libcnb tutorial (#242) 2023-10-04 08:43:03 -04:00
Buildpacks Robot 51c0f7c9d8
Bump pipeline from 1.32.0 to 1.33.0 (#246)
* Bump pipeline from 1.32.0 to 1.33.0

Bumps pipeline from 1.32.0 to 1.33.0.

Signed-off-by: GitHub <noreply@github.com>

* Update .github/workflows/pb-synchronize-labels.yml

Signed-off-by: Daniel Mikusa <dan@mikusa.com>

* Update .github/workflows/pb-tests.yml

Signed-off-by: Daniel Mikusa <dan@mikusa.com>

* Update .github/workflows/pb-update-draft-release.yml

Signed-off-by: Daniel Mikusa <dan@mikusa.com>

* Update .github/workflows/pb-update-pipeline.yml

Signed-off-by: Daniel Mikusa <dan@mikusa.com>

---------

Signed-off-by: GitHub <noreply@github.com>
Signed-off-by: Daniel Mikusa <dan@mikusa.com>
Co-authored-by: buildpack-bot <buildpack-bot@users.noreply.github.com>
Co-authored-by: Daniel Mikusa <dan@mikusa.com>
2023-07-07 14:57:14 -04:00
Buildpacks Robot 053fa626f2
Bump Go Modules (#245)
Bumps Go modules used by the project. See the commit for details on what modules were updated.

Signed-off-by: GitHub <noreply@github.com>
Co-authored-by: buildpack-bot <buildpack-bot@users.noreply.github.com>
2023-07-07 14:54:21 -04:00
Pavel Busko e9faf69a99
Distinguish provider and type when reading binding from VCAP_SERVICES (1.x BACKPORT) (#244)
Distinguish provider and type when reading binding from VCAP_SERVICES (#235)

Signed-off-by: Ralf Pannemans <ralf.pannemans@sap.com>
Co-authored-by: Ralf Pannemans <ralf.pannemans@sap.com>
2023-07-07 14:47:14 -04:00
Ralf Pannemans 60df949646
Read bindings from VCAP_SERVICES (#228)
Read bindings from VCAP_SERVICES

Signed-off-by: Ralf Pannemans <ralf.pannemans@sap.com>
2023-05-04 22:12:05 -04:00
Daniel Mikusa 8b7f79763b
Update pipeline-builder (#226)
Signed-off-by: Daniel Mikusa <dan@mikusa.com>
2023-04-12 23:06:47 +01:00
Daniel Mikusa 09cee453ad
Update main dependencies
Signed-off-by: Daniel Mikusa <dan@mikusa.com>
2023-04-12 09:22:32 -04:00
Daniel Mikusa 0f3d43690e
Remove usage of ioutil/*
Signed-off-by: Daniel Mikusa <dan@mikusa.com>
2023-04-12 09:19:56 -04:00
Daniel Mikusa fb77fe5013
Regenerate mocks with latest version of `mockery`
Signed-off-by: Daniel Mikusa <dan@mikusa.com>
2023-04-12 09:00:51 -04:00
Daniel Mikusa 539663a25f
Fix linter errors
Signed-off-by: Daniel Mikusa <dan@mikusa.com>
2023-04-12 09:00:20 -04:00
Daniel Mikusa 8660a07c64
Update lint tools & linter definitions
Signed-off-by: Daniel Mikusa <dan@mikusa.com>
2023-04-12 08:59:36 -04:00
Daniel Mikusa 25ba7f615a
Update pipelines (#168)
- Updates to most recent pipeline-builder version
- Updates pipelines to use Go 1.18
- Updates pipelines to use 'go install' instead of 'go get'
- Adds a pipeline to check for and update Go versions & to check for transitive go.mod updates, which dependabot does not do

Signed-off-by: Daniel Mikusa <dmikusa@vmware.com>

Signed-off-by: Daniel Mikusa <dmikusa@vmware.com>
Co-authored-by: Sambhav Kothari <skothari44@bloomberg.net>
2022-08-19 17:28:46 +00:00
Daniel Mikusa 6305526cf8
Use curl to install richgo (#167)
This PR changes the workflow to use 'curl' to fetch and install richgo instead of 'go get'. We could switch to 'go install', but 'curl' is faster than fetching & building.

This is what the pipeline-builder project has been doing to install richgo for quite a while now.

Signed-off-by: Daniel Mikusa <dmikusa@vmware.com>

Signed-off-by: Daniel Mikusa <dmikusa@vmware.com>
2022-08-11 08:51:16 +01:00
Daniel Mikusa 2e5389a593
Ensure directories are created when process-specific env variables are used (#159)
* Ensure directories are created when process-specific env variables are used
* Add comment to explain the need for extra calls to makedirs

Signed-off-by: Daniel Mikusa <dmikusa@vmware.com>

Co-authored-by: Sambhav Kothari <skothari44@bloomberg.net>
2022-07-14 21:45:18 -04:00
Daniel Mikusa 587c59c91d
Bump go module dependencies (#161)
I do not believe that Dependabot is running against this branch, so we'll need to manually update them occasionally.

Signed-off-by: Daniel Mikusa <dmikusa@vmware.com>
2022-07-14 21:44:07 +01:00
Buildpacks Robot 5f657b5cfc
Bump pipeline from 1.21.1 to 1.21.2 (#138)
Bumps pipeline from 1.21.1 to 1.21.2.

Signed-off-by: GitHub <noreply@github.com>

Co-authored-by: buildpack-bot <buildpack-bot@users.noreply.github.com>
2022-04-20 07:18:12 +02:00
Buildpacks Robot 6e3b073b7b
Bump pipeline from 1.20.0 to 1.21.1 (#137)
Co-authored-by: buildpack-bot <buildpack-bot@users.noreply.github.com>
2022-04-12 10:23:26 +01:00
Sambhav Kothari 52e0183e1d
Add support for buildpack api 0.8 (#131) 2022-04-11 16:13:40 +01:00
dependabot[bot] 2f1a4584c6
Bump github.com/BurntSushi/toml from 1.0.0 to 1.1.0 (#136)
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-04-09 12:44:21 +01:00
Gabe Cemaj 963d4bea64
Use semver to parse and check BP API versions (#134)
Signed-off-by: gcemaj <gcemaj@bloomberg.net>

Co-authored-by: gcemaj <gcemaj@bloomberg.net>
2022-04-07 21:53:45 +01:00
Sambhav Kothari 71c0efab66
Merge pull request #133 from gcemaj/gcemaj-launch-working-dir
Add support for working-directory in launch.toml (API>=0.8)
2022-04-05 22:11:45 +01:00
Sambhav Kothari 3d9ff459f6
Merge branch 'main' into gcemaj-launch-working-dir 2022-04-05 21:22:38 +01:00
Sambhav Kothari abe34281b4
Merge pull request #125 from buildpacks/dependabot/go_modules/github.com/stretchr/testify-1.7.1
Bump github.com/stretchr/testify from 1.7.0 to 1.7.1
2022-04-05 21:20:00 +01:00
Sambhav Kothari 8b98521c54
Merge branch 'main' into dependabot/go_modules/github.com/stretchr/testify-1.7.1 2022-04-05 21:16:53 +01:00
Sambhav Kothari e4b66fbc45
Merge pull request #135 from buildpacks/dependabot/go_modules/github.com/BurntSushi/toml-1.1.0
Bump github.com/BurntSushi/toml from 1.0.0 to 1.1.0
2022-04-05 21:09:25 +01:00
gcemaj 5c5fb9c447 [action] add working-directory to launch.toml (API>=0.8)
Signed-off-by: gcemaj <gcemaj@bloomberg.net>
2022-04-05 15:08:21 -04:00
dependabot[bot] 69cca5e154
Bump github.com/BurntSushi/toml from 1.0.0 to 1.1.0
Bumps [github.com/BurntSushi/toml](https://github.com/BurntSushi/toml) from 1.0.0 to 1.1.0.
- [Release notes](https://github.com/BurntSushi/toml/releases)
- [Commits](https://github.com/BurntSushi/toml/compare/v1.0.0...v1.1.0)

---
updated-dependencies:
- dependency-name: github.com/BurntSushi/toml
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-04-05 19:03:25 +00:00
Sambhav Kothari a9e6d2093f
Merge branch 'main' into dependabot/go_modules/github.com/stretchr/testify-1.7.1 2022-04-02 02:00:39 +01:00
Sambhav Kothari 31fc6e39de
Merge pull request #129 from buildpacks/dependabot/go_modules/github.com/onsi/gomega-1.19.0
Bump github.com/onsi/gomega from 1.18.1 to 1.19.0
2022-04-02 02:00:21 +01:00
dependabot[bot] 8ebd909600
Bump github.com/onsi/gomega from 1.18.1 to 1.19.0
Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.18.1 to 1.19.0.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.18.1...v1.19.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-28 19:05:28 +00:00
dependabot[bot] fdc1649156
Bump github.com/stretchr/testify from 1.7.0 to 1.7.1
Bumps [github.com/stretchr/testify](https://github.com/stretchr/testify) from 1.7.0 to 1.7.1.
- [Release notes](https://github.com/stretchr/testify/releases)
- [Commits](https://github.com/stretchr/testify/compare/v1.7.0...v1.7.1)

---
updated-dependencies:
- dependency-name: github.com/stretchr/testify
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-03-16 19:05:09 +00:00
Sambhav Kothari e2931f6866
Merge pull request #123 from buildpacks/update/pipeline
Bump pipeline from 1.19.0 to 1.20.0
2022-03-11 05:59:03 +00:00
buildpack-bot 007e6d2e3e Bump pipeline from 1.19.0 to 1.20.0
Bumps pipeline from 1.19.0 to 1.20.0.

Signed-off-by: GitHub <noreply@github.com>
2022-03-11 05:10:39 +00:00
Sambhav Kothari 0df8dedcfe
Merge pull request #118 from dmikusa-pivotal/deps-update 2022-03-03 19:03:31 +00:00
Daniel Mikusa 7ed3dc927c
Updates to go 1.17
Updates go.mod to use go 1.17 to unlock features in newer versions of Go.

Signed-off-by: Daniel Mikusa <dmikusa@vmware.com>
2022-03-03 12:34:13 -05:00
Emily Casey bc167dd856
Merge pull request #117 from buildpacks/update/pipeline
Bump pipeline from 1.18.0 to 1.19.0
2022-02-25 09:50:17 -05:00
buildpack-bot 714a5dc525 Bump pipeline from 1.18.0 to 1.19.0
Bumps pipeline from 1.18.0 to 1.19.0.

Signed-off-by: GitHub <noreply@github.com>
2022-02-15 05:10:59 +00:00
Sambhav Kothari fdc252a082
Merge pull request #116 from buildpacks/update/pipeline
Bump pipeline from 1.17.0 to 1.18.0
2022-02-10 06:10:28 +00:00
buildpack-bot 62037e0b7a Bump pipeline from 1.17.0 to 1.18.0
Bumps pipeline from 1.17.0 to 1.18.0.

Signed-off-by: GitHub <noreply@github.com>
2022-02-10 05:10:51 +00:00
Sambhav Kothari 6a941db0cd
Merge pull request #115 from dmikusa-pivotal/lifecycle_0_13_3 2022-02-02 19:35:08 +00:00
Daniel Mikusa f496f6e88d
Adds WithBOMLabel Config Option to control writing BOM Labels
In some environments and with some applications, a long enough BOM label may be generated that it will either break Kubernetes or cause your application to fail to start. With the changes in lifecycle 0.13.3, we are enabling the BOM label again, but due to the issue above we also need a way for users to disable it if there are problems.

When running `libcnb.Build` you may now include an `Option` of `WithBOMLabel` that has an argument of either true or false. If not set, it defaults to false. This controls the output of libcnb.BOMEntry items in `launch.toml` and `build.toml`. If true, BOM entries are written. If false, BOM entries are not written, even if they are returned by the buildpack.

Signed-off-by: Daniel Mikusa <dmikusa@vmware.com>
2022-02-01 14:08:07 -05:00
Daniel Mikusa 86ede05ba0
Permit both BOM formats at the same time
Starting with lifecycle 0.13.3, it is permitted to have both the old style label-based BOM information and the new style layer-based BOM information. If the buildpack API is 0.6 or older, label-based BOMs only is OK. If the buildpack API is 0.7, you may have both label-based BOM and layer-based BOM or just layer-based BOM. It is permitted to have just label-based BOM, however, that will generate a warning from the lifecycle.

Signed-off-by: Daniel Mikusa <dmikusa@vmware.com>
2022-01-31 09:50:30 -05:00
Sambhav Kothari d3e6e18255
Merge pull request #114 from buildpacks/dependabot/go_modules/github.com/onsi/gomega-1.18.1
Bump github.com/onsi/gomega from 1.18.0 to 1.18.1
2022-01-28 21:35:41 +00:00
dependabot[bot] a0a8708c7a
Bump github.com/onsi/gomega from 1.18.0 to 1.18.1
Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.18.0 to 1.18.1.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.18.0...v1.18.1)

---
updated-dependencies:
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-28 19:02:39 +00:00
Sambhav Kothari 53c63e4082
Merge pull request #112 from buildpacks/dependabot/go_modules/github.com/onsi/gomega-1.18.0
Bump github.com/onsi/gomega from 1.17.0 to 1.18.0
2022-01-24 23:00:38 +00:00
dependabot[bot] df6fd7ca71
Bump github.com/onsi/gomega from 1.17.0 to 1.18.0
Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.17.0 to 1.18.0.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.17.0...v1.18.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-24 19:06:33 +00:00
Sambhav Kothari d91d62a519
Merge pull request #111 from buildpacks/update/pipeline
Bump pipeline from 1.16.0 to 1.17.0
2022-01-24 07:42:44 +00:00
buildpack-bot 4d89af3641 Bump pipeline from 1.16.0 to 1.17.0
Bumps pipeline from 1.16.0 to 1.17.0.

Signed-off-by: GitHub <noreply@github.com>
2022-01-24 05:10:12 +00:00
Sambhav Kothari f4abcd0215
Merge pull request #110 from buildpacks/dependabot/go_modules/github.com/BurntSushi/toml-1.0.0 2022-01-12 19:08:04 +00:00
dependabot[bot] f04e4e2cf2
Bump github.com/BurntSushi/toml from 0.4.1 to 1.0.0
Bumps [github.com/BurntSushi/toml](https://github.com/BurntSushi/toml) from 0.4.1 to 1.0.0.
- [Release notes](https://github.com/BurntSushi/toml/releases)
- [Commits](https://github.com/BurntSushi/toml/compare/v0.4.1...v1.0.0)

---
updated-dependencies:
- dependency-name: github.com/BurntSushi/toml
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-01-12 19:03:14 +00:00
Sambhav Kothari 49c96bbc6c
Merge pull request #109 from buildpacks/samj1912-patch-1 2022-01-11 18:40:07 +00:00
Sambhav Kothari 2d891dd194
Only validate for API versions 0.5 and 0.6
libcnb is only supported for versions 0.5+ and we check for it a few lines above. Removing some unnecessary checks

Signed-off-by: Sambhav Kothari <skothari44@bloomberg.net>
2022-01-11 18:20:04 +00:00
Sambhav Kothari 896a63b4e3
Merge pull request #108 from dmikusa-pivotal/gh_issue_107 2022-01-11 18:15:26 +00:00
Daniel Mikusa 45c858b545
Validation should only occur under API 0.7
- Adds API checks before validating SBOM format, should only happen with API 0.7+
- Adds a test to confirm validation does not run if the API is less than 0.7.

Resolves #107

Signed-off-by: Daniel Mikusa <dmikusa@vmware.com>
2022-01-10 16:36:14 -05:00
Sambhav Kothari c9ad73fb07
Merge pull request #106 from buildpacks/binding
Use appropriate bindings path for detect
2022-01-06 19:58:38 +00:00
Sambhav Kothari f869c1d937
Use appropriate bindings path for detect
Signed-off-by: Sambhav Kothari <skothari44@bloomberg.net>
2022-01-05 22:21:53 +00:00
Sambhav Kothari 4a789fd9d2
Merge pull request #100 from buildpacks/update/pipeline
Bump pipeline from 1.15.1 to 1.16.0
2021-12-06 09:21:08 +00:00
buildpack-bot d67b3ffea7 Bump pipeline from 1.15.1 to 1.16.0
Bumps pipeline from 1.15.1 to 1.16.0.

Signed-off-by: GitHub <noreply@github.com>
2021-12-06 05:11:25 +00:00
Sambhav Kothari 128cdb9dbe
Merge pull request #99 from dmikusa-pivotal/sbom-bug
Fixes bug with Warning message regarding SBOM format
2021-11-29 23:10:52 +00:00
Daniel Mikusa 77cb45d022
Fixes bug with Warning message regarding SBOM format
In the current implementation, it is possible for the `Warning: this buildpack is including both old and new format SBOM...` message to be triggered incorrectly. The warning is displayed if the launch or build object are not empty & if the API is `0.7`. This isn't right though. If you have no SBOM entries, but you have process types or labels, then it would be not empty & you'd see this message incorrectly.

This PR adjust the criteria such that you'll see this warning message if launch or build are not empty, if API is `0.7` and if BOM entries is not empty.

Signed-off-by: Daniel Mikusa <dmikusa@vmware.com>
2021-11-29 16:35:16 -05:00
Sambhav Kothari a33b3677ad
Merge pull request #98 from buildpacks/polish-pr-97
Polish PR: Additional updates for SBOM Support
2021-11-24 08:34:09 +00:00
Sambhav Kothari 6d86013d6d Make validate SBOM private
Signed-off-by: Sambhav Kothari <skothari44@bloomberg.net>
2021-11-23 18:52:42 +00:00
Daniel Mikusa 66f3e9f328
Additional updates for SBOM Support
- Adds SBOMFormats (maps to `sbom-formats` in buildpack.toml) to the BuildpackInfo struct. This makes the information accessible to buildpacks.
- Modifies build such that it only writes the old-style build and launch BOM information if the buildpack API is less than 0.7. If it's 0.7+, it should not write the old style format as that can conflict with the new SBOM format and cause the lifecycle to fail. Omits a warning message if this occurs.
- Adds validation of the new SBOM files that are written by a buildpack. We check that the extension matches up with a valid MIME type that is listed in buildpack.toml's `sbom-formats` field. If it does not match up, then it fails. This should not generally happen with published buildpacks. This check can be helpful while authoring buildpacks, to ensure everything is correctly setup.

Signed-off-by: Daniel Mikusa <dmikusa@vmware.com>
Co-authored-by: Sambhav Kothari <sambhavs.email@gmail.com>
Signed-off-by: Daniel Mikusa <dmikusa@vmware.com>
2021-11-20 21:35:28 -05:00
Sambhav Kothari 6aa81e5081
Merge pull request #95 from dmikusa-pivotal/bom-rfc-95
Adds a convenience method for getting the build, launch and layer BOM file paths
2021-11-18 03:15:25 +00:00
Daniel Mikusa c06fd640c3
Adds a convenience method for getting the build, launch and layer BOM file paths
This PR includes:

- convenience methods on the Layers and Layer object for fetching the SBoM path
- deprecated messages if using old-style BOM functionality

Co-authored-by: Sambhav Kothari <sambhavs.email@gmail.com>
Signed-off-by: Daniel Mikusa <dmikusa@vmware.com>
2021-11-17 20:44:18 -05:00
Sambhav Kothari 50e1696404
Merge pull request #92 from buildpacks/update/pipeline
Bump pipeline from 1.13.0 to 1.15.1
2021-11-12 09:18:45 +00:00
buildpack-bot 9cb9a2095d Bump pipeline from 1.13.0 to 1.15.1
Bumps pipeline from 1.13.0 to 1.15.1.

Signed-off-by: GitHub <noreply@github.com>
2021-11-12 05:10:26 +00:00
Sambhav Kothari 0376b8a285
Merge pull request #94 from dmikusa-pivotal/api-07 2021-11-11 21:14:11 +00:00
Daniel Mikusa 9af239f94c
Updates libcnb to support buildpacks API 0.7
Buildpacks API 0.7 brings one new feature, the functionality for SBoM output through [RFC #95](https://github.com/buildpacks/rfcs/blob/main/text/0095-sbom.md).

Without this change, you can write the SBoM information as described in RFC #95, but the lifecycle will ignore it. To make the lifecycle capture your SBoM information you need to:
1. Use a version of libcnb with this PR.
2. Update the `api = "0.8"` line in your buildpack.toml.
3. Write the SBoM files from your buildpack according to the locations in RFC #95. Libcnb does not provide any help with this activity presently, it is up to the buildpack author.
4. Use a lifecycle version with support, 0.13.0+
5. Use a pack version with platform API 0.8+

The lifecycle should then copy your SBoM files and include them into the image.

This PR is only required because the current implementation restricts usage of libcnb to specific buildpack API versions and we needed to add 0.7 to this list.

Signed-off-by: Daniel Mikusa <dmikusa@vmware.com>
2021-11-11 13:02:54 -05:00
Sambhav Kothari 70403787d2
Merge pull request #91 from buildpacks/update/pipeline
Bump pipeline from 1.12.1 to 1.13.0
2021-11-09 05:54:29 +00:00
buildpack-bot ced0ee6ed8 Bump pipeline from 1.12.1 to 1.13.0
Bumps pipeline from 1.12.1 to 1.13.0.

Signed-off-by: GitHub <noreply@github.com>
2021-11-09 05:10:07 +00:00
Sambhav Kothari c564678891
Merge pull request #90 from buildpacks/dependabot/go_modules/github.com/onsi/gomega-1.17.0
Bump github.com/onsi/gomega from 1.16.0 to 1.17.0
2021-11-08 23:16:52 +00:00
dependabot[bot] 5c9b56b7c6
Bump github.com/onsi/gomega from 1.16.0 to 1.17.0
Bumps [github.com/onsi/gomega](https://github.com/onsi/gomega) from 1.16.0 to 1.17.0.
- [Release notes](https://github.com/onsi/gomega/releases)
- [Changelog](https://github.com/onsi/gomega/blob/master/CHANGELOG.md)
- [Commits](https://github.com/onsi/gomega/compare/v1.16.0...v1.17.0)

---
updated-dependencies:
- dependency-name: github.com/onsi/gomega
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2021-11-08 19:04:45 +00:00
Sambhav Kothari 8c10662dc7
Merge pull request #86 from jghiloni/issue-85
Add struct tag to Buildpack.Path field
2021-11-04 22:15:53 +00:00
Josh Ghiloni b2b6b4b4c1
Make substring match more explicit in buildpack toml test
Signed-off-by: Josh Ghiloni <jghiloni@vmware.com>
2021-11-04 13:33:50 -04:00
Josh Ghiloni 33e2828ac7
Omit Path field from serialization altogether
Signed-off-by: Josh Ghiloni <jghiloni@vmware.com>
2021-11-04 11:20:26 -04:00
Josh Ghiloni a965403652
Add struct tag to Buildpack.Path field
Most Go applications use github.com/burntsushi/toml as their toml
parsing library. When struct tags are omitted, the library will do
case-insensitive deserialization, so a property called "path" will
successfully unmarshal into the Path struct field. However, if you
marshal a Buildpack instance to toml, the "Path = " field appears
regardless of whether it is set or not, and technically violates the
buildpack toml spec. This PR adds a struct tag to not only marshal the
field to the "path" property in the resultant toml, but also adds the
"omitempty" modifier to ignore the field altogether when not set.

Signed-off-by: Josh Ghiloni <jghiloni@vmware.com>
2021-11-04 10:21:07 -04:00
Sambhav Kothari bc6e6ca9a4
Merge pull request #84 from buildpacks/update/pipeline
Bump pipeline from 1.12.0 to 1.12.1
2021-11-04 07:08:48 +00:00
buildpack-bot 536a77887b Bump pipeline from 1.12.0 to 1.12.1
Bumps pipeline from 1.12.0 to 1.12.1.

Signed-off-by: GitHub <noreply@github.com>
2021-11-04 05:09:37 +00:00
56 changed files with 3097 additions and 676 deletions

View File

@ -4,6 +4,8 @@ updates:
directory: /
schedule:
interval: daily
ignore:
- dependency-name: github.com/onsi/gomega
labels:
- semver:patch
- type:dependency-upgrade

15
.github/labels.yml vendored
View File

@ -25,3 +25,18 @@
- 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

View File

@ -14,7 +14,19 @@ test:
set -euo pipefail
GO111MODULE=on go get -u -ldflags="-s -w" github.com/kyoh86/richgo
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

View File

@ -1 +1 @@
1.12.0
1.37.5

View File

@ -1 +0,0 @@
1.4.0

View File

@ -12,7 +12,7 @@ jobs:
runs-on:
- ubuntu-latest
steps:
- uses: mheap/github-action-required-labels@v1
- uses: mheap/github-action-required-labels@v5
with:
count: 1
labels: semver:major, semver:minor, semver:patch
@ -22,7 +22,7 @@ jobs:
runs-on:
- ubuntu-latest
steps:
- uses: mheap/github-action-required-labels@v1
- uses: mheap/github-action-required-labels@v5
with:
count: 1
labels: type:bug, type:dependency-upgrade, type:documentation, type:enhancement, type:question, type:task

View File

@ -2,7 +2,7 @@ name: Synchronize Labels
"on":
push:
branches:
- main
- release-1.x
paths:
- .github/labels.yml
jobs:
@ -11,7 +11,7 @@ jobs:
runs-on:
- ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v4
- uses: micnncim/action-label-syncer@v1
env:
GITHUB_TOKEN: ${{ secrets.IMPLEMENTATION_GITHUB_TOKEN }}

54
.github/workflows/pb-tests.yml vendored Normal file
View File

@ -0,0 +1,54 @@
name: Tests
"on":
merge_group:
types:
- checks_requested
branches:
- main
pull_request: {}
push:
branches:
- release-1.x
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.20"
- 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"

View File

@ -2,7 +2,7 @@ name: Update Draft Release
"on":
push:
branches:
- main
- release-1.x
jobs:
update:
name: Update Draft Release

72
.github/workflows/pb-update-go.yml vendored Normal file
View File

@ -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.20"
- 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)
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.20"
- 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 }}

View File

@ -2,7 +2,7 @@ name: Update Pipeline
"on":
push:
branches:
- main
- release-1.x
paths:
- .github/pipeline-descriptor.yml
schedule:
@ -14,17 +14,17 @@ jobs:
runs-on:
- ubuntu-latest
steps:
- uses: actions/setup-go@v2
- uses: actions/setup-go@v5
with:
go-version: "1.16"
go-version: "1.20"
- name: Install octo
run: |
#!/usr/bin/env bash
set -euo pipefail
GO111MODULE=on go get -u -ldflags="-s -w" github.com/paketo-buildpacks/pipeline-builder/cmd/octo
- uses: actions/checkout@v2
go install -ldflags="-s -w" github.com/paketo-buildpacks/pipeline-builder/cmd/octo@latest
- uses: actions/checkout@v4
- name: Update Pipeline
id: pipeline
run: |
@ -38,6 +38,7 @@ jobs:
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)
@ -54,15 +55,23 @@ jobs:
)
git add .github/
git add .gitignore
if [ -f scripts/build.sh ]; then
git add scripts/build.sh
fi
git checkout -- .
echo "::set-output name=old-version::${OLD_VERSION}"
echo "::set-output name=new-version::${NEW_VERSION}"
echo "::set-output name=release-notes::${RELEASE_NOTES//$'\n'/%0A}"
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@v3
- uses: peter-evans/create-pull-request@v6
with:
author: ${{ secrets.IMPLEMENTATION_GITHUB_USERNAME }} <${{ secrets.IMPLEMENTATION_GITHUB_USERNAME }}@users.noreply.github.com>
body: |-

View File

@ -1,37 +0,0 @@
name: Tests
"on":
pull_request: {}
push:
branches:
- main
jobs:
unit:
name: Unit Test
runs-on:
- ubuntu-latest
steps:
- uses: actions/checkout@v2
- uses: actions/cache@v2
with:
key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
path: ${{ env.HOME }}/go/pkg/mod
restore-keys: ${{ runner.os }}-go-
- uses: actions/setup-go@v2
with:
go-version: "1.16"
- name: Install richgo
run: |
#!/usr/bin/env bash
set -euo pipefail
GO111MODULE=on go get -u -ldflags="-s -w" github.com/kyoh86/richgo
- name: Run Tests
run: |
#!/usr/bin/env bash
set -euo pipefail
GOCMD=richgo make
env:
RICHGO_FORCE_COLOR: "1"

View File

@ -18,6 +18,10 @@ go get github.com/buildpacks/libcnb
https://pkg.go.dev/github.com/buildpacks/libcnb?tab=doc
#### Tutorial
[docs/tutorial.md](docs/tutorial.md)
## License
This library is released under version 2.0 of the [Apache License][a].

View File

@ -48,6 +48,9 @@ type Process struct {
// Command is exec'd directly by the os (no profile.d scripts run)
Direct bool `toml:"direct,omitempty"`
// 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"`
@ -73,6 +76,8 @@ type LaunchTOML struct {
Slices []Slice `toml:"slices"`
// BOM is a collection of entries for the bill of materials.
//
// Deprecated: as of Buildpack API 0.7, write to `layer.BOMPath()` instead
BOM []BOMEntry `toml:"bom"`
}
@ -83,6 +88,8 @@ func (l LaunchTOML) isEmpty() bool {
// BuildTOML represents the contents of build.toml.
type BuildTOML struct {
// BOM contains the build-time bill of materials.
//
// Deprecated: as of Buildpack API 0.7, write to `layer.BOMPath()` instead
BOM []BOMEntry `toml:"bom"`
// Unmet is a collection of buildpack plan entries that should be passed through to subsequent providers.
@ -94,6 +101,8 @@ func (b BuildTOML) isEmpty() bool {
}
// BOMEntry contains a bill of materials entry.
//
// Deprecated: as of Buildpack API 0.7, BOM should use standard formats like CycloneDX going forward
type BOMEntry struct {
// Name represents the name of the entry.
Name string `toml:"name"`

122
build.go
View File

@ -25,6 +25,7 @@ import (
"strings"
"github.com/BurntSushi/toml"
"github.com/Masterminds/semver/v3"
"github.com/buildpacks/libcnb/internal"
"github.com/buildpacks/libcnb/poet"
@ -58,6 +59,8 @@ type BuildContext struct {
// BuildResult contains the results of detection.
type BuildResult struct {
// BOM contains entries to be appended to the app image Bill of Materials and/or build Bill of Materials.
//
// Deprecated: as of Buildpack API 0.7, write to `layer.BOMPath()` instead
BOM *BOM
// Labels are the image labels contributed by the buildpack.
@ -81,10 +84,21 @@ type BuildResult struct {
}
// BOM contains all Bill of Materials entries
//
// Deprecated: as of Buildpack API 0.7, write to `layer.BOMPath()` instead
type BOM struct {
Entries []BOMEntry
}
// Constants to track minimum and maximum supported Buildpack API versions
const (
// MinSupportedBPVersion indicates the minium supported version of the Buildpacks API
MinSupportedBPVersion = "0.5"
// MaxSupportedBPVersion indicates the maximum supported version of the Buildpacks API
MaxSupportedBPVersion = "0.8"
)
// NewBuildResult creates a new BuildResult instance, initializing empty fields.
func NewBuildResult() BuildResult {
return BuildResult{
@ -118,6 +132,7 @@ type Builder interface {
func Build(builder Builder, options ...Option) {
config := Config{
arguments: os.Args,
bomLabel: false,
environmentWriter: internal.EnvironmentWriter{},
exitHandler: internal.NewExitHandler(),
tomlWriter: internal.TOMLWriter{},
@ -127,11 +142,6 @@ func Build(builder Builder, options ...Option) {
config = option(config)
}
if len(config.arguments) != 4 {
config.exitHandler.Error(fmt.Errorf("expected 3 arguments and received %d", len(config.arguments)-1))
return
}
var (
err error
file string
@ -165,16 +175,47 @@ func Build(builder Builder, options ...Option) {
}
logger.Debugf("Buildpack: %+v", ctx.Buildpack)
API := strings.TrimSpace(ctx.Buildpack.API)
if API != "0.5" && API != "0.6" {
config.exitHandler.Error(errors.New("this version of libcnb is only compatible with buildpack APIs 0.5 and 0.6"))
API, err := semver.NewVersion(ctx.Buildpack.API)
if err != nil {
config.exitHandler.Error(errors.New("version cannot be parsed"))
return
}
ctx.Layers = Layers{config.arguments[1]}
logger.Debugf("Layers: %+v", ctx.Layers)
compatVersionCheck, _ := semver.NewConstraint(fmt.Sprintf(">= %s, <= %s", MinSupportedBPVersion, MaxSupportedBPVersion))
if !compatVersionCheck.Check(API) {
config.exitHandler.Error(fmt.Errorf("this version of libcnb is only compatible with buildpack APIs >= %s, <= %s", MinSupportedBPVersion, MaxSupportedBPVersion))
return
}
var buildpackPlanPath string
ctx.Platform.Path = config.arguments[2]
if API.LessThan(semver.MustParse("0.8")) {
if len(config.arguments) != 4 {
config.exitHandler.Error(fmt.Errorf("expected 3 arguments and received %d", len(config.arguments)-1))
return
}
ctx.Layers = Layers{config.arguments[1]}
ctx.Platform.Path = config.arguments[2]
buildpackPlanPath = config.arguments[3]
} else {
layersDir, ok := os.LookupEnv("CNB_LAYERS_DIR")
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("CNB_PLATFORM_DIR")
if !ok {
config.exitHandler.Error(fmt.Errorf("expected CNB_PLATFORM_DIR to be set"))
return
}
buildpackPlanPath, ok = os.LookupEnv("CNB_BP_PLAN_PATH")
if !ok {
config.exitHandler.Error(fmt.Errorf("expected CNB_BP_PLAN_PATH to be set"))
return
}
}
logger.Debugf("Layers: %+v", ctx.Layers)
if logger.IsDebugEnabled() {
logger.Debug(PlatformFormatter(ctx.Platform))
}
@ -201,18 +242,17 @@ func Build(builder Builder, options ...Option) {
ctx.PersistentMetadata = store.Metadata
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)
if ctx.StackID, ok = os.LookupEnv("CNB_STACK_ID"); !ok {
config.exitHandler.Error(fmt.Errorf("CNB_STACK_ID not set"))
return
logger.Debug("CNB_STACK_ID not set")
} else {
logger.Debugf("Stack: %s", ctx.StackID)
}
logger.Debugf("Stack: %s", ctx.StackID)
result, err := builder.Build(ctx)
if err != nil {
@ -274,7 +314,7 @@ func Build(builder Builder, options ...Option) {
file = filepath.Join(ctx.Layers.Path, fmt.Sprintf("%s.toml", layer.Name))
logger.Debugf("Writing layer metadata: %s <= %+v", file, layer)
var toWrite interface{} = layer
if API == "0.5" {
if API.Equal(semver.MustParse("0.5")) {
toWrite = internal.LayerAPI5{
Build: layer.LayerTypes.Build,
Cache: layer.LayerTypes.Cache,
@ -302,8 +342,16 @@ func Build(builder Builder, options ...Option) {
}
}
if API.GreaterThan(semver.MustParse("0.7")) || API.Equal(semver.MustParse("0.7")) {
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
}
}
// Deprecated: as of Buildpack API 0.7, to be removed in a future version
var launchBOM, buildBOM []BOMEntry
if result.BOM != nil {
if result.BOM != nil && config.bomLabel {
for _, entry := range result.BOM.Entries {
if entry.Launch {
launchBOM = append(launchBOM, entry)
@ -325,7 +373,7 @@ func Build(builder Builder, options ...Option) {
file = filepath.Join(ctx.Layers.Path, "launch.toml")
logger.Debugf("Writing application metadata: %s <= %+v", file, launch)
if API == "0.5" {
if API.LessThan(semver.MustParse("0.6")) {
for _, process := range launch.Processes {
if process.Default {
logger.Info("WARNING: Launch layer is setting default=true, but that is not supported until API version 0.6. This setting will be ignored.")
@ -333,6 +381,15 @@ func Build(builder Builder, options ...Option) {
}
}
if API.LessThan(semver.MustParse("0.8")) {
for i, process := range launch.Processes {
if process.WorkingDirectory != "" {
logger.Infof("WARNING: Launch layer is setting working-directory=%s, but that is not supported until API version 0.8. This setting will be ignored.", process.WorkingDirectory)
launch.Processes[i].WorkingDirectory = ""
}
}
}
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
@ -347,6 +404,7 @@ func Build(builder Builder, options ...Option) {
if !build.isEmpty() {
file = filepath.Join(ctx.Layers.Path, "build.toml")
logger.Debugf("Writing build metadata: %s <= %+v", file, build)
if err = config.tomlWriter.Write(file, build); err != nil {
config.exitHandler.Error(fmt.Errorf("unable to write build metadata %s\n%w", file, err))
return
@ -375,3 +433,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
}

View File

@ -19,7 +19,6 @@ package libcnb_test
import (
"bytes"
"fmt"
"io/ioutil"
"os"
"path/filepath"
"testing"
@ -58,15 +57,13 @@ func testBuild(t *testing.T, context spec.G, it spec.S) {
it.Before(func() {
var err error
applicationPath, err = ioutil.TempDir("", "build-application-path")
Expect(err).NotTo(HaveOccurred())
applicationPath = t.TempDir()
applicationPath, err = filepath.EvalSymlinks(applicationPath)
Expect(err).NotTo(HaveOccurred())
builder = &mocks.Builder{}
buildpackPath, err = ioutil.TempDir("", "build-buildpack-path")
Expect(err).NotTo(HaveOccurred())
buildpackPath = t.TempDir()
Expect(os.Setenv("CNB_BUILDPACK_DIR", buildpackPath)).To(Succeed())
bpTOMLContents = `
@ -108,14 +105,14 @@ test-key = "test-value"
err = buildpackTOML.Execute(&b, map[string]string{"APIVersion": "0.6"})
Expect(err).ToNot(HaveOccurred())
Expect(ioutil.WriteFile(filepath.Join(buildpackPath, "buildpack.toml"), b.Bytes(), 0600)).To(Succeed())
Expect(os.WriteFile(filepath.Join(buildpackPath, "buildpack.toml"), b.Bytes(), 0600)).To(Succeed())
f, err := ioutil.TempFile("", "build-buildpackplan-path")
f, err := os.CreateTemp("", "build-buildpackplan-path")
Expect(err).NotTo(HaveOccurred())
Expect(f.Close()).NotTo(HaveOccurred())
buildpackPlanPath = f.Name()
Expect(ioutil.WriteFile(buildpackPlanPath,
Expect(os.WriteFile(buildpackPlanPath,
[]byte(`
[[entries]]
name = "test-name"
@ -137,10 +134,9 @@ test-key = "test-value"
layerContributor = &mocks.LayerContributor{}
layersPath, err = ioutil.TempDir("", "build-layers-path")
Expect(err).NotTo(HaveOccurred())
layersPath = t.TempDir()
Expect(ioutil.WriteFile(filepath.Join(layersPath, "store.toml"),
Expect(os.WriteFile(filepath.Join(layersPath, "store.toml"),
[]byte(`
[metadata]
test-key = "test-value"
@ -148,21 +144,23 @@ test-key = "test-value"
0600),
).To(Succeed())
platformPath, err = ioutil.TempDir("", "build-platform-path")
Expect(err).NotTo(HaveOccurred())
platformPath = t.TempDir()
Expect(os.MkdirAll(filepath.Join(platformPath, "bindings", "alpha"), 0755)).To(Succeed())
Expect(ioutil.WriteFile(filepath.Join(platformPath, "bindings", "alpha", "test-secret-key"),
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"), 0600)).
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())
workingDir, err = os.Getwd()
Expect(err).NotTo(HaveOccurred())
@ -173,6 +171,9 @@ 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_BP_PLAN_PATH")).To(Succeed())
Expect(os.Unsetenv("CNB_LAYERS_DIR")).To(Succeed())
Expect(os.RemoveAll(applicationPath)).To(Succeed())
Expect(os.RemoveAll(buildpackPath)).To(Succeed())
@ -181,9 +182,9 @@ test-key = "test-value"
Expect(os.RemoveAll(platformPath)).To(Succeed())
})
context("buildpack API is not 0.5 or 0.6", func() {
context("buildpack API is not within the supported range", func() {
it.Before(func() {
Expect(ioutil.WriteFile(filepath.Join(buildpackPath, "buildpack.toml"),
Expect(os.WriteFile(filepath.Join(buildpackPath, "buildpack.toml"),
[]byte(`
api = "0.4"
@ -198,20 +199,56 @@ version = "1.1.1"
it("fails", func() {
libcnb.Build(builder,
libcnb.WithBOMLabel(true),
libcnb.WithArguments([]string{commandPath, layersPath, platformPath, buildpackPlanPath}),
libcnb.WithExitHandler(exitHandler),
)
Expect(exitHandler.Calls[0].Arguments.Get(0)).To(MatchError(
"this version of libcnb is only compatible with buildpack APIs 0.5 and 0.6",
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 for buildpack API >=0.8", func() {
for _, e := range []string{"CNB_LAYERS_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(buildpackPath, "buildpack.toml"),
[]byte(`
api = "0.8"
[buildpack]
id = "test-id"
name = "test-name"
version = "1.1.1"
`),
0600),
).To(Succeed())
os.Unsetenv(envVar)
})
it("fails", func() {
libcnb.Build(builder,
libcnb.WithBOMLabel(true),
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("encounters the wrong number of arguments", func() {
builder.On("Build", mock.Anything).Return(libcnb.NewBuildResult(), nil)
libcnb.Build(builder,
libcnb.WithBOMLabel(true),
libcnb.WithArguments([]string{commandPath}),
libcnb.WithExitHandler(exitHandler),
)
@ -219,76 +256,136 @@ version = "1.1.1"
Expect(exitHandler.Calls[0].Arguments.Get(0)).To(MatchError("expected 3 arguments and received 0"))
})
it("doesn't receive CNB_STACK_ID", func() {
Expect(os.Unsetenv("CNB_STACK_ID")).To(Succeed())
builder.On("Build", mock.Anything).Return(libcnb.NewBuildResult(), nil)
context("when BP API >= 0.8", 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())
})
libcnb.Build(builder,
libcnb.WithArguments([]string{commandPath, layersPath, platformPath, buildpackPlanPath}),
libcnb.WithExitHandler(exitHandler),
)
it("creates context", func() {
builder.On("Build", mock.Anything).Return(libcnb.NewBuildResult(), nil)
Expect(exitHandler.Calls[0].Arguments.Get(0)).To(MatchError("CNB_STACK_ID not set"))
libcnb.Build(builder,
libcnb.WithBOMLabel(true),
libcnb.WithArguments([]string{commandPath}),
)
ctx := builder.Calls[0].Arguments[0].(libcnb.BuildContext)
Expect(ctx.Application).To(Equal(libcnb.Application{Path: 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.Layers).To(Equal(libcnb.Layers{Path: layersPath}))
Expect(ctx.PersistentMetadata).To(Equal(map[string]interface{}{"test-key": "test-value"}))
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"))
})
})
it("creates context", func() {
builder.On("Build", mock.Anything).Return(libcnb.NewBuildResult(), nil)
context("when BP API < 0.8", func() {
it.Before(func() {
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())
})
libcnb.Build(builder,
libcnb.WithArguments([]string{commandPath, layersPath, platformPath, buildpackPlanPath}),
)
it("creates context", func() {
builder.On("Build", mock.Anything).Return(libcnb.NewBuildResult(), nil)
ctx := builder.Calls[0].Arguments[0].(libcnb.BuildContext)
Expect(ctx.Application).To(Equal(libcnb.Application{Path: applicationPath}))
Expect(ctx.Buildpack).To(Equal(libcnb.Buildpack{
API: "0.6",
Info: libcnb.BuildpackInfo{
ID: "test-id",
Name: "test-name",
Version: "1.1.1",
ClearEnvironment: true,
Description: "A test buildpack",
Keywords: []string{"test", "buildpack"},
Licenses: []libcnb.License{
{Type: "Apache-2.0", URI: "https://spdx.org/licenses/Apache-2.0.html"},
{Type: "Apache-1.1", URI: "https://spdx.org/licenses/Apache-1.1.html"},
},
},
Path: buildpackPath,
Stacks: []libcnb.BuildpackStack{
{
ID: "test-id",
Mixins: []string{"test-name"},
},
},
Metadata: map[string]interface{}{"test-key": "test-value"},
}))
Expect(ctx.Layers).To(Equal(libcnb.Layers{Path: layersPath}))
Expect(ctx.PersistentMetadata).To(Equal(map[string]interface{}{"test-key": "test-value"}))
Expect(ctx.Plan).To(Equal(libcnb.BuildpackPlan{
Entries: []libcnb.BuildpackPlanEntry{
{
Name: "test-name",
Metadata: map[string]interface{}{
"test-key": "test-value",
libcnb.Build(builder,
libcnb.WithBOMLabel(true),
libcnb.WithArguments([]string{commandPath, layersPath, platformPath, buildpackPlanPath}),
)
ctx := builder.Calls[0].Arguments[0].(libcnb.BuildContext)
Expect(ctx.Application).To(Equal(libcnb.Application{Path: applicationPath}))
Expect(ctx.Buildpack).To(Equal(libcnb.Buildpack{
API: "0.6",
Info: libcnb.BuildpackInfo{
ID: "test-id",
Name: "test-name",
Version: "1.1.1",
ClearEnvironment: true,
Description: "A test buildpack",
Keywords: []string{"test", "buildpack"},
Licenses: []libcnb.License{
{Type: "Apache-2.0", URI: "https://spdx.org/licenses/Apache-2.0.html"},
{Type: "Apache-1.1", URI: "https://spdx.org/licenses/Apache-1.1.html"},
},
},
},
}))
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"},
},
},
},
Environment: map[string]string{"TEST_ENV": "test-value"},
Path: platformPath,
}))
Expect(ctx.StackID).To(Equal("test-stack-id"))
Metadata: map[string]interface{}{"test-key": "test-value"},
}))
Expect(ctx.Layers).To(Equal(libcnb.Layers{Path: layersPath}))
Expect(ctx.PersistentMetadata).To(Equal(map[string]interface{}{"test-key": "test-value"}))
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"))
})
})
it("extracts buildpack path from command path if CNB_BUILDPACK_PATH is not set", func() {
@ -297,6 +394,7 @@ version = "1.1.1"
builder.On("Build", mock.Anything).Return(libcnb.NewBuildResult(), nil)
libcnb.Build(builder,
libcnb.WithBOMLabel(true),
libcnb.WithArguments([]string{filepath.Join(buildpackPath, commandPath), layersPath, platformPath, buildpackPlanPath}),
)
@ -309,6 +407,7 @@ version = "1.1.1"
builder.On("Build", mock.Anything).Return(libcnb.NewBuildResult(), fmt.Errorf("test-error"))
libcnb.Build(builder,
libcnb.WithBOMLabel(true),
libcnb.WithArguments([]string{commandPath, layersPath, platformPath, buildpackPlanPath}),
libcnb.WithExitHandler(exitHandler),
)
@ -323,6 +422,7 @@ version = "1.1.1"
Return(libcnb.BuildResult{Layers: []libcnb.LayerContributor{layerContributor}}, nil)
libcnb.Build(builder,
libcnb.WithBOMLabel(true),
libcnb.WithArguments([]string{commandPath, layersPath, platformPath, buildpackPlanPath}),
libcnb.WithTOMLWriter(tomlWriter),
)
@ -339,6 +439,7 @@ version = "1.1.1"
builder.On("Build", mock.Anything).Return(result, nil)
libcnb.Build(builder,
libcnb.WithBOMLabel(true),
libcnb.WithArguments([]string{commandPath, layersPath, platformPath, buildpackPlanPath}),
libcnb.WithEnvironmentWriter(environmentWriter),
)
@ -356,6 +457,7 @@ version = "1.1.1"
builder.On("Build", mock.Anything).Return(result, nil)
libcnb.Build(builder,
libcnb.WithBOMLabel(true),
libcnb.WithArguments([]string{commandPath, layersPath, platformPath, buildpackPlanPath}),
libcnb.WithEnvironmentWriter(environmentWriter),
)
@ -373,6 +475,7 @@ version = "1.1.1"
builder.On("Build", mock.Anything).Return(result, nil)
libcnb.Build(builder,
libcnb.WithBOMLabel(true),
libcnb.WithArguments([]string{commandPath, layersPath, platformPath, buildpackPlanPath}),
libcnb.WithEnvironmentWriter(environmentWriter),
@ -391,6 +494,7 @@ version = "1.1.1"
builder.On("Build", mock.Anything).Return(result, nil)
libcnb.Build(builder,
libcnb.WithBOMLabel(true),
libcnb.WithArguments([]string{commandPath, layersPath, platformPath, buildpackPlanPath}),
libcnb.WithEnvironmentWriter(environmentWriter),
)
@ -404,7 +508,7 @@ version = "1.1.1"
err := buildpackTOML.Execute(&b, map[string]string{"APIVersion": "0.5"})
Expect(err).ToNot(HaveOccurred())
Expect(ioutil.WriteFile(filepath.Join(buildpackPath, "buildpack.toml"), b.Bytes(), 0600)).To(Succeed())
Expect(os.WriteFile(filepath.Join(buildpackPath, "buildpack.toml"), b.Bytes(), 0600)).To(Succeed())
layer := libcnb.Layer{
Name: "test-name",
@ -422,6 +526,7 @@ version = "1.1.1"
builder.On("Build", mock.Anything).Return(result, nil)
libcnb.Build(builder,
libcnb.WithBOMLabel(true),
libcnb.WithArguments([]string{commandPath, layersPath, platformPath, buildpackPlanPath}),
libcnb.WithTOMLWriter(tomlWriter),
)
@ -453,6 +558,7 @@ version = "1.1.1"
builder.On("Build", mock.Anything).Return(result, nil)
libcnb.Build(builder,
libcnb.WithBOMLabel(true),
libcnb.WithArguments([]string{commandPath, layersPath, platformPath, buildpackPlanPath}),
libcnb.WithTOMLWriter(tomlWriter),
)
@ -501,6 +607,7 @@ version = "1.1.1"
}, nil)
libcnb.Build(builder,
libcnb.WithBOMLabel(true),
libcnb.WithArguments([]string{commandPath, layersPath, platformPath, buildpackPlanPath}),
libcnb.WithTOMLWriter(tomlWriter),
)
@ -535,12 +642,79 @@ version = "1.1.1"
}))
})
it("ignore working-directory setting and writes launch.toml (API<0.8)", func() {
builder.On("Build", mock.Anything).Return(libcnb.BuildResult{
Processes: []libcnb.Process{
{
Type: "test-type",
Command: "test-command-in-dir",
Default: true,
WorkingDirectory: "/my/directory/",
},
},
}, nil)
libcnb.Build(builder,
libcnb.WithBOMLabel(true),
libcnb.WithArguments([]string{commandPath, layersPath, platformPath, buildpackPlanPath}),
libcnb.WithTOMLWriter(tomlWriter),
)
Expect(tomlWriter.Calls[0].Arguments[0]).To(Equal(filepath.Join(layersPath, "launch.toml")))
Expect(tomlWriter.Calls[0].Arguments[1]).To(Equal(libcnb.LaunchTOML{
Processes: []libcnb.Process{
{
Type: "test-type",
Command: "test-command-in-dir",
Default: true,
},
},
}))
})
it("writes launch.toml with working-directory setting(API>=0.8)", func() {
var b bytes.Buffer
err := buildpackTOML.Execute(&b, map[string]string{"APIVersion": "0.8"})
Expect(err).ToNot(HaveOccurred())
Expect(os.WriteFile(filepath.Join(buildpackPath, "buildpack.toml"), b.Bytes(), 0600)).To(Succeed())
builder.On("Build", mock.Anything).Return(libcnb.BuildResult{
Processes: []libcnb.Process{
{
Type: "test-type",
Command: "test-command-in-dir",
Default: true,
WorkingDirectory: "/my/directory/",
},
},
}, nil)
libcnb.Build(builder,
libcnb.WithBOMLabel(true),
libcnb.WithArguments([]string{commandPath, layersPath, platformPath, buildpackPlanPath}),
libcnb.WithTOMLWriter(tomlWriter),
)
Expect(tomlWriter.Calls[0].Arguments[0]).To(Equal(filepath.Join(layersPath, "launch.toml")))
Expect(tomlWriter.Calls[0].Arguments[1]).To(Equal(libcnb.LaunchTOML{
Processes: []libcnb.Process{
{
Type: "test-type",
Command: "test-command-in-dir",
Default: true,
WorkingDirectory: "/my/directory/",
},
},
}))
})
it("writes persistent metadata", func() {
m := map[string]interface{}{"test-key": "test-value"}
builder.On("Build", mock.Anything).Return(libcnb.BuildResult{PersistentMetadata: m}, nil)
libcnb.Build(builder,
libcnb.WithBOMLabel(true),
libcnb.WithArguments([]string{commandPath, layersPath, platformPath, buildpackPlanPath}),
libcnb.WithTOMLWriter(tomlWriter),
)
@ -553,6 +727,7 @@ version = "1.1.1"
builder.On("Build", mock.Anything).Return(libcnb.NewBuildResult(), nil)
libcnb.Build(builder,
libcnb.WithBOMLabel(true),
libcnb.WithArguments([]string{commandPath, layersPath, platformPath, buildpackPlanPath}),
libcnb.WithTOMLWriter(tomlWriter),
)
@ -561,9 +736,9 @@ version = "1.1.1"
})
it("removes stale layers", func() {
Expect(ioutil.WriteFile(filepath.Join(layersPath, "alpha.toml"), []byte(""), 0600)).To(Succeed())
Expect(ioutil.WriteFile(filepath.Join(layersPath, "bravo.toml"), []byte(""), 0600)).To(Succeed())
Expect(ioutil.WriteFile(filepath.Join(layersPath, "store.toml"), []byte(""), 0600)).To(Succeed())
Expect(os.WriteFile(filepath.Join(layersPath, "alpha.toml"), []byte(""), 0600)).To(Succeed())
Expect(os.WriteFile(filepath.Join(layersPath, "bravo.toml"), []byte(""), 0600)).To(Succeed())
Expect(os.WriteFile(filepath.Join(layersPath, "store.toml"), []byte(""), 0600)).To(Succeed())
layer := libcnb.Layer{Name: "alpha"}
layerContributor.On("Contribute", mock.Anything).Return(layer, nil)
@ -573,6 +748,7 @@ version = "1.1.1"
Return(libcnb.BuildResult{Layers: []libcnb.LayerContributor{layerContributor}}, nil)
libcnb.Build(builder,
libcnb.WithBOMLabel(true),
libcnb.WithArguments([]string{commandPath, layersPath, platformPath, buildpackPlanPath}),
libcnb.WithTOMLWriter(tomlWriter),
)
@ -605,6 +781,7 @@ version = "1.1.1"
}, nil)
libcnb.Build(builder,
libcnb.WithBOMLabel(true),
libcnb.WithArguments([]string{commandPath, layersPath, platformPath, buildpackPlanPath}),
libcnb.WithTOMLWriter(tomlWriter),
)
@ -625,4 +802,213 @@ version = "1.1.1"
},
}))
})
context("Config bomLabel is false", func() {
it.Before(func() {
var err error
buildpackTOML, err = template.New("buildpack.toml").Parse(bpTOMLContents)
Expect(err).ToNot(HaveOccurred())
var b bytes.Buffer
err = buildpackTOML.Execute(&b, map[string]string{"APIVersion": "0.7"})
Expect(err).ToNot(HaveOccurred())
Expect(os.WriteFile(filepath.Join(buildpackPath, "buildpack.toml"), b.Bytes(), 0600)).To(Succeed())
})
it("writes launch.toml without BOM entries", func() {
builder.On("Build", mock.Anything).Return(libcnb.BuildResult{
BOM: &libcnb.BOM{Entries: []libcnb.BOMEntry{
{
Name: "test-launch-bom-entry",
Metadata: map[string]interface{}{"test-key": "test-value"},
Launch: true,
},
{
Name: "test-build-bom-entry",
Metadata: map[string]interface{}{"test-key": "test-value"},
},
}},
Processes: []libcnb.Process{
{
Type: "test-type",
Command: "test-command",
Default: true,
},
},
}, nil)
libcnb.Build(builder,
libcnb.WithBOMLabel(false),
libcnb.WithArguments([]string{commandPath, layersPath, platformPath, buildpackPlanPath}),
libcnb.WithTOMLWriter(tomlWriter),
)
Expect(tomlWriter.Calls[0].Arguments[0]).To(Equal(filepath.Join(layersPath, "launch.toml")))
Expect(tomlWriter.Calls[0].Arguments[1]).To(Equal(libcnb.LaunchTOML{
Processes: []libcnb.Process{
{
Type: "test-type",
Command: "test-command",
Default: true,
},
},
BOM: nil,
}))
})
it("writes build.toml without BOM entries", func() {
builder.On("Build", mock.Anything).Return(libcnb.BuildResult{
BOM: &libcnb.BOM{Entries: []libcnb.BOMEntry{
{
Name: "test-build-bom-entry",
Metadata: map[string]interface{}{"test-key": "test-value"},
Build: true,
},
{
Name: "test-launch-bom-entry",
Metadata: map[string]interface{}{"test-key": "test-value"},
Build: false,
},
}},
Unmet: []libcnb.UnmetPlanEntry{
{
Name: "test-entry",
},
},
}, nil)
libcnb.Build(builder,
libcnb.WithBOMLabel(false),
libcnb.WithArguments([]string{commandPath, layersPath, platformPath, buildpackPlanPath}),
libcnb.WithTOMLWriter(tomlWriter),
)
Expect(tomlWriter.Calls[0].Arguments[0]).To(Equal(filepath.Join(layersPath, "build.toml")))
Expect(tomlWriter.Calls[0].Arguments[1]).To(Equal(libcnb.BuildTOML{
BOM: nil,
Unmet: []libcnb.UnmetPlanEntry{
{
Name: "test-entry",
},
},
}))
})
})
context("Validates SBOM entries", func() {
it.Before(func() {
Expect(os.WriteFile(filepath.Join(buildpackPath, "buildpack.toml"),
[]byte(`
api = "0.7"
[buildpack]
id = "test-id"
name = "test-name"
version = "1.1.1"
sbom-formats = ["application/vnd.cyclonedx+json"]
`),
0600),
).To(Succeed())
builder.On("Build", mock.Anything).Return(libcnb.BuildResult{}, nil)
})
it("has no SBOM files", func() {
libcnb.Build(builder,
libcnb.WithBOMLabel(true),
libcnb.WithArguments([]string{commandPath, layersPath, platformPath, buildpackPlanPath}),
libcnb.WithExitHandler(exitHandler),
)
Expect(exitHandler.Calls).To(BeEmpty())
})
it("has no accepted formats", func() {
Expect(os.WriteFile(filepath.Join(buildpackPath, "buildpack.toml"),
[]byte(`
api = "0.7"
[buildpack]
id = "test-id"
name = "test-name"
version = "1.1.1"
sbom-formats = []
`),
0600),
).To(Succeed())
Expect(os.WriteFile(filepath.Join(layersPath, "launch.sbom.spdx.json"), []byte{}, 0600)).To(Succeed())
libcnb.Build(builder,
libcnb.WithBOMLabel(true),
libcnb.WithArguments([]string{commandPath, layersPath, platformPath, buildpackPlanPath}),
libcnb.WithExitHandler(exitHandler),
)
Expect(exitHandler.Calls[0].Arguments.Get(0)).To(MatchError("unable to validate SBOM\nunable to find actual SBOM Type application/spdx+json in list of supported SBOM types []"))
})
it("skips if API is not 0.7", func() {
Expect(os.WriteFile(filepath.Join(buildpackPath, "buildpack.toml"),
[]byte(`
api = "0.6"
[buildpack]
id = "test-id"
name = "test-name"
version = "1.1.1"
sbom-formats = []
`),
0600),
).To(Succeed())
Expect(os.WriteFile(filepath.Join(layersPath, "launch.sbom.spdx.json"), []byte{}, 0600)).To(Succeed())
libcnb.Build(builder,
libcnb.WithBOMLabel(true),
libcnb.WithArguments([]string{commandPath, layersPath, platformPath, buildpackPlanPath}),
libcnb.WithExitHandler(exitHandler),
)
Expect(exitHandler.Calls).To(BeEmpty())
})
it("has no matching formats", func() {
Expect(os.WriteFile(filepath.Join(layersPath, "launch.sbom.spdx.json"), []byte{}, 0600)).To(Succeed())
libcnb.Build(builder,
libcnb.WithBOMLabel(true),
libcnb.WithArguments([]string{commandPath, layersPath, platformPath, buildpackPlanPath}),
libcnb.WithExitHandler(exitHandler),
)
Expect(exitHandler.Calls[0].Arguments.Get(0)).To(MatchError("unable to validate SBOM\nunable to find actual SBOM Type application/spdx+json in list of supported SBOM types [application/vnd.cyclonedx+json]"))
})
it("has a matching format", func() {
Expect(os.WriteFile(filepath.Join(layersPath, "launch.sbom.cdx.json"), []byte{}, 0600)).To(Succeed())
Expect(os.WriteFile(filepath.Join(layersPath, "layer.sbom.cdx.json"), []byte{}, 0600)).To(Succeed())
libcnb.Build(builder,
libcnb.WithBOMLabel(true),
libcnb.WithArguments([]string{commandPath, layersPath, platformPath, buildpackPlanPath}),
libcnb.WithExitHandler(exitHandler),
)
Expect(exitHandler.Calls).To(BeEmpty())
})
it("has a junk format", func() {
Expect(os.WriteFile(filepath.Join(layersPath, "launch.sbom.random.json"), []byte{}, 0600)).To(Succeed())
Expect(os.WriteFile(filepath.Join(layersPath, "layer.sbom.cdx.json"), []byte{}, 0600)).To(Succeed())
libcnb.Build(builder,
libcnb.WithBOMLabel(true),
libcnb.WithArguments([]string{commandPath, layersPath, platformPath, buildpackPlanPath}),
libcnb.WithExitHandler(exitHandler),
)
Expect(exitHandler.Calls[0].Arguments.Get(0)).To(MatchError("unable to validate SBOM\nunable to parse SBOM unknown\nunable to translate from random.json to SBOMFormat"))
})
})
}

View File

@ -41,6 +41,9 @@ type BuildpackInfo struct {
// 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
@ -91,7 +94,7 @@ type Buildpack struct {
Info BuildpackInfo `toml:"buildpack"`
// Path is the path to the buildpack.
Path string
Path string `toml:"-"`
// Stacks is the collection of stacks supported by the buildpack.
Stacks []BuildpackStack `toml:"stacks"`

51
buildpack_test.go Normal file
View File

@ -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"
. "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.6",
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 = ")))
})
}

View File

@ -65,6 +65,7 @@ type ExecDWriter interface {
// Config is an object that contains configurable properties for execution.
type Config struct {
arguments []string
bomLabel bool
environmentWriter EnvironmentWriter
exitHandler ExitHandler
tomlWriter TOMLWriter
@ -113,3 +114,12 @@ func WithExecDWriter(execdWriter ExecDWriter) Option {
return config
}
}
// WithBOMLabel creates an Option that enables/disables writing the BOM Label
// Deprecated: as of Buildpack API 0.7, to be removed in a future version
func WithBOMLabel(bomLabel bool) Option {
return func(config Config) Config {
config.bomLabel = bomLabel
return config
}
}

View File

@ -24,6 +24,7 @@ import (
"strings"
"github.com/BurntSushi/toml"
"github.com/Masterminds/semver/v3"
"github.com/buildpacks/libcnb/internal"
"github.com/buildpacks/libcnb/poet"
@ -77,11 +78,6 @@ func Detect(detector Detector, options ...Option) {
config = option(config)
}
if len(config.arguments) != 3 {
config.exitHandler.Error(fmt.Errorf("expected 2 arguments and received %d", len(config.arguments)-1))
return
}
var (
err error
file string
@ -115,20 +111,46 @@ func Detect(detector Detector, options ...Option) {
}
logger.Debugf("Buildpack: %+v", ctx.Buildpack)
API := strings.TrimSpace(ctx.Buildpack.API)
if API != "0.5" && API != "0.6" {
config.exitHandler.Error(errors.New("this version of libcnb is only compatible with buildpack API 0.5 and 0.6"))
API, err := semver.NewVersion(ctx.Buildpack.API)
if err != nil {
config.exitHandler.Error(errors.New("version cannot be parsed"))
return
}
ctx.Platform.Path = config.arguments[1]
compatVersionCheck, _ := semver.NewConstraint(fmt.Sprintf(">= %s, <= %s", MinSupportedBPVersion, MaxSupportedBPVersion))
if !compatVersionCheck.Check(API) {
config.exitHandler.Error(fmt.Errorf("this version of libcnb is only compatible with buildpack APIs >= %s, <= %s", MinSupportedBPVersion, MaxSupportedBPVersion))
return
}
var buildPlanPath string
if API.LessThan(semver.MustParse("0.8")) {
if len(config.arguments) != 3 {
config.exitHandler.Error(fmt.Errorf("expected 2 arguments and received %d", len(config.arguments)-1))
return
}
ctx.Platform.Path = config.arguments[1]
buildPlanPath = config.arguments[2]
} else {
ctx.Platform.Path, ok = os.LookupEnv("CNB_PLATFORM_DIR")
if !ok {
config.exitHandler.Error(fmt.Errorf("expected CNB_PLATFORM_DIR to be set"))
return
}
buildPlanPath, ok = os.LookupEnv("CNB_BUILD_PLAN_PATH")
if !ok {
config.exitHandler.Error(fmt.Errorf("expected CNB_BUILD_PLAN_PATH to be set"))
return
}
}
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))
if ctx.Platform.Bindings, err = NewBindingsForBuild(ctx.Platform.Path); err != nil {
config.exitHandler.Error(fmt.Errorf("unable to read platform bindings %s\n%w", ctx.Platform.Path, err))
return
}
logger.Debugf("Platform Bindings: %+v", ctx.Platform.Bindings)
@ -141,10 +163,10 @@ func Detect(detector Detector, options ...Option) {
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
logger.Debug("CNB_STACK_ID not set")
} else {
logger.Debugf("Stack: %s", ctx.StackID)
}
logger.Debugf("Stack: %s", ctx.StackID)
result, err := detector.Detect(ctx)
if err != nil {
@ -167,10 +189,9 @@ 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))
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
}
}

View File

@ -18,7 +18,6 @@ package libcnb_test
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"testing"
@ -50,16 +49,15 @@ func testDetect(t *testing.T, context spec.G, it spec.S) {
it.Before(func() {
var err error
applicationPath, err = ioutil.TempDir("", "detect-application-path")
Expect(err).NotTo(HaveOccurred())
applicationPath = t.TempDir()
applicationPath, err = filepath.EvalSymlinks(applicationPath)
Expect(err).NotTo(HaveOccurred())
buildpackPath, err = ioutil.TempDir("", "detect-buildpack-path")
buildpackPath = t.TempDir()
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.6"
@ -89,7 +87,7 @@ test-key = "test-value"
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()
@ -103,21 +101,22 @@ test-key = "test-value"
exitHandler.On("Fail")
exitHandler.On("Pass")
platformPath, err = ioutil.TempDir("", "detect-platform-path")
Expect(err).NotTo(HaveOccurred())
platformPath = t.TempDir()
Expect(os.MkdirAll(filepath.Join(platformPath, "bindings", "alpha"), 0755)).To(Succeed())
Expect(ioutil.WriteFile(filepath.Join(platformPath, "bindings", "alpha", "test-secret-key"),
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"), 0600)).
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())
@ -128,6 +127,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())
@ -135,9 +136,9 @@ test-key = "test-value"
Expect(os.RemoveAll(platformPath)).To(Succeed())
})
context("buildpack API is not 0.5 or 0.6", func() {
context("buildpack API is not within the supported range", func() {
it.Before(func() {
Expect(ioutil.WriteFile(filepath.Join(buildpackPath, "buildpack.toml"),
Expect(os.WriteFile(filepath.Join(buildpackPath, "buildpack.toml"),
[]byte(`
api = "0.4"
@ -157,7 +158,7 @@ version = "1.1.1"
)
Expect(exitHandler.Calls[0].Arguments.Get(0)).To(MatchError(
"this version of libcnb is only compatible with buildpack API 0.5 and 0.6",
fmt.Sprintf("this version of libcnb is only compatible with buildpack APIs >= %s, <= %s", libcnb.MinSupportedBPVersion, libcnb.MaxSupportedBPVersion),
))
})
})
@ -173,65 +174,130 @@ version = "1.1.1"
Expect(exitHandler.Calls[0].Arguments.Get(0)).To(MatchError("expected 2 arguments and received 0"))
})
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 for buildpack API >=0.8", 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(detector,
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{Pass: true}, nil)
context("when BP API >= 0.8", 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())
})
libcnb.Detect(detector,
libcnb.WithArguments([]string{commandPath, platformPath, buildPlanPath}),
libcnb.WithExitHandler(exitHandler),
)
it("creates context", func() {
detector.On("Detect", mock.Anything).Return(libcnb.DetectResult{Pass: true}, nil)
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.6",
Info: libcnb.BuildpackInfo{
ID: "test-id",
Name: "test-name",
Version: "1.1.1",
ClearEnvironment: true,
Description: "A test buildpack",
Keywords: []string{"test", "buildpack"},
Licenses: []libcnb.License{
{Type: "Apache-2.0", URI: "https://spdx.org/licenses/Apache-2.0.html"},
{Type: "Apache-1.1", URI: "https://spdx.org/licenses/Apache-1.1.html"},
libcnb.Detect(detector,
libcnb.WithArguments([]string{commandPath}),
libcnb.WithExitHandler(exitHandler),
)
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.8",
Info: libcnb.BuildpackInfo{
ID: "test-id",
Name: "test-name",
Version: "1.1.1",
},
},
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",
Path: filepath.Join(platformPath, "bindings", "alpha"),
Secret: map[string]string{
"test-secret-key": "test-secret-value",
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",
},
},
},
},
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"))
})
})
context("when BP API < 0.8", func() {
it.Before(func() {
Expect(os.Unsetenv("CNB_PLATFORM_DIR")).To(Succeed())
Expect(os.Unsetenv("CNB_BUILD_PLAN_PATH")).To(Succeed())
})
it("creates context", func() {
detector.On("Detect", mock.Anything).Return(libcnb.DetectResult{Pass: true}, nil)
libcnb.Detect(detector,
libcnb.WithArguments([]string{commandPath, platformPath, buildPlanPath}),
libcnb.WithExitHandler(exitHandler),
)
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.6",
Info: libcnb.BuildpackInfo{
ID: "test-id",
Name: "test-name",
Version: "1.1.1",
ClearEnvironment: true,
Description: "A test buildpack",
Keywords: []string{"test", "buildpack"},
Licenses: []libcnb.License{
{Type: "Apache-2.0", URI: "https://spdx.org/licenses/Apache-2.0.html"},
{Type: "Apache-1.1", URI: "https://spdx.org/licenses/Apache-1.1.html"},
},
},
Path: buildpackPath,
Stacks: []libcnb.BuildpackStack{
{
ID: "test-id",
Mixins: []string{"test-name"},
},
},
Metadata: map[string]interface{}{"test-key": "test-value"},
}))
})
})
it("extracts buildpack path from command path if CNB_BUILDPACK_PATH is not set", func() {

219
docs/build.md Normal file
View File

@ -0,0 +1,219 @@
# Building
A buildpack is suitably packaged `detect` binary, a `build` binary and a `buildpack.toml`. Here we look at the implementation logic of the build binary for our buildpack. This starts by declaring a `struct` that we call `Builder`. The purpose of our struct is to satisfy `libcnb`s [`Builder`](https://pkg.go.dev/github.com/buildpacks/libcnb?utm_source=gopls#Builder) interface providing a receiver with signature `func Build(context BuildContext) (BuildResult, error)`. An implementation of `Builder` is passed to `libcnb.Main` as the main entry point to the `build` binary.
In our `Detector` we use a [`poet`](https://pkg.go.dev/github.com/buildpacks/libcnb/poet) logger instance. The logger instance will be used to inform the user of the progress of our `detect` phase.
```go
type Builder struct {
Logger poet.Logger
}
```
## Implementing Build
The most simple implementation of `func (context BuildContext) (BuildResult, error)` returns a `BuildResult` containing an ordered list `LayerContributor`s which will each contribute layers to the image.
```go
func (d Builder) Build(context libcnb.BuildContext) (libcnb.BuildResult, error) {
return libcnb.NewBuildResult(), nil
}
```
The trivial passing implementation of `Build` contributes no layers to the result.
This trivial case ignores the `Plans` field of the `DetectResult` struct. In order to understand the `Plans` field we look at the concept of provides and requires.
### Builders and Contributors
It is recommended that a single `Builder` provides each layer using a `Contributor`. In this example we are contributing a single layer containing our extracted archive. Therefore, our `Builder` will orchestrate a single `Contributor`.
### Accessing `BuildPlan` Metadata
The `detect` phases passes metadata to the `build` phase in the [`BuildpackPlan`](https://pkg.go.dev/github.com/buildpacks/libcnb#BuildpackPlan) (note: not to be confused with the more general [`BuildPlan`](https://pkg.go.dev/github.com/buildpacks/libcnb#BuildPlan)). Multiple buildpack `Detect` executions can require that the `Build` of our example buildpack executes. As there may be requirements from multiple `detect` binaries, we must merge all the entries in the buildplan that correspond to our "example" buildpack.
```go
func (b Builder) Build(context libcnb.BuildContext) (libcnb.BuildResult, error) {
b.Logger.Title(context.Buildpack)
// Resolve the "version" that the detect phase adds to the BuildPlan metadata
version, err := resolveVersion(context.Plan)
if err != nil {
return libcnb.NewBuildResult(), err
}
// If "version" was not supplied, use a default
if version == "" {
version = "0.0.1"
}
return libcnb.NewBuildResult(), nil
}
// resolveVersion takes the buildpack plan and resolves the version metadata
// It then returrns the "version" information extracted from the metadata.
func resolveVersion(plan libcnb.BuildpackPlan) (string, error) {
exampleBuildPlan := libcnb.BuildpackPlanEntry{}
// Find the buildpack plan entry that releates to our "example" buildpack
for _, e := range plan.Entries {
if e.Name == "example" {
exampleBuildPlan = e
}
}
// Extract the "version" metadata if present
if version, ok := exampleBuildPlan.Metadata["version"].(string); ok {
return version, nil
}
return "", fmt.Errorf("unable to resolve version from metadata.")
}
```
In general, multiple buildpacks could `Require` our example buildpack. In that general case there may be multiple `BuildpackPlan` entries referring to our `example` buildpack. A production-level utility function to resolve the build plan can be found in [`libpak`](https://github.com/paketo-buildpacks/libpak/blob/f24422191fc6a2a02178337d96dda1210faaae9f/buildpack_plan.go#L69).
### Parsing the Buildpack Metadata
We download the files to extract into a layer from an archive on the Internet. To make this easier to maintain, we embed the download URL as a string template in the `builpack.toml` metadata. Our download URL is of the form `"https://source.fake/releases/v{{ .version }}/example_{{ .version }}_linux_amd64.tar.gz"`. We accept the version parameter as detected in the `detect` phase, and replace the version into our download URL.
```go
func (b Builder) Build(context libcnb.BuildContext) (libcnb.BuildResult, error) {
b.Logger.Title(context.Buildpack)
version, err := resolveVersion(context.Plan)
if err != nil {
return libcnb.NewBuildResult(), err
}
if version == "" {
version = "0.0.1"
}
// Use the standard Go text/template to compute the download URL from the
// version resolved from the buildpack plan.
urlTemplateMetadata, found := context.Buildpack.Metadata["url"]
if !found {
return libcnb.NewBuildResult(), fmt.Errorf("no url template in buildpack.toml metadata")
}
urlTemplate, ok := urlTemplateMetadata.(string)
if !ok {
return libcnb.NewBuildResult(), fmt.Errorf("unable to parse buildpack.toml metadata")
}
archiveURL, err := template.New("ArchiveVersion").Parse(urlTemplate)
if err != nil {
return libcnb.NewBuildResult(), fmt.Errorf("unable buildpack.toml url metadata is not a valid template")
}
var url bytes.Buffer
archiveURL.Execute(&url, struct{ version string }{version: version})
return libcnb.NewBuildResult(), nil
}
```
Now that we know our download URL, we are in a position to contribute a layer containing the extracted files.
### Contributing a Layer
To contribute a layer we implement the [`LayerContributor`](https://pkg.go.dev/github.com/buildpacks/libcnb#LayerContributor) interface. Our `Contributor` will download an archive from the provided URL. Again, we borrow a utility function from `libpak` to extract the archive to an output layer. You could use standard Go archive functions to do this, but it's more work.
```go
type Contributor struct {
Logger log.Logger
Archive string
}
func (Contributor) Name() string {
return "example"
}
func (c Contributor) Contribute(layer libcnb.Layer) (libcnb.Layer, error) {
// the values for caching layers are documented at
// https://buildpacks.io/docs/buildpack-author-guide/caching-strategies/
// Here we make the layer available for caching, as a build layer and as a
// launch layer on the output image
layer.Launch = true
layer.Cache = true
layer.Build = true
if err := os.MkdirAll(layer.Exec.Path, 0755); err != nil {
return libcnb.Layer{}, err
}
// Fetch the archive from the Internet
resp, err := http.Get(c.Archive)
if err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to download archive\n%w", err)
}
defer resp.Body.Close()
// Extract the archive into the output layer
if err := crush.Extract(resp.Body, layer.Path, 0); err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to expand archive\n%w", err)
}
return layer, nil
}
```
We add our contributor to the `libcnb.BuildContext` layers. `libcnb` will ensure each `LayerContributor` is executed in the order that they are appended to the `BuildResult.Layers` list.
```go
func (b Builder) Build(context libcnb.BuildContext) (libcnb.BuildResult, error) {
b.Logger.Title(context.Buildpack)
...
contributor := Contributor{
Logger: b.Logger,
Archive: url.String(),
}
result := libcnb.NewBuildResult()
result.Layers = append(result.Layers, contributor)
return result, nil
}
```
### Contributing a Startup Script and Environmental Variable
Finally, we assume that our run image contains a `bash` shell. An [`exec.d` style](https://buildpacks.io/docs/reference/spec/migration/buildpack-api-0.4-0.5/#execd) executable is contributed as to run on container startup.
An environment variable `EXAMPLE` is contributed to the image with the default value of `1.2.3`. It is left as an exercise to the reader to pass the `version` information from `Build` through the instance of `type Contributor struct` to the `EXAMPLE` environmental variable.
```go
const ExampleExecD string = `
#!/bin/bash
echo Executed ${EXAMPLE} on Startup
`
func (c Contributor) Contribute(layer libcnb.Layer) (libcnb.Layer, error) {
layer.Launch = true
layer.Cache = true
layer.Build = true
if err := os.MkdirAll(layer.Exec.Path, 0755); err != nil {
return libcnb.Layer{}, err
}
// fetch and extract tar.gz
resp, err := http.Get(c.Archive)
if err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to download archive\n%w", err)
}
defer resp.Body.Close()
if err := crush.Extract(resp.Body, layer.Path, 0); err != nil {
return libcnb.Layer{}, fmt.Errorf("unable to expand archive\n%w", err)
}
// We are using the exec.d interface here per - https://github.com/buildpacks/spec/blob/main/buildpack.md#execd
// #nosec G306
if err := os.WriteFile(layer.Exec.FilePath("example"), []byte(ExampleExecD), 0755); err != nil {
return libcnb.Layer{}, err
}
// Set environment variable
layer.LaunchEnvironment.Default("EXAMPLE", "1.2.3")
return layer, nil
}
```
## Summary
We have taken a quick tour of implementing a `build` binary using `libcnb`. We have seen how `libcnb` offers a straightforward `BuildResult` that can be used to return a number of `LayerContributor`s. In turn, each `LayerContributor` can contribute files to the output image, scripts executed on startup and environmental variable on the output image. Our tour of the `libcnb` API included a look at `libcnb.BuildContext` through which we have access to the `BuildPlan` computed after the `detect` phase and buildpack metadata.

224
docs/detect.md Normal file
View File

@ -0,0 +1,224 @@
# Detection
A buildpack is suitably packaged `detect` binary, a `build` binary and a `buildpack.toml`. Here we look at the implementation logic of the detect binary for our example buildpack. This starts by declaring a `struct` that we call `Detector`. The purpose of our struct is to satisfy `libcnb`s [`Detector`](https://pkg.go.dev/github.com/buildpacks/libcnb?utm_source=gopls#Detector) interface providing a receiver with signature `func Detect(context DetectContext) (DetectResult, error)`. An implementation of `Detector` is passed to `libcnb.Main` as the main entry point to the `detect` binary.
In our `Detector` we use a [`poet`](https://pkg.go.dev/github.com/buildpacks/libcnb/poet) logger instance. The logger instance will be used to inform the user of the progress of our `detect` phase.
```go
type Detector struct {
Logger poet.Logger
}
```
## Implementing Detect
The most simple implementation of `func (context DetectContext) (DetectResult, error)` returns a `DetectResult` ensuring that the this buildpack does not contribute to the build phase.
```go
func (d Detector) Detect(context DetectContext) (DetectResult, error) {
return DetectResult{}, nil
}
```
The trivial passing implementation of `Detect` ensures that the `build` binary of this buildpack will run during the build phase.
```go
func (d Detector) Detect(context DetectContext) (DetectResult, error) {
return DetectResult{Pass: true}, nil
}
```
These two trivial cases ignore the `Plans` field of the `DetectResult` struct. In order to understand the `Plans` field we look at the concept of provides and requires.
### Provides and Requires
Buildpacks may depend on other buildpacks. The dependencies are computed at runtime. Buildpacks can **provide** functionality that other buildpacks use. For example, a buildpack may provide a runtime such as the python runtime or a Java runtime environment; other buildpacks may detect that an application is a python or Java application and require the provided runtime. Buildpacks may also **require** functionality provided by other buildpacks.
A buildpack can offer multiple provides. However, it is common for a buildpack to offer only a single provide. Each provision is identified by a string. Here we declare `Example` as an identifier.
```go
const (
Example = "example"
)
```
Extending a trivial passing implementation with `Provides` allows another buildpack to require this buildpack.
```go
func (d Detector) Detect(context DetectContext) (DetectResult, error) {
return 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: Example},
},
}
}
}, nil
}
```
The detect phase of another buildpack may require this buildpack. This is commonly used when the detect phase gathers information that is passed as metadata to the build phase. Similarly, it is common for a buildpack to require itself. This will allow the detect phase of a buildpack to pass information to the build phase.
```go
func (d Detector) Detect(context DetectContext) (DetectResult, error) {
return 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: Example},
},
Requires: []libcmb.BuildPlanRequire{
{Name: Example},
}
}
}
}, nil
}
```
Here we pass the key-value pair `(version, 1.0)` as metadata from the detect phase of the example buildpack to the build phase. Metadata can be arbitrarily nested, but it must be textual as it is serialized to TOML at the end of the detect phase.
```go
func (d Detector) Detect(context DetectContext) (DetectResult, error) {
return 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: Example},
},
Requires: []libcmb.BuildPlanRequire{
{
Name: Example,
// Pass arbitrary metadata to the Example build phase
Metadata: {
"version": "1.0",
}
},
}
}
}
}, nil
}
```
The `detect` binary is provided with read-only access to the application source code. As an example we can detect the presence of a file called `example.txt`. If this file does not exist, then we can fail at the detect phase.
```go
func (d Detector) Detect(context DetectContext) (DetectResult, error) {
const exampleFile := "example.txt"
// Construct a path to exampleFile in the Application source project
requirementsFile := filepath.Join(context.Application.Path, exampleFile)
// Test for the existance of exampleFile
if _, err := os.Stat(exampleFile); err != nil && os.IsNotExist(err) {
return libcnb.DetectResult{}, err
}
d.Logger.Debugf("Found %s example file -> %s", exampleFile)
...
}
```
We have defined a `detect` binary that passes if `example.txt` is present in the source application and fails otherwise.
During the detect phase the buildpack has access to static metadata declared in the `buildpack.toml` file.
## Accessing buildpack metadata
It is common for static metadata to be declared in a `buildpack.toml`. This is commonly used to pass URLs to the buildpack that are used to download artefacts. As an example, a `buildpack.toml` may declare the download location for the `example` artefact:
```toml
[metadata]
[[metadata.dependencies]]
url = "https://source.fake/releases/v{{ .version }}/example_{{ .version }}_linux_amd64.tar.gz"
```
In our running example the detect phase of our buildpack detects the presence of `example.txt`. We may want a buildpack definition to change the name of the file for detection.
```toml
[metadata]
[[metadata.dependencies]]
example-file = "custom-example.txt"
```
The `Detect(context)` function can access the metadata in the buildpack definition.
```go
func (d Detector) Detect(context DetectContext) (DetectResult, error) {
var exampleFile := "example.txt"
if configuredExampleFile, ok; context.Buildpack.Metadata["example-file"]; ok {
exampleFile = configuredExampleFile.(string)
}
// Construct a path to exampleFile in the Application source project
requirementsFile := filepath.Join(context.Application.Path, exampleFile)
// Test for the existance of exampleFile
if _, err := os.Stat(exampleFile); err != nil && os.IsNotExist(err) {
return libcnb.DetectResult{Pass: true}, err
}
d.Logger.Debugf("Found %s example file -> %s", exampleFile)
...
}
```
If `example.txt` can be overridden it is common to also provide an environment variable to allow overriding. Here we allow `example.txt` to be overridden using either a static definition in the `buildpack.toml` file or by the presence of a `BP_EXAMPLE_FILE` environment variable.
```go
const (
BpExampleFile = "BP_EXAMPLE_FILE"
)
func (d Detector) Detect(context DetectContext) (DetectResult, error) {
var exampleFile := "example.txt"
if configuredExampleFile, ok; context.Buildpack.Metadata["example-file"]; ok {
exampleFile = configuredExampleFile.(string)
}
if file, ok := context.Platform.Environment(BpExamplefile); ok {
exampleFile = file
}
// Construct a path to exampleFile in the Application source project
requirementsFile := filepath.Join(context.Application.Path, exampleFile)
// Test for the existance of exampleFile
if _, err := os.Stat(exampleFile); err != nil && os.IsNotExist(err) {
return libcnb.DetectResult{Pass: true}, err
}
d.Logger.Debugf("Found %s example file -> %s", exampleFile)
...
}
```
Buildpack metadata and environment variables are useful when passing configuration to a buildpack `detect` binary. Passing sensitive data, such as secrets, requires a different approach.
## Secrets and other bindings
Secrets are available through the `DetectContext`. Here we look for a binding named "GIT_TOKEN" which we expect to define a secret called "TOKEN". We assume the existence of a function with signature `findBinding([]libcnb.Bindings, string) (libcnb.Binding, error)`.
```go
const (
GitTokenBinding = "GIT_TOKEN"
GitToken = "TOKEN"
)
func (d Detector) Detect(context DetectContext) (DetectResult, error) {
bindings := context.Platform.Bindings
gitTokenBinding, err := findBinding(bindings, GitTokenBinding)
if err != nil {
return DetectResult{}, fmt.errorf("unable to access binding %s", GitTokenBinding)
}
if gitToken, exists := gitTokenBinding.Secret[GitToken]; !exists {
return DetectResult{}, fmt.errorf("unable to find secret %s in binding %s", GitToken, GitTokenBinding)
}
...
}
```
## Summary
We have taken a quick tour of implementing a `detect` binary using `libcnb`. We have seen how `libcnb` offers a straightforward `DetectResult` that can be used to signal success or failure of the detect phase. In addition the `DetectResult` can offer `Provides` and specify `Requires`. Our tour of the `libcnb` API included a look at `libcnb.DetectContext` through which we have access to environment variables, buildpack metadata and bindings.

44
docs/tutorial.md Normal file
View File

@ -0,0 +1,44 @@
# `libcnb` tutorial
[https://pkg.go.dev/github.com/buildpacks/libcnb?tab=doc](https://pkg.go.dev/github.com/buildpacks/libcnb?tab=doc) provides the reference documentation for `libcnb`. In this tutorial we walk through an example implementation using data structures and functions described in the reference documentation.
In general, buildpacks are responsible for
* installing runtimes eg: a `python` interpreter,
* installing dependency managers eg: Python's `pip` dependency manager,
* resolving application dependencies, and
* building/packaging the application code.
Commonly each of these responsibilities is factored out into a separate buildpacks.
In this tutorial we take a simplified look at a generic buildpack and implement both the `detect` and `build` binaries for an example buildpack.
## Scenario
Our buildpack will have the following properties:
* if the source project contains `example.txt` or `BP_EXAMPLE_FILE=some-file.txt` then the buildpack `detect` process will succeed.
* if `detect` passes, then our `build` binary will contribute a single layer to the output image. The layer will contain the contents of a gzipped archive fetched from an Internet location.
We first [`detect`](detect.md) whether this buildpack should contribute to the build. If detection passes we then run [`build`](build.md) to extract our example gzipped archive.
The main application of our buildpack will combine our implementation of `Detector` and `Builder` to provide the `detect` and `build` binaries:
```go
func main() {
detector := Detector{Logger: log.New(os.Stdout)}
builder := Builder{Logger: log.New(os.Stdout)}
libcnb.Main(detector, builder)
}
```
## Learning Goals
At the end of this tutorial you will have
* examined the separation of `detect` responsibilities from `build` responsibilities,
* introspected a source application to satisfy `detect`,
* passed metadata between the `detect` and `build` phases using the build plan,
* contributed a layer to an output image that contains the contents of an archive,
* set a default environment variable `EXAMPLE` on the output image, and
* defined an exec.d startup script on the output image.

View File

@ -26,7 +26,7 @@ import (
"github.com/buildpacks/libcnb"
)
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

View File

@ -28,7 +28,7 @@ import (
"github.com/buildpacks/libcnb/mocks"
)
func testExecD(t *testing.T, context spec.G, it spec.S) {
func testExecD(t *testing.T, _ spec.G, it spec.S) {
var (
Expect = NewWithT(t).Expect

View File

@ -17,7 +17,6 @@
package libcnb_test
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
@ -39,13 +38,7 @@ func testFormatter(t *testing.T, context spec.G, it spec.S) {
)
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())
app = t.TempDir()
})
it("lists empty directory contents", func() {
@ -67,9 +60,7 @@ func testFormatter(t *testing.T, context spec.G, it spec.S) {
)
it.Before(func() {
var err error
bp, err = ioutil.TempDir("", "buildpack-path-formatter")
Expect(err).NotTo(HaveOccurred())
bp = t.TempDir()
})
it.After(func() {
@ -95,9 +86,7 @@ func testFormatter(t *testing.T, context spec.G, it spec.S) {
)
it.Before(func() {
var err error
plat.Path, err = ioutil.TempDir("", "platform-formatter")
Expect(err).NotTo(HaveOccurred())
plat.Path = t.TempDir()
})
it.After(func() {

19
go.mod
View File

@ -1,10 +1,21 @@
module github.com/buildpacks/libcnb
go 1.15
go 1.20
require (
github.com/BurntSushi/toml v0.4.1
github.com/onsi/gomega v1.16.0
github.com/BurntSushi/toml v1.3.2
github.com/Masterminds/semver/v3 v3.2.1
github.com/onsi/gomega v1.33.0
github.com/sclevine/spec v1.4.0
github.com/stretchr/testify v1.7.0
github.com/stretchr/testify v1.9.0
)
require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/stretchr/objx v0.5.2 // indirect
golang.org/x/net v0.24.0 // indirect
golang.org/x/text v0.14.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)

122
go.sum
View File

@ -1,106 +1,32 @@
github.com/BurntSushi/toml v0.4.1 h1:GaI7EiDXDRfa8VshkTj7Fym7ha+y8/XxIgD2okUIjLw=
github.com/BurntSushi/toml v0.4.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/BurntSushi/toml v1.3.2 h1:o7IhLm0Msx3BaB+n3Ag7L8EVlByGnpq14C4YWiu/gL8=
github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
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/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
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.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
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.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A=
github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc=
github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0=
github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY=
github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo=
github.com/onsi/gomega v1.16.0 h1:6gjqkI8iiRHMvdccRJM8rVKjCWk6ZIm6FTm3ddIe4/c=
github.com/onsi/gomega v1.16.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY=
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
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/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=
github.com/onsi/ginkgo/v2 v2.17.1 h1:V++EzdbhI4ZV4ev0UTIj0PzhzOcReJFyJaLjtSF55M8=
github.com/onsi/gomega v1.33.0 h1:snPCflnZrpMsy94p4lXVEkHo12lmPnc3vY5XBbreexE=
github.com/onsi/gomega v1.33.0/go.mod h1:+925n5YtiFsLzzafLUHzVMBpvvRAzrydIBiSIxjX3wY=
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 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
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/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A=
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781 h1:DzZ89McO9/gWPsQXS/FVKAlG02ZjaQ6AlZRBimEYOd0=
golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT/9KSuxbyM7479/AVlXFRxuMCk=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/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-20201020160332-67f06af15bc9/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-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/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-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da h1:b3NXsE2LusjYGGjL5bxEVZZORm/YEFFrWFjR8eFrw/c=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
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 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
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.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
google.golang.org/protobuf v1.26.0 h1:bxAC2xTBsZGibn2RTntX0oH50xLsqy1OxA9tTL3p/lk=
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
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.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/net v0.24.0 h1:1PcaxkF854Fu3+lvBIx5SYn9wRlBzzcnHZSiaFFAb0w=
golang.org/x/net v0.24.0/go.mod h1:2Q7sJY5mzlzWjKtYUEXSlBWCdyaioyXzRB2RtU8KVE8=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=
golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
golang.org/x/tools v0.17.0 h1:FvmRgNOcs3kOa+T20R1uhfP9F6HgG2mfxDv1vrx1Htc=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
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.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 h1:dUUwHk2QECo/6vqA44rthZ8ie2QXMNeKRTHCNY2nXvo=
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=

View File

@ -5,7 +5,6 @@ linters:
disable-all: true
enable:
- bodyclose
- deadcode
- dogsled
- exportloopref
- gocritic
@ -18,12 +17,10 @@ linters:
- nakedret
- revive
- staticcheck
- structcheck
- stylecheck
- typecheck
- unconvert
- unused
- varcheck
- whitespace
linters-settings:

View File

@ -33,5 +33,6 @@ func TestUnit(t *testing.T) {
suite("Main", testMain)
suite("Platform", testPlatform)
suite("ExecD", testExecD)
suite("BuildpackTOML", testBuildpackTOML)
suite.Run(t)
}

View File

@ -18,7 +18,6 @@ package internal
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"strings"
@ -45,7 +44,7 @@ func NewConfigMapFromPath(path string) (ConfigMap, error) {
} else if stat.IsDir() {
continue
}
contents, err := ioutil.ReadFile(file)
contents, err := os.ReadFile(file)
if err != nil {
return nil, fmt.Errorf("unable to read file %s\n%w", file, err)
}

View File

@ -17,7 +17,6 @@
package internal_test
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
@ -28,7 +27,7 @@ import (
"github.com/buildpacks/libcnb/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,13 +35,7 @@ func testConfigMap(t *testing.T, context spec.G, it spec.S) {
)
it.Before(func() {
var err error
path, err = ioutil.TempDir("", "config-map")
Expect(err).NotTo(HaveOccurred())
})
it.After(func() {
Expect(os.RemoveAll(path)).To(Succeed())
path = t.TempDir()
})
it("returns an empty ConfigMap when directory does not exist", func() {
@ -55,7 +48,7 @@ func testConfigMap(t *testing.T, context spec.G, it spec.S) {
})
it("loads the ConfigMap from a directory", func() {
Expect(ioutil.WriteFile(filepath.Join(path, "test-key"), []byte("test-value"), 0600)).To(Succeed())
Expect(os.WriteFile(filepath.Join(path, "test-key"), []byte("test-value"), 0600)).To(Succeed())
cm, err := internal.NewConfigMapFromPath(path)
Expect(err).NotTo(HaveOccurred())
@ -66,7 +59,7 @@ func testConfigMap(t *testing.T, context spec.G, it spec.S) {
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(ioutil.WriteFile(
Expect(os.WriteFile(
filepath.Join(path, ".hidden", "test-key"),
[]byte("test-value"),
0600,
@ -82,7 +75,7 @@ func testConfigMap(t *testing.T, context spec.G, it spec.S) {
})
it("ignores hidden files", func() {
Expect(ioutil.WriteFile(filepath.Join(path, ".hidden-key"), []byte("hidden-value"), 0600)).To(Succeed())
Expect(os.WriteFile(filepath.Join(path, ".hidden-key"), []byte("hidden-value"), 0600)).To(Succeed())
cm, err := internal.NewConfigMapFromPath(path)
Expect(err).NotTo(HaveOccurred())

View File

@ -17,7 +17,6 @@
package internal_test
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
@ -28,7 +27,7 @@ import (
"github.com/buildpacks/libcnb/internal"
)
func testDirectoryContents(t *testing.T, context spec.G, it spec.S) {
func testDirectoryContents(t *testing.T, _ spec.G, it spec.S) {
var (
Expect = NewWithT(t).Expect
@ -36,13 +35,7 @@ func testDirectoryContents(t *testing.T, context spec.G, it spec.S) {
)
it.Before(func() {
var err error
path, err = ioutil.TempDir("", "directory-contents")
Expect(err).NotTo(HaveOccurred())
})
it.After(func() {
Expect(os.RemoveAll(path)).To(Succeed())
path = t.TempDir()
})
it("lists empty directory contents", func() {

View File

@ -18,7 +18,6 @@ package internal
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
)
@ -38,8 +37,14 @@ func (w EnvironmentWriter) Write(path string, environment map[string]string) err
for key, value := range environment {
f := filepath.Join(path, key)
// 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)
}
// #nosec
if err := ioutil.WriteFile(f, []byte(value), 0644); err != nil {
if err := os.WriteFile(f, []byte(value), 0644); err != nil {
return fmt.Errorf("unable to write file %s\n%w", f, err)
}
}

View File

@ -17,7 +17,6 @@
package internal_test
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
@ -28,7 +27,7 @@ import (
"github.com/buildpacks/libcnb/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,13 +36,7 @@ func testEnvironmentWriter(t *testing.T, context spec.G, it spec.S) {
)
it.Before(func() {
var err error
path, err = ioutil.TempDir("", "environment-writer")
Expect(err).NotTo(HaveOccurred())
Expect(os.RemoveAll(path)).To(Succeed())
})
it.After(func() {
path = t.TempDir()
Expect(os.RemoveAll(path)).To(Succeed())
})
@ -54,15 +47,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())

View File

@ -26,7 +26,7 @@ import (
"github.com/buildpacks/libcnb/internal"
)
func testExecDWriter(t *testing.T, context spec.G, it spec.S) {
func testExecDWriter(t *testing.T, _ spec.G, it spec.S) {
var (
Expect = NewWithT(t).Expect

View File

@ -27,7 +27,7 @@ import (
"github.com/buildpacks/libcnb/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

View File

@ -17,7 +17,6 @@
package internal_test
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
@ -28,7 +27,7 @@ import (
"github.com/buildpacks/libcnb/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,17 +37,10 @@ func testTOMLWriter(t *testing.T, context spec.G, it spec.S) {
)
it.Before(func() {
var err error
parent, err = ioutil.TempDir("", "toml-writer")
Expect(err).NotTo(HaveOccurred())
parent = t.TempDir()
path = filepath.Join(parent, "text.toml")
})
it.After(func() {
Expect(os.RemoveAll(parent)).To(Succeed())
})
it("writes the contents of a given object out to a .toml file", func() {
err := tomlWriter.Write(path, map[string]string{
"some-field": "some-value",
@ -56,7 +48,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"`))
})

View File

@ -26,9 +26,18 @@ import (
"github.com/buildpacks/libcnb/internal"
)
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"
)
// Exec represents the exec.d layer location
type Exec struct {
// Path is the path to the exec.d directory.
Path string
}
@ -68,9 +77,47 @@ func (p Profile) ProcessAddf(processType string, name string, format string, a .
p.Addf(filepath.Join(processType, name), format, a...)
}
// Contribute represents a layer managed by the buildpack.
type Layer struct {
// 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)
}
// Layer represents a layer managed by the buildpack.
type Layer struct {
// LayerTypes indicates the type of layer
LayerTypes `toml:"types"`
@ -99,6 +146,10 @@ type Layer struct {
Exec Exec `toml:"-"`
}
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 {
@ -116,7 +167,6 @@ type LayerTypes struct {
// LayerContributor is an interface for types that create layers.
type LayerContributor interface {
// Contribute accepts a layer and transforms it, returning a layer.
Contribute(layer Layer) (Layer, error)
@ -126,7 +176,6 @@ type LayerContributor interface {
// Layers represents the layers part of the specification.
type Layers struct {
// Path is the layers filesystem location.
Path string
}
@ -162,3 +211,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))
}

View File

@ -17,7 +17,6 @@
package libcnb_test
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
@ -83,10 +82,7 @@ func testLayer(t *testing.T, context spec.G, it spec.S) {
context("Layers", func() {
it.Before(func() {
var err error
path, err = ioutil.TempDir("", "layers")
Expect(err).NotTo(HaveOccurred())
path = t.TempDir()
layers = libcnb.Layers{Path: path}
})
@ -110,8 +106,38 @@ func testLayer(t *testing.T, context spec.G, it spec.S) {
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 0.5 metadata", func() {
Expect(ioutil.WriteFile(
Expect(os.WriteFile(
filepath.Join(path, "test-name.toml"),
[]byte(`
launch = true
@ -132,7 +158,7 @@ test-key = "test-value"
})
it("reads existing 0.6 metadata", func() {
Expect(ioutil.WriteFile(
Expect(os.WriteFile(
filepath.Join(path, "test-name.toml"),
[]byte(`
[types]
@ -154,7 +180,7 @@ test-key = "test-value"
})
it("reads existing 0.6 metadata with launch, build and cache all false", func() {
Expect(ioutil.WriteFile(
Expect(os.WriteFile(
filepath.Join(path, "test-name.toml"),
[]byte(`
[types]

View File

@ -17,7 +17,6 @@
package libcnb_test
import (
"io/ioutil"
"os"
"path/filepath"
"testing"
@ -30,7 +29,7 @@ import (
"github.com/buildpacks/libcnb/mocks"
)
func testMain(t *testing.T, context spec.G, it spec.S) {
func testMain(t *testing.T, _ spec.G, it spec.S) {
var (
Expect = NewWithT(t).Expect
@ -52,18 +51,17 @@ func testMain(t *testing.T, context spec.G, it spec.S) {
it.Before(func() {
var err error
applicationPath, err = ioutil.TempDir("", "main-application-path")
Expect(err).NotTo(HaveOccurred())
applicationPath = t.TempDir()
applicationPath, err = filepath.EvalSymlinks(applicationPath)
Expect(err).NotTo(HaveOccurred())
builder = &mocks.Builder{}
buildpackPath, err = ioutil.TempDir("", "main-buildpack-path")
buildpackPath = t.TempDir()
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.6"
@ -89,12 +87,12 @@ test-key = "test-value"
0600),
).To(Succeed())
f, err := ioutil.TempFile("", "main-buildpackplan-path")
f, err := os.CreateTemp("", "main-buildpackplan-path")
Expect(err).NotTo(HaveOccurred())
Expect(f.Close()).NotTo(HaveOccurred())
buildpackPlanPath = f.Name()
Expect(ioutil.WriteFile(buildpackPlanPath,
Expect(os.WriteFile(buildpackPlanPath,
[]byte(`
[[entries]]
name = "test-name"
@ -106,7 +104,7 @@ test-key = "test-value"
0600),
).To(Succeed())
f, err = ioutil.TempFile("", "main-buildplan-path")
f, err = os.CreateTemp("", "main-buildplan-path")
Expect(err).NotTo(HaveOccurred())
Expect(f.Close()).NotTo(HaveOccurred())
buildPlanPath = f.Name()
@ -121,10 +119,9 @@ test-key = "test-value"
exitHandler.On("Pass", mock.Anything)
exitHandler.On("Fail", mock.Anything)
layersPath, err = ioutil.TempDir("", "main-layers-path")
Expect(err).NotTo(HaveOccurred())
layersPath = t.TempDir()
Expect(ioutil.WriteFile(filepath.Join(layersPath, "store.toml"),
Expect(os.WriteFile(filepath.Join(layersPath, "store.toml"),
[]byte(`
[metadata]
test-key = "test-value"
@ -132,24 +129,23 @@ test-key = "test-value"
0600),
).To(Succeed())
platformPath, err = ioutil.TempDir("", "main-platform-path")
Expect(err).NotTo(HaveOccurred())
platformPath = t.TempDir()
Expect(os.MkdirAll(filepath.Join(platformPath, "bindings", "alpha", "metadata"), 0755)).To(Succeed())
Expect(ioutil.WriteFile(
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(ioutil.WriteFile(
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(ioutil.WriteFile(filepath.Join(platformPath, "env", "TEST_ENV"), []byte("test-value"), 0600)).
Expect(os.WriteFile(filepath.Join(platformPath, "env", "TEST_ENV"), []byte("test-value"), 0600)).
To(Succeed())
tomlWriter = &mocks.TOMLWriter{}

View File

@ -1,4 +1,4 @@
// Code generated by mockery 2.9.4. DO NOT EDIT.
// Code generated by mockery v2.24.0. DO NOT EDIT.
package mocks
@ -18,13 +18,16 @@ func (_m *Builder) Build(context libcnb.BuildContext) (libcnb.BuildResult, error
ret := _m.Called(context)
var r0 libcnb.BuildResult
var r1 error
if rf, ok := ret.Get(0).(func(libcnb.BuildContext) (libcnb.BuildResult, error)); ok {
return rf(context)
}
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 {
@ -33,3 +36,18 @@ func (_m *Builder) Build(context libcnb.BuildContext) (libcnb.BuildResult, error
return r0, r1
}
type mockConstructorTestingTNewBuilder interface {
mock.TestingT
Cleanup(func())
}
// NewBuilder creates a new instance of Builder. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewBuilder(t mockConstructorTestingTNewBuilder) *Builder {
mock := &Builder{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@ -1,4 +1,4 @@
// Code generated by mockery 2.9.4. DO NOT EDIT.
// Code generated by mockery v2.24.0. DO NOT EDIT.
package mocks
@ -18,13 +18,16 @@ func (_m *Detector) Detect(context libcnb.DetectContext) (libcnb.DetectResult, e
ret := _m.Called(context)
var r0 libcnb.DetectResult
var r1 error
if rf, ok := ret.Get(0).(func(libcnb.DetectContext) (libcnb.DetectResult, error)); ok {
return rf(context)
}
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 {
@ -33,3 +36,18 @@ func (_m *Detector) Detect(context libcnb.DetectContext) (libcnb.DetectResult, e
return r0, r1
}
type mockConstructorTestingTNewDetector interface {
mock.TestingT
Cleanup(func())
}
// NewDetector creates a new instance of Detector. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewDetector(t mockConstructorTestingTNewDetector) *Detector {
mock := &Detector{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@ -1,4 +1,4 @@
// Code generated by mockery 2.9.4. DO NOT EDIT.
// Code generated by mockery v2.24.0. DO NOT EDIT.
package mocks
@ -22,3 +22,18 @@ func (_m *EnvironmentWriter) Write(dir string, environment map[string]string) er
return r0
}
type mockConstructorTestingTNewEnvironmentWriter interface {
mock.TestingT
Cleanup(func())
}
// 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.
func NewEnvironmentWriter(t mockConstructorTestingTNewEnvironmentWriter) *EnvironmentWriter {
mock := &EnvironmentWriter{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@ -1,4 +1,4 @@
// Code generated by mockery 2.9.4. DO NOT EDIT.
// Code generated by mockery v2.24.0. DO NOT EDIT.
package mocks
@ -14,6 +14,10 @@ func (_m *ExecD) Execute() (map[string]string, error) {
ret := _m.Called()
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 {
@ -22,7 +26,6 @@ func (_m *ExecD) Execute() (map[string]string, error) {
}
}
var r1 error
if rf, ok := ret.Get(1).(func() error); ok {
r1 = rf()
} else {
@ -31,3 +34,18 @@ func (_m *ExecD) Execute() (map[string]string, error) {
return r0, r1
}
type mockConstructorTestingTNewExecD interface {
mock.TestingT
Cleanup(func())
}
// 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.
func NewExecD(t mockConstructorTestingTNewExecD) *ExecD {
mock := &ExecD{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@ -1,4 +1,4 @@
// Code generated by mockery 2.9.4. DO NOT EDIT.
// Code generated by mockery v2.24.0. DO NOT EDIT.
package mocks
@ -22,3 +22,18 @@ func (_m *ExecDWriter) Write(value map[string]string) error {
return r0
}
type mockConstructorTestingTNewExecDWriter interface {
mock.TestingT
Cleanup(func())
}
// 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.
func NewExecDWriter(t mockConstructorTestingTNewExecDWriter) *ExecDWriter {
mock := &ExecDWriter{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@ -1,4 +1,4 @@
// Code generated by mockery 2.9.4. DO NOT EDIT.
// Code generated by mockery v2.24.0. DO NOT EDIT.
package mocks
@ -23,3 +23,18 @@ func (_m *ExitHandler) Fail() {
func (_m *ExitHandler) Pass() {
_m.Called()
}
type mockConstructorTestingTNewExitHandler interface {
mock.TestingT
Cleanup(func())
}
// 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.
func NewExitHandler(t mockConstructorTestingTNewExitHandler) *ExitHandler {
mock := &ExitHandler{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@ -1,4 +1,4 @@
// Code generated by mockery 2.9.4. DO NOT EDIT.
// Code generated by mockery v2.24.0. DO NOT EDIT.
package mocks
@ -18,13 +18,16 @@ func (_m *LayerContributor) Contribute(layer libcnb.Layer) (libcnb.Layer, error)
ret := _m.Called(layer)
var r0 libcnb.Layer
var r1 error
if rf, ok := ret.Get(0).(func(libcnb.Layer) (libcnb.Layer, error)); ok {
return rf(layer)
}
if rf, ok := ret.Get(0).(func(libcnb.Layer) libcnb.Layer); ok {
r0 = rf(layer)
} else {
r0 = ret.Get(0).(libcnb.Layer)
}
var r1 error
if rf, ok := ret.Get(1).(func(libcnb.Layer) error); ok {
r1 = rf(layer)
} else {
@ -47,3 +50,18 @@ func (_m *LayerContributor) Name() string {
return r0
}
type mockConstructorTestingTNewLayerContributor interface {
mock.TestingT
Cleanup(func())
}
// NewLayerContributor creates a new instance of LayerContributor. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations.
func NewLayerContributor(t mockConstructorTestingTNewLayerContributor) *LayerContributor {
mock := &LayerContributor{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@ -1,4 +1,4 @@
// Code generated by mockery 2.9.4. DO NOT EDIT.
// Code generated by mockery v2.24.0. DO NOT EDIT.
package mocks
@ -22,3 +22,18 @@ func (_m *TOMLWriter) Write(path string, value interface{}) error {
return r0
}
type mockConstructorTestingTNewTOMLWriter interface {
mock.TestingT
Cleanup(func())
}
// 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.
func NewTOMLWriter(t mockConstructorTestingTNewTOMLWriter) *TOMLWriter {
mock := &TOMLWriter{}
mock.Mock.Test(t)
t.Cleanup(func() { mock.AssertExpectations(t) })
return mock
}

View File

@ -17,6 +17,7 @@
package libcnb
import (
"encoding/json"
"fmt"
"os"
"path/filepath"
@ -41,8 +42,12 @@ const (
// See the Service Binding Specification for Kubernetes for more details - https://k8s-service-bindings.github.io/spec/
EnvServiceBindings = "SERVICE_BINDING_ROOT"
// EnvVcapServices is the name of the environment variable that contains the bindings in cloudfoundry
EnvVcapServices = "VCAP_SERVICES"
// EnvCNBBindings is the name of the environment variable that contains the path to the CNB bindings directory.
// See the CNB bindings extension spec for more details - https://github.com/buildpacks/spec/blob/main/extensions/bindings.md
//
// Deprecated: Use the Service Binding Specification for Kubernetes instead -
// https://github.com/buildpacks/rfcs/blob/main/text/0055-deprecate-service-bindings.md.
EnvCNBBindings = "CNB_BINDINGS"
@ -112,6 +117,56 @@ func NewBindingFromPath(path string) (Binding, error) {
return NewBinding(filepath.Base(path), path, secret), 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
}
func (b Binding) String() string {
var s []string
for k := range b.Secret {
@ -162,6 +217,10 @@ func NewBindingsForLaunch() (Bindings, error) {
return NewBindingsFromPath(path)
}
if content, ok := os.LookupEnv(EnvVcapServices); ok {
return NewBindingsFromVcapServicesEnv(content)
}
return Bindings{}, nil
}
@ -200,6 +259,10 @@ func NewBindingsForBuild(platformDir string) (Bindings, error) {
if path, ok := os.LookupEnv(EnvCNBBindings); ok {
return NewBindingsFromPath(path)
}
if content, ok := os.LookupEnv(EnvVcapServices); ok {
return NewBindingsFromVcapServicesEnv(content)
}
return NewBindingsFromPath(filepath.Join(platformDir, "bindings"))
}

View File

@ -18,7 +18,6 @@ package libcnb_test
import (
"fmt"
"io/ioutil"
"os"
"path/filepath"
"testing"
@ -37,39 +36,112 @@ func testPlatform(t *testing.T, context spec.G, it spec.S) {
)
it.Before(func() {
var err error
platformPath, err = ioutil.TempDir("", "platform")
platformPath = t.TempDir()
path = filepath.Join(platformPath, "bindings")
Expect(err).NotTo(HaveOccurred())
})
it.After(func() {
Expect(os.RemoveAll(path)).To(Succeed())
context("Cloudfoundry VCAP_SERVICES", func() {
context("Build", 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))
bindings, err := libcnb.NewBindingsForBuild("")
Expect(err).NotTo(HaveOccurred())
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("creates empty bindings from empty VCAP_SERVICES", func() {
t.Setenv(libcnb.EnvVcapServices, "{}")
bindings, err := libcnb.NewBindingsForBuild("")
Expect(err).NotTo(HaveOccurred())
Expect(bindings).To(HaveLen(0))
})
})
context("Launch", 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))
bindings, err := libcnb.NewBindingsForLaunch()
Expect(err).NotTo(HaveOccurred())
Expect(bindings).To(HaveLen(3))
types := []string{bindings[0].Type, bindings[1].Type, bindings[2].Type}
Expect(types).To(ContainElements("elephantsql-type", "sendgrid-type", "postgres"))
})
it("creates empty bindings from empty VCAP_SERVICES", func() {
t.Setenv(libcnb.EnvVcapServices, "{}")
bindings, err := libcnb.NewBindingsForLaunch()
Expect(err).NotTo(HaveOccurred())
Expect(bindings).To(HaveLen(0))
})
})
})
context("CNB Bindings", func() {
it.Before(func() {
Expect(os.MkdirAll(filepath.Join(path, "alpha", "metadata"), 0755)).To(Succeed())
Expect(ioutil.WriteFile(filepath.Join(path, "alpha", "metadata", "kind"), []byte("test-kind"), 0600)).To(Succeed())
Expect(ioutil.WriteFile(filepath.Join(path, "alpha", "metadata", "provider"), []byte("test-provider"), 0600)).To(Succeed())
Expect(ioutil.WriteFile(filepath.Join(path, "alpha", "metadata", "test-metadata-key"), []byte("test-metadata-value"), 0600)).To(Succeed())
Expect(ioutil.WriteFile(filepath.Join(path, "alpha", "metadata", "test-metadata-key-trimmed"), []byte(" test-metadata-value-trimmed \n"), 0600)).To(Succeed())
Expect(os.WriteFile(filepath.Join(path, "alpha", "metadata", "kind"), []byte("test-kind"), 0600)).To(Succeed())
Expect(os.WriteFile(filepath.Join(path, "alpha", "metadata", "provider"), []byte("test-provider"), 0600)).To(Succeed())
Expect(os.WriteFile(filepath.Join(path, "alpha", "metadata", "test-metadata-key"), []byte("test-metadata-value"), 0600)).To(Succeed())
Expect(os.WriteFile(filepath.Join(path, "alpha", "metadata", "test-metadata-key-trimmed"), []byte(" test-metadata-value-trimmed \n"), 0600)).To(Succeed())
Expect(os.MkdirAll(filepath.Join(path, "alpha", "secret"), 0755)).To(Succeed())
Expect(ioutil.WriteFile(filepath.Join(path, "alpha", "secret", "test-secret-key"), []byte("test-secret-value"), 0600)).To(Succeed())
Expect(ioutil.WriteFile(filepath.Join(path, "alpha", "secret", "test-secret-key-trimmed"), []byte(" test-secret-value-trimmed \n"), 0600)).To(Succeed())
Expect(os.WriteFile(filepath.Join(path, "alpha", "secret", "test-secret-key"), []byte("test-secret-value"), 0600)).To(Succeed())
Expect(os.WriteFile(filepath.Join(path, "alpha", "secret", "test-secret-key-trimmed"), []byte(" test-secret-value-trimmed \n"), 0600)).To(Succeed())
Expect(os.MkdirAll(filepath.Join(path, "bravo", "metadata"), 0755)).To(Succeed())
Expect(ioutil.WriteFile(filepath.Join(path, "bravo", "metadata", "kind"), []byte("test-kind"), 0600)).To(Succeed())
Expect(ioutil.WriteFile(filepath.Join(path, "bravo", "metadata", "provider"), []byte("test-provider"), 0600)).To(Succeed())
Expect(ioutil.WriteFile(filepath.Join(path, "bravo", "metadata", "test-metadata-key"), []byte("test-metadata-value"), 0600)).To(Succeed())
Expect(ioutil.WriteFile(filepath.Join(path, "bravo", "metadata", "test-metadata-key-trimmed"), []byte(" test-metadata-value-trimmed \n"), 0600)).To(Succeed())
Expect(os.WriteFile(filepath.Join(path, "bravo", "metadata", "kind"), []byte("test-kind"), 0600)).To(Succeed())
Expect(os.WriteFile(filepath.Join(path, "bravo", "metadata", "provider"), []byte("test-provider"), 0600)).To(Succeed())
Expect(os.WriteFile(filepath.Join(path, "bravo", "metadata", "test-metadata-key"), []byte("test-metadata-value"), 0600)).To(Succeed())
Expect(os.WriteFile(filepath.Join(path, "bravo", "metadata", "test-metadata-key-trimmed"), []byte(" test-metadata-value-trimmed \n"), 0600)).To(Succeed())
Expect(os.MkdirAll(filepath.Join(path, "bravo", "secret"), 0755)).To(Succeed())
Expect(ioutil.WriteFile(filepath.Join(path, "bravo", "secret", "test-secret-key"), []byte("test-secret-value"), 0600)).To(Succeed())
Expect(ioutil.WriteFile(filepath.Join(path, "bravo", "secret", "test-secret-key-trimmed"), []byte(" test-secret-value-trimmed \n"), 0600)).To(Succeed())
Expect(os.WriteFile(filepath.Join(path, "bravo", "secret", "test-secret-key"), []byte("test-secret-value"), 0600)).To(Succeed())
Expect(os.WriteFile(filepath.Join(path, "bravo", "secret", "test-secret-key-trimmed"), []byte(" test-secret-value-trimmed \n"), 0600)).To(Succeed())
Expect(os.MkdirAll(filepath.Join(path, ".hidden", "metadata"), 0755)).To(Succeed())
Expect(ioutil.WriteFile(filepath.Join(path, ".hidden", "metadata", "kind"), []byte("test-kind"), 0600)).To(Succeed())
Expect(ioutil.WriteFile(filepath.Join(path, ".hiddenFile"), []byte("test-kind"), 0600)).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())
})
context("Binding", func() {
@ -201,18 +273,18 @@ func testPlatform(t *testing.T, context spec.G, it spec.S) {
context("Kubernetes Service Bindings", func() {
it.Before(func() {
Expect(os.MkdirAll(filepath.Join(path, "alpha"), 0755)).To(Succeed())
Expect(ioutil.WriteFile(filepath.Join(path, "alpha", "type"), []byte("test-type"), 0600)).To(Succeed())
Expect(ioutil.WriteFile(filepath.Join(path, "alpha", "provider"), []byte("test-provider"), 0600)).To(Succeed())
Expect(ioutil.WriteFile(filepath.Join(path, "alpha", "test-secret-key"), []byte("test-secret-value"), 0600)).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(ioutil.WriteFile(filepath.Join(path, "bravo", "type"), []byte("test-type"), 0600)).To(Succeed())
Expect(ioutil.WriteFile(filepath.Join(path, "bravo", "provider"), []byte("test-provider"), 0600)).To(Succeed())
Expect(ioutil.WriteFile(filepath.Join(path, "bravo", "test-secret-key"), []byte("test-secret-value"), 0600)).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(os.MkdirAll(filepath.Join(path, ".hidden", "metadata"), 0755)).To(Succeed())
Expect(ioutil.WriteFile(filepath.Join(path, ".hidden", "metadata", "kind"), []byte("test-kind"), 0600)).To(Succeed())
Expect(ioutil.WriteFile(filepath.Join(path, ".hiddenFile"), []byte("test-kind"), 0600)).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())
})
context("Binding", func() {

68
testdata/vcap_services.json vendored Normal file
View File

@ -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": []
}
]
}

View File

@ -1,7 +1,184 @@
module github.com/buildpacks/libcnb/tools
go 1.15
go 1.20
require golang.org/x/tools v0.1.4
require golang.org/x/tools v0.8.0
require github.com/golangci/golangci-lint v1.41.1
require github.com/golangci/golangci-lint v1.52.2
require (
4d63.com/gocheckcompilerdirectives v1.2.1 // indirect
4d63.com/gochecknoglobals v0.2.1 // indirect
github.com/Abirdcfly/dupword v0.0.11 // indirect
github.com/Antonboom/errname v0.1.9 // indirect
github.com/Antonboom/nilnil v0.1.4 // indirect
github.com/BurntSushi/toml v1.2.1 // indirect
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24 // indirect
github.com/GaijinEntertainment/go-exhaustruct/v2 v2.3.0 // indirect
github.com/Masterminds/semver v1.5.0 // indirect
github.com/OpenPeeDeeP/depguard v1.1.1 // indirect
github.com/alexkohler/prealloc v1.0.0 // indirect
github.com/alingse/asasalint v0.0.11 // indirect
github.com/ashanbrown/forbidigo v1.5.1 // indirect
github.com/ashanbrown/makezero v1.1.1 // indirect
github.com/beorn7/perks v1.0.1 // indirect
github.com/bkielbasa/cyclop v1.2.0 // indirect
github.com/blizzy78/varnamelen v0.8.0 // indirect
github.com/bombsimon/wsl/v3 v3.4.0 // indirect
github.com/breml/bidichk v0.2.4 // indirect
github.com/breml/errchkjson v0.3.1 // indirect
github.com/butuzov/ireturn v0.1.1 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/charithe/durationcheck v0.0.10 // indirect
github.com/chavacava/garif v0.0.0-20230227094218-b8c73b2037b8 // indirect
github.com/curioswitch/go-reassign v0.2.0 // indirect
github.com/daixiang0/gci v0.10.1 // indirect
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/denis-tingaikin/go-header v0.4.3 // indirect
github.com/esimonov/ifshort v1.0.4 // indirect
github.com/ettle/strcase v0.1.1 // indirect
github.com/fatih/color v1.15.0 // indirect
github.com/fatih/structtag v1.2.0 // indirect
github.com/firefart/nonamedreturns v1.0.4 // indirect
github.com/fsnotify/fsnotify v1.6.0 // indirect
github.com/fzipp/gocyclo v0.6.0 // indirect
github.com/go-critic/go-critic v0.7.0 // 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.1.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-xmlfmt/xmlfmt v1.1.2 // indirect
github.com/gobwas/glob v0.2.3 // indirect
github.com/gofrs/flock v0.8.1 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golangci/check v0.0.0-20180506172741-cfe4005ccda2 // indirect
github.com/golangci/dupl v0.0.0-20180902072040-3e9179ac440a // indirect
github.com/golangci/go-misc v0.0.0-20220329215616-d24fe342adfe // indirect
github.com/golangci/gofmt v0.0.0-20220901101216-f2edd75033f2 // indirect
github.com/golangci/lint-1 v0.0.0-20191013205115-297bf364a8e0 // indirect
github.com/golangci/maligned v0.0.0-20180506175553-b1d89398deca // indirect
github.com/golangci/misspell v0.4.0 // indirect
github.com/golangci/revgrep v0.0.0-20220804021717-745bb2f7c2e6 // indirect
github.com/golangci/unconvert v0.0.0-20180507085042-28b1c447d1f4 // indirect
github.com/google/go-cmp v0.5.9 // indirect
github.com/gordonklaus/ineffassign v0.0.0-20230107090616-13ace0543b28 // 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/errwrap v1.1.0 // indirect
github.com/hashicorp/go-multierror v1.1.1 // indirect
github.com/hashicorp/go-version v1.6.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.5.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/julz/importas v0.1.0 // indirect
github.com/junk1tm/musttag v0.5.0 // indirect
github.com/kisielk/errcheck v1.6.3 // indirect
github.com/kisielk/gotool v1.0.0 // indirect
github.com/kkHAIKE/contextcheck v1.1.4 // indirect
github.com/kulti/thelper v0.6.3 // indirect
github.com/kunwardeep/paralleltest v1.0.6 // indirect
github.com/kyoh86/exportloopref v0.1.11 // indirect
github.com/ldez/gomoddirectives v0.2.3 // indirect
github.com/ldez/tagliatelle v0.4.0 // indirect
github.com/leonklingele/grouper v1.1.1 // indirect
github.com/lufeee/execinquery v1.2.1 // indirect
github.com/magiconair/properties v1.8.7 // 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.18 // indirect
github.com/mattn/go-runewidth v0.0.14 // indirect
github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect
github.com/mbilski/exhaustivestruct v1.2.0 // indirect
github.com/mgechev/revive v1.3.1 // indirect
github.com/mitchellh/go-homedir v1.1.0 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/moricho/tparallel v0.3.1 // indirect
github.com/nakabonne/nestif v0.3.1 // indirect
github.com/nbutton23/zxcvbn-go v0.0.0-20210217022336-fa2cb2858354 // indirect
github.com/nishanths/exhaustive v0.10.0 // indirect
github.com/nishanths/predeclared v0.2.2 // indirect
github.com/nunnatsa/ginkgolinter v0.11.0 // 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.0.7 // indirect
github.com/phayes/checkstyle v0.0.0-20170904204023-bfd46e6a821d // indirect
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/polyfloyd/go-errorlint v1.4.0 // indirect
github.com/prometheus/client_golang v1.14.0 // indirect
github.com/prometheus/client_model v0.3.0 // indirect
github.com/prometheus/common v0.42.0 // indirect
github.com/prometheus/procfs v0.9.0 // indirect
github.com/quasilyte/go-ruleguard v0.3.19 // 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/rivo/uniseg v0.4.4 // indirect
github.com/ryancurrah/gomodguard v1.3.0 // indirect
github.com/ryanrolds/sqlclosecheck v0.4.0 // indirect
github.com/sanposhiho/wastedassign/v2 v2.0.7 // indirect
github.com/sashamelentyev/interfacebloat v1.1.0 // indirect
github.com/sashamelentyev/usestdlibvars v1.23.0 // indirect
github.com/securego/gosec/v2 v2.15.0 // indirect
github.com/shazow/go-diff v0.0.0-20160112020656-b6b7b6733b8c // indirect
github.com/sirupsen/logrus v1.9.0 // indirect
github.com/sivchari/containedctx v1.0.3 // indirect
github.com/sivchari/nosnakecase v1.7.0 // indirect
github.com/sivchari/tenv v1.7.1 // indirect
github.com/sonatard/noctx v0.0.2 // indirect
github.com/sourcegraph/go-diff v0.7.0 // indirect
github.com/spf13/afero v1.9.5 // indirect
github.com/spf13/cast v1.5.0 // indirect
github.com/spf13/cobra v1.7.0 // indirect
github.com/spf13/jwalterweatherman v1.1.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
github.com/spf13/viper v1.15.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.0 // indirect
github.com/stretchr/testify v1.8.2 // indirect
github.com/subosito/gotenv v1.4.2 // indirect
github.com/sylvia7788/contextcheck v1.0.9 // indirect
github.com/t-yuki/gocover-cobertura v0.0.0-20180217150009-aaee18c8195c // indirect
github.com/tdakkota/asciicheck v0.2.0 // indirect
github.com/tetafro/godot v1.4.11 // indirect
github.com/timakin/bodyclose v0.0.0-20221125081123-e39cf3fc478e // indirect
github.com/timonwong/loggercheck v0.9.4 // indirect
github.com/tomarrell/wrapcheck/v2 v2.8.1 // indirect
github.com/tommy-muehle/go-mnd/v2 v2.5.1 // indirect
github.com/ultraware/funlen v0.0.3 // indirect
github.com/ultraware/whitespace v0.0.5 // indirect
github.com/uudashr/gocognit v1.0.6 // indirect
github.com/yagipy/maintidx v1.0.0 // indirect
github.com/yeya24/promlinter v0.2.0 // indirect
gitlab.com/bosi/decorder v0.2.3 // indirect
go.uber.org/atomic v1.10.0 // indirect
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.24.0 // indirect
golang.org/x/exp v0.0.0-20230321023759-10a507213a29 // indirect
golang.org/x/exp/typeparams v0.0.0-20230321023759-10a507213a29 // indirect
golang.org/x/mod v0.10.0 // indirect
golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.7.0 // indirect
golang.org/x/text v0.9.0 // indirect
golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 // indirect
google.golang.org/protobuf v1.30.0 // 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.4.3 // indirect
mvdan.cc/gofumpt v0.5.0 // indirect
mvdan.cc/interfacer v0.0.0-20180901003855-c20040233aed // indirect
mvdan.cc/lint v0.0.0-20170908181259-adc824a0674b // indirect
mvdan.cc/unparam v0.0.0-20230312165513-e84e2d14e3b8 // indirect
)

File diff suppressed because it is too large Load Diff

View File

@ -1,3 +1,4 @@
//go:build tools
// +build tools
package tools