├── .gitignore ├── scripts ├── .syncignore ├── options.json ├── .util │ ├── tools.json │ ├── print.sh │ └── tools.sh ├── publish.sh └── smoke.sh ├── smoke ├── testdata │ ├── go │ │ ├── .gitignore │ │ ├── go.mod │ │ ├── go.sum │ │ ├── README.md │ │ └── main.go │ ├── python │ │ ├── .gitignore │ │ ├── Procfile │ │ ├── Pipfile │ │ ├── templates │ │ │ └── index.html │ │ ├── README.md │ │ ├── server.py │ │ └── Pipfile.lock │ ├── nodejs │ │ ├── .gitignore │ │ ├── README.md │ │ ├── package.json │ │ └── server.js │ ├── procfile │ │ ├── config.yml │ │ ├── web │ │ │ └── hello-world.txt │ │ ├── Procfile │ │ └── static-file-server-1.8.0-linux-amd64 │ ├── ruby │ │ ├── config.ru │ │ ├── Gemfile │ │ ├── app.rb │ │ └── README.md │ ├── java-native-image │ │ ├── src │ │ │ ├── main │ │ │ │ ├── resources │ │ │ │ │ └── application.properties │ │ │ │ └── java │ │ │ │ │ └── io │ │ │ │ │ └── paketo │ │ │ │ │ └── demo │ │ │ │ │ └── DemoApplication.java │ │ │ └── test │ │ │ │ └── java │ │ │ │ └── io │ │ │ │ └── paketo │ │ │ │ └── demo │ │ │ │ └── DemoApplicationTests.java │ │ ├── .mvn │ │ │ └── wrapper │ │ │ │ ├── maven-wrapper.jar │ │ │ │ ├── maven-wrapper.properties │ │ │ │ └── MavenWrapperDownloader.java │ │ ├── README.md │ │ ├── .gitignore │ │ ├── pom.xml │ │ ├── mvnw.cmd │ │ └── mvnw │ ├── javascript-frontend │ │ ├── public │ │ │ ├── robots.txt │ │ │ ├── favicon.ico │ │ │ └── index.html │ │ ├── src │ │ │ ├── App.css │ │ │ ├── index.js │ │ │ └── App.js │ │ ├── .gitignore │ │ ├── README.md │ │ └── package.json │ ├── dotnet │ │ ├── obj │ │ │ ├── runtime.csproj.nuget.g.targets │ │ │ ├── project.nuget.cache │ │ │ ├── runtime.csproj.nuget.g.props │ │ │ ├── project.assets.json │ │ │ └── runtime.csproj.nuget.dgspec.json │ │ ├── runtime.csproj │ │ ├── README.md │ │ └── Program.cs │ ├── httpd │ │ ├── htdocs │ │ │ └── index.html │ │ ├── README.md │ │ └── httpd.conf │ └── nginx │ │ ├── public │ │ └── index.html │ │ ├── README.md │ │ ├── nginx.conf │ │ └── mime.types ├── init_test.go ├── dotnet_test.go ├── procfile_test.go ├── go_test.go ├── nodejs_test.go ├── ruby_test.go ├── python_test.go ├── java_test.go ├── java_native_image_test.go └── web_servers_test.go ├── .github ├── .syncignore ├── CODEOWNERS ├── renovate.json ├── workflows │ ├── synchronize-labels.yml │ ├── lint-yaml.yml │ ├── test-builder.yml │ ├── update-github-config.yml │ ├── approve-bot-pr.yml │ ├── test-pull-request.yml │ ├── update-go-mod-version.yml │ ├── push-image.yml │ ├── update-builder-toml.yml │ ├── update-test-data.yml │ └── create-release.yml ├── release-drafter-config.yml └── labels.yml ├── README.md ├── go.mod ├── LICENSE └── go.sum /.gitignore: -------------------------------------------------------------------------------- 1 | .bin/ 2 | -------------------------------------------------------------------------------- /scripts/.syncignore: -------------------------------------------------------------------------------- 1 | options.json -------------------------------------------------------------------------------- /smoke/testdata/go/.gitignore: -------------------------------------------------------------------------------- 1 | !vendor 2 | -------------------------------------------------------------------------------- /smoke/testdata/python/.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | venv -------------------------------------------------------------------------------- /smoke/testdata/nodejs/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ 2 | -------------------------------------------------------------------------------- /smoke/testdata/procfile/config.yml: -------------------------------------------------------------------------------- 1 | folder: web 2 | -------------------------------------------------------------------------------- /smoke/testdata/procfile/web/hello-world.txt: -------------------------------------------------------------------------------- 1 | Hello World! -------------------------------------------------------------------------------- /smoke/testdata/python/Procfile: -------------------------------------------------------------------------------- 1 | web: gunicorn server:app 2 | -------------------------------------------------------------------------------- /.github/.syncignore: -------------------------------------------------------------------------------- 1 | CODEOWNERS 2 | dependabot.yml 3 | renovate.json -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @paketo-buildpacks/builders-maintainers 2 | -------------------------------------------------------------------------------- /scripts/options.json: -------------------------------------------------------------------------------- 1 | { 2 | "push_builder_to_local_registry": true 3 | } 4 | -------------------------------------------------------------------------------- /smoke/testdata/ruby/config.ru: -------------------------------------------------------------------------------- 1 | require './app' 2 | run Sinatra::Application 3 | -------------------------------------------------------------------------------- /smoke/testdata/java-native-image/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /scripts/.util/tools.json: -------------------------------------------------------------------------------- 1 | { 2 | "crane": "v0.20.7", 3 | "pack": "v0.39.1" 4 | } 5 | -------------------------------------------------------------------------------- /smoke/testdata/procfile/Procfile: -------------------------------------------------------------------------------- 1 | web: ./static-file-server-1.8.0-linux-amd64 --config config.yml 2 | -------------------------------------------------------------------------------- /smoke/testdata/ruby/Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | ruby '~> 3' 4 | 5 | gem 'puma' 6 | gem 'sinatra' 7 | -------------------------------------------------------------------------------- /smoke/testdata/javascript-frontend/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /smoke/testdata/go/go.mod: -------------------------------------------------------------------------------- 1 | module github.com/paketo-buildpacks/samples/go/mod 2 | 3 | go 1.15 4 | 5 | require github.com/gorilla/mux v1.8.0 6 | -------------------------------------------------------------------------------- /smoke/testdata/javascript-frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paketo-buildpacks/builder-jammy-base/HEAD/smoke/testdata/javascript-frontend/public/favicon.ico -------------------------------------------------------------------------------- /smoke/testdata/procfile/static-file-server-1.8.0-linux-amd64: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paketo-buildpacks/builder-jammy-base/HEAD/smoke/testdata/procfile/static-file-server-1.8.0-linux-amd64 -------------------------------------------------------------------------------- /smoke/testdata/go/go.sum: -------------------------------------------------------------------------------- 1 | github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= 2 | github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= 3 | -------------------------------------------------------------------------------- /smoke/testdata/java-native-image/.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/paketo-buildpacks/builder-jammy-base/HEAD/smoke/testdata/java-native-image/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /smoke/testdata/dotnet/obj/runtime.csproj.nuget.g.targets: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /smoke/testdata/python/Pipfile: -------------------------------------------------------------------------------- 1 | [packages] 2 | Flask = "==2.1.3" 3 | gunicorn = "*" 4 | itsdangerous = "==2.1.2" 5 | 6 | [dev-packages] 7 | tox = "*" 8 | coverage = "*" 9 | "flake8" = "*" 10 | flask-testing = "*" 11 | -------------------------------------------------------------------------------- /smoke/testdata/javascript-frontend/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | font-size: large; 3 | text-align: center; 4 | } 5 | 6 | .App img { 7 | display: block; 8 | margin-left: auto; 9 | margin-right: auto; 10 | width: 50%; 11 | } 12 | -------------------------------------------------------------------------------- /smoke/testdata/java-native-image/.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.3/apache-maven-3.6.3-bin.zip 2 | wrapperUrl=https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar 3 | -------------------------------------------------------------------------------- /smoke/testdata/javascript-frontend/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | ReactDOM.render( 6 | 7 | 8 | , 9 | document.getElementById('root') 10 | ); 11 | -------------------------------------------------------------------------------- /smoke/testdata/dotnet/obj/project.nuget.cache: -------------------------------------------------------------------------------- 1 | { 2 | "version": 2, 3 | "dgSpecHash": "jpCLVXQFwpwtdWzwIcGgkt61LJcD8OAErgte8zO7IDiGI+L4I77KWipQuEmgt/th6mqVISAAKEyvHsFodpYTcg==", 4 | "success": true, 5 | "projectFilePath": "/tmp/runtime/runtime.csproj", 6 | "expectedPackageFiles": [], 7 | "logs": [] 8 | } -------------------------------------------------------------------------------- /smoke/testdata/nodejs/README.md: -------------------------------------------------------------------------------- 1 | # Node.js Sample App using NPM 2 | 3 | ## Building 4 | 5 | `pack build npm-sample --buildpack docker.io/paketobuildpacks/nodejs` 6 | 7 | ## Running 8 | 9 | `docker run --interactive --tty --publish 8080:8080 npm-sample` 10 | 11 | ## Viewing 12 | 13 | `curl http://localhost:8080` 14 | -------------------------------------------------------------------------------- /smoke/testdata/dotnet/runtime.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | net8.0 6 | enable 7 | enable 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /smoke/testdata/go/README.md: -------------------------------------------------------------------------------- 1 | # Go Sample App using Mod 2 | 3 | ## Building 4 | 5 | `pack build mod-sample --buildpack docker.io/paketobuildpacks/go` 6 | 7 | ## Running 8 | 9 | `docker run --interactive --tty --env PORT=8080 --publish 8080:8080 mod-sample` 10 | 11 | ## Viewing 12 | 13 | `curl http://localhost:8080` 14 | -------------------------------------------------------------------------------- /smoke/testdata/javascript-frontend/src/App.js: -------------------------------------------------------------------------------- 1 | import './App.css'; 2 | 3 | function App() { 4 | return
5 | Paketo Buildpacks logo 6 | Powered by Paketo Buildpacks 7 |
; 8 | } 9 | 10 | export default App; 11 | -------------------------------------------------------------------------------- /smoke/testdata/httpd/htdocs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Powered By Paketo Buildpacks 5 | 6 | 7 | 8 | 9 | ` 10 | -------------------------------------------------------------------------------- /smoke/testdata/nginx/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Powered By Paketo Buildpacks 5 | 6 | 7 | 8 | 9 | ` 10 | -------------------------------------------------------------------------------- /smoke/testdata/java-native-image/src/test/java/io/paketo/demo/DemoApplicationTests.java: -------------------------------------------------------------------------------- 1 | package io.paketo.demo; 2 | 3 | import org.junit.jupiter.api.Test; 4 | import org.springframework.boot.test.context.SpringBootTest; 5 | 6 | @SpringBootTest 7 | class DemoApplicationTests { 8 | 9 | @Test 10 | void contextLoads() { 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /smoke/testdata/python/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Powered By Paketo Buildpacks 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /smoke/testdata/python/README.md: -------------------------------------------------------------------------------- 1 | # Python sample app using pipenv package manager 2 | 3 | ## Building 4 | 5 | `pack build pipenv-sample --buildpack docker.io/paketobuildpacks/python` 6 | 7 | ## Running 8 | 9 | `docker run --interactive --tty --env PORT=8080 --publish 8080:8080 pipenv-sample` 10 | 11 | ## Viewing 12 | 13 | `curl http://localhost:8080` 14 | -------------------------------------------------------------------------------- /smoke/testdata/dotnet/README.md: -------------------------------------------------------------------------------- 1 | # Dotnet Core Sample App using Runtime only 2 | 3 | ## Building 4 | 5 | `pack build dotnet-runtime-sample --buildpack docker.io/paketobuildpacks/dotnet-core` 6 | 7 | ## Running 8 | 9 | `docker run --interactive --tty --env PORT=8080 --publish 8080:8080 dotnet-runtime-sample` 10 | 11 | ## Viewing 12 | 13 | `curl http://localhost:8080` 14 | -------------------------------------------------------------------------------- /smoke/testdata/nginx/README.md: -------------------------------------------------------------------------------- 1 | # NGINX Server Sample Application 2 | 3 | ## Building 4 | 5 | ```bash 6 | pack build my-nginx-app --buildpack docker.io/paketobuildpacks/nginx 7 | ``` 8 | 9 | ## Running 10 | 11 | ```bash 12 | docker run --tty --env PORT=8080 --publish 8080:8080 my-nginx-app 13 | ``` 14 | 15 | ## Viewing 16 | 17 | ```bash 18 | curl -s localhost:8080 19 | ``` 20 | -------------------------------------------------------------------------------- /smoke/testdata/httpd/README.md: -------------------------------------------------------------------------------- 1 | # HTTPD Server Sample Application 2 | 3 | ## Building 4 | 5 | ```bash 6 | pack build my-httpd-app --buildpack docker.io/paketobuildpacks/httpd --builder paketobuildpacks/builder:full 7 | ``` 8 | 9 | ## Running 10 | 11 | ```bash 12 | docker run --tty --env PORT=8080 --publish 8080:8080 my-httpd-app 13 | ``` 14 | 15 | ## Viewing 16 | 17 | ```bash 18 | curl -s localhost:8080 19 | ``` 20 | -------------------------------------------------------------------------------- /smoke/testdata/java-native-image/src/main/java/io/paketo/demo/DemoApplication.java: -------------------------------------------------------------------------------- 1 | package io.paketo.demo; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class DemoApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(DemoApplication.class, args); 11 | } 12 | 13 | } 14 | -------------------------------------------------------------------------------- /smoke/testdata/ruby/app.rb: -------------------------------------------------------------------------------- 1 | require 'sinatra' 2 | configure { set :server, :puma } 3 | 4 | get '/' do 5 | ' 6 | 7 | 8 | Powered By Paketo Buildpacks 9 | 10 | 11 | 12 | 13 | ' 14 | end 15 | -------------------------------------------------------------------------------- /smoke/testdata/javascript-frontend/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /smoke/testdata/ruby/README.md: -------------------------------------------------------------------------------- 1 | # Ruby sample app using Puma web server 2 | 3 | ## Building 4 | 5 | `pack build puma-sample --buildpack paketo-buildpacks/ruby` 6 | 7 | ## Running 8 | 9 | `docker run --interactive --tty --publish 9292:9292 puma-sample` 10 | 11 | `9292` is the default port for rack compliant web servers. As of date, the puma 12 | buildpack requires that the app source have a `config.ru` file. 13 | 14 | ## Viewing 15 | 16 | `curl http://localhost:9292` 17 | -------------------------------------------------------------------------------- /smoke/testdata/java-native-image/README.md: -------------------------------------------------------------------------------- 1 | # Java Native Image Sample Application 2 | 3 | ## Building 4 | 5 | ```bash 6 | pack build applications/native-image \ 7 | --builder paketobuildpacks/builder-jammy-base \ 8 | --env BP_NATIVE_IMAGE=true 9 | ``` 10 | 11 | ## Running 12 | 13 | ```bash 14 | docker run --rm --tty --publish 8080:8080 applications/native-image 15 | ``` 16 | 17 | ## Viewing 18 | 19 | ```bash 20 | curl -s http://localhost:8080/actuator/health | jq . 21 | ``` 22 | -------------------------------------------------------------------------------- /smoke/testdata/nodejs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "author": "Paketo Buildpacks", 3 | "dependencies": { 4 | "express": "^4.17.0" 5 | }, 6 | "description": "Sample Node.js Application using NPM", 7 | "license": "Apache-2.0", 8 | "name": "sample", 9 | "repository": { 10 | "type": "git", 11 | "url": "https://github.com/paketo-buildpacks/samples" 12 | }, 13 | "scripts": { 14 | "start": "echo \"start\" && node server.js" 15 | }, 16 | "version": "0.0.0" 17 | } 18 | -------------------------------------------------------------------------------- /.github/renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": [ 4 | "github>paketo-buildpacks/github-config//renovate/renovate-default" 5 | ], 6 | "enabledManagers": ["gomod"], 7 | "packageRules": [ 8 | { 9 | "matchPaths": ["**/smoke/testdata/**"], 10 | "enabled": false, 11 | "description": "The test data are updated by syncing them with the samples." 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /.github/workflows/synchronize-labels.yml: -------------------------------------------------------------------------------- 1 | name: Synchronize Labels 2 | "on": 3 | push: 4 | branches: 5 | - main 6 | paths: 7 | - .github/labels.yml 8 | workflow_dispatch: {} 9 | jobs: 10 | synchronize: 11 | name: Synchronize Labels 12 | runs-on: 13 | - ubuntu-24.04 14 | steps: 15 | - uses: actions/checkout@v6 16 | - uses: micnncim/action-label-syncer@v1 17 | env: 18 | GITHUB_TOKEN: ${{ github.token }} 19 | -------------------------------------------------------------------------------- /smoke/testdata/nodejs/server.js: -------------------------------------------------------------------------------- 1 | const express = require('express'); 2 | const port = process.env.PORT || 8080; 3 | 4 | const app = express(); 5 | 6 | app.get('/', (request, response) => { 7 | response.send(` 8 | 9 | 10 | Powered By Paketo Buildpacks 11 | 12 | 13 | 14 | 15 | `); 16 | }); 17 | 18 | app.listen(port); 19 | -------------------------------------------------------------------------------- /smoke/testdata/java-native-image/.gitignore: -------------------------------------------------------------------------------- 1 | HELP.md 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /smoke/testdata/python/server.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request, render_template 2 | import subprocess 3 | import gunicorn 4 | 5 | 6 | app = Flask(__name__) 7 | 8 | @app.route("/") 9 | def hello(): 10 | return render_template('index.html') 11 | 12 | @app.route('/execute', methods=['POST']) 13 | def execute(): 14 | with open('runtime.py', 'w') as f: 15 | f.write(request.values.get('code')) 16 | return subprocess.check_output(["python", "runtime.py"]) 17 | 18 | @app.route('/versions') 19 | def versions(): 20 | version = gunicorn.__version__ 21 | return "Gunicorn version: " + version 22 | 23 | app.debug=True 24 | -------------------------------------------------------------------------------- /.github/release-drafter-config.yml: -------------------------------------------------------------------------------- 1 | # Config for https://github.com/release-drafter/release-drafter 2 | name-template: '$RESOLVED_VERSION' 3 | tag-template: 'v$RESOLVED_VERSION' 4 | filter-by-commitish: true 5 | commitish: main 6 | 7 | change-template: '- $TITLE [#$NUMBER] by [@$AUTHOR](https://github.com/$AUTHOR)' 8 | template: | 9 | ## Full Changelog 10 | 11 | Following pull requests got merged for this release: 12 | 13 | $CHANGES 14 | 15 | version-resolver: 16 | major: 17 | labels: 18 | - 'semver:major' 19 | minor: 20 | labels: 21 | - 'semver:minor' 22 | patch: 23 | labels: 24 | - 'semver:patch' 25 | default: patch 26 | -------------------------------------------------------------------------------- /smoke/testdata/nginx/nginx.conf: -------------------------------------------------------------------------------- 1 | worker_processes 1; 2 | daemon off; 3 | 4 | error_log stderr; 5 | events { worker_connections 1024; } 6 | 7 | http { 8 | charset utf-8; 9 | log_format cloudfoundry 'NginxLog "$request" $status $body_bytes_sent'; 10 | access_log /dev/stdout cloudfoundry; 11 | default_type application/octet-stream; 12 | include mime.types; 13 | sendfile on; 14 | 15 | tcp_nopush on; 16 | keepalive_timeout 30; 17 | port_in_redirect off; # Ensure that redirects don't include the internal container PORT - 8080 18 | 19 | server { 20 | listen {{port}}; 21 | root public; 22 | index index.html index.htm Default.htm; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /smoke/testdata/go/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "log" 6 | "net/http" 7 | "os" 8 | 9 | "github.com/gorilla/mux" 10 | ) 11 | 12 | const INDEX = ` 13 | 14 | 15 | Powered By Paketo Buildpacks 16 | 17 | 18 | 19 | 20 | ` 21 | 22 | func main() { 23 | router := mux.NewRouter() 24 | router.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) { 25 | fmt.Fprintln(w, INDEX) 26 | }) 27 | 28 | log.Fatal(http.ListenAndServe(":"+os.Getenv("PORT"), router)) 29 | } 30 | -------------------------------------------------------------------------------- /.github/workflows/lint-yaml.yml: -------------------------------------------------------------------------------- 1 | name: Lint Workflows 2 | 3 | on: 4 | pull_request: 5 | paths: 6 | - '.github/**.yml' 7 | - '.github/**.yaml' 8 | 9 | jobs: 10 | lintYaml: 11 | runs-on: ubuntu-24.04 12 | steps: 13 | - uses: actions/checkout@v6 14 | 15 | - name: Checkout github-config 16 | uses: actions/checkout@v6 17 | with: 18 | repository: paketo-buildpacks/github-config 19 | path: github-config 20 | 21 | - name: Set up Python 22 | uses: actions/setup-python@v5 23 | with: 24 | python-version: 3.8 25 | 26 | - name: Install yamllint 27 | run: pip install yamllint 28 | 29 | - name: Lint YAML files 30 | run: yamllint ./.github -c github-config/.github/.yamllint 31 | -------------------------------------------------------------------------------- /smoke/testdata/javascript-frontend/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Create React App 2 | 3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `npm start` 10 | 11 | Runs the app in the development mode.\ 12 | Open [http://localhost:3000](http://localhost:3000) to view it in your browser. 13 | 14 | The page will reload when you make changes.\ 15 | You may also see any lint errors in the console. 16 | 17 | ### `npm run build` 18 | 19 | Builds the app for production to the `build` folder.\ 20 | It correctly bundles React in production mode and optimizes the build for the best performance. 21 | 22 | The build is minified and the filenames include the hashes.\ 23 | Your app is ready to be deployed! 24 | 25 | See the section about 26 | [deployment](https://facebook.github.io/create-react-app/docs/deployment) for 27 | more information. 28 | 29 | -------------------------------------------------------------------------------- /smoke/init_test.go: -------------------------------------------------------------------------------- 1 | package smoke_test 2 | 3 | import ( 4 | "flag" 5 | "testing" 6 | "time" 7 | 8 | "github.com/onsi/gomega/format" 9 | "github.com/sclevine/spec" 10 | "github.com/sclevine/spec/report" 11 | 12 | . "github.com/onsi/gomega" 13 | ) 14 | 15 | var Builder string 16 | 17 | func init() { 18 | flag.StringVar(&Builder, "name", "", "") 19 | } 20 | 21 | func TestSmoke(t *testing.T) { 22 | format.MaxLength = 0 23 | Expect := NewWithT(t).Expect 24 | 25 | flag.Parse() 26 | 27 | Expect(Builder).NotTo(Equal("")) 28 | 29 | SetDefaultEventuallyTimeout(60 * time.Second) 30 | 31 | suite := spec.New("Smoke", spec.Parallel(), spec.Report(report.Terminal{})) 32 | suite("Dotnet", testDotnet) 33 | suite("Go", testGo) 34 | suite("Java Native Image", testJavaNativeImage) 35 | suite("Java", testJava) 36 | suite("Node.js", testNodejs) 37 | suite("Procfile", testProcfile) 38 | suite("Python", testPython) 39 | suite("Ruby", testRuby) 40 | suite("Web Servers", testWebServers) 41 | suite.Run(t) 42 | } 43 | -------------------------------------------------------------------------------- /smoke/testdata/javascript-frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-nginx", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.16.4", 7 | "@testing-library/react": "^13.1.1", 8 | "@testing-library/user-event": "^13.5.0", 9 | "react": "^18.0.0", 10 | "react-dom": "^18.0.0", 11 | "react-scripts": "5.0.1", 12 | "web-vitals": "^2.1.4" 13 | }, 14 | "scripts": { 15 | "start": "react-scripts start", 16 | "build": "react-scripts build", 17 | "test": "react-scripts test", 18 | "eject": "react-scripts eject" 19 | }, 20 | "eslintConfig": { 21 | "extends": [ 22 | "react-app", 23 | "react-app/jest" 24 | ] 25 | }, 26 | "browserslist": { 27 | "production": [ 28 | ">0.2%", 29 | "not dead", 30 | "not op_mini all" 31 | ], 32 | "development": [ 33 | "last 1 chrome version", 34 | "last 1 firefox version", 35 | "last 1 safari version" 36 | ] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Paketo Jammy Base Builder 2 | 3 | ## `paketobuildpacks/builder-jammy-base` 4 | 5 | This builder uses the [Paketo Jammy Base 6 | Stack](https://github.com/paketo-buildpacks/jammy-base-stack) (Ubuntu Jammy 7 | Jellyfish build and run images) with buildpacks for Java, 8 | Java Native Image, Go, Python, .NET, Node.js, Apache HTTPD, NGINX and Procfile. 9 | 10 | To see which versions of build and run images, buildpacks, and the lifecycle 11 | that are contained within a given builder version, see the 12 | [Releases](https://github.com/paketo-buildpacks/builder-jammy-base/releases) on this 13 | repository. This information is also available in the `builder.toml`. 14 | 15 | For more information about this builder and how to use it, visit the [Paketo 16 | builder documentation](https://paketo.io/docs/builders/). To learn about the 17 | stack included in this builder, visit the [Paketo stack 18 | documentation](https://paketo.io/docs/stacks/) and the [Paketo Jammy Base Stack 19 | repo](https://github.com/paketo-buildpacks/jammy-base-stack). 20 | -------------------------------------------------------------------------------- /scripts/.util/print.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eu 4 | set -o pipefail 5 | 6 | function util::print::title() { 7 | local blue reset message 8 | blue="\033[0;34m" 9 | reset="\033[0;39m" 10 | message="${1}" 11 | 12 | echo -e "\n${blue}${message}${reset}" >&2 13 | } 14 | 15 | function util::print::info() { 16 | local message 17 | message="${1}" 18 | 19 | echo -e "${message}" >&2 20 | } 21 | 22 | function util::print::error() { 23 | local message red reset 24 | message="${1}" 25 | red="\033[0;31m" 26 | reset="\033[0;39m" 27 | 28 | echo -e "${red}${message}${reset}" >&2 29 | exit 1 30 | } 31 | 32 | function util::print::success() { 33 | local message green reset 34 | message="${1}" 35 | green="\033[0;32m" 36 | reset="\033[0;39m" 37 | 38 | echo -e "${green}${message}${reset}" >&2 39 | exitcode="${2:-0}" 40 | exit "${exitcode}" 41 | } 42 | 43 | function util::print::warn() { 44 | local message yellow reset 45 | message="${1}" 46 | yellow="\033[0;33m" 47 | reset="\033[0;39m" 48 | 49 | echo -e "${yellow}${message}${reset}" >&2 50 | exit 0 51 | } 52 | -------------------------------------------------------------------------------- /smoke/testdata/dotnet/obj/runtime.csproj.nuget.g.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | True 5 | NuGet 6 | $(MSBuildThisFileDirectory)project.assets.json 7 | /home/ubuntu/.nuget/packages/ 8 | /home/ubuntu/.nuget/packages/ 9 | PackageReference 10 | 6.2.1 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /smoke/testdata/javascript-frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 21 | Paketo Buildpacks 22 | 23 | 24 | 25 |
26 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /smoke/testdata/dotnet/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | using System.Net; 7 | using System.Net.Sockets; 8 | 9 | namespace runtime 10 | { 11 | class Program 12 | { 13 | static void Main(string[] args) 14 | { 15 | string port = Environment.GetEnvironmentVariable("PORT"); 16 | TcpListener server = new TcpListener(IPAddress.Any, Int32.Parse(port)); 17 | 18 | server.Start(); 19 | 20 | while (true) 21 | { 22 | TcpClient client = server.AcceptTcpClient(); 23 | NetworkStream ns = client.GetStream(); 24 | 25 | string document = @" 26 | 27 | 28 | Powered By Paketo Buildpacks 29 | 30 | 31 | 32 | 33 | "; 34 | string payload = @"HTTP/1.1 200 OK 35 | Accept-Ranges: bytes 36 | Content-Length: " + System.Text.ASCIIEncoding.Unicode.GetByteCount(document) + @" 37 | Connection: close 38 | Content-Type: text/html; charset=utf-8 39 | 40 | " + document; 41 | 42 | byte[] msg = new byte[System.Text.ASCIIEncoding.Unicode.GetByteCount(payload)]; 43 | msg = Encoding.Default.GetBytes(payload); 44 | ns.Write(msg, 0, msg.Length); 45 | 46 | client.Close(); 47 | } 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /smoke/testdata/dotnet/obj/project.assets.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": 3, 3 | "targets": { 4 | "net6.0": {} 5 | }, 6 | "libraries": {}, 7 | "projectFileDependencyGroups": { 8 | "net6.0": [] 9 | }, 10 | "packageFolders": { 11 | "/home/ubuntu/.nuget/packages/": {} 12 | }, 13 | "project": { 14 | "version": "1.0.0", 15 | "restore": { 16 | "projectUniqueName": "/tmp/runtime/runtime.csproj", 17 | "projectName": "runtime", 18 | "projectPath": "/tmp/runtime/runtime.csproj", 19 | "packagesPath": "/home/ubuntu/.nuget/packages/", 20 | "outputPath": "/tmp/runtime/obj/", 21 | "projectStyle": "PackageReference", 22 | "configFilePaths": [ 23 | "/home/ubuntu/.nuget/NuGet/NuGet.Config" 24 | ], 25 | "originalTargetFrameworks": [ 26 | "net6.0" 27 | ], 28 | "sources": { 29 | "https://api.nuget.org/v3/index.json": {} 30 | }, 31 | "frameworks": { 32 | "net6.0": { 33 | "targetAlias": "net6.0", 34 | "projectReferences": {} 35 | } 36 | }, 37 | "warningProperties": { 38 | "warnAsError": [ 39 | "NU1605" 40 | ] 41 | } 42 | }, 43 | "frameworks": { 44 | "net6.0": { 45 | "targetAlias": "net6.0", 46 | "imports": [ 47 | "net461", 48 | "net462", 49 | "net47", 50 | "net471", 51 | "net472", 52 | "net48" 53 | ], 54 | "assetTargetFallback": true, 55 | "warn": true, 56 | "frameworkReferences": { 57 | "Microsoft.NETCore.App": { 58 | "privateAssets": "all" 59 | } 60 | }, 61 | "runtimeIdentifierGraphPath": "/usr/share/dotnet/sdk/6.0.301/RuntimeIdentifierGraph.json" 62 | } 63 | } 64 | } 65 | } -------------------------------------------------------------------------------- /smoke/testdata/dotnet/obj/runtime.csproj.nuget.dgspec.json: -------------------------------------------------------------------------------- 1 | { 2 | "format": 1, 3 | "restore": { 4 | "/tmp/runtime/runtime.csproj": {} 5 | }, 6 | "projects": { 7 | "/tmp/runtime/runtime.csproj": { 8 | "version": "1.0.0", 9 | "restore": { 10 | "projectUniqueName": "/tmp/runtime/runtime.csproj", 11 | "projectName": "runtime", 12 | "projectPath": "/tmp/runtime/runtime.csproj", 13 | "packagesPath": "/home/ubuntu/.nuget/packages/", 14 | "outputPath": "/tmp/runtime/obj/", 15 | "projectStyle": "PackageReference", 16 | "configFilePaths": [ 17 | "/home/ubuntu/.nuget/NuGet/NuGet.Config" 18 | ], 19 | "originalTargetFrameworks": [ 20 | "net6.0" 21 | ], 22 | "sources": { 23 | "https://api.nuget.org/v3/index.json": {} 24 | }, 25 | "frameworks": { 26 | "net6.0": { 27 | "targetAlias": "net6.0", 28 | "projectReferences": {} 29 | } 30 | }, 31 | "warningProperties": { 32 | "warnAsError": [ 33 | "NU1605" 34 | ] 35 | } 36 | }, 37 | "frameworks": { 38 | "net6.0": { 39 | "targetAlias": "net6.0", 40 | "imports": [ 41 | "net461", 42 | "net462", 43 | "net47", 44 | "net471", 45 | "net472", 46 | "net48" 47 | ], 48 | "assetTargetFallback": true, 49 | "warn": true, 50 | "frameworkReferences": { 51 | "Microsoft.NETCore.App": { 52 | "privateAssets": "all" 53 | } 54 | }, 55 | "runtimeIdentifierGraphPath": "/usr/share/dotnet/sdk/6.0.301/RuntimeIdentifierGraph.json" 56 | } 57 | } 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /.github/labels.yml: -------------------------------------------------------------------------------- 1 | - name: status/possible-priority 2 | description: This issue is ready to work and should be considered as a potential priority 3 | color: F9D0C4 4 | - name: status/prioritized 5 | description: This issue has been triaged and resolving it is a priority 6 | color: BFD4F2 7 | - name: status/blocked 8 | description: This issue has been triaged and resolving it is blocked on some other issue 9 | color: 848978 10 | - name: bug 11 | description: Something isn't working 12 | color: d73a4a 13 | - name: enhancement 14 | description: A new feature or request 15 | color: a2eeef 16 | - name: documentation 17 | description: This issue relates to writing documentation 18 | color: D4C5F9 19 | - name: help wanted 20 | description: Extra attention is needed 21 | color: 008672 22 | - name: semver:major 23 | description: A change requiring a major version bump 24 | color: 6b230e 25 | - name: semver:minor 26 | description: A change requiring a minor version bump 27 | color: cc6749 28 | - name: semver:patch 29 | description: A change requiring a patch version bump 30 | color: f9d0c4 31 | - name: good first issue 32 | description: A good first issue to get started with 33 | color: d3fc03 34 | - name: "failure:release" 35 | description: An issue filed automatically when a release workflow run fails 36 | color: f00a0a 37 | - name: "failure:push" 38 | description: An issue filed automatically when a push buildpackage workflow run fails 39 | color: f00a0a 40 | - name: "failure:update-builder-toml" 41 | description: An issue filed automatically when a builder.toml update workflow run fails 42 | color: f00a0a 43 | - name: "failure:update-github-config" 44 | description: An issue filed automatically when a github config update workflow run fails 45 | color: f00a0a 46 | - name: "failure:approve-bot-pr" 47 | description: An issue filed automatically when a PR auto-approve workflow run fails 48 | color: f00a0a 49 | -------------------------------------------------------------------------------- /smoke/dotnet_test.go: -------------------------------------------------------------------------------- 1 | package smoke_test 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "testing" 8 | 9 | "github.com/paketo-buildpacks/occam" 10 | "github.com/sclevine/spec" 11 | 12 | . "github.com/onsi/gomega" 13 | . "github.com/paketo-buildpacks/occam/matchers" 14 | ) 15 | 16 | func testDotnet(t *testing.T, context spec.G, it spec.S) { 17 | var ( 18 | Expect = NewWithT(t).Expect 19 | Eventually = NewWithT(t).Eventually 20 | 21 | pack occam.Pack 22 | docker occam.Docker 23 | ) 24 | 25 | it.Before(func() { 26 | pack = occam.NewPack().WithVerbose().WithNoColor() 27 | docker = occam.NewDocker() 28 | }) 29 | 30 | context("detects a Dotnet app", func() { 31 | var ( 32 | image occam.Image 33 | container occam.Container 34 | 35 | name string 36 | source string 37 | ) 38 | 39 | it.Before(func() { 40 | var err error 41 | name, err = occam.RandomName() 42 | Expect(err).NotTo(HaveOccurred()) 43 | }) 44 | 45 | it.After(func() { 46 | Expect(docker.Container.Remove.Execute(container.ID)).To(Succeed()) 47 | Expect(docker.Volume.Remove.Execute(occam.CacheVolumeNames(name))).To(Succeed()) 48 | Expect(docker.Image.Remove.Execute(image.ID)).To(Succeed()) 49 | Expect(os.RemoveAll(source)).To(Succeed()) 50 | }) 51 | 52 | it("builds successfully", func() { 53 | var err error 54 | source, err = occam.Source(filepath.Join("testdata", "dotnet")) 55 | Expect(err).NotTo(HaveOccurred()) 56 | 57 | var logs fmt.Stringer 58 | image, logs, err = pack.Build. 59 | WithPullPolicy("always"). 60 | WithBuilder(Builder). 61 | Execute(name, source) 62 | Expect(err).ToNot(HaveOccurred(), logs.String) 63 | 64 | container, err = docker.Container.Run. 65 | WithEnv(map[string]string{"PORT": "8080"}). 66 | WithPublish("8080"). 67 | Execute(image.ID) 68 | Expect(err).NotTo(HaveOccurred()) 69 | 70 | Eventually(container).Should(BeAvailable()) 71 | 72 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for ASP.NET Core Runtime"))) 73 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for .NET Execute"))) 74 | }) 75 | }) 76 | } 77 | -------------------------------------------------------------------------------- /smoke/procfile_test.go: -------------------------------------------------------------------------------- 1 | package smoke_test 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "testing" 8 | 9 | "github.com/paketo-buildpacks/occam" 10 | "github.com/sclevine/spec" 11 | 12 | . "github.com/onsi/gomega" 13 | . "github.com/paketo-buildpacks/occam/matchers" 14 | ) 15 | 16 | func testProcfile(t *testing.T, context spec.G, it spec.S) { 17 | var ( 18 | Expect = NewWithT(t).Expect 19 | Eventually = NewWithT(t).Eventually 20 | 21 | pack occam.Pack 22 | docker occam.Docker 23 | ) 24 | 25 | it.Before(func() { 26 | pack = occam.NewPack().WithVerbose().WithNoColor() 27 | docker = occam.NewDocker() 28 | }) 29 | 30 | context("detects a Procfile app", func() { 31 | var ( 32 | image occam.Image 33 | container occam.Container 34 | 35 | name string 36 | source string 37 | ) 38 | 39 | it.Before(func() { 40 | var err error 41 | name, err = occam.RandomName() 42 | Expect(err).NotTo(HaveOccurred()) 43 | }) 44 | 45 | it.After(func() { 46 | Expect(docker.Container.Remove.Execute(container.ID)).To(Succeed()) 47 | Expect(docker.Volume.Remove.Execute(occam.CacheVolumeNames(name))).To(Succeed()) 48 | Expect(docker.Image.Remove.Execute(image.ID)).To(Succeed()) 49 | Expect(os.RemoveAll(source)).To(Succeed()) 50 | }) 51 | 52 | it("builds successfully", func() { 53 | var err error 54 | source, err = occam.Source(filepath.Join("testdata", "procfile")) 55 | Expect(err).NotTo(HaveOccurred()) 56 | 57 | var logs fmt.Stringer 58 | image, logs, err = pack.Build. 59 | WithPullPolicy("always"). 60 | WithBuilder(Builder). 61 | Execute(name, source) 62 | Expect(err).ToNot(HaveOccurred(), logs.String) 63 | 64 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for Procfile"))) 65 | 66 | container, err = docker.Container.Run. 67 | WithEnv(map[string]string{"PORT": "8080"}). 68 | WithPublish("8080"). 69 | Execute(image.ID) 70 | Expect(err).NotTo(HaveOccurred()) 71 | 72 | Eventually(container).Should(BeAvailable()) 73 | Eventually(container).Should(Serve(ContainSubstring("Hello World!")).OnPort(8080).WithEndpoint("/hello-world.txt")) 74 | }) 75 | }) 76 | } 77 | -------------------------------------------------------------------------------- /smoke/go_test.go: -------------------------------------------------------------------------------- 1 | package smoke_test 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "testing" 8 | 9 | "github.com/paketo-buildpacks/occam" 10 | "github.com/sclevine/spec" 11 | 12 | . "github.com/onsi/gomega" 13 | . "github.com/paketo-buildpacks/occam/matchers" 14 | ) 15 | 16 | func testGo(t *testing.T, context spec.G, it spec.S) { 17 | var ( 18 | Expect = NewWithT(t).Expect 19 | Eventually = NewWithT(t).Eventually 20 | 21 | pack occam.Pack 22 | docker occam.Docker 23 | ) 24 | 25 | it.Before(func() { 26 | pack = occam.NewPack().WithVerbose().WithNoColor() 27 | docker = occam.NewDocker() 28 | }) 29 | 30 | context("detects a Go app", func() { 31 | var ( 32 | image occam.Image 33 | container occam.Container 34 | 35 | name string 36 | source string 37 | ) 38 | 39 | it.Before(func() { 40 | var err error 41 | name, err = occam.RandomName() 42 | Expect(err).NotTo(HaveOccurred()) 43 | }) 44 | 45 | it.After(func() { 46 | Expect(docker.Container.Remove.Execute(container.ID)).To(Succeed()) 47 | Expect(docker.Volume.Remove.Execute(occam.CacheVolumeNames(name))).To(Succeed()) 48 | Expect(docker.Image.Remove.Execute(image.ID)).To(Succeed()) 49 | Expect(os.RemoveAll(source)).To(Succeed()) 50 | }) 51 | 52 | it("builds successfully", func() { 53 | var err error 54 | source, err = occam.Source(filepath.Join("testdata", "go")) 55 | Expect(err).NotTo(HaveOccurred()) 56 | 57 | var logs fmt.Stringer 58 | image, logs, err = pack.Build. 59 | WithPullPolicy("always"). 60 | WithBuilder(Builder). 61 | Execute(name, source) 62 | Expect(err).ToNot(HaveOccurred(), logs.String) 63 | 64 | container, err = docker.Container.Run. 65 | WithEnv(map[string]string{"PORT": "8080"}). 66 | WithPublish("8080"). 67 | Execute(image.ID) 68 | Expect(err).NotTo(HaveOccurred()) 69 | 70 | Eventually(container).Should(BeAvailable()) 71 | 72 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for Go Distribution"))) 73 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for Go Mod Vendor"))) 74 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for Go Build"))) 75 | }) 76 | }) 77 | } 78 | -------------------------------------------------------------------------------- /smoke/nodejs_test.go: -------------------------------------------------------------------------------- 1 | package smoke_test 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "testing" 8 | 9 | "github.com/paketo-buildpacks/occam" 10 | "github.com/sclevine/spec" 11 | 12 | . "github.com/onsi/gomega" 13 | . "github.com/paketo-buildpacks/occam/matchers" 14 | ) 15 | 16 | func testNodejs(t *testing.T, context spec.G, it spec.S) { 17 | var ( 18 | Expect = NewWithT(t).Expect 19 | Eventually = NewWithT(t).Eventually 20 | 21 | pack occam.Pack 22 | docker occam.Docker 23 | ) 24 | 25 | it.Before(func() { 26 | pack = occam.NewPack().WithVerbose().WithNoColor() 27 | docker = occam.NewDocker() 28 | }) 29 | 30 | context("detects a Nodejs app", func() { 31 | var ( 32 | image occam.Image 33 | container occam.Container 34 | 35 | name string 36 | source string 37 | ) 38 | 39 | it.Before(func() { 40 | var err error 41 | name, err = occam.RandomName() 42 | Expect(err).NotTo(HaveOccurred()) 43 | }) 44 | 45 | it.After(func() { 46 | Expect(docker.Container.Remove.Execute(container.ID)).To(Succeed()) 47 | Expect(docker.Volume.Remove.Execute(occam.CacheVolumeNames(name))).To(Succeed()) 48 | Expect(docker.Image.Remove.Execute(image.ID)).To(Succeed()) 49 | Expect(os.RemoveAll(source)).To(Succeed()) 50 | }) 51 | 52 | it("builds successfully", func() { 53 | var err error 54 | source, err = occam.Source(filepath.Join("testdata", "nodejs")) 55 | Expect(err).NotTo(HaveOccurred()) 56 | 57 | var logs fmt.Stringer 58 | image, logs, err = pack.Build. 59 | WithPullPolicy("always"). 60 | WithBuilder(Builder). 61 | Execute(name, source) 62 | Expect(err).ToNot(HaveOccurred(), logs.String) 63 | 64 | container, err = docker.Container.Run. 65 | WithEnv(map[string]string{"PORT": "8080"}). 66 | WithPublish("8080"). 67 | Execute(image.ID) 68 | Expect(err).NotTo(HaveOccurred()) 69 | 70 | Eventually(container).Should(BeAvailable()) 71 | 72 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for Node Engine"))) 73 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for NPM Install"))) 74 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for NPM Start"))) 75 | }) 76 | }) 77 | } 78 | -------------------------------------------------------------------------------- /smoke/ruby_test.go: -------------------------------------------------------------------------------- 1 | package smoke_test 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "testing" 8 | 9 | "github.com/paketo-buildpacks/occam" 10 | "github.com/sclevine/spec" 11 | 12 | . "github.com/onsi/gomega" 13 | . "github.com/paketo-buildpacks/occam/matchers" 14 | ) 15 | 16 | func testRuby(t *testing.T, context spec.G, it spec.S) { 17 | var ( 18 | Expect = NewWithT(t).Expect 19 | Eventually = NewWithT(t).Eventually 20 | 21 | pack occam.Pack 22 | docker occam.Docker 23 | ) 24 | 25 | it.Before(func() { 26 | pack = occam.NewPack().WithVerbose().WithNoColor() 27 | docker = occam.NewDocker() 28 | }) 29 | 30 | context("detects a Ruby app", func() { 31 | var ( 32 | image occam.Image 33 | container occam.Container 34 | 35 | name string 36 | source string 37 | ) 38 | 39 | it.Before(func() { 40 | var err error 41 | name, err = occam.RandomName() 42 | Expect(err).NotTo(HaveOccurred()) 43 | }) 44 | 45 | it.After(func() { 46 | Expect(docker.Container.Remove.Execute(container.ID)).To(Succeed()) 47 | Expect(docker.Volume.Remove.Execute(occam.CacheVolumeNames(name))).To(Succeed()) 48 | Expect(docker.Image.Remove.Execute(image.ID)).To(Succeed()) 49 | Expect(os.RemoveAll(source)).To(Succeed()) 50 | }) 51 | 52 | it("builds successfully", func() { 53 | var err error 54 | source, err = occam.Source(filepath.Join("testdata", "ruby")) 55 | Expect(err).NotTo(HaveOccurred()) 56 | 57 | var logs fmt.Stringer 58 | image, logs, err = pack.Build. 59 | WithPullPolicy("always"). 60 | WithBuilder(Builder). 61 | Execute(name, source) 62 | Expect(err).ToNot(HaveOccurred(), logs.String) 63 | 64 | container, err = docker.Container.Run. 65 | WithPublish("9292"). 66 | Execute(image.ID) 67 | Expect(err).NotTo(HaveOccurred()) 68 | 69 | Eventually(container).Should(BeAvailable()) 70 | 71 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for MRI"))) 72 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for Bundler"))) 73 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for Bundle Install"))) 74 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for Puma"))) 75 | }) 76 | }) 77 | } 78 | -------------------------------------------------------------------------------- /smoke/python_test.go: -------------------------------------------------------------------------------- 1 | package smoke_test 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "testing" 8 | 9 | "github.com/paketo-buildpacks/occam" 10 | "github.com/sclevine/spec" 11 | 12 | . "github.com/onsi/gomega" 13 | . "github.com/paketo-buildpacks/occam/matchers" 14 | ) 15 | 16 | func testPython(t *testing.T, context spec.G, it spec.S) { 17 | var ( 18 | Expect = NewWithT(t).Expect 19 | Eventually = NewWithT(t).Eventually 20 | 21 | pack occam.Pack 22 | docker occam.Docker 23 | ) 24 | 25 | it.Before(func() { 26 | pack = occam.NewPack().WithVerbose().WithNoColor() 27 | docker = occam.NewDocker() 28 | }) 29 | 30 | context("detects a Python app", func() { 31 | var ( 32 | image occam.Image 33 | container occam.Container 34 | 35 | name string 36 | source string 37 | ) 38 | 39 | it.Before(func() { 40 | var err error 41 | name, err = occam.RandomName() 42 | Expect(err).NotTo(HaveOccurred()) 43 | }) 44 | 45 | it.After(func() { 46 | Expect(docker.Container.Remove.Execute(container.ID)).To(Succeed()) 47 | Expect(docker.Volume.Remove.Execute(occam.CacheVolumeNames(name))).To(Succeed()) 48 | Expect(docker.Image.Remove.Execute(image.ID)).To(Succeed()) 49 | Expect(os.RemoveAll(source)).To(Succeed()) 50 | }) 51 | 52 | it("builds successfully", func() { 53 | var err error 54 | source, err = occam.Source(filepath.Join("testdata", "python")) 55 | Expect(err).NotTo(HaveOccurred()) 56 | 57 | var logs fmt.Stringer 58 | image, logs, err = pack.Build. 59 | WithPullPolicy("always"). 60 | WithBuilder(Builder). 61 | Execute(name, source) 62 | Expect(err).ToNot(HaveOccurred(), logs.String) 63 | 64 | container, err = docker.Container.Run. 65 | WithEnv(map[string]string{"PORT": "8080"}). 66 | WithPublish("8080"). 67 | Execute(image.ID) 68 | Expect(err).NotTo(HaveOccurred()) 69 | 70 | Eventually(container).Should(BeAvailable()) 71 | 72 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for CPython"))) 73 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for Pipenv"))) 74 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for Pipenv Install"))) 75 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for Python Start"))) 76 | }) 77 | }) 78 | } 79 | -------------------------------------------------------------------------------- /smoke/testdata/java-native-image/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 4.0.0 7 | 8 | org.springframework.boot 9 | spring-boot-starter-parent 10 | 3.0.1 11 | 12 | 13 | io.paketo 14 | demo 15 | 0.0.1-SNAPSHOT 16 | demo 17 | Demo project for Spring Boot 18 | 19 | 17 20 | 21 | 22 | 23 | 24 | org.springframework.boot 25 | spring-boot-starter-actuator 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-webflux 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-starter-test 34 | test 35 | 36 | 37 | io.projectreactor 38 | reactor-test 39 | test 40 | 41 | 42 | 43 | 44 | 45 | org.graalvm.buildtools 46 | native-maven-plugin 47 | 48 | 49 | org.springframework.boot 50 | spring-boot-maven-plugin 51 | 52 | ${repackage.classifier} 53 | 54 | apps/native-image 55 | paketobuildpacks/builder:tiny 56 | 57 | true 58 | 17 59 | false 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /smoke/testdata/nginx/mime.types: -------------------------------------------------------------------------------- 1 | types { 2 | text/html html htm shtml; 3 | text/css css; 4 | text/xml xml; 5 | image/gif gif; 6 | image/jpeg jpeg jpg; 7 | application/x-javascript js; 8 | application/atom+xml atom; 9 | application/rss+xml rss; 10 | font/ttf ttf; 11 | font/woff woff; 12 | font/woff2 woff2; 13 | text/mathml mml; 14 | text/plain txt; 15 | text/vnd.sun.j2me.app-descriptor jad; 16 | text/vnd.wap.wml wml; 17 | text/x-component htc; 18 | text/cache-manifest manifest; 19 | image/png png; 20 | image/tiff tif tiff; 21 | image/vnd.wap.wbmp wbmp; 22 | image/x-icon ico; 23 | image/x-jng jng; 24 | image/x-ms-bmp bmp; 25 | image/svg+xml svg svgz; 26 | image/webp webp; 27 | application/java-archive jar war ear; 28 | application/mac-binhex40 hqx; 29 | application/msword doc; 30 | application/pdf pdf; 31 | application/postscript ps eps ai; 32 | application/rtf rtf; 33 | application/vnd.ms-excel xls; 34 | application/vnd.ms-powerpoint ppt; 35 | application/vnd.wap.wmlc wmlc; 36 | application/vnd.google-earth.kml+xml kml; 37 | application/vnd.google-earth.kmz kmz; 38 | application/x-7z-compressed 7z; 39 | application/x-cocoa cco; 40 | application/x-java-archive-diff jardiff; 41 | application/x-java-jnlp-file jnlp; 42 | application/x-makeself run; 43 | application/x-perl pl pm; 44 | application/x-pilot prc pdb; 45 | application/x-rar-compressed rar; 46 | application/x-redhat-package-manager rpm; 47 | application/x-sea sea; 48 | application/x-shockwave-flash swf; 49 | application/x-stuffit sit; 50 | application/x-tcl tcl tk; 51 | application/x-x509-ca-cert der pem crt; 52 | application/x-xpinstall xpi; 53 | application/xhtml+xml xhtml; 54 | application/zip zip; 55 | application/octet-stream bin exe dll; 56 | application/octet-stream deb; 57 | application/octet-stream dmg; 58 | application/octet-stream eot; 59 | application/octet-stream iso img; 60 | application/octet-stream msi msp msm; 61 | application/json json; 62 | audio/midi mid midi kar; 63 | audio/mpeg mp3; 64 | audio/ogg ogg; 65 | audio/x-m4a m4a; 66 | audio/x-realaudio ra; 67 | video/3gpp 3gpp 3gp; 68 | video/mp4 mp4; 69 | video/mpeg mpeg mpg; 70 | video/quicktime mov; 71 | video/webm webm; 72 | video/x-flv flv; 73 | video/x-m4v m4v; 74 | video/x-mng mng; 75 | video/x-ms-asf asx asf; 76 | video/x-ms-wmv wmv; 77 | video/x-msvideo avi; 78 | } 79 | -------------------------------------------------------------------------------- /smoke/java_test.go: -------------------------------------------------------------------------------- 1 | package smoke_test 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "testing" 8 | 9 | "github.com/paketo-buildpacks/occam" 10 | "github.com/sclevine/spec" 11 | 12 | . "github.com/onsi/gomega" 13 | . "github.com/paketo-buildpacks/occam/matchers" 14 | ) 15 | 16 | func testJava(t *testing.T, context spec.G, it spec.S) { 17 | var ( 18 | Expect = NewWithT(t).Expect 19 | Eventually = NewWithT(t).Eventually 20 | 21 | pack occam.Pack 22 | docker occam.Docker 23 | ) 24 | 25 | it.Before(func() { 26 | pack = occam.NewPack().WithVerbose().WithNoColor() 27 | docker = occam.NewDocker() 28 | }) 29 | 30 | context("detects a Java app", func() { 31 | var ( 32 | image occam.Image 33 | container occam.Container 34 | 35 | name string 36 | source string 37 | ) 38 | 39 | it.Before(func() { 40 | var err error 41 | name, err = occam.RandomName() 42 | Expect(err).NotTo(HaveOccurred()) 43 | }) 44 | 45 | it.After(func() { 46 | Expect(docker.Container.Remove.Execute(container.ID)).To(Succeed()) 47 | Expect(docker.Volume.Remove.Execute(occam.CacheVolumeNames(name))).To(Succeed()) 48 | Expect(docker.Image.Remove.Execute(image.ID)).To(Succeed()) 49 | Expect(os.RemoveAll(source)).To(Succeed()) 50 | }) 51 | 52 | it("builds successfully", func() { 53 | var err error 54 | // this is OK, the app also works for running with a JVM 55 | source, err = occam.Source(filepath.Join("testdata", "java-native-image")) 56 | Expect(err).NotTo(HaveOccurred()) 57 | 58 | var logs fmt.Stringer 59 | image, logs, err = pack.Build. 60 | WithPullPolicy("always"). 61 | WithBuilder(Builder). 62 | WithEnv(map[string]string{"BP_JVM_VERSION": "17"}). 63 | Execute(name, source) 64 | Expect(err).ToNot(HaveOccurred(), logs.String) 65 | 66 | container, err = docker.Container.Run. 67 | WithEnv(map[string]string{"PORT": "8080"}). 68 | WithPublish("8080"). 69 | Execute(image.ID) 70 | Expect(err).NotTo(HaveOccurred()) 71 | 72 | Eventually(container).Should(BeAvailable()) 73 | 74 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for BellSoft Liberica"))) 75 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for Maven"))) 76 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for Executable JAR"))) 77 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for Spring Boot"))) 78 | }) 79 | }) 80 | } 81 | -------------------------------------------------------------------------------- /.github/workflows/test-builder.yml: -------------------------------------------------------------------------------- 1 | name: Test Builder 2 | 3 | on: 4 | workflow_dispatch: {} 5 | 6 | env: 7 | BUILDERS_FILEPATH: "builders.json" 8 | 9 | jobs: 10 | preparation: 11 | name: Preparation 12 | runs-on: ubuntu-24.04 13 | outputs: 14 | combos: ${{ steps.get_combos.outputs.combos }} 15 | steps: 16 | - name: Checkout 17 | uses: actions/checkout@v6 18 | 19 | - name: Get Combos 20 | id: get_combos 21 | run: | 22 | builders=$(jq -n -c '[]') 23 | 24 | if [ -f ${{ env.BUILDERS_FILEPATH }} ]; then 25 | builders=$(jq -c '.builders' ${{ env.BUILDERS_FILEPATH }}) 26 | else 27 | # Strip off the Github org prefix from repo name 28 | # paketo-buildpacks/builder-with-some-name --> builder-with-some-name 29 | registry_repo=$(echo "${{ github.repository }}" | sed 's/^.*\///') 30 | builders=$(jq -n -c '[ 31 | { 32 | "name": "'"${registry_repo}"'", 33 | "path": ".", 34 | "container_repository": "'"${registry_repo}"'", 35 | "test_runners": ["ubuntu-24.04"] 36 | } 37 | ]') 38 | fi 39 | 40 | builders=$(jq -c '.[]' <<< "$builders") 41 | 42 | combos=$(jq -n -c '[]') 43 | 44 | for builder in $builders; do 45 | runners=$(echo $builder | jq -r '.test_runners[]') 46 | builder_name=$(echo $builder | jq -r '.name') 47 | builder_path=$(echo $builder | jq -r '.path') 48 | for runner in $runners; do 49 | combos=$( 50 | jq \ 51 | --arg builder_name "$builder_name" \ 52 | --arg runner "$runner" \ 53 | --arg builder_path "$builder_path" \ 54 | '. + [{"builder": $builder_name, "runner": $runner, "path": $builder_path}]' \ 55 | <<< "$combos" 56 | ) 57 | done 58 | done 59 | 60 | combos=$(jq -c <<< "$combos") 61 | echo "combos=$combos" 62 | echo "combos=$combos" >> "$GITHUB_OUTPUT" 63 | 64 | smoke: 65 | name: Smoke Test 66 | needs: preparation 67 | strategy: 68 | matrix: 69 | combos: ${{ fromJSON(needs.preparation.outputs.combos) }} 70 | runs-on: ${{ matrix.combos.runner }} 71 | 72 | steps: 73 | - name: Checkout 74 | uses: actions/checkout@v6 75 | 76 | - name: Setup Go 77 | uses: actions/setup-go@v6 78 | with: 79 | go-version-file: go.mod 80 | 81 | - name: Run Smoke Tests 82 | run: ./scripts/smoke.sh --builder-dir "${{ matrix.combos.path }}" 83 | -------------------------------------------------------------------------------- /smoke/java_native_image_test.go: -------------------------------------------------------------------------------- 1 | package smoke_test 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "testing" 8 | 9 | "github.com/paketo-buildpacks/occam" 10 | "github.com/sclevine/spec" 11 | 12 | . "github.com/onsi/gomega" 13 | . "github.com/paketo-buildpacks/occam/matchers" 14 | ) 15 | 16 | func testJavaNativeImage(t *testing.T, context spec.G, it spec.S) { 17 | var ( 18 | Expect = NewWithT(t).Expect 19 | Eventually = NewWithT(t).Eventually 20 | 21 | pack occam.Pack 22 | docker occam.Docker 23 | ) 24 | 25 | it.Before(func() { 26 | pack = occam.NewPack().WithVerbose().WithNoColor() 27 | docker = occam.NewDocker() 28 | }) 29 | 30 | context("detects a Java Native Image app", func() { 31 | var ( 32 | image occam.Image 33 | container occam.Container 34 | 35 | name string 36 | source string 37 | ) 38 | 39 | it.Before(func() { 40 | var err error 41 | name, err = occam.RandomName() 42 | Expect(err).NotTo(HaveOccurred()) 43 | }) 44 | 45 | it.After(func() { 46 | Expect(docker.Container.Remove.Execute(container.ID)).To(Succeed()) 47 | Expect(docker.Volume.Remove.Execute(occam.CacheVolumeNames(name))).To(Succeed()) 48 | Expect(docker.Image.Remove.Execute(image.ID)).To(Succeed()) 49 | Expect(os.RemoveAll(source)).To(Succeed()) 50 | }) 51 | 52 | it("builds successfully", func() { 53 | var err error 54 | source, err = occam.Source(filepath.Join("testdata", "java-native-image")) 55 | Expect(err).NotTo(HaveOccurred()) 56 | 57 | var logs fmt.Stringer 58 | image, logs, err = pack.Build. 59 | WithPullPolicy("always"). 60 | WithBuilder(Builder). 61 | WithEnv(map[string]string{"BP_NATIVE_IMAGE": "true", "BP_JVM_VERSION": "17", "BP_MAVEN_BUILD_ARGUMENTS": "-Pnative --batch-mode -Dmaven.test.skip=true --no-transfer-progress package", "USE_NATIVE_IMAGE_JAVA_PLATFORM_MODULE_SYSTEM": "false"}). 62 | Execute(name, source) 63 | Expect(err).ToNot(HaveOccurred(), logs.String) 64 | 65 | container, err = docker.Container.Run. 66 | WithEnv(map[string]string{"PORT": "8080"}). 67 | WithPublish("8080"). 68 | Execute(image.ID) 69 | Expect(err).NotTo(HaveOccurred()) 70 | 71 | Eventually(container).Should(BeAvailable()) 72 | 73 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for BellSoft Liberica"))) 74 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for Maven"))) 75 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for Executable JAR"))) 76 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for Spring Boot"))) 77 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for Native Image"))) 78 | }) 79 | }) 80 | } 81 | -------------------------------------------------------------------------------- /.github/workflows/update-github-config.yml: -------------------------------------------------------------------------------- 1 | name: Update shared github-config 2 | 3 | on: 4 | schedule: 5 | - cron: '47 8 * * *' # daily at 08:46 UTC 6 | workflow_dispatch: {} 7 | 8 | concurrency: github_config_update 9 | 10 | jobs: 11 | build: 12 | name: Create PR to update shared files 13 | runs-on: ubuntu-24.04 14 | steps: 15 | 16 | - name: Checkout 17 | uses: actions/checkout@v6 18 | with: 19 | token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }} 20 | 21 | - name: Checkout github-config 22 | uses: actions/checkout@v6 23 | with: 24 | repository: paketo-buildpacks/github-config 25 | path: github-config 26 | 27 | - name: Checkout Branch 28 | uses: paketo-buildpacks/github-config/actions/pull-request/checkout-branch@main 29 | with: 30 | branch: automation/github-config/update 31 | 32 | - name: Run the sync action 33 | uses: paketo-buildpacks/github-config/actions/sync@main 34 | with: 35 | workspace: /github/workspace 36 | config: /github/workspace/github-config/builder 37 | 38 | - name: Cleanup 39 | run: rm -rf github-config 40 | 41 | - name: Commit 42 | id: commit 43 | uses: paketo-buildpacks/github-config/actions/pull-request/create-commit@main 44 | with: 45 | message: "Updating github-config" 46 | pathspec: "." 47 | keyid: ${{ secrets.PAKETO_BOT_GPG_SIGNING_KEY_ID }} 48 | key: ${{ secrets.PAKETO_BOT_GPG_SIGNING_KEY }} 49 | 50 | - name: Push Branch 51 | if: ${{ steps.commit.outputs.commit_sha != '' }} 52 | uses: paketo-buildpacks/github-config/actions/pull-request/push-branch@main 53 | with: 54 | branch: automation/github-config/update 55 | 56 | - name: Open Pull Request 57 | if: ${{ steps.commit.outputs.commit_sha != '' }} 58 | uses: paketo-buildpacks/github-config/actions/pull-request/open@main 59 | with: 60 | token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }} 61 | title: "Updates github-config" 62 | branch: automation/github-config/update 63 | 64 | failure: 65 | name: Alert on Failure 66 | runs-on: ubuntu-24.04 67 | needs: [build] 68 | if: ${{ always() && needs.build.result == 'failure' }} 69 | steps: 70 | - name: File Failure Alert Issue 71 | uses: paketo-buildpacks/github-config/actions/issue/file@main 72 | with: 73 | token: ${{ secrets.GITHUB_TOKEN }} 74 | repo: ${{ github.repository }} 75 | label: "failure:update-github-config" 76 | comment_if_exists: true 77 | issue_title: "Failure: Update GitHub config workflow" 78 | issue_body: | 79 | Update GitHub config workflow [failed](https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}). 80 | comment_body: | 81 | Another failure occurred: https://github.com/${{github.repository}}/actions/runs/${{github.run_id}} 82 | -------------------------------------------------------------------------------- /.github/workflows/approve-bot-pr.yml: -------------------------------------------------------------------------------- 1 | name: Approve Bot PRs and Enable Auto-Merge 2 | 3 | on: 4 | workflow_run: 5 | workflows: ["Test Pull Request"] 6 | types: 7 | - completed 8 | 9 | jobs: 10 | download: 11 | name: Download PR Artifact 12 | if: ${{ github.event.workflow_run.event == 'pull_request' && github.event.workflow_run.conclusion == 'success' }} 13 | runs-on: ubuntu-24.04 14 | outputs: 15 | pr-author: ${{ steps.pr-data.outputs.author }} 16 | pr-number: ${{ steps.pr-data.outputs.number }} 17 | steps: 18 | - name: 'Download artifact' 19 | uses: paketo-buildpacks/github-config/actions/pull-request/download-artifact@main 20 | with: 21 | name: "event-payload" 22 | repo: ${{ github.repository }} 23 | run_id: ${{ github.event.workflow_run.id }} 24 | workspace: "/github/workspace" 25 | token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }} 26 | - id: pr-data 27 | run: | 28 | echo "author=$(jq -r '.pull_request.user.login' event.json)" >> "$GITHUB_OUTPUT" 29 | echo "number=$(jq -r '.pull_request.number' event.json)" >> "$GITHUB_OUTPUT" 30 | 31 | approve: 32 | name: Approve Bot PRs 33 | needs: download 34 | if: ${{ needs.download.outputs.pr-author == 'paketo-bot' || needs.download.outputs.pr-author == 'dependabot[bot]' }} 35 | runs-on: ubuntu-24.04 36 | steps: 37 | - name: Check Commit Verification 38 | id: unverified-commits 39 | uses: paketo-buildpacks/github-config/actions/pull-request/check-unverified-commits@main 40 | with: 41 | token: ${{ secrets.PAKETO_BOT_REVIEWER_GITHUB_TOKEN }} 42 | repo: ${{ github.repository }} 43 | number: ${{ needs.download.outputs.pr-number }} 44 | 45 | - name: Check for Human Commits 46 | id: human-commits 47 | uses: paketo-buildpacks/github-config/actions/pull-request/check-human-commits@main 48 | with: 49 | token: ${{ secrets.PAKETO_BOT_REVIEWER_GITHUB_TOKEN }} 50 | repo: ${{ github.repository }} 51 | number: ${{ needs.download.outputs.pr-number }} 52 | 53 | - name: Checkout 54 | if: steps.human-commits.outputs.human_commits == 'false' && steps.unverified-commits.outputs.unverified_commits == 'false' 55 | uses: actions/checkout@v6 56 | 57 | - name: Approve 58 | if: steps.human-commits.outputs.human_commits == 'false' && steps.unverified-commits.outputs.unverified_commits == 'false' 59 | uses: paketo-buildpacks/github-config/actions/pull-request/approve@main 60 | with: 61 | token: ${{ secrets.PAKETO_BOT_REVIEWER_GITHUB_TOKEN }} 62 | number: ${{ needs.download.outputs.pr-number }} 63 | 64 | - name: Enable Auto-Merge 65 | if: steps.human-commits.outputs.human_commits == 'false' && steps.unverified-commits.outputs.unverified_commits == 'false' 66 | run: | 67 | gh pr merge ${{ needs.download.outputs.pr-number }} --auto --rebase 68 | env: 69 | GITHUB_TOKEN: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }} 70 | -------------------------------------------------------------------------------- /scripts/publish.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eu 4 | set -o pipefail 5 | 6 | readonly PROGDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 7 | readonly ROOT_DIR="$(cd "${PROGDIR}/.." && pwd)" 8 | readonly BIN_DIR="${ROOT_DIR}/.bin" 9 | 10 | # shellcheck source=SCRIPTDIR/.util/tools.sh 11 | source "${PROGDIR}/.util/tools.sh" 12 | 13 | # shellcheck source=SCRIPTDIR/.util/print.sh 14 | source "${PROGDIR}/.util/print.sh" 15 | 16 | if [[ $BASH_VERSINFO -lt 4 ]]; then 17 | util::print::error "Before running this script please update Bash to v4 or higher (e.g. on OSX: \$ brew install bash)" 18 | fi 19 | 20 | function main() { 21 | local token 22 | local builder_toml_path="" 23 | local builder_image_ref="" 24 | token="" 25 | 26 | while [[ "${#}" != 0 ]]; do 27 | case "${1}" in 28 | --help|-h) 29 | shift 1 30 | usage 31 | exit 0 32 | ;; 33 | 34 | --builder-toml-path) 35 | builder_toml_path=${2} 36 | shift 2 37 | ;; 38 | 39 | --builder-image-ref) 40 | builder_image_ref=${2} 41 | shift 2 42 | ;; 43 | 44 | --token|-t) 45 | token="${2}" 46 | shift 2 47 | ;; 48 | 49 | "") 50 | # skip if the argument is empty 51 | shift 1 52 | ;; 53 | 54 | *) 55 | util::print::error "unknown argument \"${1}\"" 56 | esac 57 | done 58 | 59 | 60 | if [ -z "$builder_toml_path" ]; then 61 | util::print::error "--builder-toml-path is required [Example: ./builders/builder/builder.toml]" 62 | fi 63 | 64 | if [ ! -f "$builder_toml_path" ]; then 65 | util::print::error "The provided --builder-toml-path does not exist or is not a file: $builder_toml_path" 66 | fi 67 | 68 | if [ -z "$builder_image_ref" ]; then 69 | util::print::error "--builder-image-ref is required [Example: index.docker.io/username/builder:tag or localhost:5000/builder:tag]" 70 | fi 71 | 72 | tools::install "${token}" 73 | 74 | builder::publish "$builder_toml_path" "$builder_image_ref" 75 | } 76 | 77 | function usage() { 78 | cat <<-USAGE 79 | publish.sh [OPTIONS] 80 | 81 | --builder-toml-path PATH Path to the builder.toml file that defines the builder to be published, e.g. ./builders/builder/builder.toml 82 | --builder-image-ref IMAGE_REF Full image name of the registry to push the built builder to, e.g. index.docker.io/username/builder:tag or localhost:5000/builder:tag 83 | --help, -h Show this help message 84 | USAGE 85 | } 86 | 87 | function tools::install() { 88 | local token 89 | token="${1}" 90 | 91 | util::tools::pack::install \ 92 | --directory "${ROOT_DIR}/.bin" \ 93 | --token "${token}" 94 | } 95 | 96 | function builder::publish() { 97 | local builder_toml_path="$1" 98 | local builder_image_ref="$2" 99 | 100 | util::print::title "Publishing builder image ${builder_image_ref} using builder config ${builder_toml_path}..." 101 | pack builder create "${builder_image_ref}" \ 102 | --config "${builder_toml_path}" \ 103 | --publish 104 | } 105 | 106 | 107 | 108 | main "${@:-}" 109 | -------------------------------------------------------------------------------- /.github/workflows/test-pull-request.yml: -------------------------------------------------------------------------------- 1 | name: Test Pull Request 2 | 3 | on: 4 | pull_request: 5 | branches: 6 | - main 7 | env: 8 | BUILDERS_FILEPATH: "builders.json" 9 | 10 | jobs: 11 | preparation: 12 | name: Preparation 13 | runs-on: ubuntu-24.04 14 | outputs: 15 | combos: ${{ steps.get_combos.outputs.combos }} 16 | steps: 17 | - name: Checkout 18 | uses: actions/checkout@v6 19 | 20 | - name: Get Combos 21 | id: get_combos 22 | run: | 23 | builders=$(jq -n -c '[]') 24 | 25 | if [ -f ${{ env.BUILDERS_FILEPATH }} ]; then 26 | builders=$(jq -c '.builders' ${{ env.BUILDERS_FILEPATH }}) 27 | else 28 | # Strip off the Github org prefix from repo name 29 | # paketo-buildpacks/builder-with-some-name --> builder-with-some-name 30 | registry_repo=$(echo "${{ github.repository }}" | sed 's/^.*\///') 31 | builders=$(jq -n -c '[ 32 | { 33 | "name": "'"${registry_repo}"'", 34 | "path": ".", 35 | "container_repository": "'"${registry_repo}"'", 36 | "test_runners": ["ubuntu-24.04"] 37 | } 38 | ]') 39 | fi 40 | 41 | builders=$(jq -c '.[]' <<< "$builders") 42 | 43 | combos=$(jq -n -c '[]') 44 | 45 | for builder in $builders; do 46 | runners=$(echo $builder | jq -r '.test_runners[]') 47 | builder_name=$(echo $builder | jq -r '.name') 48 | builder_path=$(echo $builder | jq -r '.path') 49 | for runner in $runners; do 50 | combos=$( 51 | jq \ 52 | --arg builder_name "$builder_name" \ 53 | --arg runner "$runner" \ 54 | --arg builder_path "$builder_path" \ 55 | '. + [{"builder": $builder_name, "runner": $runner, "path": $builder_path}]' \ 56 | <<< "$combos" 57 | ) 58 | done 59 | done 60 | 61 | combos=$(jq -c <<< "$combos") 62 | echo "combos=$combos" 63 | echo "combos=$combos" >> "$GITHUB_OUTPUT" 64 | 65 | smoke: 66 | name: Smoke Test 67 | needs: preparation 68 | strategy: 69 | matrix: 70 | combos: ${{ fromJSON(needs.preparation.outputs.combos) }} 71 | runs-on: ${{ matrix.combos.runner }} 72 | 73 | steps: 74 | - name: Checkout 75 | uses: actions/checkout@v6 76 | 77 | - name: Setup Go 78 | uses: actions/setup-go@v6 79 | with: 80 | go-version-file: go.mod 81 | 82 | - name: Run Smoke Tests 83 | run: ./scripts/smoke.sh --builder-dir "${{ matrix.combos.path }}" 84 | 85 | roundup: 86 | name: Smoke Test 87 | if: ${{ always() }} 88 | runs-on: ubuntu-24.04 89 | needs: smoke 90 | steps: 91 | - run: | 92 | result="${{ needs.smoke.result }}" 93 | if [[ $result == "success" ]]; then 94 | echo "Smoke tests passed against all builders" 95 | exit 0 96 | else 97 | echo "Smoke tests failed on one or more builders" 98 | exit 1 99 | fi 100 | 101 | upload: 102 | name: Upload Workflow Event Payload 103 | runs-on: ubuntu-24.04 104 | steps: 105 | - name: Upload Artifact 106 | uses: actions/upload-artifact@v6 107 | with: 108 | name: event-payload 109 | path: ${{ github.event_path }} 110 | -------------------------------------------------------------------------------- /.github/workflows/update-go-mod-version.yml: -------------------------------------------------------------------------------- 1 | name: Update Go version 2 | 3 | on: 4 | schedule: 5 | - cron: '53 5 * * MON' # every monday at 5:53 UTC 6 | workflow_dispatch: 7 | 8 | concurrency: update-go 9 | 10 | jobs: 11 | update-go: 12 | name: Update go toolchain in go.mod 13 | runs-on: ubuntu-latest 14 | steps: 15 | - name: Check out code 16 | uses: actions/checkout@v6 17 | - name: Checkout PR Branch 18 | uses: paketo-buildpacks/github-config/actions/pull-request/checkout-branch@main 19 | with: 20 | branch: automation/go-mod-update/update-main 21 | - name: Setup Go 22 | id: setup-go 23 | uses: actions/setup-go@v6 24 | # Fetching the latest stable Go version 25 | with: 26 | go-version: stable 27 | - name: Get current go toolchain version 28 | id: current-go-version 29 | uses: paketo-buildpacks/github-config/actions/update-go-mod-version@main 30 | with: 31 | go-version: ${{ steps.setup-go.outputs.go-version }} 32 | - name: Go mod tidy 33 | run: | 34 | #!/usr/bin/env bash 35 | set -euo pipefail 36 | shopt -s inherit_errexit 37 | 38 | echo "Before running go mod tidy" 39 | echo "head -n10 go.mod " 40 | head -n10 go.mod 41 | 42 | echo "git diff" 43 | git diff 44 | 45 | echo "Running go mod tidy" 46 | go mod tidy 47 | 48 | echo "After running go mod tidy" 49 | echo "head -n10 go.mod " 50 | head -n10 go.mod 51 | 52 | echo "git diff" 53 | git diff 54 | - name: Commit 55 | id: commit 56 | uses: paketo-buildpacks/github-config/actions/pull-request/create-commit@main 57 | with: 58 | message: "Updates go mod version to ${{ steps.setup-go.outputs.go-version }}" 59 | pathspec: "." 60 | keyid: ${{ secrets.PAKETO_BOT_GPG_SIGNING_KEY_ID }} 61 | key: ${{ secrets.PAKETO_BOT_GPG_SIGNING_KEY }} 62 | 63 | - name: Push Branch 64 | if: ${{ steps.commit.outputs.commit_sha != '' }} 65 | uses: paketo-buildpacks/github-config/actions/pull-request/push-branch@main 66 | with: 67 | branch: automation/go-mod-update/update-main 68 | 69 | - name: Open Pull Request 70 | if: ${{ steps.commit.outputs.commit_sha != '' }} 71 | uses: paketo-buildpacks/github-config/actions/pull-request/open@main 72 | with: 73 | token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }} 74 | title: "Updates go mod version to ${{ steps.setup-go.outputs.go-version }}" 75 | branch: automation/go-mod-update/update-main 76 | 77 | failure: 78 | name: Alert on Failure 79 | runs-on: ubuntu-24.04 80 | needs: [update-go] 81 | if: ${{ always() && needs.update-go.result == 'failure' }} 82 | steps: 83 | - name: File Failure Alert Issue 84 | uses: paketo-buildpacks/github-config/actions/issue/file@main 85 | with: 86 | token: ${{ secrets.GITHUB_TOKEN }} 87 | repo: ${{ github.repository }} 88 | label: "failure:update-go-version" 89 | comment_if_exists: true 90 | issue_title: "Failure: Update Go Mod Version workflow" 91 | issue_body: | 92 | Update Go Mod Version workflow [failed](https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}). 93 | comment_body: | 94 | Another failure occurred: https://github.com/${{github.repository}}/actions/runs/${{github.run_id}} 95 | -------------------------------------------------------------------------------- /smoke/testdata/httpd/httpd.conf: -------------------------------------------------------------------------------- 1 | ServerRoot "${SERVER_ROOT}" 2 | Listen "${PORT}" 3 | ServerAdmin "test@example.com" 4 | ServerName "0.0.0.0" 5 | DocumentRoot "${APP_ROOT}/htdocs" 6 | 7 | LoadModule authz_core_module modules/mod_authz_core.so 8 | LoadModule authz_host_module modules/mod_authz_host.so 9 | LoadModule log_config_module modules/mod_log_config.so 10 | LoadModule env_module modules/mod_env.so 11 | LoadModule setenvif_module modules/mod_setenvif.so 12 | LoadModule dir_module modules/mod_dir.so 13 | LoadModule mime_module modules/mod_mime.so 14 | LoadModule reqtimeout_module modules/mod_reqtimeout.so 15 | LoadModule unixd_module modules/mod_unixd.so 16 | LoadModule mpm_event_module modules/mod_mpm_event.so 17 | LoadModule remoteip_module modules/mod_remoteip.so 18 | LoadModule rewrite_module modules/mod_rewrite.so 19 | LoadModule filter_module modules/mod_filter.so 20 | LoadModule deflate_module modules/mod_deflate.so 21 | LoadModule headers_module modules/mod_headers.so 22 | 23 | 24 | AllowOverride none 25 | Require all denied 26 | 27 | 28 | 29 | Options SymLinksIfOwnerMatch 30 | AllowOverride All 31 | Require all granted 32 | 33 | 34 | 35 | Require all denied 36 | 37 | 38 | 39 | DirectoryIndex index.html 40 | 41 | 42 | TypesConfig conf/mime.types 43 | AddType application/x-compress .Z 44 | AddType application/x-gzip .gz .tgz 45 | 46 | 47 | 48 | 49 | AddOutputFilterByType DEFLATE text/html text/plain text/xml text/css text/javascript application/javascript 50 | 51 | 52 | 53 | ErrorLog "/proc/self/fd/2" 54 | LogLevel info 55 | 56 | LogFormat "%a %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined 57 | LogFormat "%a %l %u %t \"%r\" %>s %b" common 58 | LogFormat "%a %l %u %t \"%r\" %>s %b vcap_request_id=%{X-Vcap-Request-Id}i peer_addr=%{c}a" extended 59 | 60 | LogFormat "%a %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio 61 | 62 | CustomLog "/proc/self/fd/1" extended 63 | 64 | 65 | 66 | PidFile "/tmp/httpd.pid" 67 | 68 | 69 | StartServers 3 70 | MinSpareThreads 75 71 | MaxSpareThreads 250 72 | ThreadsPerChild 25 73 | MaxRequestWorkers 400 74 | MaxConnectionsPerChild 0 75 | 76 | 77 | StartServers 3 78 | MinSpareThreads 75 79 | MaxSpareThreads 250 80 | ThreadsPerChild 25 81 | MaxRequestWorkers 400 82 | MaxConnectionsPerChild 0 83 | 84 | 85 | MaxMemFree 2048 86 | 87 | 88 | Timeout 60 89 | KeepAlive On 90 | MaxKeepAliveRequests 100 91 | KeepAliveTimeout 5 92 | UseCanonicalName Off 93 | UseCanonicalPhysicalPort Off 94 | AccessFileName .htaccess 95 | ServerTokens Prod 96 | ServerSignature Off 97 | HostnameLookups Off 98 | EnableMMAP Off 99 | EnableSendfile On 100 | RequestReadTimeout header=20-40,MinRate=500 body=20,MinRate=500 101 | 102 | # Adjust IP Address based on header set by proxy 103 | # 104 | RemoteIpHeader x-forwarded-for 105 | RemoteIpInternalProxy 10.0.0.0/8 172.16.0.0/12 192.168.0.0/16 106 | 107 | # Set HTTPS environment variable if we came in over secure 108 | # channel. 109 | SetEnvIf x-forwarded-proto https HTTPS=on 110 | 111 | 112 | LoadModule headers_module modules/mod_headers.so 113 | 114 | 115 | RequestHeader unset Proxy early 116 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/paketo-buildpacks/builder-jammy-base 2 | 3 | go 1.25.5 4 | 5 | require ( 6 | github.com/onsi/gomega v1.38.3 7 | github.com/paketo-buildpacks/occam v0.31.0 8 | github.com/sclevine/spec v1.4.0 9 | ) 10 | 11 | require ( 12 | dario.cat/mergo v1.0.2 // indirect 13 | github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect 14 | github.com/BurntSushi/toml v1.5.0 // indirect 15 | github.com/Microsoft/go-winio v0.6.2 // indirect 16 | github.com/cenkalti/backoff/v4 v4.3.0 // indirect 17 | github.com/containerd/errdefs v1.0.0 // indirect 18 | github.com/containerd/errdefs/pkg v0.3.0 // indirect 19 | github.com/containerd/log v0.1.0 // indirect 20 | github.com/containerd/platforms v0.2.1 // indirect 21 | github.com/containerd/stargz-snapshotter/estargz v0.16.3 // indirect 22 | github.com/cpuguy83/dockercfg v0.3.2 // indirect 23 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect 24 | github.com/distribution/reference v0.6.0 // indirect 25 | github.com/docker/docker v28.5.1+incompatible // indirect 26 | github.com/docker/go-connections v0.6.0 // indirect 27 | github.com/docker/go-units v0.5.0 // indirect 28 | github.com/ebitengine/purego v0.8.4 // indirect 29 | github.com/felixge/httpsnoop v1.0.4 // indirect 30 | github.com/gabriel-vasile/mimetype v1.4.9 // indirect 31 | github.com/go-logr/logr v1.4.3 // indirect 32 | github.com/go-logr/stdr v1.2.2 // indirect 33 | github.com/go-ole/go-ole v1.3.0 // indirect 34 | github.com/google/go-cmp v0.7.0 // indirect 35 | github.com/google/go-containerregistry v0.20.6 // indirect 36 | github.com/google/uuid v1.6.0 // indirect 37 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect 38 | github.com/klauspost/compress v1.18.0 // indirect 39 | github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 // indirect 40 | github.com/magiconair/properties v1.8.10 // indirect 41 | github.com/moby/docker-image-spec v1.3.1 // indirect 42 | github.com/moby/go-archive v0.1.0 // indirect 43 | github.com/moby/patternmatcher v0.6.0 // indirect 44 | github.com/moby/sys/sequential v0.6.0 // indirect 45 | github.com/moby/sys/user v0.4.0 // indirect 46 | github.com/moby/sys/userns v0.1.0 // indirect 47 | github.com/moby/term v0.5.2 // indirect 48 | github.com/morikuni/aec v1.0.0 // indirect 49 | github.com/oklog/ulid v1.3.1 // indirect 50 | github.com/oklog/ulid/v2 v2.1.1 // indirect 51 | github.com/opencontainers/go-digest v1.0.0 // indirect 52 | github.com/opencontainers/image-spec v1.1.1 // indirect 53 | github.com/paketo-buildpacks/freezer v0.2.2 // indirect 54 | github.com/paketo-buildpacks/packit/v2 v2.25.2 // indirect 55 | github.com/pkg/errors v0.9.1 // indirect 56 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect 57 | github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 // indirect 58 | github.com/shirou/gopsutil/v4 v4.25.6 // indirect 59 | github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af // indirect 60 | github.com/stretchr/testify v1.10.0 // indirect 61 | github.com/testcontainers/testcontainers-go v0.39.0 // indirect 62 | github.com/tklauser/go-sysconf v0.3.15 // indirect 63 | github.com/tklauser/numcpus v0.10.0 // indirect 64 | github.com/ulikunitz/xz v0.5.15 // indirect 65 | github.com/vbatts/tar-split v0.12.1 // indirect 66 | github.com/yusufpapurcu/wmi v1.2.4 // indirect 67 | go.opentelemetry.io/auto/sdk v1.1.0 // indirect 68 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 // indirect 69 | go.opentelemetry.io/otel v1.37.0 // indirect 70 | go.opentelemetry.io/otel/metric v1.37.0 // indirect 71 | go.opentelemetry.io/otel/trace v1.37.0 // indirect 72 | go.yaml.in/yaml/v3 v3.0.4 // indirect 73 | golang.org/x/crypto v0.42.0 // indirect 74 | golang.org/x/net v0.44.0 // indirect 75 | golang.org/x/sync v0.17.0 // indirect 76 | golang.org/x/sys v0.36.0 // indirect 77 | golang.org/x/text v0.29.0 // indirect 78 | gopkg.in/yaml.v3 v3.0.1 // indirect 79 | ) 80 | -------------------------------------------------------------------------------- /.github/workflows/push-image.yml: -------------------------------------------------------------------------------- 1 | name: Push Builder Image 2 | 3 | on: 4 | release: 5 | types: 6 | - published 7 | 8 | env: 9 | BUILDERS_FILEPATH: "builders.json" 10 | 11 | jobs: 12 | preparation: 13 | name: Preparation 14 | runs-on: ubuntu-24.04 15 | outputs: 16 | builders: ${{ steps.get-builders.outputs.builders }} 17 | tag: ${{ steps.event.outputs.tag }} 18 | steps: 19 | - name: Checkout 20 | uses: actions/checkout@v6 21 | 22 | - name: Get Builders 23 | id: get-builders 24 | run: | 25 | builders=$(jq -n -c '[]') 26 | 27 | if [ -f ${{ env.BUILDERS_FILEPATH }} ]; then 28 | builders=$(jq -c '.builders' ${{ env.BUILDERS_FILEPATH }}) 29 | else 30 | # Strip off the Github org prefix from repo name 31 | # paketo-buildpacks/builder-with-some-name --> builder-with-some-name 32 | registry_repo=$(echo "${{ github.repository }}" | sed 's/^.*\///') 33 | builders=$(jq -n -c '[ 34 | { 35 | "name": "'"${registry_repo}"'", 36 | "path": ".", 37 | "container_repository": "'"${registry_repo}"'" 38 | } 39 | ]') 40 | fi 41 | 42 | # Filter only the necessary fields 43 | builders=$(echo "$builders" | jq 'map({ 44 | name, 45 | path, 46 | container_repository 47 | })') 48 | 49 | builders=$(jq -c <<< "$builders") 50 | echo "builders=$builders" 51 | echo "builders=$builders" >> "$GITHUB_OUTPUT" 52 | 53 | - name: Parse Event 54 | id: event 55 | run: | 56 | echo "tag=$(jq -r '.release.tag_name' "${GITHUB_EVENT_PATH}" | sed s/^v//)" >> "$GITHUB_OUTPUT" 57 | 58 | push: 59 | name: Push 60 | needs: preparation 61 | runs-on: ubuntu-24.04 62 | strategy: 63 | matrix: 64 | builders: ${{ fromJSON(needs.preparation.outputs.builders) }} 65 | steps: 66 | 67 | - name: Checkout 68 | uses: actions/checkout@v6 69 | 70 | - name: Create Builder Image and Push To Dockerhub 71 | env: 72 | PAKETO_BUILDPACKS_DOCKERHUB_USERNAME: ${{ secrets.PAKETO_BUILDPACKS_DOCKERHUB_USERNAME }} 73 | PAKETO_BUILDPACKS_DOCKERHUB_PASSWORD: ${{ secrets.PAKETO_BUILDPACKS_DOCKERHUB_PASSWORD }} 74 | GITHUB_REPOSITORY_OWNER: ${{ github.repository_owner }} 75 | run: | 76 | # shellcheck source=./scripts/.util/tools.sh 77 | source "./scripts/.util/tools.sh" 78 | 79 | util::tools::crane::install --directory "./.bin" 80 | 81 | DOCKERHUB_ORG="${GITHUB_REPOSITORY_OWNER/-/}" # translates 'paketo-buildpacks' to 'paketobuildpacks' 82 | container_repository=${{ matrix.builders.container_repository }} 83 | 84 | echo "${PAKETO_BUILDPACKS_DOCKERHUB_PASSWORD}" | docker login --username "${PAKETO_BUILDPACKS_DOCKERHUB_USERNAME}" --password-stdin 85 | 86 | ./scripts/publish.sh --builder-toml-path "${{ matrix.builders.path }}/builder.toml" \ 87 | --builder-image-ref "${DOCKERHUB_ORG}/${container_repository}:${{ needs.preparation.outputs.tag }}" 88 | 89 | ./.bin/crane copy "${DOCKERHUB_ORG}/${container_repository}:${{ needs.preparation.outputs.tag }}" "${DOCKERHUB_ORG}/${container_repository}:latest" 90 | 91 | failure: 92 | name: Alert on Failure 93 | runs-on: ubuntu-24.04 94 | needs: [push] 95 | if: ${{ always() && needs.push.result == 'failure' }} 96 | steps: 97 | - name: File Failure Alert Issue 98 | uses: paketo-buildpacks/github-config/actions/issue/file@main 99 | with: 100 | token: ${{ secrets.GITHUB_TOKEN }} 101 | repo: ${{ github.repository }} 102 | label: "failure:push" 103 | comment_if_exists: true 104 | issue_title: "Failure: Push Image workflow" 105 | issue_body: | 106 | Push Image workflow [failed](https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}). 107 | comment_body: | 108 | Another failure occurred: https://github.com/${{github.repository}}/actions/runs/${{github.run_id}} 109 | -------------------------------------------------------------------------------- /.github/workflows/update-builder-toml.yml: -------------------------------------------------------------------------------- 1 | name: Update builder.toml files and Send Pull Request 2 | 3 | on: 4 | schedule: 5 | - cron: '27 2,14 * * *' # daily at 02:27 and 14:27 UTC 6 | workflow_dispatch: {} 7 | 8 | concurrency: builder_update 9 | 10 | env: 11 | BUILDERS_FILEPATH: "builders.json" 12 | 13 | jobs: 14 | preparation: 15 | name: Preparation 16 | runs-on: ubuntu-24.04 17 | outputs: 18 | builders: ${{ steps.get-builders.outputs.builders }} 19 | steps: 20 | - name: Checkout 21 | uses: actions/checkout@v6 22 | 23 | - name: Get Builders 24 | id: get-builders 25 | run: | 26 | builders=$(jq -n -c '[]') 27 | 28 | if [ -f ${{ env.BUILDERS_FILEPATH }} ]; then 29 | builders=$(jq -c '.builders' ${{ env.BUILDERS_FILEPATH }}) 30 | else 31 | # Strip off the Github org prefix from repo name 32 | # paketo-buildpacks/builder-with-some-name --> builder-with-some-name 33 | registry_repo=$(echo "${{ github.repository }}" | sed 's/^.*\///') 34 | builders=$(jq -n -c '[ 35 | { 36 | "name": "'"${registry_repo}"'", 37 | "path": ".", 38 | "container_repository": "'"${registry_repo}"'" 39 | } 40 | ]') 41 | fi 42 | 43 | # Filter only the necessary fields 44 | builders=$(echo "$builders" | jq 'map({ 45 | name, 46 | path, 47 | container_repository 48 | })') 49 | 50 | builders=$(jq -c <<< "$builders") 51 | echo "builders=$builders" 52 | echo "builders=$builders" >> "$GITHUB_OUTPUT" 53 | 54 | update: 55 | name: Update builder.toml 56 | runs-on: ubuntu-24.04 57 | outputs: 58 | commit_sha: ${{ steps.commit.outputs.commit_sha }} 59 | needs: preparation 60 | strategy: 61 | max-parallel: 1 62 | matrix: 63 | builders: ${{ fromJSON(needs.preparation.outputs.builders) }} 64 | 65 | steps: 66 | - name: Check out 67 | uses: actions/checkout@v6 68 | 69 | - name: Checkout branch 70 | uses: paketo-buildpacks/github-config/actions/pull-request/checkout-branch@main 71 | with: 72 | branch: "automation/builder-toml-update" 73 | 74 | - name: Update builder.toml 75 | uses: paketo-buildpacks/github-config/actions/builder/update@main 76 | with: 77 | token: ${{ secrets.GITHUB_TOKEN }} 78 | filename: "${{ matrix.builders.path }}/builder.toml" 79 | 80 | - name: Git commit 81 | id: commit 82 | uses: paketo-buildpacks/github-config/actions/pull-request/create-commit@main 83 | with: 84 | message: "Update ${{ matrix.builders.name }} builder.toml" 85 | pathspec: "${{ matrix.builders.path }}/builder.toml" 86 | keyid: ${{ secrets.PAKETO_BOT_GPG_SIGNING_KEY_ID }} 87 | key: ${{ secrets.PAKETO_BOT_GPG_SIGNING_KEY }} 88 | 89 | - name: Git push 90 | if: ${{ steps.commit.outputs.commit_sha != '' }} 91 | uses: paketo-buildpacks/github-config/actions/pull-request/push-branch@main 92 | with: 93 | branch: "automation/builder-toml-update" 94 | 95 | - name: Output commit sha 96 | if: ${{ steps.commit.outputs.commit_sha != '' }} 97 | run: echo "commit_sha=${{ steps.commit.outputs.commit_sha }}" >> "$GITHUB_OUTPUT" 98 | 99 | open-pull-request: 100 | name: Open pull request 101 | runs-on: ubuntu-24.04 102 | needs: update 103 | if: ${{ needs.update.outputs.commit_sha != '' }} 104 | steps: 105 | - name: Check out 106 | uses: actions/checkout@v6 107 | 108 | - name: Checkout branch 109 | uses: paketo-buildpacks/github-config/actions/pull-request/checkout-branch@main 110 | with: 111 | branch: "automation/builder-toml-update" 112 | 113 | - name: Open Pull Request 114 | uses: paketo-buildpacks/github-config/actions/pull-request/open@main 115 | with: 116 | token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }} 117 | title: "Updating builder.toml files" 118 | branch: "automation/builder-toml-update" 119 | 120 | failure: 121 | name: Alert on Failure 122 | runs-on: ubuntu-24.04 123 | needs: [update, open-pull-request] 124 | if: ${{ always() && ( needs.update.result == 'failure' || needs.open-pull-request.result == 'failure' ) }} 125 | steps: 126 | - name: File Failure Alert Issue 127 | uses: paketo-buildpacks/github-config/actions/issue/file@main 128 | with: 129 | token: ${{ secrets.GITHUB_TOKEN }} 130 | repo: ${{ github.repository }} 131 | label: "failure:update-builder-toml" 132 | comment_if_exists: true 133 | issue_title: "Failure: Update Builder TOML workflow" 134 | issue_body: | 135 | Update Builder TOML workflow [failed](https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}). 136 | comment_body: | 137 | Another failure occurred: https://github.com/${{github.repository}}/actions/runs/${{github.run_id}} 138 | -------------------------------------------------------------------------------- /.github/workflows/update-test-data.yml: -------------------------------------------------------------------------------- 1 | name: Update test data from samples 2 | 3 | on: 4 | schedule: 5 | - cron: '46 4 * * *' # daily at 04:46 UTC 6 | workflow_dispatch: {} 7 | 8 | concurrency: test_data_update 9 | 10 | env: 11 | BUILDERS_FILEPATH: "builders.json" 12 | 13 | jobs: 14 | build: 15 | name: Create PR to sync test data with the samples 16 | runs-on: ubuntu-24.04 17 | steps: 18 | 19 | - name: Checkout 20 | uses: actions/checkout@v6 21 | with: 22 | token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }} 23 | 24 | - name: ${{ env.BUILDERS_FILEPATH }} exists 25 | id: builder_file_exists 26 | run: | 27 | if [ ! -f ${{ env.BUILDERS_FILEPATH }} ]; then 28 | echo "${{ env.BUILDERS_FILEPATH }} does not exist, skipping" 29 | echo "exists=false" >> $GITHUB_OUTPUT 30 | else 31 | echo "exists=true" >> $GITHUB_OUTPUT 32 | fi 33 | 34 | - name: Checkout paketo-buildpacks samples 35 | if: ${{ steps.builder_file_exists.outputs.exists == 'true' }} 36 | uses: actions/checkout@v6 37 | with: 38 | repository: paketo-buildpacks/samples 39 | path: samples 40 | 41 | - name: Checkout Branch 42 | if: ${{ steps.builder_file_exists.outputs.exists == 'true' }} 43 | uses: paketo-buildpacks/github-config/actions/pull-request/checkout-branch@main 44 | with: 45 | branch: automation/testdata/update 46 | 47 | - name: Run the sync action 48 | if: ${{ steps.builder_file_exists.outputs.exists == 'true' }} 49 | run: | 50 | builders=$(jq -c '.builders.[]' ${{ env.BUILDERS_FILEPATH }}) 51 | for builder in $builders; do 52 | test_data=$(echo $builder | jq -c '.test_data // []' | jq -c '.[]') 53 | for td in $test_data; do 54 | sample_dir=$(echo $td | jq -r '.sample_dir // empty') 55 | if [ -z "$sample_dir" ]; then 56 | echo "sample_dir can not be empty" 57 | exit 1 58 | fi 59 | 60 | sample_dir="samples/${sample_dir}/" 61 | 62 | test_dir=$(echo $td | jq -r '.test_dir // empty') 63 | if [ -z "$test_dir" ]; then 64 | echo "test_dir can not be empty" 65 | exit 1 66 | fi 67 | test_dir="${test_dir}/" 68 | 69 | echo 70 | echo "syncing from ${sample_dir} to ${test_dir}" 71 | echo 72 | 73 | args=( 74 | --recursive 75 | "${sample_dir}" 76 | "${test_dir}" 77 | --delete 78 | ) 79 | 80 | if [[ -f "${test_dir}/.syncignore" ]]; then 81 | args+=( 82 | --exclude=".syncignore" 83 | --exclude-from="${test_dir}/.syncignore" 84 | ) 85 | fi 86 | 87 | rsync ${args[*]} 88 | 89 | echo 90 | echo "sync complete" 91 | echo 92 | done 93 | done 94 | 95 | - name: Cleanup 96 | if: ${{ steps.builder_file_exists.outputs.exists == 'true' }} 97 | run: rm -rf samples 98 | 99 | - name: Commit 100 | if: ${{ steps.builder_file_exists.outputs.exists == 'true' }} 101 | id: commit 102 | uses: paketo-buildpacks/github-config/actions/pull-request/create-commit@main 103 | with: 104 | message: "Updating test data from samples" 105 | pathspec: "." 106 | keyid: ${{ secrets.PAKETO_BOT_GPG_SIGNING_KEY_ID }} 107 | key: ${{ secrets.PAKETO_BOT_GPG_SIGNING_KEY }} 108 | 109 | - name: Push Branch 110 | if: ${{ steps.commit.outputs.commit_sha != '' }} 111 | uses: paketo-buildpacks/github-config/actions/pull-request/push-branch@main 112 | with: 113 | branch: automation/testdata/update 114 | 115 | - name: Open Pull Request 116 | if: ${{ steps.commit.outputs.commit_sha != '' }} 117 | uses: paketo-buildpacks/github-config/actions/pull-request/open@main 118 | with: 119 | token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }} 120 | title: "Updates test data from samples" 121 | branch: automation/testdata/update 122 | label: "semver:patch" 123 | 124 | failure: 125 | name: Alert on Failure 126 | runs-on: ubuntu-24.04 127 | needs: [build] 128 | if: ${{ always() && needs.build.result == 'failure' }} 129 | steps: 130 | - name: File Failure Alert Issue 131 | uses: paketo-buildpacks/github-config/actions/issue/file@main 132 | with: 133 | token: ${{ secrets.GITHUB_TOKEN }} 134 | repo: ${{ github.repository }} 135 | label: "failure:update-github-config" 136 | comment_if_exists: true 137 | issue_title: "Failure: Update GitHub config workflow" 138 | issue_body: | 139 | Update GitHub config workflow [failed](https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}). 140 | comment_body: | 141 | Another failure occurred: https://github.com/${{github.repository}}/actions/runs/${{github.run_id}} 142 | -------------------------------------------------------------------------------- /smoke/testdata/java-native-image/.mvn/wrapper/MavenWrapperDownloader.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2007-present the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | import java.net.*; 17 | import java.io.*; 18 | import java.nio.channels.*; 19 | import java.util.Properties; 20 | 21 | public class MavenWrapperDownloader { 22 | 23 | private static final String WRAPPER_VERSION = "0.5.6"; 24 | /** 25 | * Default URL to download the maven-wrapper.jar from, if no 'downloadUrl' is provided. 26 | */ 27 | private static final String DEFAULT_DOWNLOAD_URL = "https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/" 28 | + WRAPPER_VERSION + "/maven-wrapper-" + WRAPPER_VERSION + ".jar"; 29 | 30 | /** 31 | * Path to the maven-wrapper.properties file, which might contain a downloadUrl property to 32 | * use instead of the default one. 33 | */ 34 | private static final String MAVEN_WRAPPER_PROPERTIES_PATH = 35 | ".mvn/wrapper/maven-wrapper.properties"; 36 | 37 | /** 38 | * Path where the maven-wrapper.jar will be saved to. 39 | */ 40 | private static final String MAVEN_WRAPPER_JAR_PATH = 41 | ".mvn/wrapper/maven-wrapper.jar"; 42 | 43 | /** 44 | * Name of the property which should be used to override the default download url for the wrapper. 45 | */ 46 | private static final String PROPERTY_NAME_WRAPPER_URL = "wrapperUrl"; 47 | 48 | public static void main(String args[]) { 49 | System.out.println("- Downloader started"); 50 | File baseDirectory = new File(args[0]); 51 | System.out.println("- Using base directory: " + baseDirectory.getAbsolutePath()); 52 | 53 | // If the maven-wrapper.properties exists, read it and check if it contains a custom 54 | // wrapperUrl parameter. 55 | File mavenWrapperPropertyFile = new File(baseDirectory, MAVEN_WRAPPER_PROPERTIES_PATH); 56 | String url = DEFAULT_DOWNLOAD_URL; 57 | if(mavenWrapperPropertyFile.exists()) { 58 | FileInputStream mavenWrapperPropertyFileInputStream = null; 59 | try { 60 | mavenWrapperPropertyFileInputStream = new FileInputStream(mavenWrapperPropertyFile); 61 | Properties mavenWrapperProperties = new Properties(); 62 | mavenWrapperProperties.load(mavenWrapperPropertyFileInputStream); 63 | url = mavenWrapperProperties.getProperty(PROPERTY_NAME_WRAPPER_URL, url); 64 | } catch (IOException e) { 65 | System.out.println("- ERROR loading '" + MAVEN_WRAPPER_PROPERTIES_PATH + "'"); 66 | } finally { 67 | try { 68 | if(mavenWrapperPropertyFileInputStream != null) { 69 | mavenWrapperPropertyFileInputStream.close(); 70 | } 71 | } catch (IOException e) { 72 | // Ignore ... 73 | } 74 | } 75 | } 76 | System.out.println("- Downloading from: " + url); 77 | 78 | File outputFile = new File(baseDirectory.getAbsolutePath(), MAVEN_WRAPPER_JAR_PATH); 79 | if(!outputFile.getParentFile().exists()) { 80 | if(!outputFile.getParentFile().mkdirs()) { 81 | System.out.println( 82 | "- ERROR creating output directory '" + outputFile.getParentFile().getAbsolutePath() + "'"); 83 | } 84 | } 85 | System.out.println("- Downloading to: " + outputFile.getAbsolutePath()); 86 | try { 87 | downloadFileFromURL(url, outputFile); 88 | System.out.println("Done"); 89 | System.exit(0); 90 | } catch (Throwable e) { 91 | System.out.println("- Error downloading"); 92 | e.printStackTrace(); 93 | System.exit(1); 94 | } 95 | } 96 | 97 | private static void downloadFileFromURL(String urlString, File destination) throws Exception { 98 | if (System.getenv("MVNW_USERNAME") != null && System.getenv("MVNW_PASSWORD") != null) { 99 | String username = System.getenv("MVNW_USERNAME"); 100 | char[] password = System.getenv("MVNW_PASSWORD").toCharArray(); 101 | Authenticator.setDefault(new Authenticator() { 102 | @Override 103 | protected PasswordAuthentication getPasswordAuthentication() { 104 | return new PasswordAuthentication(username, password); 105 | } 106 | }); 107 | } 108 | URL website = new URL(urlString); 109 | ReadableByteChannel rbc; 110 | rbc = Channels.newChannel(website.openStream()); 111 | FileOutputStream fos = new FileOutputStream(destination); 112 | fos.getChannel().transferFrom(rbc, 0, Long.MAX_VALUE); 113 | fos.close(); 114 | rbc.close(); 115 | } 116 | 117 | } 118 | -------------------------------------------------------------------------------- /smoke/web_servers_test.go: -------------------------------------------------------------------------------- 1 | package smoke_test 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "path/filepath" 7 | "testing" 8 | 9 | "github.com/paketo-buildpacks/occam" 10 | "github.com/sclevine/spec" 11 | 12 | . "github.com/onsi/gomega" 13 | . "github.com/paketo-buildpacks/occam/matchers" 14 | ) 15 | 16 | func testWebServers(t *testing.T, context spec.G, it spec.S) { 17 | var ( 18 | Expect = NewWithT(t).Expect 19 | Eventually = NewWithT(t).Eventually 20 | 21 | pack occam.Pack 22 | docker occam.Docker 23 | ) 24 | 25 | it.Before(func() { 26 | pack = occam.NewPack().WithVerbose().WithNoColor() 27 | docker = occam.NewDocker() 28 | }) 29 | 30 | context("detects a HTTPD app", func() { 31 | var ( 32 | image occam.Image 33 | container occam.Container 34 | 35 | name string 36 | source string 37 | ) 38 | 39 | it.Before(func() { 40 | var err error 41 | name, err = occam.RandomName() 42 | Expect(err).NotTo(HaveOccurred()) 43 | }) 44 | 45 | it.After(func() { 46 | Expect(docker.Container.Remove.Execute(container.ID)).To(Succeed()) 47 | Expect(docker.Volume.Remove.Execute(occam.CacheVolumeNames(name))).To(Succeed()) 48 | Expect(docker.Image.Remove.Execute(image.ID)).To(Succeed()) 49 | Expect(os.RemoveAll(source)).To(Succeed()) 50 | }) 51 | 52 | it("builds successfully", func() { 53 | var err error 54 | source, err = occam.Source(filepath.Join("testdata", "httpd")) 55 | Expect(err).NotTo(HaveOccurred()) 56 | 57 | var logs fmt.Stringer 58 | image, logs, err = pack.Build. 59 | WithPullPolicy("always"). 60 | WithBuilder(Builder). 61 | Execute(name, source) 62 | Expect(err).ToNot(HaveOccurred(), logs.String) 63 | 64 | container, err = docker.Container.Run. 65 | WithEnv(map[string]string{"PORT": "8080"}). 66 | WithPublish("8080"). 67 | Execute(image.ID) 68 | Expect(err).NotTo(HaveOccurred()) 69 | 70 | Eventually(container).Should(BeAvailable()) 71 | 72 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for Apache HTTP Server"))) 73 | }) 74 | }) 75 | 76 | context("detects a NGINX app", func() { 77 | var ( 78 | image occam.Image 79 | container occam.Container 80 | 81 | name string 82 | source string 83 | ) 84 | 85 | it.Before(func() { 86 | var err error 87 | name, err = occam.RandomName() 88 | Expect(err).NotTo(HaveOccurred()) 89 | }) 90 | 91 | it.After(func() { 92 | Expect(docker.Container.Remove.Execute(container.ID)).To(Succeed()) 93 | Expect(docker.Volume.Remove.Execute(occam.CacheVolumeNames(name))).To(Succeed()) 94 | Expect(docker.Image.Remove.Execute(image.ID)).To(Succeed()) 95 | Expect(os.RemoveAll(source)).To(Succeed()) 96 | }) 97 | 98 | it("builds successfully", func() { 99 | var err error 100 | source, err = occam.Source(filepath.Join("testdata", "nginx")) 101 | Expect(err).NotTo(HaveOccurred()) 102 | 103 | var logs fmt.Stringer 104 | image, logs, err = pack.Build. 105 | WithPullPolicy("always"). 106 | WithBuilder(Builder). 107 | Execute(name, source) 108 | Expect(err).ToNot(HaveOccurred(), logs.String) 109 | 110 | container, err = docker.Container.Run. 111 | WithEnv(map[string]string{"PORT": "8080"}). 112 | WithPublish("8080"). 113 | Execute(image.ID) 114 | Expect(err).NotTo(HaveOccurred()) 115 | 116 | Eventually(container).Should(BeAvailable()) 117 | 118 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for Nginx Server"))) 119 | }) 120 | }) 121 | 122 | context("detects a JavaScript frontend app", func() { 123 | var ( 124 | image occam.Image 125 | container occam.Container 126 | 127 | name string 128 | source string 129 | ) 130 | 131 | it.Before(func() { 132 | var err error 133 | name, err = occam.RandomName() 134 | Expect(err).NotTo(HaveOccurred()) 135 | }) 136 | 137 | it.After(func() { 138 | Expect(docker.Container.Remove.Execute(container.ID)).To(Succeed()) 139 | Expect(docker.Volume.Remove.Execute(occam.CacheVolumeNames(name))).To(Succeed()) 140 | Expect(docker.Image.Remove.Execute(image.ID)).To(Succeed()) 141 | Expect(os.RemoveAll(source)).To(Succeed()) 142 | }) 143 | 144 | context("app uses react and httpd", func() { 145 | it("builds successfully", func() { 146 | var err error 147 | source, err = occam.Source(filepath.Join("testdata", "javascript-frontend")) 148 | Expect(err).NotTo(HaveOccurred()) 149 | 150 | var logs fmt.Stringer 151 | image, logs, err = pack.Build. 152 | WithPullPolicy("always"). 153 | WithBuilder(Builder). 154 | WithEnv(map[string]string{ 155 | "BP_NODE_RUN_SCRIPTS": "build", 156 | "BP_WEB_SERVER": "httpd", 157 | "BP_WEB_SERVER_ROOT": "build", 158 | "BP_WEB_SERVER_ENABLE_PUSH_STATE": "true", 159 | }). 160 | Execute(name, source) 161 | Expect(err).ToNot(HaveOccurred(), logs.String) 162 | 163 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for Node Engine"))) 164 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for NPM Install"))) 165 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for Node Run Script"))) 166 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for Apache HTTP Server"))) 167 | 168 | container, err = docker.Container.Run. 169 | WithEnv(map[string]string{"PORT": "8080"}). 170 | WithPublish("8080"). 171 | Execute(image.ID) 172 | Expect(err).NotTo(HaveOccurred()) 173 | 174 | Eventually(container).Should(Serve(ContainSubstring("Paketo Buildpacks")).OnPort(8080)) 175 | }) 176 | }) 177 | 178 | context("app uses react and nginx", func() { 179 | it("builds successfully", func() { 180 | var err error 181 | source, err = occam.Source(filepath.Join("testdata", "javascript-frontend")) 182 | Expect(err).NotTo(HaveOccurred()) 183 | 184 | var logs fmt.Stringer 185 | image, logs, err = pack.Build. 186 | WithPullPolicy("always"). 187 | WithBuilder(Builder). 188 | WithEnv(map[string]string{ 189 | "BP_NODE_RUN_SCRIPTS": "build", 190 | "BP_WEB_SERVER": "nginx", 191 | "BP_WEB_SERVER_ROOT": "build", 192 | "BP_WEB_SERVER_ENABLE_PUSH_STATE": "true", 193 | }). 194 | Execute(name, source) 195 | Expect(err).ToNot(HaveOccurred(), logs.String) 196 | 197 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for Node Engine"))) 198 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for NPM Install"))) 199 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for Node Run Script"))) 200 | Expect(logs).To(ContainLines(ContainSubstring("Paketo Buildpack for Nginx Server"))) 201 | 202 | container, err = docker.Container.Run. 203 | WithEnv(map[string]string{"PORT": "8080"}). 204 | WithPublish("8080"). 205 | Execute(image.ID) 206 | Expect(err).NotTo(HaveOccurred()) 207 | 208 | Eventually(container).Should(Serve(ContainSubstring("Paketo Buildpacks")).OnPort(8080)) 209 | }) 210 | }) 211 | }) 212 | } 213 | -------------------------------------------------------------------------------- /scripts/.util/tools.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eu 4 | set -o pipefail 5 | 6 | # shellcheck source=SCRIPTDIR/print.sh 7 | source "$(dirname "${BASH_SOURCE[0]}")/print.sh" 8 | 9 | function util::tools::os() { 10 | case "$(uname)" in 11 | "Darwin") 12 | echo "${1:-darwin}" 13 | ;; 14 | 15 | "Linux") 16 | echo "linux" 17 | ;; 18 | 19 | *) 20 | util::print::error "Unknown OS \"$(uname)\"" 21 | exit 1 22 | esac 23 | } 24 | 25 | function util::tools::arch() { 26 | case "$(uname -m)" in 27 | arm64|aarch64) 28 | echo "arm64" 29 | ;; 30 | 31 | amd64|x86_64) 32 | if [[ "${1:-}" == "--blank-amd64" ]]; then 33 | echo "" 34 | elif [[ "${1:-}" == "--uname-format-amd64" ]]; then 35 | echo "x86_64" 36 | else 37 | echo "amd64" 38 | fi 39 | ;; 40 | 41 | *) 42 | util::print::error "Unknown Architecture \"$(uname -m)\"" 43 | exit 1 44 | esac 45 | } 46 | 47 | function util::tools::path::export() { 48 | local dir 49 | dir="${1}" 50 | 51 | if ! echo "${PATH}" | grep -q "${dir}"; then 52 | PATH="${dir}:$PATH" 53 | export PATH 54 | fi 55 | } 56 | 57 | function util::tools::jam::install() { 58 | local dir token 59 | token="" 60 | 61 | while [[ "${#}" != 0 ]]; do 62 | case "${1}" in 63 | --directory) 64 | dir="${2}" 65 | shift 2 66 | ;; 67 | 68 | --token) 69 | token="${2}" 70 | shift 2 71 | ;; 72 | 73 | *) 74 | util::print::error "unknown argument \"${1}\"" 75 | esac 76 | done 77 | 78 | mkdir -p "${dir}" 79 | util::tools::path::export "${dir}" 80 | 81 | if [[ ! -f "${dir}/jam" ]]; then 82 | local version curl_args os arch 83 | 84 | version="$(jq -r .jam "$(dirname "${BASH_SOURCE[0]}")/tools.json")" 85 | 86 | curl_args=( 87 | "--fail" 88 | "--silent" 89 | "--location" 90 | "--output" "${dir}/jam" 91 | ) 92 | 93 | if [[ "${token}" != "" ]]; then 94 | curl_args+=("--header" "Authorization: Token ${token}") 95 | fi 96 | 97 | util::print::title "Installing jam ${version}" 98 | 99 | os=$(util::tools::os) 100 | arch=$(util::tools::arch) 101 | 102 | curl "https://github.com/paketo-buildpacks/jam/releases/download/${version}/jam-${os}-${arch}" \ 103 | "${curl_args[@]}" 104 | 105 | chmod +x "${dir}/jam" 106 | else 107 | util::print::info "Using $("${dir}"/jam version)" 108 | fi 109 | } 110 | 111 | function util::tools::pack::install() { 112 | local dir token 113 | token="" 114 | 115 | while [[ "${#}" != 0 ]]; do 116 | case "${1}" in 117 | --directory) 118 | dir="${2}" 119 | shift 2 120 | ;; 121 | 122 | --token) 123 | token="${2}" 124 | shift 2 125 | ;; 126 | 127 | *) 128 | util::print::error "unknown argument \"${1}\"" 129 | esac 130 | done 131 | 132 | mkdir -p "${dir}" 133 | util::tools::path::export "${dir}" 134 | 135 | if [[ ! -f "${dir}/pack" ]]; then 136 | local version curl_args os arch 137 | 138 | version="$(jq -r .pack "$(dirname "${BASH_SOURCE[0]}")/tools.json")" 139 | 140 | local pack_config_enable_experimental 141 | if [ -f "$(dirname "${BASH_SOURCE[0]}")/../options.json" ]; then 142 | pack_config_enable_experimental="$(jq -r .pack_config_enable_experimental "$(dirname "${BASH_SOURCE[0]}")/../options.json")" 143 | else 144 | pack_config_enable_experimental="false" 145 | fi 146 | 147 | tmp_location="/tmp/pack.tgz" 148 | curl_args=( 149 | "--fail" 150 | "--silent" 151 | "--location" 152 | "--output" "${tmp_location}" 153 | ) 154 | 155 | if [[ "${token}" != "" ]]; then 156 | curl_args+=("--header" "Authorization: Token ${token}") 157 | fi 158 | 159 | util::print::title "Installing pack ${version}" 160 | 161 | os=$(util::tools::os macos) 162 | arch=$(util::tools::arch --blank-amd64) 163 | 164 | curl "https://github.com/buildpacks/pack/releases/download/${version}/pack-${version}-${os}${arch:+-$arch}.tgz" \ 165 | "${curl_args[@]}" 166 | 167 | tar xzf "${tmp_location}" -C "${dir}" 168 | chmod +x "${dir}/pack" 169 | 170 | if [[ "${pack_config_enable_experimental}" == "true" ]]; then 171 | "${dir}"/pack config experimental true 172 | fi 173 | 174 | rm "${tmp_location}" 175 | else 176 | util::print::info "Using pack $("${dir}"/pack version)" 177 | fi 178 | } 179 | 180 | function util::tools::tests::checkfocus() { 181 | testout="${1}" 182 | if grep -q 'Focused: [1-9]' "${testout}"; then 183 | echo "Detected Focused Test(s) - setting exit code to 197" 184 | rm "${testout}" 185 | util::print::success "** GO Test Succeeded **" 197 186 | fi 187 | rm "${testout}" 188 | } 189 | 190 | function util::tools::crane::install() { 191 | local dir token 192 | token="" 193 | 194 | while [[ "${#}" != 0 ]]; do 195 | case "${1}" in 196 | --directory) 197 | dir="${2}" 198 | shift 2 199 | ;; 200 | 201 | --token) 202 | token="${2}" 203 | shift 2 204 | ;; 205 | 206 | *) 207 | util::print::error "unknown argument \"${1}\"" 208 | esac 209 | done 210 | 211 | mkdir -p "${dir}" 212 | util::tools::path::export "${dir}" 213 | 214 | if [[ ! -f "${dir}/crane" ]]; then 215 | local version curl_args os arch 216 | 217 | version="$(jq -r .crane "$(dirname "${BASH_SOURCE[0]}")/tools.json")" 218 | 219 | curl_args=( 220 | "--fail" 221 | "--silent" 222 | "--location" 223 | ) 224 | 225 | if [[ "${token}" != "" ]]; then 226 | curl_args+=("--header" "Authorization: Token ${token}") 227 | fi 228 | 229 | util::print::title "Installing crane ${version}" 230 | 231 | os=$(util::tools::os) 232 | arch=$(util::tools::arch --uname-format-amd64) 233 | 234 | 235 | curl "https://github.com/google/go-containerregistry/releases/download/${version}/go-containerregistry_Linux_${arch}.tar.gz" \ 236 | "${curl_args[@]}" | tar -C "${dir}" -xz crane 237 | 238 | chmod +x "${dir}/crane" 239 | fi 240 | } 241 | 242 | 243 | # Returns a random unused port 244 | function get::random::port() { 245 | local port=$(shuf -i 50000-65000 -n 1) 246 | netstat -lat | grep $port > /dev/null 247 | if [[ $? == 1 ]] ; then 248 | echo $port 249 | else 250 | echo get::random::port 251 | fi 252 | } 253 | 254 | # Starts a local registry on the given port and returns the pid 255 | function local::registry::start() { 256 | local registryPort registryPid localRegistry 257 | 258 | registryPort="$1" 259 | localRegistry="127.0.0.1:$registryPort" 260 | 261 | # Start a local in-memory registry so we can work with oci archives 262 | PORT=$registryPort crane registry serve --insecure > /dev/null 2>&1 & 263 | registryPid=$! 264 | 265 | # Stop the registry if execution is interrupted 266 | trap "kill $registryPid" 1 2 3 6 267 | 268 | # Wait for the registry to be available 269 | until crane catalog $localRegistry > /dev/null 2>&1; do 270 | sleep 1 271 | done 272 | 273 | echo $registryPid 274 | } -------------------------------------------------------------------------------- /smoke/testdata/java-native-image/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 50 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 124 | 125 | FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 162 | if ERRORLEVEL 1 goto error 163 | goto end 164 | 165 | :error 166 | set ERROR_CODE=1 167 | 168 | :end 169 | @endlocal & set ERROR_CODE=%ERROR_CODE% 170 | 171 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 172 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 173 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 174 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 175 | :skipRcPost 176 | 177 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 178 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 179 | 180 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 181 | 182 | exit /B %ERROR_CODE% 183 | -------------------------------------------------------------------------------- /scripts/smoke.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -eu 4 | set -o pipefail 5 | 6 | readonly PROGDIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" 7 | readonly ROOTDIR="$(cd "${PROGDIR}/.." && pwd)" 8 | readonly OPTIONS_JSON="${ROOTDIR}/scripts/options.json" 9 | 10 | # shellcheck source=SCRIPTDIR/.util/tools.sh 11 | source "${PROGDIR}/.util/tools.sh" 12 | 13 | # shellcheck source=SCRIPTDIR/.util/print.sh 14 | source "${PROGDIR}/.util/print.sh" 15 | 16 | function main() { 17 | local name token 18 | local registryPort registryPid localRegistryUrl pushBuilderToLocalRegistry 19 | local builderDir builderArray targetPlatform 20 | token="" 21 | registryPid="" 22 | builderDir="" 23 | builderArray=() 24 | targetPlatform="" 25 | localRegistryUrl="" 26 | pushBuilderToLocalRegistry="" 27 | 28 | while [[ "${#}" != 0 ]]; do 29 | case "${1}" in 30 | --help|-h) 31 | shift 1 32 | usage 33 | exit 0 34 | ;; 35 | 36 | --name|-n) 37 | name="${2}" 38 | shift 2 39 | ;; 40 | 41 | --builder-dir) 42 | builderDir="${2}" 43 | shift 2 44 | ;; 45 | 46 | --target-platform) 47 | targetPlatform="${2}" 48 | shift 2 49 | ;; 50 | 51 | --local-registry-url) 52 | localRegistryUrl="${2}" 53 | shift 2 54 | ;; 55 | 56 | --push-builder-to-local-registry) 57 | pushBuilderToLocalRegistry="${2}" 58 | shift 2 59 | ;; 60 | 61 | --token|-t) 62 | token="${2}" 63 | shift 2 64 | ;; 65 | 66 | "") 67 | # skip if the argument is empty 68 | shift 1 69 | ;; 70 | 71 | *) 72 | util::print::error "unknown argument \"${1}\"" 73 | esac 74 | done 75 | 76 | if [[ -z "${targetPlatform}" ]]; then 77 | os=$(util::tools::os macos) 78 | arch=$(util::tools::arch) 79 | targetPlatform="${os}/${arch}" 80 | fi 81 | 82 | if [ -n "${builderDir}" ]; then 83 | if [[ -z "${name:-}" ]]; then 84 | name="testbuilder" 85 | util::print::info "Using default name for the builder: ${name}" 86 | fi 87 | builderArray+=($(jq -n --arg name "$name" --arg path "$builderDir" '{name: $name, path: $path}' | jq -c '.')) 88 | elif [[ -f ${ROOTDIR}/builders.json ]]; then 89 | builderArray=($(jq -r -c '.builders[]' ${ROOTDIR}/builders.json)) 90 | else 91 | if [[ -z "${name:-}" ]]; then 92 | name="testbuilder" 93 | util::print::info "Using default name for the builder: ${name}" 94 | fi 95 | util::print::info "Using current directory as the builder directory." 96 | builderArray+=($(jq -n --arg name "$name" --arg path "$ROOTDIR" '{name: $name, path: $path}' | jq -c '.')) 97 | fi 98 | 99 | util::print::info "Found the following builder directories:" 100 | for builder in "${builderArray[@]}"; do 101 | builderName=$(jq -r '.name' <<<"${builder}") 102 | builderPath=$(jq -r '.path' <<<"${builder}") 103 | util::print::info " - Name: ${builderName}, Path: ${builderPath}" 104 | done 105 | 106 | tools::install "${token}" 107 | 108 | if [ "${pushBuilderToLocalRegistry}" != "" ]; then 109 | if [[ "${pushBuilderToLocalRegistry}" != "true" && "${pushBuilderToLocalRegistry}" != "false" ]]; then 110 | util::print::error "--push-builder-to-local-registry must be 'true' or 'false'" 111 | fi 112 | util::print::info "Using command line argument for push_builder_to_local_registry: ${pushBuilderToLocalRegistry}" 113 | elif [ -f $OPTIONS_JSON ]; then 114 | pushBuilderToLocalRegistry="$(jq -r '.push_builder_to_local_registry //false' $OPTIONS_JSON)" 115 | else 116 | pushBuilderToLocalRegistry="false" 117 | fi 118 | 119 | trap 'cleanup "$registryPid" "$builderName"' EXIT 120 | 121 | # Set up local registry to push the builder(s) 122 | if [[ "${pushBuilderToLocalRegistry}" == "true" && "${localRegistryUrl}" == "" ]]; then 123 | registryPort=$(get::random::port) 124 | registryPid=$(local::registry::start $registryPort) 125 | localRegistryUrl="127.0.0.1:$registryPort" 126 | util::print::info "Started local registry at ${localRegistryUrl} with PID ${registryPid}" 127 | fi 128 | 129 | local testout 130 | testout=$(mktemp) 131 | for builderDir in "${builderArray[@]}"; do 132 | builderName=$(jq -r '.name' <<<"${builderDir}") 133 | builderDir=$(jq -r '.path' <<<"${builderDir}") 134 | 135 | util::print::info "Running tests for builder: ${builderName}" 136 | 137 | if [[ ! -d "${builderDir}" ]]; then 138 | util::print::error "Builder directory ${builderDir} does not exist." 139 | fi 140 | 141 | if [[ ! -d "${builderDir}/smoke" ]]; then 142 | util::print::warn "** WARNING No Smoke tests **" 143 | fi 144 | 145 | builder::create "${builderName}" "${builderDir}" "${localRegistryUrl}" "${targetPlatform}" 146 | 147 | if [[ "${pushBuilderToLocalRegistry}" == "true" ]]; then 148 | builderName="$localRegistryUrl/$builderName" 149 | else 150 | builderName="$builderName" 151 | fi 152 | 153 | image::pull::lifecycle "${builderName}" "${pushBuilderToLocalRegistry}" 154 | 155 | tests::run "${builderName}" "${testout}" 156 | done 157 | 158 | util::tools::tests::checkfocus "${testout}" 159 | util::print::success "** GO Test Succeeded for all builders**" 160 | } 161 | 162 | function cleanup (){ 163 | local registryPid builderName 164 | registryPid="${1}" 165 | builderName="${2}" 166 | 167 | if [[ "${registryPid}" != "" ]]; then 168 | kill "${registryPid}" 169 | fi 170 | if [[ "${builderName}" != "" ]]; then 171 | docker rmi "${builderName}" || true 172 | fi 173 | } 174 | 175 | function usage() { 176 | cat <<-USAGE 177 | smoke.sh [OPTIONS] 178 | 179 | Runs the smoke test suite. 180 | 181 | OPTIONS 182 | --help -h prints the command usage 183 | --name -n sets the name of the builder that is built for testing 184 | --token token used to download assets from GitHub (e.g. jam, pack, etc) (optional) 185 | --builder-dir sets the directory of the builder to test. Defaults to the current directory. 186 | --target-platform sets the target platform to build the builder image for. E.g. linux/amd64, linux/arm64, etc. 187 | Defaults host machine's OS/arch. 188 | --local-registry-url sets the local registry URL to push the builder image to. E.g. 127.0.0.1:5000 189 | --push-builder-to-local-registry if "true", pushes the builder image to a local registry. 190 | Defaults value false or the value in scripts/options.json if the file exists. 191 | Note: if true and --local-registry-url is not set, a local registry will be started on a random port. 192 | USAGE 193 | } 194 | 195 | function tools::install() { 196 | local token 197 | token="${1}" 198 | 199 | util::tools::crane::install \ 200 | --directory "${ROOTDIR}/.bin" \ 201 | --token "${token}" 202 | 203 | util::tools::pack::install \ 204 | --directory "${ROOTDIR}/.bin" \ 205 | --token "${token}" 206 | } 207 | 208 | function builder::create() { 209 | local builderName path localRegistryUrl 210 | builderName="${1}" 211 | path="${2}" 212 | localRegistryUrl="${3:-}" 213 | targetPlatform="${4}" 214 | 215 | if [[ "${localRegistryUrl}" != "" ]]; then 216 | util::print::title "Creating ${localRegistryUrl}/${builderName} builder image..." 217 | pack builder create "${localRegistryUrl}/${builderName}" --config "${path}/builder.toml" --target "${targetPlatform}" --publish 218 | else 219 | util::print::title "Creating ${builderName} builder image..." 220 | pack builder create "${builderName}" --config "${path}/builder.toml" --target "${targetPlatform}" 221 | fi 222 | } 223 | 224 | function image::pull::lifecycle() { 225 | local name lifecycle_image pushBuilderToLocalRegistry 226 | name="${1}" 227 | pushBuilderToLocalRegistry="${2:-false}" 228 | 229 | if [[ "${pushBuilderToLocalRegistry}" == "true" ]]; then 230 | lifecycle_image="index.docker.io/buildpacksio/lifecycle:$( 231 | pack builder inspect "${name}" --output json \ 232 | | jq -r '.remote_info.lifecycle.version' 233 | )" 234 | else 235 | lifecycle_image="index.docker.io/buildpacksio/lifecycle:$( 236 | pack builder inspect "${name}" --output json \ 237 | | jq -r '.local_info.lifecycle.version' 238 | )" 239 | fi 240 | 241 | util::print::title "Pulling lifecycle image..." 242 | docker pull "${lifecycle_image}" 243 | } 244 | 245 | function tests::run() { 246 | local name 247 | name="${1}" 248 | 249 | util::print::title "Run Builder Smoke Tests for ${name}..." 250 | 251 | export CGO_ENABLED=0 252 | pushd "${builderDir}" > /dev/null 253 | if GOMAXPROCS="${GOMAXPROCS:-4}" go test -count=1 -timeout 0 ./smoke/... -v -run Smoke --name "${name}" | tee "${testout}"; then 254 | util::print::info "** GO Test Succeeded with ${name} **" 255 | else 256 | util::print::error "** GO Test Failed with ${name} **" 257 | fi 258 | popd > /dev/null 259 | } 260 | 261 | main "${@:-}" 262 | -------------------------------------------------------------------------------- /smoke/testdata/java-native-image/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # https://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /etc/mavenrc ] ; then 40 | . /etc/mavenrc 41 | fi 42 | 43 | if [ -f "$HOME/.mavenrc" ] ; then 44 | . "$HOME/.mavenrc" 45 | fi 46 | 47 | fi 48 | 49 | # OS specific support. $var _must_ be set to either true or false. 50 | cygwin=false; 51 | darwin=false; 52 | mingw=false 53 | case "`uname`" in 54 | CYGWIN*) cygwin=true ;; 55 | MINGW*) mingw=true;; 56 | Darwin*) darwin=true 57 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 58 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 59 | if [ -z "$JAVA_HOME" ]; then 60 | if [ -x "/usr/libexec/java_home" ]; then 61 | export JAVA_HOME="`/usr/libexec/java_home`" 62 | else 63 | export JAVA_HOME="/Library/Java/Home" 64 | fi 65 | fi 66 | ;; 67 | esac 68 | 69 | if [ -z "$JAVA_HOME" ] ; then 70 | if [ -r /etc/gentoo-release ] ; then 71 | JAVA_HOME=`java-config --jre-home` 72 | fi 73 | fi 74 | 75 | if [ -z "$M2_HOME" ] ; then 76 | ## resolve links - $0 may be a link to maven's home 77 | PRG="$0" 78 | 79 | # need this for relative symlinks 80 | while [ -h "$PRG" ] ; do 81 | ls=`ls -ld "$PRG"` 82 | link=`expr "$ls" : '.*-> \(.*\)$'` 83 | if expr "$link" : '/.*' > /dev/null; then 84 | PRG="$link" 85 | else 86 | PRG="`dirname "$PRG"`/$link" 87 | fi 88 | done 89 | 90 | saveddir=`pwd` 91 | 92 | M2_HOME=`dirname "$PRG"`/.. 93 | 94 | # make it fully qualified 95 | M2_HOME=`cd "$M2_HOME" && pwd` 96 | 97 | cd "$saveddir" 98 | # echo Using m2 at $M2_HOME 99 | fi 100 | 101 | # For Cygwin, ensure paths are in UNIX format before anything is touched 102 | if $cygwin ; then 103 | [ -n "$M2_HOME" ] && 104 | M2_HOME=`cygpath --unix "$M2_HOME"` 105 | [ -n "$JAVA_HOME" ] && 106 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 107 | [ -n "$CLASSPATH" ] && 108 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 109 | fi 110 | 111 | # For Mingw, ensure paths are in UNIX format before anything is touched 112 | if $mingw ; then 113 | [ -n "$M2_HOME" ] && 114 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 115 | [ -n "$JAVA_HOME" ] && 116 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 117 | fi 118 | 119 | if [ -z "$JAVA_HOME" ]; then 120 | javaExecutable="`which javac`" 121 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 122 | # readlink(1) is not available as standard on Solaris 10. 123 | readLink=`which readlink` 124 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 125 | if $darwin ; then 126 | javaHome="`dirname \"$javaExecutable\"`" 127 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 128 | else 129 | javaExecutable="`readlink -f \"$javaExecutable\"`" 130 | fi 131 | javaHome="`dirname \"$javaExecutable\"`" 132 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 133 | JAVA_HOME="$javaHome" 134 | export JAVA_HOME 135 | fi 136 | fi 137 | fi 138 | 139 | if [ -z "$JAVACMD" ] ; then 140 | if [ -n "$JAVA_HOME" ] ; then 141 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 142 | # IBM's JDK on AIX uses strange locations for the executables 143 | JAVACMD="$JAVA_HOME/jre/sh/java" 144 | else 145 | JAVACMD="$JAVA_HOME/bin/java" 146 | fi 147 | else 148 | JAVACMD="`which java`" 149 | fi 150 | fi 151 | 152 | if [ ! -x "$JAVACMD" ] ; then 153 | echo "Error: JAVA_HOME is not defined correctly." >&2 154 | echo " We cannot execute $JAVACMD" >&2 155 | exit 1 156 | fi 157 | 158 | if [ -z "$JAVA_HOME" ] ; then 159 | echo "Warning: JAVA_HOME environment variable is not set." 160 | fi 161 | 162 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 163 | 164 | # traverses directory structure from process work directory to filesystem root 165 | # first directory with .mvn subdirectory is considered project base directory 166 | find_maven_basedir() { 167 | 168 | if [ -z "$1" ] 169 | then 170 | echo "Path not specified to find_maven_basedir" 171 | return 1 172 | fi 173 | 174 | basedir="$1" 175 | wdir="$1" 176 | while [ "$wdir" != '/' ] ; do 177 | if [ -d "$wdir"/.mvn ] ; then 178 | basedir=$wdir 179 | break 180 | fi 181 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 182 | if [ -d "${wdir}" ]; then 183 | wdir=`cd "$wdir/.."; pwd` 184 | fi 185 | # end of workaround 186 | done 187 | echo "${basedir}" 188 | } 189 | 190 | # concatenates all lines of a file 191 | concat_lines() { 192 | if [ -f "$1" ]; then 193 | echo "$(tr -s '\n' ' ' < "$1")" 194 | fi 195 | } 196 | 197 | BASE_DIR=`find_maven_basedir "$(pwd)"` 198 | if [ -z "$BASE_DIR" ]; then 199 | exit 1; 200 | fi 201 | 202 | ########################################################################################## 203 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 204 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 205 | ########################################################################################## 206 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then 207 | if [ "$MVNW_VERBOSE" = true ]; then 208 | echo "Found .mvn/wrapper/maven-wrapper.jar" 209 | fi 210 | else 211 | if [ "$MVNW_VERBOSE" = true ]; then 212 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." 213 | fi 214 | if [ -n "$MVNW_REPOURL" ]; then 215 | jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 216 | else 217 | jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar" 218 | fi 219 | while IFS="=" read key value; do 220 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;; 221 | esac 222 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" 223 | if [ "$MVNW_VERBOSE" = true ]; then 224 | echo "Downloading from: $jarUrl" 225 | fi 226 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" 227 | if $cygwin; then 228 | wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` 229 | fi 230 | 231 | if command -v wget > /dev/null; then 232 | if [ "$MVNW_VERBOSE" = true ]; then 233 | echo "Found wget ... using wget" 234 | fi 235 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 236 | wget "$jarUrl" -O "$wrapperJarPath" 237 | else 238 | wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" 239 | fi 240 | elif command -v curl > /dev/null; then 241 | if [ "$MVNW_VERBOSE" = true ]; then 242 | echo "Found curl ... using curl" 243 | fi 244 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 245 | curl -o "$wrapperJarPath" "$jarUrl" -f 246 | else 247 | curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f 248 | fi 249 | 250 | else 251 | if [ "$MVNW_VERBOSE" = true ]; then 252 | echo "Falling back to using Java to download" 253 | fi 254 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" 255 | # For Cygwin, switch paths to Windows format before running javac 256 | if $cygwin; then 257 | javaClass=`cygpath --path --windows "$javaClass"` 258 | fi 259 | if [ -e "$javaClass" ]; then 260 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 261 | if [ "$MVNW_VERBOSE" = true ]; then 262 | echo " - Compiling MavenWrapperDownloader.java ..." 263 | fi 264 | # Compiling the Java class 265 | ("$JAVA_HOME/bin/javac" "$javaClass") 266 | fi 267 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 268 | # Running the downloader 269 | if [ "$MVNW_VERBOSE" = true ]; then 270 | echo " - Running MavenWrapperDownloader.java ..." 271 | fi 272 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") 273 | fi 274 | fi 275 | fi 276 | fi 277 | ########################################################################################## 278 | # End of extension 279 | ########################################################################################## 280 | 281 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 282 | if [ "$MVNW_VERBOSE" = true ]; then 283 | echo $MAVEN_PROJECTBASEDIR 284 | fi 285 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 286 | 287 | # For Cygwin, switch paths to Windows format before running java 288 | if $cygwin; then 289 | [ -n "$M2_HOME" ] && 290 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 291 | [ -n "$JAVA_HOME" ] && 292 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 293 | [ -n "$CLASSPATH" ] && 294 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 295 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 296 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 297 | fi 298 | 299 | # Provide a "standardized" way to retrieve the CLI args that will 300 | # work with both Windows and non-Windows executions. 301 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 302 | export MAVEN_CMD_LINE_ARGS 303 | 304 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 305 | 306 | exec "$JAVACMD" \ 307 | $MAVEN_OPTS \ 308 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 309 | "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 310 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 311 | -------------------------------------------------------------------------------- /.github/workflows/create-release.yml: -------------------------------------------------------------------------------- 1 | name: Create Release 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | concurrency: release 9 | 10 | env: 11 | BUILDERS_FILEPATH: "builders.json" 12 | 13 | jobs: 14 | preparation: 15 | name: Preparation 16 | runs-on: ubuntu-24.04 17 | outputs: 18 | builders: ${{ steps.get-builders.outputs.builders }} 19 | combos: ${{ steps.get_combos.outputs.combos }} 20 | steps: 21 | - name: Checkout 22 | uses: actions/checkout@v6 23 | 24 | - name: Get Builders 25 | id: get-builders 26 | run: | 27 | builders=$(jq -n -c '[]') 28 | 29 | if [ -f ${{ env.BUILDERS_FILEPATH }} ]; then 30 | builders=$(jq -c '.builders' ${{ env.BUILDERS_FILEPATH }}) 31 | else 32 | # Strip off the Github org prefix from repo name 33 | # paketo-buildpacks/builder-with-some-name --> builder-with-some-name 34 | registry_repo=$(echo "${{ github.repository }}" | sed 's/^.*\///') 35 | builders=$(jq -n -c '[ 36 | { 37 | "name": "'"${registry_repo}"'", 38 | "path": ".", 39 | "container_repository": "'"${registry_repo}"'", 40 | "test_runners": ["ubuntu-24.04"] 41 | } 42 | ]') 43 | fi 44 | 45 | # Filter only the necessary fields 46 | builders=$(echo "$builders" | jq 'map({ 47 | name, 48 | path, 49 | container_repository, 50 | test_runners 51 | })') 52 | 53 | builders=$(jq -c <<< "$builders") 54 | echo "builders=$builders" 55 | echo "builders=$builders" >> "$GITHUB_OUTPUT" 56 | 57 | - name: Get Combos 58 | id: get_combos 59 | run: | 60 | builders=$(echo '${{ steps.get-builders.outputs.builders }}' | jq -c '.[]') 61 | combos=$(jq -n -c '[]') 62 | 63 | for builder in $builders; do 64 | runners=$(echo $builder | jq -r '.test_runners[]') 65 | builder_name=$(echo $builder | jq -r '.name') 66 | builder_path=$(echo $builder | jq -r '.path') 67 | for runner in $runners; do 68 | combos=$( 69 | jq \ 70 | --arg builder_name "$builder_name" \ 71 | --arg runner "$runner" \ 72 | --arg builder_path "$builder_path" \ 73 | '. + [{"builder": $builder_name, "runner": $runner, "path": $builder_path}]' \ 74 | <<< "$combos" 75 | ) 76 | done 77 | done 78 | 79 | combos=$(jq -c <<< "$combos") 80 | echo "combos=$combos" 81 | echo "combos=$combos" >> "$GITHUB_OUTPUT" 82 | 83 | builder_files_changed: 84 | name: Builder Files Changed 85 | runs-on: ubuntu-24.04 86 | needs: preparation 87 | outputs: 88 | builders_changed: ${{ steps.compare_previous_release.outputs.builders_changed }} 89 | steps: 90 | - name: Checkout 91 | uses: actions/checkout@v6 92 | with: 93 | fetch-depth: 0 # gets full history 94 | 95 | - name: Compare previous releases 96 | id: compare_previous_release 97 | run: | 98 | builders=$(echo '${{ needs.preparation.outputs.builders }}' | jq -c '.[]') 99 | for builder in $builders; do 100 | builder_path=$(echo "$builder" | jq -r '.path') 101 | 102 | changed=$(git diff --name-only $(git describe --tags --abbrev=0) -- $builder_path/builder.toml) 103 | if [ -z "${changed}" ] 104 | then 105 | # We do not write on the github output because this step runs multiple times 106 | # and it might overwrite any true value written by the else statement. 107 | echo "No changes for $builder_path/builder.toml" 108 | else 109 | echo "Changes detected for $builder_path/builder.toml" 110 | echo "builders_changed=true" >> $GITHUB_OUTPUT 111 | fi 112 | done 113 | 114 | smoke: 115 | name: Smoke Test 116 | needs: [preparation, builder_files_changed] 117 | if: ${{ needs.builder_files_changed.outputs.builders_changed == 'true' }} 118 | strategy: 119 | matrix: 120 | combos: ${{ fromJSON(needs.preparation.outputs.combos) }} 121 | runs-on: ${{ matrix.combos.runner }} 122 | 123 | steps: 124 | - name: Checkout 125 | uses: actions/checkout@v6 126 | 127 | - name: Setup Go 128 | uses: actions/setup-go@v6 129 | with: 130 | go-version-file: go.mod 131 | 132 | - name: Run Smoke Tests 133 | run: ./scripts/smoke.sh --builder-dir "${{ matrix.combos.path }}" 134 | 135 | get_builders_info: 136 | name: Get Builders Info 137 | runs-on: ubuntu-24.04 138 | needs: [preparation, builder_files_changed, smoke] 139 | strategy: 140 | matrix: 141 | builders: ${{ fromJSON(needs.preparation.outputs.builders) }} 142 | services: 143 | registry: 144 | image: registry:3 145 | ports: 146 | - 5000:5000 147 | outputs: 148 | builder_info: ${{ steps.notes.outputs.body }} 149 | steps: 150 | - name: Checkout 151 | uses: actions/checkout@v6 152 | 153 | - name: Setup Go 154 | uses: actions/setup-go@v6 155 | with: 156 | go-version-file: go.mod 157 | 158 | - name: Get pack version 159 | id: pack-version 160 | run: | 161 | version=$(jq -r .pack "scripts/.util/tools.json") 162 | echo "version=${version#v}" >> "$GITHUB_OUTPUT" 163 | 164 | - name: Install Global Pack 165 | uses: buildpacks/github-actions/setup-pack@main 166 | with: 167 | pack-version: ${{ steps.pack-version.outputs.version }} 168 | 169 | - name: Builder info 170 | id: notes 171 | run: | 172 | registry_builder_name=localhost:5000/${{ matrix.builders.name }} 173 | 174 | ./scripts/publish.sh --builder-toml-path "${{ matrix.builders.path }}/builder.toml" \ 175 | --builder-image-ref "${registry_builder_name}" 176 | 177 | pack inspect-builder $registry_builder_name | grep -v 'Inspecting builder' \ 178 | | grep -v 'REMOTE:' \ 179 | | grep -v 'LOCAL:' \ 180 | | grep -v '\(not present\)' \ 181 | | grep -v 'Warning' > "${{ matrix.builders.name }}-info.md" 182 | 183 | - name: Upload info for ${{ matrix.builders.name }} 184 | uses: actions/upload-artifact@v6 185 | with: 186 | name: "${{ matrix.builders.name }}-info.md" 187 | path: "${{ matrix.builders.name }}-info.md" 188 | 189 | release: 190 | name: Release 191 | runs-on: ubuntu-24.04 192 | needs: [preparation, get_builders_info] 193 | steps: 194 | - name: Checkout With History 195 | uses: actions/checkout@v6 196 | with: 197 | fetch-depth: 0 # gets full history 198 | 199 | - name: Download Release Note File(s) 200 | uses: actions/download-artifact@v7 201 | with: 202 | path: info 203 | pattern: "*info.md" 204 | merge-multiple: true 205 | 206 | - name: Generate Release Notes and Assets 207 | id: generate_release_notes_and_assets 208 | run: | 209 | set -euo pipefail 210 | shopt -s inherit_errexit 211 | 212 | builders=$(echo '${{ needs.preparation.outputs.builders }}' | jq -c '.[]') 213 | builders_length=$(echo '${{ needs.preparation.outputs.builders }}' | jq 'length') 214 | 215 | release_notes_dir="info" 216 | 217 | # Start with an empty array 218 | assets=$(jq -n -c '[]') 219 | 220 | # Generate release assets only if we have more than one builder 221 | if [ "$builders_length" -gt 1 ]; then 222 | for builder in $builders; do 223 | builder_name=$(echo "$builder" | jq -r '.name') 224 | 225 | assets="$(jq -c \ 226 | --arg release_notes_dir "${release_notes_dir}" \ 227 | --arg builder_name "${builder_name}" \ 228 | '. += [ 229 | { 230 | "path": ($release_notes_dir + "/" + $builder_name + "-" + "info.md"), 231 | "name": ($builder_name + "-" + "info.md"), 232 | "content_type": "text/markdown" 233 | } 234 | ]' <<<"${assets}")" 235 | done 236 | fi 237 | 238 | echo "assets=${assets}" >> "$GITHUB_OUTPUT" 239 | 240 | # translates 'paketo-buildpacks' to 'paketobuildpacks' 241 | DOCKERHUB_ORG="${GITHUB_REPOSITORY_OWNER/-/}" 242 | 243 | release_body="" 244 | if [ "$builders_length" -gt 1 ]; then 245 | for builder in $builders; do 246 | container_repository=$(echo "$builder" | jq -r '.container_repository') 247 | release_body+="Builder: \`${DOCKERHUB_ORG}/${container_repository}\`\n" 248 | done 249 | payload="{\"body\" : \"\n${release_body}\"}" 250 | else 251 | builder=$(echo "$builders" | head -n 1) 252 | release_body="$(cat $release_notes_dir/$(echo "$builder" | jq -r '.name')-info.md \ 253 | | sed -e '/./,$!d' \ 254 | | awk -F, '{printf "%s\\n", $0}')" 255 | payload="{\"body\" : \"\`\`\`\n${release_body}\n\`\`\`\"}" 256 | fi 257 | 258 | echo "release_body=${payload}" >> "$GITHUB_OUTPUT" 259 | 260 | - name: Publish Release 261 | id: publish 262 | uses: release-drafter/release-drafter@v6 263 | with: 264 | config-name: release-drafter-config.yml 265 | publish: true 266 | env: 267 | GITHUB_TOKEN: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }} 268 | 269 | - name: Update Release Notes 270 | run: | 271 | set -euo pipefail 272 | shopt -s inherit_errexit 273 | 274 | payload='${{ steps.generate_release_notes_and_assets.outputs.release_body }}' 275 | curl --fail \ 276 | -X PATCH \ 277 | -H "Accept: application/vnd.github.v3+json" \ 278 | -H "Authorization: token ${GITHUB_TOKEN}" \ 279 | "https://api.github.com/repos/${GITHUB_REPOSITORY}/releases/${RELEASE_ID}" \ 280 | -d "${payload}" 281 | 282 | assets='${{ steps.generate_release_notes_and_assets.outputs.assets }}' 283 | 284 | # Update release assets 285 | for row in $(echo "${assets}" | jq -c '.[]'); do 286 | path=$(echo "$row" | jq -r '.path') 287 | name=$(echo "$row" | jq -r '.name') 288 | content_type=$(echo "$row" | jq -r '.content_type') 289 | 290 | echo "Uploading asset: ${name} from path: ${path}" 291 | 292 | curl -L \ 293 | -X POST \ 294 | -H "Accept: application/vnd.github.v3+json" \ 295 | -H "Authorization: token ${GITHUB_TOKEN}" \ 296 | -H "Content-Type: ${content_type}" \ 297 | "https://uploads.github.com/repos/${GITHUB_REPOSITORY}/releases/${RELEASE_ID}/assets?name=${name}" \ 298 | --data-binary "@${path}" 299 | done 300 | env: 301 | RELEASE_ID: ${{ steps.publish.outputs.id }} 302 | GITHUB_TOKEN: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }} 303 | 304 | failure: 305 | name: Alert on Failure 306 | runs-on: ubuntu-24.04 307 | needs: [ smoke, release, get_builders_info, preparation ] 308 | if: ${{ always() && needs.smoke.result == 'failure' || needs.release.result == 'failure' || needs.get_builders_info.result == 'failure' || needs.preparation.result == 'failure' }} 309 | steps: 310 | - name: File Failure Alert Issue 311 | uses: paketo-buildpacks/github-config/actions/issue/file@main 312 | with: 313 | token: ${{ secrets.GITHUB_TOKEN }} 314 | repo: ${{ github.repository }} 315 | label: "failure:release" 316 | comment_if_exists: true 317 | issue_title: "Failure: Create Release workflow" 318 | issue_body: | 319 | Create Release workflow [failed](https://github.com/${{github.repository}}/actions/runs/${{github.run_id}}). 320 | comment_body: | 321 | Another failure occurred: https://github.com/${{github.repository}}/actions/runs/${{github.run_id}} 322 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | -------------------------------------------------------------------------------- /go.sum: -------------------------------------------------------------------------------- 1 | dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= 2 | dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= 3 | github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= 4 | github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= 5 | github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= 6 | github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= 7 | github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= 8 | github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= 9 | github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0= 10 | github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= 11 | github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= 12 | github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= 13 | github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= 14 | github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= 15 | github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= 16 | github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= 17 | github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= 18 | github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= 19 | github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= 20 | github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= 21 | github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= 22 | github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= 23 | github.com/containerd/stargz-snapshotter/estargz v0.16.3 h1:7evrXtoh1mSbGj/pfRccTampEyKpjpOnS3CyiV1Ebr8= 24 | github.com/containerd/stargz-snapshotter/estargz v0.16.3/go.mod h1:uyr4BfYfOj3G9WBVE8cOlQmXAbPN9VEQpBBeJIuOipU= 25 | github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= 26 | github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= 27 | github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= 28 | github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= 29 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 30 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 31 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= 32 | github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 33 | github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= 34 | github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= 35 | github.com/docker/cli v28.4.0+incompatible h1:RBcf3Kjw2pMtwui5V0DIMdyeab8glEw5QY0UUU4C9kY= 36 | github.com/docker/cli v28.4.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= 37 | github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= 38 | github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= 39 | github.com/docker/docker v28.5.1+incompatible h1:Bm8DchhSD2J6PsFzxC35TZo4TLGR2PdW/E69rU45NhM= 40 | github.com/docker/docker v28.5.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= 41 | github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8= 42 | github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo= 43 | github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= 44 | github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= 45 | github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= 46 | github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= 47 | github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707 h1:2tV76y6Q9BB+NEBasnqvs7e49aEBFI8ejC89PSnWH+4= 48 | github.com/dsnet/compress v0.0.2-0.20230904184137-39efe44ab707/go.mod h1:qssHWj60/X5sZFNxpG4HBPDHVqxNm4DfnCKgrbZOT+s= 49 | github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw= 50 | github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= 51 | github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= 52 | github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= 53 | github.com/gabriel-vasile/mimetype v1.4.9 h1:5k+WDwEsD9eTLL8Tz3L0VnmVh9QxGjRmjBvAG7U/oYY= 54 | github.com/gabriel-vasile/mimetype v1.4.9/go.mod h1:WnSQhFKJuBlRyLiKohA/2DtIlPFAbguNaG7QCHcyGok= 55 | github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= 56 | github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= 57 | github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= 58 | github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= 59 | github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= 60 | github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= 61 | github.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE= 62 | github.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78= 63 | github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= 64 | github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= 65 | github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= 66 | github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= 67 | github.com/google/go-containerregistry v0.20.6 h1:cvWX87UxxLgaH76b4hIvya6Dzz9qHB31qAwjAohdSTU= 68 | github.com/google/go-containerregistry v0.20.6/go.mod h1:T0x8MuoAoKX/873bkeSfLD2FAkwCDf9/HZgsFJ02E2Y= 69 | github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 h1:EEHtgt9IwisQ2AZ4pIsMjahcegHh6rmhqxzIRQIyepY= 70 | github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U= 71 | github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= 72 | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= 73 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= 74 | github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= 75 | github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= 76 | github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= 77 | github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= 78 | github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= 79 | github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= 80 | github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= 81 | github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35 h1:PpXWgLPs+Fqr325bN2FD2ISlRRztXibcX6e8f5FR5Dc= 82 | github.com/lufia/plan9stats v0.0.0-20250317134145-8bc96cf8fc35/go.mod h1:autxFIvghDt3jPTLoqZ9OZ7s9qTGNAWmYCjVFWPX/zg= 83 | github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= 84 | github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= 85 | github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= 86 | github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= 87 | github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= 88 | github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= 89 | github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ= 90 | github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo= 91 | github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= 92 | github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= 93 | github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw= 94 | github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= 95 | github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= 96 | github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= 97 | github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= 98 | github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= 99 | github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= 100 | github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= 101 | github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= 102 | github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= 103 | github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= 104 | github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= 105 | github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= 106 | github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= 107 | github.com/oklog/ulid/v2 v2.1.1 h1:suPZ4ARWLOJLegGFiZZ1dFAkqzhMjL3J1TzI+5wHz8s= 108 | github.com/oklog/ulid/v2 v2.1.1/go.mod h1:rcEKHmBBKfef9DhnvX7y1HZBYxjXb0cP5ExxNsTT1QQ= 109 | github.com/onsi/ginkgo/v2 v2.25.3 h1:Ty8+Yi/ayDAGtk4XxmmfUy4GabvM+MegeB4cDLRi6nw= 110 | github.com/onsi/ginkgo/v2 v2.25.3/go.mod h1:43uiyQC4Ed2tkOzLsEYm7hnrb7UJTWHYNsuy3bG/snE= 111 | github.com/onsi/gomega v1.38.3 h1:eTX+W6dobAYfFeGC2PV6RwXRu/MyT+cQguijutvkpSM= 112 | github.com/onsi/gomega v1.38.3/go.mod h1:ZCU1pkQcXDO5Sl9/VVEGlDyp+zm0m1cmeG5TOzLgdh4= 113 | github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= 114 | github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= 115 | github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= 116 | github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= 117 | github.com/paketo-buildpacks/freezer v0.2.2 h1:p0qcGMQ54uGMzDFNkfpDpxddNl8+OTqt3teAWIkmCu4= 118 | github.com/paketo-buildpacks/freezer v0.2.2/go.mod h1:8dkcCqZKYFKxDV4MuooIR/Fr/LmpXqpr8R9mFvkGerE= 119 | github.com/paketo-buildpacks/occam v0.31.0 h1:OyKSqhFAT5gJB1wFENzl+LK0NSHG0/HBADIzP9lacvA= 120 | github.com/paketo-buildpacks/occam v0.31.0/go.mod h1:fbTuJwZDWW7mWmdyL7xpgOZMCyeHIQjpl5d0CMil2dM= 121 | github.com/paketo-buildpacks/packit/v2 v2.25.2 h1:58P1slWSMWBlwFsAOYoUAZA56TBzJYwWFRMlUrm7epA= 122 | github.com/paketo-buildpacks/packit/v2 v2.25.2/go.mod h1:WmU6cj0CG+2gAb/SKj+gxq12shyxrOpHS3rAJyrgR5E= 123 | github.com/pborman/getopt v0.0.0-20170112200414-7148bc3a4c30/go.mod h1:85jBQOZwpVEaDAr341tbn15RS4fCAsIst0qp7i8ex1o= 124 | github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= 125 | github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 126 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 127 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= 128 | github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= 129 | github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55 h1:o4JXh1EVt9k/+g42oCprj/FisM4qX9L3sZB3upGN2ZU= 130 | github.com/power-devops/perfstat v0.0.0-20240221224432-82ca36839d55/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= 131 | github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII= 132 | github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o= 133 | github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8= 134 | github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM= 135 | github.com/shirou/gopsutil/v4 v4.25.6 h1:kLysI2JsKorfaFPcYmcJqbzROzsBWEOAtw6A7dIfqXs= 136 | github.com/shirou/gopsutil/v4 v4.25.6/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c= 137 | github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af h1:Sp5TG9f7K39yfB+If0vjp97vuT74F72r8hfRpP8jLU0= 138 | github.com/sirupsen/logrus v1.9.4-0.20230606125235-dd1b4c2e81af/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= 139 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= 140 | github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= 141 | github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= 142 | github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 143 | github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= 144 | github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= 145 | github.com/testcontainers/testcontainers-go v0.39.0 h1:uCUJ5tA+fcxbFAB0uP3pIK3EJ2IjjDUHFSZ1H1UxAts= 146 | github.com/testcontainers/testcontainers-go v0.39.0/go.mod h1:qmHpkG7H5uPf/EvOORKvS6EuDkBUPE3zpVGaH9NL7f8= 147 | github.com/tklauser/go-sysconf v0.3.15 h1:VE89k0criAymJ/Os65CSn1IXaol+1wrsFHEB8Ol49K4= 148 | github.com/tklauser/go-sysconf v0.3.15/go.mod h1:Dmjwr6tYFIseJw7a3dRLJfsHAMXZ3nEnL/aZY+0IuI4= 149 | github.com/tklauser/numcpus v0.10.0 h1:18njr6LDBk1zuna922MgdjQuJFjrdppsZG60sHGfjso= 150 | github.com/tklauser/numcpus v0.10.0/go.mod h1:BiTKazU708GQTYF4mB+cmlpT2Is1gLk7XVuEeem8LsQ= 151 | github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= 152 | github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= 153 | github.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnnbo= 154 | github.com/vbatts/tar-split v0.12.1/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA= 155 | github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= 156 | github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= 157 | go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= 158 | go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= 159 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0 h1:F7Jx+6hwnZ41NSFTO5q4LYDtJRXBf2PD0rNBkeB/lus= 160 | go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.61.0/go.mod h1:UHB22Z8QsdRDrnAtX4PntOl36ajSxcdUMt1sF7Y6E7Q= 161 | go.opentelemetry.io/otel v1.37.0 h1:9zhNfelUvx0KBfu/gb+ZgeAfAgtWrfHJZcAqFC228wQ= 162 | go.opentelemetry.io/otel v1.37.0/go.mod h1:ehE/umFRLnuLa/vSccNq9oS1ErUlkkK71gMcN34UG8I= 163 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0 h1:Ahq7pZmv87yiyn3jeFz/LekZmPLLdKejuO3NcK9MssM= 164 | go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.37.0/go.mod h1:MJTqhM0im3mRLw1i8uGHnCvUEeS7VwRyxlLC78PA18M= 165 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 h1:wpMfgF8E1rkrT1Z6meFh1NDtownE9Ii3n3X2GJYjsaU= 166 | go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0/go.mod h1:wAy0T/dUbs468uOlkT31xjvqQgEVXv58BRFWEgn5v/0= 167 | go.opentelemetry.io/otel/metric v1.37.0 h1:mvwbQS5m0tbmqML4NqK+e3aDiO02vsf/WgbsdpcPoZE= 168 | go.opentelemetry.io/otel/metric v1.37.0/go.mod h1:04wGrZurHYKOc+RKeye86GwKiTb9FKm1WHtO+4EVr2E= 169 | go.opentelemetry.io/otel/sdk v1.37.0 h1:ItB0QUqnjesGRvNcmAcU0LyvkVyGJ2xftD29bWdDvKI= 170 | go.opentelemetry.io/otel/sdk v1.37.0/go.mod h1:VredYzxUvuo2q3WRcDnKDjbdvmO0sCzOvVAiY+yUkAg= 171 | go.opentelemetry.io/otel/sdk/metric v1.36.0 h1:r0ntwwGosWGaa0CrSt8cuNuTcccMXERFwHX4dThiPis= 172 | go.opentelemetry.io/otel/sdk/metric v1.36.0/go.mod h1:qTNOhFDfKRwX0yXOqJYegL5WRaW376QbB7P4Pb0qva4= 173 | go.opentelemetry.io/otel/trace v1.37.0 h1:HLdcFNbRQBE2imdSEgm/kwqmQj1Or1l/7bW6mxVK7z4= 174 | go.opentelemetry.io/otel/trace v1.37.0/go.mod h1:TlgrlQ+PtQO5XFerSPUYG0JSgGyryXewPGyayAWSBS0= 175 | go.opentelemetry.io/proto/otlp v1.7.0 h1:jX1VolD6nHuFzOYso2E73H85i92Mv8JQYk0K9vz09os= 176 | go.opentelemetry.io/proto/otlp v1.7.0/go.mod h1:fSKjH6YJ7HDlwzltzyMj036AJ3ejJLCgCSHGj4efDDo= 177 | go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs= 178 | go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8= 179 | go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= 180 | go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= 181 | golang.org/x/crypto v0.42.0 h1:chiH31gIWm57EkTXpwnqf8qeuMUi0yekh6mT2AvFlqI= 182 | golang.org/x/crypto v0.42.0/go.mod h1:4+rDnOTJhQCx2q7/j6rAN5XDw8kPjeaXEUR2eL94ix8= 183 | golang.org/x/net v0.44.0 h1:evd8IRDyfNBMBTTY5XRF1vaZlD+EmWx6x8PkhR04H/I= 184 | golang.org/x/net v0.44.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= 185 | golang.org/x/sync v0.17.0 h1:l60nONMj9l5drqw6jlhIELNv9I0A4OFgRsG9k2oT9Ug= 186 | golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= 187 | golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 188 | golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= 189 | golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 190 | golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 191 | golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 192 | golang.org/x/sys v0.36.0 h1:KVRy2GtZBrk1cBYA7MKu5bEZFxQk4NIDV6RLVcC8o0k= 193 | golang.org/x/sys v0.36.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= 194 | golang.org/x/term v0.35.0 h1:bZBVKBudEyhRcajGcNc3jIfWPqV4y/Kt2XcoigOWtDQ= 195 | golang.org/x/term v0.35.0/go.mod h1:TPGtkTLesOwf2DE8CgVYiZinHAOuy5AYUYT1lENIZnA= 196 | golang.org/x/text v0.29.0 h1:1neNs90w9YzJ9BocxfsQNHKuAT4pkghyXc4nhZ6sJvk= 197 | golang.org/x/text v0.29.0/go.mod h1:7MhJOA9CD2qZyOKYazxdYMF85OwPdEr9jTtBpO7ydH4= 198 | golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= 199 | golang.org/x/time v0.12.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= 200 | golang.org/x/tools v0.37.0 h1:DVSRzp7FwePZW356yEAChSdNcQo6Nsp+fex1SUW09lE= 201 | golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= 202 | google.golang.org/genproto v0.0.0-20241118233622-e639e219e697 h1:ToEetK57OidYuqD4Q5w+vfEnPvPpuTwedCNVohYJfNk= 203 | google.golang.org/genproto/googleapis/api v0.0.0-20241113202542-65e8d215514f h1:M65LEviCfuZTfrfzwwEoxVtgvfkFkBUbFnRbxCXuXhU= 204 | google.golang.org/genproto/googleapis/api v0.0.0-20241113202542-65e8d215514f/go.mod h1:Yo94eF2nj7igQt+TiJ49KxjIH8ndLYPZMIRSiRcEbg0= 205 | google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8 h1:TqExAhdPaB60Ux47Cn0oLV07rGnxZzIsaRhQaqS666A= 206 | google.golang.org/genproto/googleapis/rpc v0.0.0-20241223144023-3abc09e42ca8/go.mod h1:lcTa1sDdWEIHMWlITnIczmw5w60CF9ffkb8Z+DVmmjA= 207 | google.golang.org/grpc v1.67.3 h1:OgPcDAFKHnH8X3O4WcO4XUc8GRDeKsKReqbQtiCj7N8= 208 | google.golang.org/grpc v1.67.3/go.mod h1:YGaHCc6Oap+FzBJTZLBzkGSYt/cvGPFTPxkn7QfSU8s= 209 | google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A= 210 | google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY= 211 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= 212 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= 213 | gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= 214 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 215 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= 216 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= 217 | gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= 218 | gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= 219 | -------------------------------------------------------------------------------- /smoke/testdata/python/Pipfile.lock: -------------------------------------------------------------------------------- 1 | { 2 | "_meta": { 3 | "hash": { 4 | "sha256": "0097800581d075b74af129559abd752817e4cc66b23df5082a3d0795f41b10a4" 5 | }, 6 | "pipfile-spec": 6, 7 | "requires": {}, 8 | "sources": [ 9 | { 10 | "name": "pypi", 11 | "url": "https://pypi.org/simple", 12 | "verify_ssl": true 13 | } 14 | ] 15 | }, 16 | "default": { 17 | "click": { 18 | "hashes": [ 19 | "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e", 20 | "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48" 21 | ], 22 | "markers": "python_version >= '3.7'", 23 | "version": "==8.1.3" 24 | }, 25 | "flask": { 26 | "hashes": [ 27 | "sha256:15972e5017df0575c3d6c090ba168b6db90259e620ac8d7ea813a396bad5b6cb", 28 | "sha256:9013281a7402ad527f8fd56375164f3aa021ecfaff89bfe3825346c24f87e04c" 29 | ], 30 | "index": "pypi", 31 | "version": "==2.1.3" 32 | }, 33 | "gunicorn": { 34 | "hashes": [ 35 | "sha256:9dcc4547dbb1cb284accfb15ab5667a0e5d1881cc443e0677b4882a4067a807e", 36 | "sha256:e0a968b5ba15f8a328fdfd7ab1fcb5af4470c28aaf7e55df02a99bc13138e6e8" 37 | ], 38 | "index": "pypi", 39 | "version": "==20.1.0" 40 | }, 41 | "itsdangerous": { 42 | "hashes": [ 43 | "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44", 44 | "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a" 45 | ], 46 | "index": "pypi", 47 | "version": "==2.1.2" 48 | }, 49 | "jinja2": { 50 | "hashes": [ 51 | "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852", 52 | "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61" 53 | ], 54 | "markers": "python_version >= '3.7'", 55 | "version": "==3.1.2" 56 | }, 57 | "markupsafe": { 58 | "hashes": [ 59 | "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003", 60 | "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88", 61 | "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5", 62 | "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7", 63 | "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a", 64 | "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603", 65 | "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1", 66 | "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135", 67 | "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247", 68 | "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6", 69 | "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601", 70 | "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77", 71 | "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02", 72 | "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e", 73 | "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63", 74 | "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f", 75 | "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980", 76 | "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b", 77 | "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812", 78 | "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff", 79 | "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96", 80 | "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1", 81 | "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925", 82 | "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a", 83 | "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6", 84 | "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e", 85 | "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f", 86 | "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4", 87 | "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f", 88 | "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3", 89 | "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c", 90 | "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a", 91 | "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417", 92 | "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a", 93 | "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a", 94 | "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37", 95 | "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452", 96 | "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933", 97 | "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a", 98 | "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7" 99 | ], 100 | "markers": "python_version >= '3.7'", 101 | "version": "==2.1.1" 102 | }, 103 | "setuptools": { 104 | "hashes": [ 105 | "sha256:512e5536220e38146176efb833d4a62aa726b7bbff82cfbc8ba9eaa3996e0b17", 106 | "sha256:f62ea9da9ed6289bfe868cd6845968a2c854d1427f8548d52cae02a42b4f0356" 107 | ], 108 | "markers": "python_version >= '3.7'", 109 | "version": "==65.5.0" 110 | }, 111 | "werkzeug": { 112 | "hashes": [ 113 | "sha256:7ea2d48322cc7c0f8b3a215ed73eabd7b5d75d0b50e31ab006286ccff9e00b8f", 114 | "sha256:f979ab81f58d7318e064e99c4506445d60135ac5cd2e177a2de0089bfd4c9bd5" 115 | ], 116 | "markers": "python_version >= '3.7'", 117 | "version": "==2.2.2" 118 | } 119 | }, 120 | "develop": { 121 | "click": { 122 | "hashes": [ 123 | "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e", 124 | "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48" 125 | ], 126 | "markers": "python_version >= '3.7'", 127 | "version": "==8.1.3" 128 | }, 129 | "coverage": { 130 | "hashes": [ 131 | "sha256:027018943386e7b942fa832372ebc120155fd970837489896099f5cfa2890f79", 132 | "sha256:11b990d520ea75e7ee8dcab5bc908072aaada194a794db9f6d7d5cfd19661e5a", 133 | "sha256:12adf310e4aafddc58afdb04d686795f33f4d7a6fa67a7a9d4ce7d6ae24d949f", 134 | "sha256:1431986dac3923c5945271f169f59c45b8802a114c8f548d611f2015133df77a", 135 | "sha256:1ef221513e6f68b69ee9e159506d583d31aa3567e0ae84eaad9d6ec1107dddaa", 136 | "sha256:20c8ac5386253717e5ccc827caad43ed66fea0efe255727b1053a8154d952398", 137 | "sha256:2198ea6fc548de52adc826f62cb18554caedfb1d26548c1b7c88d8f7faa8f6ba", 138 | "sha256:255758a1e3b61db372ec2736c8e2a1fdfaf563977eedbdf131de003ca5779b7d", 139 | "sha256:265de0fa6778d07de30bcf4d9dc471c3dc4314a23a3c6603d356a3c9abc2dfcf", 140 | "sha256:33a7da4376d5977fbf0a8ed91c4dffaaa8dbf0ddbf4c8eea500a2486d8bc4d7b", 141 | "sha256:42eafe6778551cf006a7c43153af1211c3aaab658d4d66fa5fcc021613d02518", 142 | "sha256:4433b90fae13f86fafff0b326453dd42fc9a639a0d9e4eec4d366436d1a41b6d", 143 | "sha256:4a5375e28c5191ac38cca59b38edd33ef4cc914732c916f2929029b4bfb50795", 144 | "sha256:4a8dbc1f0fbb2ae3de73eb0bdbb914180c7abfbf258e90b311dcd4f585d44bd2", 145 | "sha256:59f53f1dc5b656cafb1badd0feb428c1e7bc19b867479ff72f7a9dd9b479f10e", 146 | "sha256:5dbec3b9095749390c09ab7c89d314727f18800060d8d24e87f01fb9cfb40b32", 147 | "sha256:633713d70ad6bfc49b34ead4060531658dc6dfc9b3eb7d8a716d5873377ab745", 148 | "sha256:6b07130585d54fe8dff3d97b93b0e20290de974dc8177c320aeaf23459219c0b", 149 | "sha256:6c4459b3de97b75e3bd6b7d4b7f0db13f17f504f3d13e2a7c623786289dd670e", 150 | "sha256:6d4817234349a80dbf03640cec6109cd90cba068330703fa65ddf56b60223a6d", 151 | "sha256:723e8130d4ecc8f56e9a611e73b31219595baa3bb252d539206f7bbbab6ffc1f", 152 | "sha256:784f53ebc9f3fd0e2a3f6a78b2be1bd1f5575d7863e10c6e12504f240fd06660", 153 | "sha256:7b6be138d61e458e18d8e6ddcddd36dd96215edfe5f1168de0b1b32635839b62", 154 | "sha256:7ccf362abd726b0410bf8911c31fbf97f09f8f1061f8c1cf03dfc4b6372848f6", 155 | "sha256:83516205e254a0cb77d2d7bb3632ee019d93d9f4005de31dca0a8c3667d5bc04", 156 | "sha256:851cf4ff24062c6aec510a454b2584f6e998cada52d4cb58c5e233d07172e50c", 157 | "sha256:8f830ed581b45b82451a40faabb89c84e1a998124ee4212d440e9c6cf70083e5", 158 | "sha256:94e2565443291bd778421856bc975d351738963071e9b8839ca1fc08b42d4bef", 159 | "sha256:95203854f974e07af96358c0b261f1048d8e1083f2de9b1c565e1be4a3a48cfc", 160 | "sha256:97117225cdd992a9c2a5515db1f66b59db634f59d0679ca1fa3fe8da32749cae", 161 | "sha256:98e8a10b7a314f454d9eff4216a9a94d143a7ee65018dd12442e898ee2310578", 162 | "sha256:a1170fa54185845505fbfa672f1c1ab175446c887cce8212c44149581cf2d466", 163 | "sha256:a6b7d95969b8845250586f269e81e5dfdd8ff828ddeb8567a4a2eaa7313460c4", 164 | "sha256:a8fb6cf131ac4070c9c5a3e21de0f7dc5a0fbe8bc77c9456ced896c12fcdad91", 165 | "sha256:af4fffaffc4067232253715065e30c5a7ec6faac36f8fc8d6f64263b15f74db0", 166 | "sha256:b4a5be1748d538a710f87542f22c2cad22f80545a847ad91ce45e77417293eb4", 167 | "sha256:b5604380f3415ba69de87a289a2b56687faa4fe04dbee0754bfcae433489316b", 168 | "sha256:b9023e237f4c02ff739581ef35969c3739445fb059b060ca51771e69101efffe", 169 | "sha256:bc8ef5e043a2af066fa8cbfc6e708d58017024dc4345a1f9757b329a249f041b", 170 | "sha256:c4ed2820d919351f4167e52425e096af41bfabacb1857186c1ea32ff9983ed75", 171 | "sha256:cca4435eebea7962a52bdb216dec27215d0df64cf27fc1dd538415f5d2b9da6b", 172 | "sha256:d900bb429fdfd7f511f868cedd03a6bbb142f3f9118c09b99ef8dc9bf9643c3c", 173 | "sha256:d9ecf0829c6a62b9b573c7bb6d4dcd6ba8b6f80be9ba4fc7ed50bf4ac9aecd72", 174 | "sha256:dbdb91cd8c048c2b09eb17713b0c12a54fbd587d79adcebad543bc0cd9a3410b", 175 | "sha256:de3001a203182842a4630e7b8d1a2c7c07ec1b45d3084a83d5d227a3806f530f", 176 | "sha256:e07f4a4a9b41583d6eabec04f8b68076ab3cd44c20bd29332c6572dda36f372e", 177 | "sha256:ef8674b0ee8cc11e2d574e3e2998aea5df5ab242e012286824ea3c6970580e53", 178 | "sha256:f4f05d88d9a80ad3cac6244d36dd89a3c00abc16371769f1340101d3cb899fc3", 179 | "sha256:f642e90754ee3e06b0e7e51bce3379590e76b7f76b708e1a71ff043f87025c84", 180 | "sha256:fc2af30ed0d5ae0b1abdb4ebdce598eafd5b35397d4d75deb341a614d333d987" 181 | ], 182 | "index": "pypi", 183 | "version": "==6.5.0" 184 | }, 185 | "distlib": { 186 | "hashes": [ 187 | "sha256:14bad2d9b04d3a36127ac97f30b12a19268f211063d8f8ee4f47108896e11b46", 188 | "sha256:f35c4b692542ca110de7ef0bea44d73981caeb34ca0b9b6b2e6d7790dda8f80e" 189 | ], 190 | "version": "==0.3.6" 191 | }, 192 | "filelock": { 193 | "hashes": [ 194 | "sha256:55447caa666f2198c5b6b13a26d2084d26fa5b115c00d065664b2124680c4edc", 195 | "sha256:617eb4e5eedc82fc5f47b6d61e4d11cb837c56cb4544e39081099fa17ad109d4" 196 | ], 197 | "markers": "python_version >= '3.7'", 198 | "version": "==3.8.0" 199 | }, 200 | "flake8": { 201 | "hashes": [ 202 | "sha256:6fbe320aad8d6b95cec8b8e47bc933004678dc63095be98528b7bdd2a9f510db", 203 | "sha256:7a1cf6b73744f5806ab95e526f6f0d8c01c66d7bbe349562d22dfca20610b248" 204 | ], 205 | "index": "pypi", 206 | "version": "==5.0.4" 207 | }, 208 | "flask": { 209 | "hashes": [ 210 | "sha256:15972e5017df0575c3d6c090ba168b6db90259e620ac8d7ea813a396bad5b6cb", 211 | "sha256:9013281a7402ad527f8fd56375164f3aa021ecfaff89bfe3825346c24f87e04c" 212 | ], 213 | "index": "pypi", 214 | "version": "==2.1.3" 215 | }, 216 | "flask-testing": { 217 | "hashes": [ 218 | "sha256:0a734d7b68e63a9410b413cd7b1f96456f9a858bd09a6222d465650cc782eb01" 219 | ], 220 | "index": "pypi", 221 | "version": "==0.8.1" 222 | }, 223 | "itsdangerous": { 224 | "hashes": [ 225 | "sha256:2c2349112351b88699d8d4b6b075022c0808887cb7ad10069318a8b0bc88db44", 226 | "sha256:5dbbc68b317e5e42f327f9021763545dc3fc3bfe22e6deb96aaf1fc38874156a" 227 | ], 228 | "index": "pypi", 229 | "version": "==2.1.2" 230 | }, 231 | "jinja2": { 232 | "hashes": [ 233 | "sha256:31351a702a408a9e7595a8fc6150fc3f43bb6bf7e319770cbc0db9df9437e852", 234 | "sha256:6088930bfe239f0e6710546ab9c19c9ef35e29792895fed6e6e31a023a182a61" 235 | ], 236 | "markers": "python_version >= '3.7'", 237 | "version": "==3.1.2" 238 | }, 239 | "markupsafe": { 240 | "hashes": [ 241 | "sha256:0212a68688482dc52b2d45013df70d169f542b7394fc744c02a57374a4207003", 242 | "sha256:089cf3dbf0cd6c100f02945abeb18484bd1ee57a079aefd52cffd17fba910b88", 243 | "sha256:10c1bfff05d95783da83491be968e8fe789263689c02724e0c691933c52994f5", 244 | "sha256:33b74d289bd2f5e527beadcaa3f401e0df0a89927c1559c8566c066fa4248ab7", 245 | "sha256:3799351e2336dc91ea70b034983ee71cf2f9533cdff7c14c90ea126bfd95d65a", 246 | "sha256:3ce11ee3f23f79dbd06fb3d63e2f6af7b12db1d46932fe7bd8afa259a5996603", 247 | "sha256:421be9fbf0ffe9ffd7a378aafebbf6f4602d564d34be190fc19a193232fd12b1", 248 | "sha256:43093fb83d8343aac0b1baa75516da6092f58f41200907ef92448ecab8825135", 249 | "sha256:46d00d6cfecdde84d40e572d63735ef81423ad31184100411e6e3388d405e247", 250 | "sha256:4a33dea2b688b3190ee12bd7cfa29d39c9ed176bda40bfa11099a3ce5d3a7ac6", 251 | "sha256:4b9fe39a2ccc108a4accc2676e77da025ce383c108593d65cc909add5c3bd601", 252 | "sha256:56442863ed2b06d19c37f94d999035e15ee982988920e12a5b4ba29b62ad1f77", 253 | "sha256:671cd1187ed5e62818414afe79ed29da836dde67166a9fac6d435873c44fdd02", 254 | "sha256:694deca8d702d5db21ec83983ce0bb4b26a578e71fbdbd4fdcd387daa90e4d5e", 255 | "sha256:6a074d34ee7a5ce3effbc526b7083ec9731bb3cbf921bbe1d3005d4d2bdb3a63", 256 | "sha256:6d0072fea50feec76a4c418096652f2c3238eaa014b2f94aeb1d56a66b41403f", 257 | "sha256:6fbf47b5d3728c6aea2abb0589b5d30459e369baa772e0f37a0320185e87c980", 258 | "sha256:7f91197cc9e48f989d12e4e6fbc46495c446636dfc81b9ccf50bb0ec74b91d4b", 259 | "sha256:86b1f75c4e7c2ac2ccdaec2b9022845dbb81880ca318bb7a0a01fbf7813e3812", 260 | "sha256:8dc1c72a69aa7e082593c4a203dcf94ddb74bb5c8a731e4e1eb68d031e8498ff", 261 | "sha256:8e3dcf21f367459434c18e71b2a9532d96547aef8a871872a5bd69a715c15f96", 262 | "sha256:8e576a51ad59e4bfaac456023a78f6b5e6e7651dcd383bcc3e18d06f9b55d6d1", 263 | "sha256:96e37a3dc86e80bf81758c152fe66dbf60ed5eca3d26305edf01892257049925", 264 | "sha256:97a68e6ada378df82bc9f16b800ab77cbf4b2fada0081794318520138c088e4a", 265 | "sha256:99a2a507ed3ac881b975a2976d59f38c19386d128e7a9a18b7df6fff1fd4c1d6", 266 | "sha256:a49907dd8420c5685cfa064a1335b6754b74541bbb3706c259c02ed65b644b3e", 267 | "sha256:b09bf97215625a311f669476f44b8b318b075847b49316d3e28c08e41a7a573f", 268 | "sha256:b7bd98b796e2b6553da7225aeb61f447f80a1ca64f41d83612e6139ca5213aa4", 269 | "sha256:b87db4360013327109564f0e591bd2a3b318547bcef31b468a92ee504d07ae4f", 270 | "sha256:bcb3ed405ed3222f9904899563d6fc492ff75cce56cba05e32eff40e6acbeaa3", 271 | "sha256:d4306c36ca495956b6d568d276ac11fdd9c30a36f1b6eb928070dc5360b22e1c", 272 | "sha256:d5ee4f386140395a2c818d149221149c54849dfcfcb9f1debfe07a8b8bd63f9a", 273 | "sha256:dda30ba7e87fbbb7eab1ec9f58678558fd9a6b8b853530e176eabd064da81417", 274 | "sha256:e04e26803c9c3851c931eac40c695602c6295b8d432cbe78609649ad9bd2da8a", 275 | "sha256:e1c0b87e09fa55a220f058d1d49d3fb8df88fbfab58558f1198e08c1e1de842a", 276 | "sha256:e72591e9ecd94d7feb70c1cbd7be7b3ebea3f548870aa91e2732960fa4d57a37", 277 | "sha256:e8c843bbcda3a2f1e3c2ab25913c80a3c5376cd00c6e8c4a86a89a28c8dc5452", 278 | "sha256:efc1913fd2ca4f334418481c7e595c00aad186563bbc1ec76067848c7ca0a933", 279 | "sha256:f121a1420d4e173a5d96e47e9a0c0dcff965afdf1626d28de1460815f7c4ee7a", 280 | "sha256:fc7b548b17d238737688817ab67deebb30e8073c95749d55538ed473130ec0c7" 281 | ], 282 | "markers": "python_version >= '3.7'", 283 | "version": "==2.1.1" 284 | }, 285 | "mccabe": { 286 | "hashes": [ 287 | "sha256:348e0240c33b60bbdf4e523192ef919f28cb2c3d7d5c7794f74009290f236325", 288 | "sha256:6c2d30ab6be0e4a46919781807b4f0d834ebdd6c6e3dca0bda5a15f863427b6e" 289 | ], 290 | "markers": "python_version >= '3.6'", 291 | "version": "==0.7.0" 292 | }, 293 | "packaging": { 294 | "hashes": [ 295 | "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb", 296 | "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522" 297 | ], 298 | "markers": "python_version >= '3.6'", 299 | "version": "==21.3" 300 | }, 301 | "platformdirs": { 302 | "hashes": [ 303 | "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788", 304 | "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19" 305 | ], 306 | "markers": "python_version >= '3.7'", 307 | "version": "==2.5.2" 308 | }, 309 | "pluggy": { 310 | "hashes": [ 311 | "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159", 312 | "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3" 313 | ], 314 | "markers": "python_version >= '3.6'", 315 | "version": "==1.0.0" 316 | }, 317 | "py": { 318 | "hashes": [ 319 | "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719", 320 | "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378" 321 | ], 322 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", 323 | "version": "==1.11.0" 324 | }, 325 | "pycodestyle": { 326 | "hashes": [ 327 | "sha256:2c9607871d58c76354b697b42f5d57e1ada7d261c261efac224b664affdc5785", 328 | "sha256:d1735fc58b418fd7c5f658d28d943854f8a849b01a5d0a1e6f3f3fdd0166804b" 329 | ], 330 | "markers": "python_version >= '3.6'", 331 | "version": "==2.9.1" 332 | }, 333 | "pyflakes": { 334 | "hashes": [ 335 | "sha256:4579f67d887f804e67edb544428f264b7b24f435b263c4614f384135cea553d2", 336 | "sha256:491feb020dca48ccc562a8c0cbe8df07ee13078df59813b83959cbdada312ea3" 337 | ], 338 | "markers": "python_version >= '3.6'", 339 | "version": "==2.5.0" 340 | }, 341 | "pyparsing": { 342 | "hashes": [ 343 | "sha256:2b020ecf7d21b687f219b71ecad3631f644a47f01403fa1d1036b0c6416d70fb", 344 | "sha256:5026bae9a10eeaefb61dab2f09052b9f4307d44aee4eda64b309723d8d206bbc" 345 | ], 346 | "markers": "python_full_version >= '3.6.8'", 347 | "version": "==3.0.9" 348 | }, 349 | "six": { 350 | "hashes": [ 351 | "sha256:1e61c37477a1626458e36f7b1d82aa5c9b094fa4802892072e49de9c60c4c926", 352 | "sha256:8abb2f1d86890a2dfb989f9a77cfcfd3e47c2a354b01111771326f8aa26e0254" 353 | ], 354 | "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2'", 355 | "version": "==1.16.0" 356 | }, 357 | "tomli": { 358 | "hashes": [ 359 | "sha256:939de3e7a6161af0c887ef91b7d41a53e7c5a1ca976325f429cb46ea9bc30ecc", 360 | "sha256:de526c12914f0c550d15924c62d72abc48d6fe7364aa87328337a31007fe8a4f" 361 | ], 362 | "markers": "python_version >= '3.7' and python_version < '3.11'", 363 | "version": "==2.0.1" 364 | }, 365 | "tox": { 366 | "hashes": [ 367 | "sha256:44f3c347c68c2c68799d7d44f1808f9d396fc8a1a500cbc624253375c7ae107e", 368 | "sha256:bf037662d7c740d15c9924ba23bb3e587df20598697bb985ac2b49bdc2d847f6" 369 | ], 370 | "index": "pypi", 371 | "version": "==3.26.0" 372 | }, 373 | "virtualenv": { 374 | "hashes": [ 375 | "sha256:227ea1b9994fdc5ea31977ba3383ef296d7472ea85be9d6732e42a91c04e80da", 376 | "sha256:d07dfc5df5e4e0dbc92862350ad87a36ed505b978f6c39609dc489eadd5b0d27" 377 | ], 378 | "markers": "python_version >= '3.6'", 379 | "version": "==20.16.5" 380 | }, 381 | "werkzeug": { 382 | "hashes": [ 383 | "sha256:7ea2d48322cc7c0f8b3a215ed73eabd7b5d75d0b50e31ab006286ccff9e00b8f", 384 | "sha256:f979ab81f58d7318e064e99c4506445d60135ac5cd2e177a2de0089bfd4c9bd5" 385 | ], 386 | "markers": "python_version >= '3.7'", 387 | "version": "==2.2.2" 388 | } 389 | } 390 | } 391 | --------------------------------------------------------------------------------