├── .github
├── pipeline-version
├── CODEOWNERS
├── dependabot.yml
├── workflows
│ ├── pb-synchronize-labels.yml
│ ├── pb-minimal-labels.yml
│ ├── pb-update-draft-release.yml
│ ├── pb-update-go.yml
│ ├── pb-update-pipeline.yml
│ ├── pb-update-tomcat-9.yml
│ ├── pb-update-tomcat-11.yml
│ ├── pb-update-tomcat-10-1.yml
│ ├── pb-update-tomcat-logging-support.yml
│ ├── pb-update-tomcat-lifecycle-support.yml
│ ├── pb-update-tomcat-access-logging-support.yml
│ ├── pb-tests.yml
│ └── pb-create-package.yml
├── release-drafter.yml
├── labels.yml
└── pipeline-descriptor.yml
├── tomcat
├── testdata
│ ├── warfiles
│ │ ├── api.war
│ │ └── ui.war
│ ├── c31f9fd9b9458dd8dda54ce879dc7b08f8de0e638cb0936abcaa2316e7460c1e
│ │ └── stub-tomcat.tar.gz
│ ├── e0a7e163cc9f1ffd41c8de3942c7c6b505090b7484c2ba9be846334e31c44a2c
│ │ └── stub-tomcat-logging-support.jar
│ ├── 22e708cfd301430cbcf8d1c2289503d8288d50df519ff4db7cca0ff9fe83c324
│ │ └── stub-external-configuration.tar.gz
│ ├── 723126712c0b22a7fe409664adf1fbb78cf3040e313a82c06696f5058e190534
│ │ └── stub-tomcat-lifecycle-support.jar
│ ├── d723bfe2ba67dfa92b24e3b6c7b2d0e6a963de7313350e306d470e44e330a5d2
│ │ └── stub-tomcat-access-logging-support.jar
│ ├── 060818cbcdc2008563f0f9e2428ecf4a199a5821c5b8b1dcd11a67666c1e2cd6
│ │ └── stub-external-configuration-with-directory.tar.gz
│ ├── c31f9fd9b9458dd8dda54ce879dc7b08f8de0e638cb0936abcaa2316e7460c1e.toml
│ ├── 22e708cfd301430cbcf8d1c2289503d8288d50df519ff4db7cca0ff9fe83c324.toml
│ ├── 060818cbcdc2008563f0f9e2428ecf4a199a5821c5b8b1dcd11a67666c1e2cd6.toml
│ ├── e0a7e163cc9f1ffd41c8de3942c7c6b505090b7484c2ba9be846334e31c44a2c.toml
│ ├── 723126712c0b22a7fe409664adf1fbb78cf3040e313a82c06696f5058e190534.toml
│ └── d723bfe2ba67dfa92b24e3b6c7b2d0e6a963de7313350e306d470e44e330a5d2.toml
├── init_test.go
├── home.go
├── home_test.go
├── detect.go
├── detect_test.go
├── build.go
├── base.go
├── build_test.go
└── base_test.go
├── internal
└── util
│ ├── containsWarFiles.go
│ └── replaceInCatalinaProps.go
├── NOTICE
├── .gitignore
├── resources
├── context.xml
├── logging.properties
└── server.xml
├── scripts
└── build.sh
├── helper
├── init_test.go
├── access_logging_support.go
└── access_logging_support_test.go
├── cmd
├── main
│ └── main.go
└── helper
│ └── main.go
├── go.mod
├── go.sum
├── README.md
└── LICENSE
/.github/pipeline-version:
--------------------------------------------------------------------------------
1 | 1.44.0
2 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | * @paketo-buildpacks/java-maintainers
--------------------------------------------------------------------------------
/tomcat/testdata/warfiles/api.war:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paketo-buildpacks/apache-tomcat/HEAD/tomcat/testdata/warfiles/api.war
--------------------------------------------------------------------------------
/tomcat/testdata/warfiles/ui.war:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paketo-buildpacks/apache-tomcat/HEAD/tomcat/testdata/warfiles/ui.war
--------------------------------------------------------------------------------
/tomcat/testdata/c31f9fd9b9458dd8dda54ce879dc7b08f8de0e638cb0936abcaa2316e7460c1e/stub-tomcat.tar.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paketo-buildpacks/apache-tomcat/HEAD/tomcat/testdata/c31f9fd9b9458dd8dda54ce879dc7b08f8de0e638cb0936abcaa2316e7460c1e/stub-tomcat.tar.gz
--------------------------------------------------------------------------------
/tomcat/testdata/e0a7e163cc9f1ffd41c8de3942c7c6b505090b7484c2ba9be846334e31c44a2c/stub-tomcat-logging-support.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paketo-buildpacks/apache-tomcat/HEAD/tomcat/testdata/e0a7e163cc9f1ffd41c8de3942c7c6b505090b7484c2ba9be846334e31c44a2c/stub-tomcat-logging-support.jar
--------------------------------------------------------------------------------
/tomcat/testdata/22e708cfd301430cbcf8d1c2289503d8288d50df519ff4db7cca0ff9fe83c324/stub-external-configuration.tar.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paketo-buildpacks/apache-tomcat/HEAD/tomcat/testdata/22e708cfd301430cbcf8d1c2289503d8288d50df519ff4db7cca0ff9fe83c324/stub-external-configuration.tar.gz
--------------------------------------------------------------------------------
/tomcat/testdata/723126712c0b22a7fe409664adf1fbb78cf3040e313a82c06696f5058e190534/stub-tomcat-lifecycle-support.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paketo-buildpacks/apache-tomcat/HEAD/tomcat/testdata/723126712c0b22a7fe409664adf1fbb78cf3040e313a82c06696f5058e190534/stub-tomcat-lifecycle-support.jar
--------------------------------------------------------------------------------
/tomcat/testdata/d723bfe2ba67dfa92b24e3b6c7b2d0e6a963de7313350e306d470e44e330a5d2/stub-tomcat-access-logging-support.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paketo-buildpacks/apache-tomcat/HEAD/tomcat/testdata/d723bfe2ba67dfa92b24e3b6c7b2d0e6a963de7313350e306d470e44e330a5d2/stub-tomcat-access-logging-support.jar
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: gomod
4 | directory: /
5 | schedule:
6 | interval: daily
7 | ignore:
8 | - dependency-name: github.com/onsi/gomega
9 | labels:
10 | - semver:patch
11 | - type:dependency-upgrade
12 |
--------------------------------------------------------------------------------
/tomcat/testdata/060818cbcdc2008563f0f9e2428ecf4a199a5821c5b8b1dcd11a67666c1e2cd6/stub-external-configuration-with-directory.tar.gz:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/paketo-buildpacks/apache-tomcat/HEAD/tomcat/testdata/060818cbcdc2008563f0f9e2428ecf4a199a5821c5b8b1dcd11a67666c1e2cd6/stub-external-configuration-with-directory.tar.gz
--------------------------------------------------------------------------------
/tomcat/testdata/c31f9fd9b9458dd8dda54ce879dc7b08f8de0e638cb0936abcaa2316e7460c1e.toml:
--------------------------------------------------------------------------------
1 | id = "tomcat"
2 | uri = "https://localhost/stub-tomcat.tar.gz"
3 | sha256 = "c31f9fd9b9458dd8dda54ce879dc7b08f8de0e638cb0936abcaa2316e7460c1e"
4 | purl = "pkg:generic/tomcat@1.1.1"
5 | cpes = [
6 | "cpe:2.3:a:apache:tomcat:1.1.1:*:*:*:*:*:*:*"
7 | ]
--------------------------------------------------------------------------------
/tomcat/testdata/22e708cfd301430cbcf8d1c2289503d8288d50df519ff4db7cca0ff9fe83c324.toml:
--------------------------------------------------------------------------------
1 | id = "tomcat-external-configuration"
2 | uri = "https://localhost/stub-external-configuration.tar.gz"
3 | sha256 = "22e708cfd301430cbcf8d1c2289503d8288d50df519ff4db7cca0ff9fe83c324"
4 | purl = "pkg:generic/tomcat@1.1.1"
5 | cpes = [
6 | "cpe:2.3:a:apache:tomcat:1.1.1:*:*:*:*:*:*:*"
7 | ]
--------------------------------------------------------------------------------
/tomcat/testdata/060818cbcdc2008563f0f9e2428ecf4a199a5821c5b8b1dcd11a67666c1e2cd6.toml:
--------------------------------------------------------------------------------
1 | id = "tomcat-external-configuration"
2 | uri = "https://localhost/stub-external-configuration-with-directory.tar.gz"
3 | sha256 = "060818cbcdc2008563f0f9e2428ecf4a199a5821c5b8b1dcd11a67666c1e2cd6"
4 | purl = "pkg:generic/tomcat@1.1.1"
5 | cpes = [
6 | "cpe:2.3:a:apache:tomcat:1.1.1:*:*:*:*:*:*:*"
7 | ]
--------------------------------------------------------------------------------
/tomcat/testdata/e0a7e163cc9f1ffd41c8de3942c7c6b505090b7484c2ba9be846334e31c44a2c.toml:
--------------------------------------------------------------------------------
1 | id = "tomcat-logging-support"
2 | uri = "https://localhost/stub-tomcat-logging-support.jar"
3 | sha256 = "e0a7e163cc9f1ffd41c8de3942c7c6b505090b7484c2ba9be846334e31c44a2c"
4 | purl = "pkg:generic/tomcat-logging-support@3.3.0"
5 | cpes = [
6 | "cpe:2.3:a:cloudfoundry:tomcat-logging-support:3.3.0:*:*:*:*:*:*:*"
7 | ]
--------------------------------------------------------------------------------
/tomcat/testdata/723126712c0b22a7fe409664adf1fbb78cf3040e313a82c06696f5058e190534.toml:
--------------------------------------------------------------------------------
1 | id = "tomcat-lifecycle-support"
2 | uri = "https://localhost/stub-tomcat-lifecycle-support.jar"
3 | sha256 = "723126712c0b22a7fe409664adf1fbb78cf3040e313a82c06696f5058e190534"
4 | purl = "pkg:generic/tomcat-lifecycle-support@3.3.0"
5 | cpes = [
6 | "cpe:2.3:a:cloudfoundry:tomcat-lifecycle-support:3.3.0:*:*:*:*:*:*:*"
7 | ]
--------------------------------------------------------------------------------
/internal/util/containsWarFiles.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "os"
5 | "strings"
6 | )
7 |
8 | func ContainsWarFiles(dir string) (bool, error) {
9 | files, err := os.ReadDir(dir)
10 | if err != nil {
11 | return false, err
12 | }
13 |
14 | for _, file := range files {
15 | if strings.HasSuffix(file.Name(), ".war") {
16 | return true, nil
17 | }
18 | }
19 | return false, nil
20 | }
21 |
--------------------------------------------------------------------------------
/tomcat/testdata/d723bfe2ba67dfa92b24e3b6c7b2d0e6a963de7313350e306d470e44e330a5d2.toml:
--------------------------------------------------------------------------------
1 | id = "tomcat-access-logging-support"
2 | uri = "https://localhost/stub-tomcat-access-logging-support.jar"
3 | sha256 = "d723bfe2ba67dfa92b24e3b6c7b2d0e6a963de7313350e306d470e44e330a5d2"
4 | purl = "pkg:generic/tomcat-access-logging-support@3.3.0"
5 | cpes = [
6 | "cpe:2.3:a:cloudfoundry:tomcat-access-logging-support:3.3.0:*:*:*:*:*:*:*"
7 | ]
--------------------------------------------------------------------------------
/NOTICE:
--------------------------------------------------------------------------------
1 | apache-tomcat
2 |
3 | Copyright (c) 2020-Present CloudFoundry.org Foundation, Inc. All Rights Reserved.
4 |
5 | This project is licensed to you under the Apache License, Version 2.0 (the "License").
6 | You may not use this project except in compliance with the License.
7 |
8 | This project may include a number of subcomponents with separate copyright notices
9 | and license terms. Your use of these subcomponents is subject to the terms and
10 | conditions of the subcomponent's license, as noted in the LICENSE file.
11 |
--------------------------------------------------------------------------------
/.github/workflows/pb-synchronize-labels.yml:
--------------------------------------------------------------------------------
1 | name: Synchronize Labels
2 | "on":
3 | push:
4 | branches:
5 | - main
6 | paths:
7 | - .github/labels.yml
8 | jobs:
9 | synchronize:
10 | name: Synchronize Labels
11 | runs-on:
12 | - ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v4
15 | - uses: micnncim/action-label-syncer@v1
16 | env:
17 | GITHUB_TOKEN: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }}
18 |
--------------------------------------------------------------------------------
/internal/util/replaceInCatalinaProps.go:
--------------------------------------------------------------------------------
1 | package util
2 |
3 | import (
4 | "os"
5 | "strings"
6 | )
7 |
8 | func ReplaceInCatalinaProps(fileName string) error {
9 | input, err := os.ReadFile(fileName)
10 | if err != nil {
11 | return err
12 | }
13 |
14 | lines := strings.Split(string(input), "\n")
15 |
16 | for i, line := range lines {
17 | if strings.Contains(line, "common.loader=") {
18 | lines[i] = strings.Replace(lines[i], "common.loader=", "common.loader=${BPI_TOMCAT_ADDITIONAL_COMMON_JARS},", 1)
19 | }
20 | }
21 | output := strings.Join(lines, "\n")
22 | err = os.WriteFile(fileName, []byte(output), 0644)
23 | if err != nil {
24 | return err
25 | }
26 | return nil
27 | }
28 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Copyright 2018-2020 the original author or authors.
2 | #
3 | # Licensed under the Apache License, Version 2.0 (the "License");
4 | # you may not use this file except in compliance with the License.
5 | # You may obtain a copy of the License at
6 | #
7 | # https://www.apache.org/licenses/LICENSE-2.0
8 | #
9 | # Unless required by applicable law or agreed to in writing, software
10 | # distributed under the License is distributed on an "AS IS" BASIS,
11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 | # See the License for the specific language governing permissions and
13 | # limitations under the License.
14 |
15 | bin/
16 | linux/
17 | dependencies/
18 | package/
19 | scratch/
20 |
21 |
--------------------------------------------------------------------------------
/resources/context.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/.github/release-drafter.yml:
--------------------------------------------------------------------------------
1 | template: $CHANGES
2 | name-template: $RESOLVED_VERSION
3 | tag-template: v$RESOLVED_VERSION
4 | categories:
5 | - title: ⭐️ Enhancements
6 | labels:
7 | - type:enhancement
8 | - title: "\U0001F41E Bug Fixes"
9 | labels:
10 | - type:bug
11 | - title: "\U0001F4D4 Documentation"
12 | labels:
13 | - type:documentation
14 | - title: ⛏ Dependency Upgrades
15 | labels:
16 | - type:dependency-upgrade
17 | - title: "\U0001F6A7 Tasks"
18 | labels:
19 | - type:task
20 | exclude-labels:
21 | - type:question
22 | version-resolver:
23 | major:
24 | labels:
25 | - semver:major
26 | minor:
27 | labels:
28 | - semver:minor
29 | patch:
30 | labels:
31 | - semver:patch
32 | default: patch
33 |
--------------------------------------------------------------------------------
/.github/workflows/pb-minimal-labels.yml:
--------------------------------------------------------------------------------
1 | name: Minimal Labels
2 | "on":
3 | pull_request:
4 | types:
5 | - synchronize
6 | - reopened
7 | - labeled
8 | - unlabeled
9 | jobs:
10 | semver:
11 | name: Minimal Semver Labels
12 | runs-on:
13 | - ubuntu-latest
14 | steps:
15 | - uses: mheap/github-action-required-labels@v5
16 | with:
17 | count: 1
18 | labels: semver:major, semver:minor, semver:patch
19 | mode: exactly
20 | type:
21 | name: Minimal Type Labels
22 | runs-on:
23 | - ubuntu-latest
24 | steps:
25 | - uses: mheap/github-action-required-labels@v5
26 | with:
27 | count: 1
28 | labels: type:bug, type:dependency-upgrade, type:documentation, type:enhancement, type:question, type:task
29 | mode: exactly
30 |
--------------------------------------------------------------------------------
/scripts/build.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 | set -euo pipefail
3 |
4 | GOMOD=$(head -1 go.mod | awk '{print $2}')
5 | GOOS="linux" GOARCH="amd64" go build -ldflags='-s -w' -o "linux/amd64/bin/helper" "$GOMOD/cmd/helper"
6 | GOOS="linux" GOARCH="arm64" go build -ldflags='-s -w' -o "linux/arm64/bin/helper" "$GOMOD/cmd/helper"
7 | GOOS="linux" GOARCH="amd64" go build -ldflags='-s -w' -o linux/amd64/bin/main "$GOMOD/cmd/main"
8 | GOOS="linux" GOARCH="arm64" go build -ldflags='-s -w' -o linux/arm64/bin/main "$GOMOD/cmd/main"
9 |
10 | if [ "${STRIP:-false}" != "false" ]; then
11 | strip linux/amd64/bin/helper linux/arm64/bin/helper
12 | strip linux/amd64/bin/main linux/arm64/bin/main
13 | fi
14 |
15 | if [ "${COMPRESS:-none}" != "none" ]; then
16 | $COMPRESS linux/amd64/bin/helper linux/arm64/bin/helper
17 | $COMPRESS linux/amd64/bin/main linux/arm64/bin/main
18 | fi
19 |
20 | ln -fs main linux/amd64/bin/build
21 | ln -fs main linux/arm64/bin/build
22 | ln -fs main linux/amd64/bin/detect
23 | ln -fs main linux/arm64/bin/detect
--------------------------------------------------------------------------------
/helper/init_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2020 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 |
17 | package helper_test
18 |
19 | import (
20 | "testing"
21 |
22 | "github.com/sclevine/spec"
23 | "github.com/sclevine/spec/report"
24 | )
25 |
26 | func TestUnit(t *testing.T) {
27 | suite := spec.New("helper", spec.Report(report.Terminal{}))
28 | suite("AccessLoggingSupport", testAccessLoggingSupport)
29 | suite.Run(t)
30 | }
31 |
--------------------------------------------------------------------------------
/.github/workflows/pb-update-draft-release.yml:
--------------------------------------------------------------------------------
1 | name: Update Draft Release
2 | "on":
3 | push:
4 | branches:
5 | - main
6 | jobs:
7 | update:
8 | name: Update Draft Release
9 | runs-on:
10 | - ubuntu-latest
11 | steps:
12 | - id: release-drafter
13 | uses: release-drafter/release-drafter@v5
14 | env:
15 | GITHUB_TOKEN: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }}
16 | - uses: actions/checkout@v4
17 | - name: Update draft release with buildpack information
18 | uses: docker://ghcr.io/paketo-buildpacks/actions/draft-release:main
19 | with:
20 | github_token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }}
21 | release_body: ${{ steps.release-drafter.outputs.body }}
22 | release_id: ${{ steps.release-drafter.outputs.id }}
23 | release_name: ${{ steps.release-drafter.outputs.name }}
24 | release_tag_name: ${{ steps.release-drafter.outputs.tag_name }}
25 |
--------------------------------------------------------------------------------
/cmd/main/main.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2020 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 |
17 | package main
18 |
19 | import (
20 | "os"
21 |
22 | "github.com/paketo-buildpacks/libpak"
23 | "github.com/paketo-buildpacks/libpak/bard"
24 |
25 | "github.com/paketo-buildpacks/apache-tomcat/v8/tomcat"
26 | )
27 |
28 | func main() {
29 | logger := bard.NewLogger(os.Stdout)
30 |
31 | libpak.Main(
32 | tomcat.Detect{Logger: logger},
33 | tomcat.Build{Logger: logger},
34 | )
35 | }
36 |
--------------------------------------------------------------------------------
/tomcat/init_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2020 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 |
17 | package tomcat_test
18 |
19 | import (
20 | "testing"
21 |
22 | "github.com/sclevine/spec"
23 | "github.com/sclevine/spec/report"
24 | )
25 |
26 | func TestUnit(t *testing.T) {
27 | suite := spec.New("tomcat", spec.Report(report.Terminal{}))
28 | suite("Base", testBase)
29 | suite("Build", testBuild)
30 | suite("Detect", testDetect)
31 | suite("Home", testHome)
32 | suite.Run(t)
33 | }
34 |
--------------------------------------------------------------------------------
/cmd/helper/main.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2020 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 |
17 | package main
18 |
19 | import (
20 | "os"
21 |
22 | "github.com/paketo-buildpacks/libpak/bard"
23 | "github.com/paketo-buildpacks/libpak/sherpa"
24 |
25 | "github.com/paketo-buildpacks/apache-tomcat/v8/helper"
26 | )
27 |
28 | func main() {
29 | sherpa.Execute(func() error {
30 | return sherpa.Helpers(map[string]sherpa.ExecD{
31 | "access-logging-support": helper.AccessLoggingSupport{Logger: bard.NewLogger(os.Stdout)},
32 | })
33 | })
34 | }
35 |
--------------------------------------------------------------------------------
/resources/logging.properties:
--------------------------------------------------------------------------------
1 | #
2 | # Copyright 2018-2020 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 |
17 | handlers: org.cloudfoundry.tomcat.logging.CloudFoundryConsoleHandler
18 | .handlers: org.cloudfoundry.tomcat.logging.CloudFoundryConsoleHandler
19 |
20 | org.cloudfoundry.tomcat.logging.CloudFoundryConsoleHandler.level: FINE
21 |
22 | org.apache.catalina.core.ContainerBase.[Catalina].[localhost].level: INFO
23 | org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/manager].level: INFO
24 | org.apache.catalina.core.ContainerBase.[Catalina].[localhost].[/host-manager].level: INFO
25 |
--------------------------------------------------------------------------------
/.github/labels.yml:
--------------------------------------------------------------------------------
1 | - name: semver:major
2 | description: A change requiring a major version bump
3 | color: f9d0c4
4 | - name: semver:minor
5 | description: A change requiring a minor version bump
6 | color: f9d0c4
7 | - name: semver:patch
8 | description: A change requiring a patch version bump
9 | color: f9d0c4
10 | - name: type:bug
11 | description: A general bug
12 | color: e3d9fc
13 | - name: type:dependency-upgrade
14 | description: A dependency upgrade
15 | color: e3d9fc
16 | - name: type:documentation
17 | description: A documentation update
18 | color: e3d9fc
19 | - name: type:enhancement
20 | description: A general enhancement
21 | color: e3d9fc
22 | - name: type:question
23 | description: A user question
24 | color: e3d9fc
25 | - name: type:task
26 | description: A general task
27 | color: e3d9fc
28 | - name: type:informational
29 | description: Provides information or notice to the community
30 | color: e3d9fc
31 | - name: type:poll
32 | description: Request for feedback from the community
33 | color: e3d9fc
34 | - name: note:ideal-for-contribution
35 | description: An issue that a contributor can help us with
36 | color: 54f7a8
37 | - name: note:on-hold
38 | description: We can't start working on this issue yet
39 | color: 54f7a8
40 | - name: note:good-first-issue
41 | description: A good first issue to get started with
42 | color: 54f7a8
43 |
--------------------------------------------------------------------------------
/helper/access_logging_support.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2020 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 |
17 | package helper
18 |
19 | import (
20 | "os"
21 | "strings"
22 |
23 | "github.com/paketo-buildpacks/libpak/bard"
24 | )
25 |
26 | type AccessLoggingSupport struct {
27 | Logger bard.Logger
28 | }
29 |
30 | func (a AccessLoggingSupport) Execute() (map[string]string, error) {
31 | if _, ok := os.LookupEnv("BPL_TOMCAT_ACCESS_LOGGING_ENABLED"); !ok {
32 | return nil, nil
33 | }
34 |
35 | a.Logger.Info("Tomcat Access Logging Enabled")
36 |
37 | var values []string
38 | if s, ok := os.LookupEnv("JAVA_TOOL_OPTIONS"); ok {
39 | values = append(values, s)
40 | }
41 |
42 | values = append(values, "-Daccess.logging.enabled=true")
43 |
44 | return map[string]string{"JAVA_TOOL_OPTIONS": strings.Join(values, " ")}, nil
45 | }
46 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/paketo-buildpacks/apache-tomcat/v8
2 |
3 | go 1.25
4 |
5 | require (
6 | github.com/buildpacks/libcnb v1.30.4
7 | github.com/heroku/color v0.0.6
8 | github.com/onsi/gomega v1.38.3
9 | github.com/paketo-buildpacks/libjvm v1.46.0
10 | github.com/paketo-buildpacks/libpak v1.73.0
11 | github.com/sclevine/spec v1.4.0
12 | )
13 |
14 | require (
15 | github.com/BurntSushi/toml v1.5.0 // indirect
16 | github.com/Masterminds/semver/v3 v3.4.0 // indirect
17 | github.com/creack/pty v1.1.24 // indirect
18 | github.com/davecgh/go-spew v1.1.1 // indirect
19 | github.com/google/go-cmp v0.7.0 // indirect
20 | github.com/h2non/filetype v1.1.3 // indirect
21 | github.com/imdario/mergo v0.3.16 // indirect
22 | github.com/magiconair/properties v1.8.10 // indirect
23 | github.com/mattn/go-colorable v0.1.14 // indirect
24 | github.com/mattn/go-isatty v0.0.20 // indirect
25 | github.com/mattn/go-shellwords v1.0.12 // indirect
26 | github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect
27 | github.com/pavlo-v-chernykh/keystore-go/v4 v4.5.0 // indirect
28 | github.com/pmezard/go-difflib v1.0.0 // indirect
29 | github.com/stretchr/objx v0.5.3 // indirect
30 | github.com/stretchr/testify v1.11.1 // indirect
31 | github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 // indirect
32 | go.yaml.in/yaml/v3 v3.0.4 // indirect
33 | golang.org/x/crypto v0.46.0 // indirect
34 | golang.org/x/net v0.48.0 // indirect
35 | golang.org/x/sys v0.39.0 // indirect
36 | golang.org/x/text v0.32.0 // indirect
37 | gopkg.in/yaml.v3 v3.0.1 // indirect
38 | software.sslmate.com/src/go-pkcs12 v0.6.0 // indirect
39 | )
40 |
--------------------------------------------------------------------------------
/resources/server.xml:
--------------------------------------------------------------------------------
1 |
2 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
29 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/tomcat/home.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2020 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 |
17 | package tomcat
18 |
19 | import (
20 | "fmt"
21 | "os"
22 |
23 | "github.com/buildpacks/libcnb"
24 | "github.com/paketo-buildpacks/libpak"
25 | "github.com/paketo-buildpacks/libpak/bard"
26 | "github.com/paketo-buildpacks/libpak/crush"
27 | )
28 |
29 | type Home struct {
30 | LayerContributor libpak.DependencyLayerContributor
31 | Logger bard.Logger
32 | }
33 |
34 | func NewHome(dependency libpak.BuildpackDependency, cache libpak.DependencyCache) (Home, libcnb.BOMEntry) {
35 | contrib, entry := libpak.NewDependencyLayer(dependency, cache, libcnb.LayerTypes{
36 | Cache: true,
37 | Launch: true,
38 | })
39 | return Home{LayerContributor: contrib}, entry
40 | }
41 |
42 | func (h Home) Contribute(layer libcnb.Layer) (libcnb.Layer, error) {
43 | h.LayerContributor.Logger = h.Logger
44 |
45 | return h.LayerContributor.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) {
46 | h.Logger.Bodyf("Expanding to %s", layer.Path)
47 | if err := crush.Extract(artifact, layer.Path, 1); err != nil {
48 | return libcnb.Layer{}, fmt.Errorf("unable to expand Tomcat\n%w", err)
49 | }
50 |
51 | layer.LaunchEnvironment.Default("CATALINA_HOME", layer.Path)
52 |
53 | return layer, nil
54 | })
55 | }
56 |
57 | func (h Home) Name() string {
58 | return h.LayerContributor.LayerName()
59 | }
60 |
--------------------------------------------------------------------------------
/helper/access_logging_support_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2020 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 |
17 | package helper_test
18 |
19 | import (
20 | "os"
21 | "testing"
22 |
23 | . "github.com/onsi/gomega"
24 | "github.com/sclevine/spec"
25 |
26 | "github.com/paketo-buildpacks/apache-tomcat/v8/helper"
27 | )
28 |
29 | func testAccessLoggingSupport(t *testing.T, context spec.G, it spec.S) {
30 | var (
31 | Expect = NewWithT(t).Expect
32 |
33 | a = helper.AccessLoggingSupport{}
34 | )
35 |
36 | it("returns if $BPL_ACCESS_LOGGING_ENABLED is not set", func() {
37 | Expect(a.Execute()).To(BeNil())
38 | })
39 |
40 | context("$BPL_TOMCAT_ACCESS_LOGGING_ENABLED", func() {
41 | it.Before(func() {
42 | Expect(os.Setenv("BPL_TOMCAT_ACCESS_LOGGING_ENABLED", "")).To(Succeed())
43 | })
44 |
45 | it.After(func() {
46 | Expect(os.Unsetenv("BPL_TOMCAT_ACCESS_LOGGING_ENABLED")).To(Succeed())
47 | })
48 |
49 | it("contributes configuration", func() {
50 | Expect(a.Execute()).To(Equal(map[string]string{"JAVA_TOOL_OPTIONS": "-Daccess.logging.enabled=true"}))
51 | })
52 |
53 | context("$JAVA_TOOL_OPTIONS", func() {
54 | it.Before(func() {
55 | Expect(os.Setenv("JAVA_TOOL_OPTIONS", "test-java-tool-options")).To(Succeed())
56 | })
57 |
58 | it.After(func() {
59 | Expect(os.Unsetenv("JAVA_TOOL_OPTIONS")).To(Succeed())
60 | })
61 |
62 | it("contributes configuration appended to existing $JAVA_TOOL_OPTIONS", func() {
63 | Expect(a.Execute()).To(Equal(map[string]string{
64 | "JAVA_TOOL_OPTIONS": "test-java-tool-options -Daccess.logging.enabled=true",
65 | }))
66 | })
67 | })
68 | })
69 |
70 | }
71 |
--------------------------------------------------------------------------------
/tomcat/home_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2020 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 |
17 | package tomcat_test
18 |
19 | import (
20 | "os"
21 | "path/filepath"
22 | "testing"
23 |
24 | "github.com/buildpacks/libcnb"
25 | . "github.com/onsi/gomega"
26 | "github.com/paketo-buildpacks/libpak"
27 | "github.com/sclevine/spec"
28 |
29 | "github.com/paketo-buildpacks/apache-tomcat/v8/tomcat"
30 | )
31 |
32 | func testHome(t *testing.T, context spec.G, it spec.S) {
33 | var (
34 | Expect = NewWithT(t).Expect
35 |
36 | ctx libcnb.BuildContext
37 | )
38 |
39 | it.Before(func() {
40 | var err error
41 |
42 | ctx.Layers.Path, err = os.MkdirTemp("", "home-layers")
43 | Expect(err).NotTo(HaveOccurred())
44 | })
45 |
46 | it.After(func() {
47 | Expect(os.RemoveAll(ctx.Layers.Path)).To(Succeed())
48 | })
49 |
50 | it("contributes catalina home", func() {
51 | dep := libpak.BuildpackDependency{
52 | ID: "tomcat",
53 | URI: "https://localhost/stub-tomcat.tar.gz",
54 | SHA256: "c31f9fd9b9458dd8dda54ce879dc7b08f8de0e638cb0936abcaa2316e7460c1e",
55 | PURL: "pkg:generic/tomcat@1.1.1",
56 | CPEs: []string{"cpe:2.3:a:apache:tomcat:1.1.1:*:*:*:*:*:*:*"},
57 | }
58 | dc := libpak.DependencyCache{CachePath: "testdata"}
59 |
60 | h, _ := tomcat.NewHome(dep, dc)
61 |
62 | layer, err := ctx.Layers.Layer("test-layer")
63 | Expect(err).NotTo(HaveOccurred())
64 |
65 | layer, err = h.Contribute(layer)
66 | Expect(err).NotTo(HaveOccurred())
67 |
68 | Expect(layer.Launch).To(BeTrue())
69 | Expect(filepath.Join(layer.Path, "fixture-marker")).To(BeARegularFile())
70 | Expect(layer.LaunchEnvironment["CATALINA_HOME.default"]).To(Equal(layer.Path))
71 | })
72 | }
73 |
--------------------------------------------------------------------------------
/.github/pipeline-descriptor.yml:
--------------------------------------------------------------------------------
1 | github:
2 | username: ${{ secrets.JAVA_GITHUB_USERNAME }}
3 | token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }}
4 |
5 | codeowners:
6 | - path: "*"
7 | owner: "@paketo-buildpacks/java-maintainers"
8 |
9 | helpers:
10 | "bin/helper": "$GOMOD/cmd/helper"
11 |
12 | package:
13 | repositories: ["docker.io/paketobuildpacks/apache-tomcat"]
14 | register: true
15 | registry_token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }}
16 |
17 | docker_credentials:
18 | - registry: docker.io
19 | username: ${{ secrets.PAKETO_BUILDPACKS_DOCKERHUB_USERNAME }}
20 | password: ${{ secrets.PAKETO_BUILDPACKS_DOCKERHUB_PASSWORD }}
21 |
22 | dependencies:
23 | - name: Tomcat 9
24 | id: tomcat
25 | version_pattern: "9\\.[\\d]+\\.[\\d]+"
26 | uses: docker://ghcr.io/paketo-buildpacks/actions/tomcat-dependency:main
27 | with:
28 | uri: https://downloads.apache.org/tomcat/tomcat-9
29 | - name: Tomcat 10.1
30 | id: tomcat
31 | version_pattern: "10\\.1\\.[\\d]+"
32 | uses: docker://ghcr.io/paketo-buildpacks/actions/tomcat-dependency:main
33 | with:
34 | uri: https://downloads.apache.org/tomcat/tomcat-10
35 | version_regex: "(10)\\.(1)\\.([\\d]+)"
36 | - name: Tomcat 11
37 | id: tomcat
38 | version_pattern: "11\\.[\\d]+.[\\d]+"
39 | uses: docker://ghcr.io/paketo-buildpacks/actions/tomcat-dependency:main
40 | with:
41 | uri: https://downloads.apache.org/tomcat/tomcat-11
42 | version_regex: "(11)\\.([\\d]+)\\.([\\d]+)"
43 | - id: tomcat-access-logging-support
44 | uses: docker://ghcr.io/paketo-buildpacks/actions/maven-dependency:main
45 | with:
46 | uri: https://repo1.maven.org/maven2
47 | group_id: org.cloudfoundry
48 | artifact_id: tomcat-access-logging-support
49 | version_regex: "^[\\d]+\\.[\\d]+\\.[\\d]+\\.RELEASE$"
50 | source_classifier: sources
51 | - id: tomcat-lifecycle-support
52 | uses: docker://ghcr.io/paketo-buildpacks/actions/maven-dependency:main
53 | with:
54 | uri: https://repo1.maven.org/maven2
55 | group_id: org.cloudfoundry
56 | artifact_id: tomcat-lifecycle-support
57 | version_regex: "^[\\d]+\\.[\\d]+\\.[\\d]+\\.RELEASE$"
58 | source_classifier: sources
59 | - id: tomcat-logging-support
60 | uses: docker://ghcr.io/paketo-buildpacks/actions/maven-dependency:main
61 | with:
62 | uri: https://repo1.maven.org/maven2
63 | group_id: org.cloudfoundry
64 | artifact_id: tomcat-logging-support
65 | version_regex: "^[\\d]+\\.[\\d]+\\.[\\d]+\\.RELEASE$"
66 | source_classifier: sources
67 |
--------------------------------------------------------------------------------
/.github/workflows/pb-update-go.yml:
--------------------------------------------------------------------------------
1 | name: Update Go
2 | "on":
3 | schedule:
4 | - cron: 37 2 * * 1
5 | workflow_dispatch: {}
6 | jobs:
7 | update:
8 | name: Update Go
9 | runs-on:
10 | - ubuntu-latest
11 | steps:
12 | - uses: actions/setup-go@v5
13 | with:
14 | go-version: "1.25"
15 | - uses: actions/checkout@v4
16 | - name: Update Go Version & Modules
17 | id: update-go
18 | run: |
19 | #!/usr/bin/env bash
20 |
21 | set -euo pipefail
22 |
23 | if [ -z "${GO_VERSION:-}" ]; then
24 | echo "No go version set"
25 | exit 1
26 | fi
27 |
28 | OLD_GO_VERSION=$(grep -P '^go \d\.\d+' go.mod | cut -d ' ' -f 2 | cut -d '.' -f 1-2)
29 |
30 | go mod edit -go="$GO_VERSION"
31 | go mod tidy
32 | go get -u -t ./...
33 | go mod tidy
34 |
35 | git add go.mod go.sum
36 | git checkout -- .
37 |
38 | if [ "$OLD_GO_VERSION" == "$GO_VERSION" ]; then
39 | COMMIT_TITLE="Bump Go Modules"
40 | COMMIT_BODY="Bumps Go modules used by the project. See the commit for details on what modules were updated."
41 | COMMIT_SEMVER="semver:patch"
42 | else
43 | COMMIT_TITLE="Bump Go from ${OLD_GO_VERSION} to ${GO_VERSION}"
44 | COMMIT_BODY="Bumps Go from ${OLD_GO_VERSION} to ${GO_VERSION} and update Go modules used by the project. See the commit for details on what modules were updated."
45 | COMMIT_SEMVER="semver:minor"
46 | fi
47 |
48 | echo "commit-title=${COMMIT_TITLE}" >> "$GITHUB_OUTPUT"
49 | echo "commit-body=${COMMIT_BODY}" >> "$GITHUB_OUTPUT"
50 | echo "commit-semver=${COMMIT_SEMVER}" >> "$GITHUB_OUTPUT"
51 | env:
52 | GO_VERSION: "1.25"
53 | - uses: peter-evans/create-pull-request@v6
54 | with:
55 | author: ${{ secrets.JAVA_GITHUB_USERNAME }} <${{ secrets.JAVA_GITHUB_USERNAME }}@users.noreply.github.com>
56 | body: |-
57 | ${{ steps.update-go.outputs.commit-body }}
58 |
59 |
60 | Release Notes
61 | ${{ steps.pipeline.outputs.release-notes }}
62 |
63 | branch: update/go
64 | commit-message: |-
65 | ${{ steps.update-go.outputs.commit-title }}
66 |
67 | ${{ steps.update-go.outputs.commit-body }}
68 | delete-branch: true
69 | labels: ${{ steps.update-go.outputs.commit-semver }}, type:task
70 | signoff: true
71 | title: ${{ steps.update-go.outputs.commit-title }}
72 | token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }}
73 |
--------------------------------------------------------------------------------
/tomcat/detect.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2020 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 |
17 | package tomcat
18 |
19 | import (
20 | "fmt"
21 | "os"
22 | "path/filepath"
23 |
24 | "github.com/buildpacks/libcnb"
25 | "github.com/paketo-buildpacks/apache-tomcat/v8/internal/util"
26 | "github.com/paketo-buildpacks/libjvm"
27 | "github.com/paketo-buildpacks/libpak"
28 | "github.com/paketo-buildpacks/libpak/bard"
29 | )
30 |
31 | const (
32 | PlanEntryJVMApplication = "jvm-application"
33 | PlanEntryJVMApplicationPackage = "jvm-application-package"
34 | PlanEntryJRE = "jre"
35 | PlanEntrySyft = "syft"
36 | PlanEntryJavaApplicationServer = "java-app-server"
37 | JavaAppServerTomcat = "tomcat"
38 | )
39 |
40 | type Detect struct {
41 | Logger bard.Logger
42 | }
43 |
44 | func (d Detect) Detect(context libcnb.DetectContext) (libcnb.DetectResult, error) {
45 | cr, err := libpak.NewConfigurationResolver(context.Buildpack, &d.Logger)
46 | if err != nil {
47 | return libcnb.DetectResult{}, fmt.Errorf("unable to create configuration resolver\n%w", err)
48 | }
49 |
50 | appServer, _ := cr.Resolve("BP_JAVA_APP_SERVER")
51 | if appServer != "" && appServer != JavaAppServerTomcat {
52 | d.Logger.Infof("SKIPPED: buildpack does not match requested app server of [%s], buildpack supports [%s]", appServer, JavaAppServerTomcat)
53 | return libcnb.DetectResult{Pass: false}, nil
54 | }
55 |
56 | warFilesExist, _ := util.ContainsWarFiles(context.Application.Path)
57 | if !warFilesExist {
58 | m, err := libjvm.NewManifest(context.Application.Path)
59 | if err != nil {
60 | return libcnb.DetectResult{}, fmt.Errorf("unable to read manifest\n%w", err)
61 | }
62 |
63 | if _, ok := m.Get("Main-Class"); ok {
64 | d.Logger.Info("SKIPPED: Manifest attribute 'Main-Class' was found")
65 | return libcnb.DetectResult{Pass: false}, nil
66 | }
67 | }
68 |
69 | result := libcnb.DetectResult{
70 | Pass: true,
71 | Plans: []libcnb.BuildPlan{
72 | {
73 | Provides: []libcnb.BuildPlanProvide{
74 | {Name: PlanEntryJVMApplication},
75 | {Name: PlanEntryJavaApplicationServer},
76 | },
77 | Requires: []libcnb.BuildPlanRequire{
78 | {Name: PlanEntrySyft},
79 | {Name: PlanEntryJRE, Metadata: map[string]interface{}{"launch": true}},
80 | {Name: PlanEntryJVMApplicationPackage},
81 | {Name: PlanEntryJVMApplication},
82 | {Name: PlanEntryJavaApplicationServer},
83 | },
84 | },
85 | },
86 | }
87 |
88 | file := filepath.Join(context.Application.Path, "WEB-INF")
89 | if _, err := os.Stat(file); err != nil && !os.IsNotExist(err) {
90 | return libcnb.DetectResult{}, fmt.Errorf("unable to stat file %s\n%w", file, err)
91 | } else if os.IsNotExist(err) && !warFilesExist {
92 | d.Logger.Info("PASSED: a WEB-INF directory was not found, this is normal when building from source")
93 | return result, nil
94 | }
95 |
96 | result.Plans[0].Provides = append(result.Plans[0].Provides, libcnb.BuildPlanProvide{Name: PlanEntryJVMApplicationPackage})
97 | return result, nil
98 | }
99 |
--------------------------------------------------------------------------------
/.github/workflows/pb-update-pipeline.yml:
--------------------------------------------------------------------------------
1 | name: Update Pipeline
2 | "on":
3 | push:
4 | branches:
5 | - main
6 | paths:
7 | - .github/pipeline-descriptor.yml
8 | schedule:
9 | - cron: 0 5 * * 1-5
10 | workflow_dispatch: {}
11 | jobs:
12 | update:
13 | name: Update Pipeline
14 | runs-on:
15 | - ubuntu-latest
16 | steps:
17 | - uses: actions/setup-go@v5
18 | with:
19 | go-version: "1.25"
20 | - name: Install octo
21 | run: |
22 | #!/usr/bin/env bash
23 |
24 | set -euo pipefail
25 |
26 | go install -ldflags="-s -w" github.com/paketo-buildpacks/pipeline-builder/cmd/octo@latest
27 | - uses: actions/checkout@v4
28 | - name: Update Pipeline
29 | id: pipeline
30 | run: |
31 | #!/usr/bin/env bash
32 |
33 | set -euo pipefail
34 |
35 | if [[ -f .github/pipeline-version ]]; then
36 | OLD_VERSION=$(cat .github/pipeline-version)
37 | else
38 | OLD_VERSION="0.0.0"
39 | fi
40 |
41 | rm .github/workflows/pb-*.yml || true
42 | octo --descriptor "${DESCRIPTOR}"
43 |
44 | PAYLOAD=$(gh api /repos/paketo-buildpacks/pipeline-builder/releases/latest)
45 |
46 | NEW_VERSION=$(jq -n -r --argjson PAYLOAD "${PAYLOAD}" '$PAYLOAD.name')
47 | echo "${NEW_VERSION}" > .github/pipeline-version
48 |
49 | RELEASE_NOTES=$(
50 | gh api \
51 | -F text="$(jq -n -r --argjson PAYLOAD "${PAYLOAD}" '$PAYLOAD.body')" \
52 | -F mode="gfm" \
53 | -F context="paketo-buildpacks/pipeline-builder" \
54 | -X POST /markdown
55 | )
56 |
57 | git add .github/
58 | git add .gitignore
59 |
60 | if [ -f scripts/build.sh ]; then
61 | git add scripts/build.sh
62 | fi
63 |
64 | git checkout -- .
65 |
66 | echo "old-version=${OLD_VERSION}" >> "$GITHUB_OUTPUT"
67 | echo "new-version=${NEW_VERSION}" >> "$GITHUB_OUTPUT"
68 |
69 | DELIMITER=$(openssl rand -hex 16) # roughly the same entropy as uuid v4 used in https://github.com/actions/toolkit/blob/b36e70495fbee083eb20f600eafa9091d832577d/packages/core/src/file-command.ts#L28
70 | printf "release-notes<<%s\n%s\n%s\n" "${DELIMITER}" "${RELEASE_NOTES}" "${DELIMITER}" >> "${GITHUB_OUTPUT}" # see https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#multiline-strings
71 | env:
72 | DESCRIPTOR: .github/pipeline-descriptor.yml
73 | GITHUB_TOKEN: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }}
74 | - uses: peter-evans/create-pull-request@v6
75 | with:
76 | author: ${{ secrets.JAVA_GITHUB_USERNAME }} <${{ secrets.JAVA_GITHUB_USERNAME }}@users.noreply.github.com>
77 | body: |-
78 | Bumps pipeline from `${{ steps.pipeline.outputs.old-version }}` to `${{ steps.pipeline.outputs.new-version }}`.
79 |
80 |
81 | Release Notes
82 | ${{ steps.pipeline.outputs.release-notes }}
83 |
84 | branch: update/pipeline
85 | commit-message: |-
86 | Bump pipeline from ${{ steps.pipeline.outputs.old-version }} to ${{ steps.pipeline.outputs.new-version }}
87 |
88 | Bumps pipeline from ${{ steps.pipeline.outputs.old-version }} to ${{ steps.pipeline.outputs.new-version }}.
89 | delete-branch: true
90 | labels: semver:patch, type:task
91 | signoff: true
92 | title: Bump pipeline from ${{ steps.pipeline.outputs.old-version }} to ${{ steps.pipeline.outputs.new-version }}
93 | token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }}
94 |
--------------------------------------------------------------------------------
/.github/workflows/pb-update-tomcat-9.yml:
--------------------------------------------------------------------------------
1 | name: Update Tomcat 9
2 | "on":
3 | schedule:
4 | - cron: 0 5 * * 1-5
5 | workflow_dispatch: {}
6 | jobs:
7 | update:
8 | name: Update Buildpack Dependency
9 | runs-on:
10 | - ubuntu-latest
11 | steps:
12 | - uses: actions/setup-go@v5
13 | with:
14 | go-version: "1.25"
15 | - name: Install update-buildpack-dependency
16 | run: |
17 | #!/usr/bin/env bash
18 |
19 | set -euo pipefail
20 |
21 | go install -ldflags="-s -w" github.com/paketo-buildpacks/libpak/cmd/update-buildpack-dependency@latest
22 | - uses: buildpacks/github-actions/setup-tools@v5.9.7
23 | with:
24 | crane-version: 0.20.3
25 | yj-version: 5.1.0
26 | - uses: actions/checkout@v4
27 | - id: dependency
28 | uses: docker://ghcr.io/paketo-buildpacks/actions/tomcat-dependency:main
29 | with:
30 | uri: https://downloads.apache.org/tomcat/tomcat-9
31 | - name: Update Buildpack Dependency
32 | id: buildpack
33 | run: |
34 | #!/usr/bin/env bash
35 |
36 | set -euo pipefail
37 |
38 | VERSION_DEPS=$(yj -tj < buildpack.toml | jq -r ".metadata.dependencies[] | select( .id == env.ID ) | select( .version | test( env.VERSION_PATTERN ) )")
39 | ARCH=${ARCH:-amd64}
40 | OLD_VERSION=$(echo "$VERSION_DEPS" | jq -r 'select( .purl | contains( env.ARCH ) ) | .version')
41 |
42 | if [ -z "$OLD_VERSION" ]; then
43 | ARCH="" # empty means noarch
44 | OLD_VERSION=$(echo "$VERSION_DEPS" | jq -r ".version")
45 | fi
46 |
47 | update-buildpack-dependency \
48 | --buildpack-toml buildpack.toml \
49 | --id "${ID}" \
50 | --arch "${ARCH}" \
51 | --version-pattern "${VERSION_PATTERN}" \
52 | --version "${VERSION}" \
53 | --cpe-pattern "${CPE_PATTERN:-}" \
54 | --cpe "${CPE:-}" \
55 | --purl-pattern "${PURL_PATTERN:-}" \
56 | --purl "${PURL:-}" \
57 | --uri "${URI}" \
58 | --sha256 "${SHA256}" \
59 | --source "${SOURCE_URI}" \
60 | --source-sha256 "${SOURCE_SHA256}"
61 |
62 | git add buildpack.toml
63 | git checkout -- .
64 |
65 | if [ "$(echo "$OLD_VERSION" | awk -F '.' '{print $1}')" != "$(echo "$VERSION" | awk -F '.' '{print $1}')" ]; then
66 | LABEL="semver:major"
67 | elif [ "$(echo "$OLD_VERSION" | awk -F '.' '{print $2}')" != "$(echo "$VERSION" | awk -F '.' '{print $2}')" ]; then
68 | LABEL="semver:minor"
69 | else
70 | LABEL="semver:patch"
71 | fi
72 |
73 | echo "old-version=${OLD_VERSION}" >> "$GITHUB_OUTPUT"
74 | echo "new-version=${VERSION}" >> "$GITHUB_OUTPUT"
75 | echo "version-label=${LABEL}" >> "$GITHUB_OUTPUT"
76 | env:
77 | ARCH: ""
78 | CPE: ${{ steps.dependency.outputs.cpe }}
79 | CPE_PATTERN: ""
80 | ID: tomcat
81 | PURL: ${{ steps.dependency.outputs.purl }}
82 | PURL_PATTERN: ""
83 | SHA256: ${{ steps.dependency.outputs.sha256 }}
84 | SOURCE_SHA256: ${{ steps.dependency.outputs.source_sha256 }}
85 | SOURCE_URI: ${{ steps.dependency.outputs.source }}
86 | URI: ${{ steps.dependency.outputs.uri }}
87 | VERSION: ${{ steps.dependency.outputs.version }}
88 | VERSION_PATTERN: 9\.[\d]+\.[\d]+
89 | - uses: peter-evans/create-pull-request@v6
90 | with:
91 | author: ${{ secrets.JAVA_GITHUB_USERNAME }} <${{ secrets.JAVA_GITHUB_USERNAME }}@users.noreply.github.com>
92 | body: Bumps `Tomcat 9` from `${{ steps.buildpack.outputs.old-version }}` to `${{ steps.buildpack.outputs.new-version }}`.
93 | branch: update/buildpack/tomcat-9
94 | commit-message: |-
95 | Bump Tomcat 9 from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }}
96 |
97 | Bumps Tomcat 9 from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }}.
98 | delete-branch: true
99 | labels: ${{ steps.buildpack.outputs.version-label }}, type:dependency-upgrade
100 | signoff: true
101 | title: Bump Tomcat 9 from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }}
102 | token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }}
103 |
--------------------------------------------------------------------------------
/.github/workflows/pb-update-tomcat-11.yml:
--------------------------------------------------------------------------------
1 | name: Update Tomcat 11
2 | "on":
3 | schedule:
4 | - cron: 0 5 * * 1-5
5 | workflow_dispatch: {}
6 | jobs:
7 | update:
8 | name: Update Buildpack Dependency
9 | runs-on:
10 | - ubuntu-latest
11 | steps:
12 | - uses: actions/setup-go@v5
13 | with:
14 | go-version: "1.25"
15 | - name: Install update-buildpack-dependency
16 | run: |
17 | #!/usr/bin/env bash
18 |
19 | set -euo pipefail
20 |
21 | go install -ldflags="-s -w" github.com/paketo-buildpacks/libpak/cmd/update-buildpack-dependency@latest
22 | - uses: buildpacks/github-actions/setup-tools@v5.9.7
23 | with:
24 | crane-version: 0.20.3
25 | yj-version: 5.1.0
26 | - uses: actions/checkout@v4
27 | - id: dependency
28 | uses: docker://ghcr.io/paketo-buildpacks/actions/tomcat-dependency:main
29 | with:
30 | uri: https://downloads.apache.org/tomcat/tomcat-11
31 | version_regex: (11)\.([\d]+)\.([\d]+)
32 | - name: Update Buildpack Dependency
33 | id: buildpack
34 | run: |
35 | #!/usr/bin/env bash
36 |
37 | set -euo pipefail
38 |
39 | VERSION_DEPS=$(yj -tj < buildpack.toml | jq -r ".metadata.dependencies[] | select( .id == env.ID ) | select( .version | test( env.VERSION_PATTERN ) )")
40 | ARCH=${ARCH:-amd64}
41 | OLD_VERSION=$(echo "$VERSION_DEPS" | jq -r 'select( .purl | contains( env.ARCH ) ) | .version')
42 |
43 | if [ -z "$OLD_VERSION" ]; then
44 | ARCH="" # empty means noarch
45 | OLD_VERSION=$(echo "$VERSION_DEPS" | jq -r ".version")
46 | fi
47 |
48 | update-buildpack-dependency \
49 | --buildpack-toml buildpack.toml \
50 | --id "${ID}" \
51 | --arch "${ARCH}" \
52 | --version-pattern "${VERSION_PATTERN}" \
53 | --version "${VERSION}" \
54 | --cpe-pattern "${CPE_PATTERN:-}" \
55 | --cpe "${CPE:-}" \
56 | --purl-pattern "${PURL_PATTERN:-}" \
57 | --purl "${PURL:-}" \
58 | --uri "${URI}" \
59 | --sha256 "${SHA256}" \
60 | --source "${SOURCE_URI}" \
61 | --source-sha256 "${SOURCE_SHA256}"
62 |
63 | git add buildpack.toml
64 | git checkout -- .
65 |
66 | if [ "$(echo "$OLD_VERSION" | awk -F '.' '{print $1}')" != "$(echo "$VERSION" | awk -F '.' '{print $1}')" ]; then
67 | LABEL="semver:major"
68 | elif [ "$(echo "$OLD_VERSION" | awk -F '.' '{print $2}')" != "$(echo "$VERSION" | awk -F '.' '{print $2}')" ]; then
69 | LABEL="semver:minor"
70 | else
71 | LABEL="semver:patch"
72 | fi
73 |
74 | echo "old-version=${OLD_VERSION}" >> "$GITHUB_OUTPUT"
75 | echo "new-version=${VERSION}" >> "$GITHUB_OUTPUT"
76 | echo "version-label=${LABEL}" >> "$GITHUB_OUTPUT"
77 | env:
78 | ARCH: ""
79 | CPE: ${{ steps.dependency.outputs.cpe }}
80 | CPE_PATTERN: ""
81 | ID: tomcat
82 | PURL: ${{ steps.dependency.outputs.purl }}
83 | PURL_PATTERN: ""
84 | SHA256: ${{ steps.dependency.outputs.sha256 }}
85 | SOURCE_SHA256: ${{ steps.dependency.outputs.source_sha256 }}
86 | SOURCE_URI: ${{ steps.dependency.outputs.source }}
87 | URI: ${{ steps.dependency.outputs.uri }}
88 | VERSION: ${{ steps.dependency.outputs.version }}
89 | VERSION_PATTERN: 11\.[\d]+.[\d]+
90 | - uses: peter-evans/create-pull-request@v6
91 | with:
92 | author: ${{ secrets.JAVA_GITHUB_USERNAME }} <${{ secrets.JAVA_GITHUB_USERNAME }}@users.noreply.github.com>
93 | body: Bumps `Tomcat 11` from `${{ steps.buildpack.outputs.old-version }}` to `${{ steps.buildpack.outputs.new-version }}`.
94 | branch: update/buildpack/tomcat-11
95 | commit-message: |-
96 | Bump Tomcat 11 from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }}
97 |
98 | Bumps Tomcat 11 from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }}.
99 | delete-branch: true
100 | labels: ${{ steps.buildpack.outputs.version-label }}, type:dependency-upgrade
101 | signoff: true
102 | title: Bump Tomcat 11 from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }}
103 | token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }}
104 |
--------------------------------------------------------------------------------
/.github/workflows/pb-update-tomcat-10-1.yml:
--------------------------------------------------------------------------------
1 | name: Update Tomcat 10.1
2 | "on":
3 | schedule:
4 | - cron: 0 5 * * 1-5
5 | workflow_dispatch: {}
6 | jobs:
7 | update:
8 | name: Update Buildpack Dependency
9 | runs-on:
10 | - ubuntu-latest
11 | steps:
12 | - uses: actions/setup-go@v5
13 | with:
14 | go-version: "1.25"
15 | - name: Install update-buildpack-dependency
16 | run: |
17 | #!/usr/bin/env bash
18 |
19 | set -euo pipefail
20 |
21 | go install -ldflags="-s -w" github.com/paketo-buildpacks/libpak/cmd/update-buildpack-dependency@latest
22 | - uses: buildpacks/github-actions/setup-tools@v5.9.7
23 | with:
24 | crane-version: 0.20.3
25 | yj-version: 5.1.0
26 | - uses: actions/checkout@v4
27 | - id: dependency
28 | uses: docker://ghcr.io/paketo-buildpacks/actions/tomcat-dependency:main
29 | with:
30 | uri: https://downloads.apache.org/tomcat/tomcat-10
31 | version_regex: (10)\.(1)\.([\d]+)
32 | - name: Update Buildpack Dependency
33 | id: buildpack
34 | run: |
35 | #!/usr/bin/env bash
36 |
37 | set -euo pipefail
38 |
39 | VERSION_DEPS=$(yj -tj < buildpack.toml | jq -r ".metadata.dependencies[] | select( .id == env.ID ) | select( .version | test( env.VERSION_PATTERN ) )")
40 | ARCH=${ARCH:-amd64}
41 | OLD_VERSION=$(echo "$VERSION_DEPS" | jq -r 'select( .purl | contains( env.ARCH ) ) | .version')
42 |
43 | if [ -z "$OLD_VERSION" ]; then
44 | ARCH="" # empty means noarch
45 | OLD_VERSION=$(echo "$VERSION_DEPS" | jq -r ".version")
46 | fi
47 |
48 | update-buildpack-dependency \
49 | --buildpack-toml buildpack.toml \
50 | --id "${ID}" \
51 | --arch "${ARCH}" \
52 | --version-pattern "${VERSION_PATTERN}" \
53 | --version "${VERSION}" \
54 | --cpe-pattern "${CPE_PATTERN:-}" \
55 | --cpe "${CPE:-}" \
56 | --purl-pattern "${PURL_PATTERN:-}" \
57 | --purl "${PURL:-}" \
58 | --uri "${URI}" \
59 | --sha256 "${SHA256}" \
60 | --source "${SOURCE_URI}" \
61 | --source-sha256 "${SOURCE_SHA256}"
62 |
63 | git add buildpack.toml
64 | git checkout -- .
65 |
66 | if [ "$(echo "$OLD_VERSION" | awk -F '.' '{print $1}')" != "$(echo "$VERSION" | awk -F '.' '{print $1}')" ]; then
67 | LABEL="semver:major"
68 | elif [ "$(echo "$OLD_VERSION" | awk -F '.' '{print $2}')" != "$(echo "$VERSION" | awk -F '.' '{print $2}')" ]; then
69 | LABEL="semver:minor"
70 | else
71 | LABEL="semver:patch"
72 | fi
73 |
74 | echo "old-version=${OLD_VERSION}" >> "$GITHUB_OUTPUT"
75 | echo "new-version=${VERSION}" >> "$GITHUB_OUTPUT"
76 | echo "version-label=${LABEL}" >> "$GITHUB_OUTPUT"
77 | env:
78 | ARCH: ""
79 | CPE: ${{ steps.dependency.outputs.cpe }}
80 | CPE_PATTERN: ""
81 | ID: tomcat
82 | PURL: ${{ steps.dependency.outputs.purl }}
83 | PURL_PATTERN: ""
84 | SHA256: ${{ steps.dependency.outputs.sha256 }}
85 | SOURCE_SHA256: ${{ steps.dependency.outputs.source_sha256 }}
86 | SOURCE_URI: ${{ steps.dependency.outputs.source }}
87 | URI: ${{ steps.dependency.outputs.uri }}
88 | VERSION: ${{ steps.dependency.outputs.version }}
89 | VERSION_PATTERN: 10\.1\.[\d]+
90 | - uses: peter-evans/create-pull-request@v6
91 | with:
92 | author: ${{ secrets.JAVA_GITHUB_USERNAME }} <${{ secrets.JAVA_GITHUB_USERNAME }}@users.noreply.github.com>
93 | body: Bumps `Tomcat 10.1` from `${{ steps.buildpack.outputs.old-version }}` to `${{ steps.buildpack.outputs.new-version }}`.
94 | branch: update/buildpack/tomcat-10-1
95 | commit-message: |-
96 | Bump Tomcat 10.1 from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }}
97 |
98 | Bumps Tomcat 10.1 from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }}.
99 | delete-branch: true
100 | labels: ${{ steps.buildpack.outputs.version-label }}, type:dependency-upgrade
101 | signoff: true
102 | title: Bump Tomcat 10.1 from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }}
103 | token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }}
104 |
--------------------------------------------------------------------------------
/.github/workflows/pb-update-tomcat-logging-support.yml:
--------------------------------------------------------------------------------
1 | name: Update tomcat-logging-support
2 | "on":
3 | schedule:
4 | - cron: 0 5 * * 1-5
5 | workflow_dispatch: {}
6 | jobs:
7 | update:
8 | name: Update Buildpack Dependency
9 | runs-on:
10 | - ubuntu-latest
11 | steps:
12 | - uses: actions/setup-go@v5
13 | with:
14 | go-version: "1.25"
15 | - name: Install update-buildpack-dependency
16 | run: |
17 | #!/usr/bin/env bash
18 |
19 | set -euo pipefail
20 |
21 | go install -ldflags="-s -w" github.com/paketo-buildpacks/libpak/cmd/update-buildpack-dependency@latest
22 | - uses: buildpacks/github-actions/setup-tools@v5.9.7
23 | with:
24 | crane-version: 0.20.3
25 | yj-version: 5.1.0
26 | - uses: actions/checkout@v4
27 | - id: dependency
28 | uses: docker://ghcr.io/paketo-buildpacks/actions/maven-dependency:main
29 | with:
30 | artifact_id: tomcat-logging-support
31 | group_id: org.cloudfoundry
32 | source_classifier: sources
33 | uri: https://repo1.maven.org/maven2
34 | version_regex: ^[\d]+\.[\d]+\.[\d]+\.RELEASE$
35 | - name: Update Buildpack Dependency
36 | id: buildpack
37 | run: |
38 | #!/usr/bin/env bash
39 |
40 | set -euo pipefail
41 |
42 | VERSION_DEPS=$(yj -tj < buildpack.toml | jq -r ".metadata.dependencies[] | select( .id == env.ID ) | select( .version | test( env.VERSION_PATTERN ) )")
43 | ARCH=${ARCH:-amd64}
44 | OLD_VERSION=$(echo "$VERSION_DEPS" | jq -r 'select( .purl | contains( env.ARCH ) ) | .version')
45 |
46 | if [ -z "$OLD_VERSION" ]; then
47 | ARCH="" # empty means noarch
48 | OLD_VERSION=$(echo "$VERSION_DEPS" | jq -r ".version")
49 | fi
50 |
51 | update-buildpack-dependency \
52 | --buildpack-toml buildpack.toml \
53 | --id "${ID}" \
54 | --arch "${ARCH}" \
55 | --version-pattern "${VERSION_PATTERN}" \
56 | --version "${VERSION}" \
57 | --cpe-pattern "${CPE_PATTERN:-}" \
58 | --cpe "${CPE:-}" \
59 | --purl-pattern "${PURL_PATTERN:-}" \
60 | --purl "${PURL:-}" \
61 | --uri "${URI}" \
62 | --sha256 "${SHA256}" \
63 | --source "${SOURCE_URI}" \
64 | --source-sha256 "${SOURCE_SHA256}"
65 |
66 | git add buildpack.toml
67 | git checkout -- .
68 |
69 | if [ "$(echo "$OLD_VERSION" | awk -F '.' '{print $1}')" != "$(echo "$VERSION" | awk -F '.' '{print $1}')" ]; then
70 | LABEL="semver:major"
71 | elif [ "$(echo "$OLD_VERSION" | awk -F '.' '{print $2}')" != "$(echo "$VERSION" | awk -F '.' '{print $2}')" ]; then
72 | LABEL="semver:minor"
73 | else
74 | LABEL="semver:patch"
75 | fi
76 |
77 | echo "old-version=${OLD_VERSION}" >> "$GITHUB_OUTPUT"
78 | echo "new-version=${VERSION}" >> "$GITHUB_OUTPUT"
79 | echo "version-label=${LABEL}" >> "$GITHUB_OUTPUT"
80 | env:
81 | ARCH: ""
82 | CPE: ${{ steps.dependency.outputs.cpe }}
83 | CPE_PATTERN: ""
84 | ID: tomcat-logging-support
85 | PURL: ${{ steps.dependency.outputs.purl }}
86 | PURL_PATTERN: ""
87 | SHA256: ${{ steps.dependency.outputs.sha256 }}
88 | SOURCE_SHA256: ${{ steps.dependency.outputs.source_sha256 }}
89 | SOURCE_URI: ${{ steps.dependency.outputs.source }}
90 | URI: ${{ steps.dependency.outputs.uri }}
91 | VERSION: ${{ steps.dependency.outputs.version }}
92 | VERSION_PATTERN: '[\d]+\.[\d]+\.[\d]+'
93 | - uses: peter-evans/create-pull-request@v6
94 | with:
95 | author: ${{ secrets.JAVA_GITHUB_USERNAME }} <${{ secrets.JAVA_GITHUB_USERNAME }}@users.noreply.github.com>
96 | body: Bumps `tomcat-logging-support` from `${{ steps.buildpack.outputs.old-version }}` to `${{ steps.buildpack.outputs.new-version }}`.
97 | branch: update/buildpack/tomcat-logging-support
98 | commit-message: |-
99 | Bump tomcat-logging-support from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }}
100 |
101 | Bumps tomcat-logging-support from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }}.
102 | delete-branch: true
103 | labels: ${{ steps.buildpack.outputs.version-label }}, type:dependency-upgrade
104 | signoff: true
105 | title: Bump tomcat-logging-support from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }}
106 | token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }}
107 |
--------------------------------------------------------------------------------
/.github/workflows/pb-update-tomcat-lifecycle-support.yml:
--------------------------------------------------------------------------------
1 | name: Update tomcat-lifecycle-support
2 | "on":
3 | schedule:
4 | - cron: 0 5 * * 1-5
5 | workflow_dispatch: {}
6 | jobs:
7 | update:
8 | name: Update Buildpack Dependency
9 | runs-on:
10 | - ubuntu-latest
11 | steps:
12 | - uses: actions/setup-go@v5
13 | with:
14 | go-version: "1.25"
15 | - name: Install update-buildpack-dependency
16 | run: |
17 | #!/usr/bin/env bash
18 |
19 | set -euo pipefail
20 |
21 | go install -ldflags="-s -w" github.com/paketo-buildpacks/libpak/cmd/update-buildpack-dependency@latest
22 | - uses: buildpacks/github-actions/setup-tools@v5.9.7
23 | with:
24 | crane-version: 0.20.3
25 | yj-version: 5.1.0
26 | - uses: actions/checkout@v4
27 | - id: dependency
28 | uses: docker://ghcr.io/paketo-buildpacks/actions/maven-dependency:main
29 | with:
30 | artifact_id: tomcat-lifecycle-support
31 | group_id: org.cloudfoundry
32 | source_classifier: sources
33 | uri: https://repo1.maven.org/maven2
34 | version_regex: ^[\d]+\.[\d]+\.[\d]+\.RELEASE$
35 | - name: Update Buildpack Dependency
36 | id: buildpack
37 | run: |
38 | #!/usr/bin/env bash
39 |
40 | set -euo pipefail
41 |
42 | VERSION_DEPS=$(yj -tj < buildpack.toml | jq -r ".metadata.dependencies[] | select( .id == env.ID ) | select( .version | test( env.VERSION_PATTERN ) )")
43 | ARCH=${ARCH:-amd64}
44 | OLD_VERSION=$(echo "$VERSION_DEPS" | jq -r 'select( .purl | contains( env.ARCH ) ) | .version')
45 |
46 | if [ -z "$OLD_VERSION" ]; then
47 | ARCH="" # empty means noarch
48 | OLD_VERSION=$(echo "$VERSION_DEPS" | jq -r ".version")
49 | fi
50 |
51 | update-buildpack-dependency \
52 | --buildpack-toml buildpack.toml \
53 | --id "${ID}" \
54 | --arch "${ARCH}" \
55 | --version-pattern "${VERSION_PATTERN}" \
56 | --version "${VERSION}" \
57 | --cpe-pattern "${CPE_PATTERN:-}" \
58 | --cpe "${CPE:-}" \
59 | --purl-pattern "${PURL_PATTERN:-}" \
60 | --purl "${PURL:-}" \
61 | --uri "${URI}" \
62 | --sha256 "${SHA256}" \
63 | --source "${SOURCE_URI}" \
64 | --source-sha256 "${SOURCE_SHA256}"
65 |
66 | git add buildpack.toml
67 | git checkout -- .
68 |
69 | if [ "$(echo "$OLD_VERSION" | awk -F '.' '{print $1}')" != "$(echo "$VERSION" | awk -F '.' '{print $1}')" ]; then
70 | LABEL="semver:major"
71 | elif [ "$(echo "$OLD_VERSION" | awk -F '.' '{print $2}')" != "$(echo "$VERSION" | awk -F '.' '{print $2}')" ]; then
72 | LABEL="semver:minor"
73 | else
74 | LABEL="semver:patch"
75 | fi
76 |
77 | echo "old-version=${OLD_VERSION}" >> "$GITHUB_OUTPUT"
78 | echo "new-version=${VERSION}" >> "$GITHUB_OUTPUT"
79 | echo "version-label=${LABEL}" >> "$GITHUB_OUTPUT"
80 | env:
81 | ARCH: ""
82 | CPE: ${{ steps.dependency.outputs.cpe }}
83 | CPE_PATTERN: ""
84 | ID: tomcat-lifecycle-support
85 | PURL: ${{ steps.dependency.outputs.purl }}
86 | PURL_PATTERN: ""
87 | SHA256: ${{ steps.dependency.outputs.sha256 }}
88 | SOURCE_SHA256: ${{ steps.dependency.outputs.source_sha256 }}
89 | SOURCE_URI: ${{ steps.dependency.outputs.source }}
90 | URI: ${{ steps.dependency.outputs.uri }}
91 | VERSION: ${{ steps.dependency.outputs.version }}
92 | VERSION_PATTERN: '[\d]+\.[\d]+\.[\d]+'
93 | - uses: peter-evans/create-pull-request@v6
94 | with:
95 | author: ${{ secrets.JAVA_GITHUB_USERNAME }} <${{ secrets.JAVA_GITHUB_USERNAME }}@users.noreply.github.com>
96 | body: Bumps `tomcat-lifecycle-support` from `${{ steps.buildpack.outputs.old-version }}` to `${{ steps.buildpack.outputs.new-version }}`.
97 | branch: update/buildpack/tomcat-lifecycle-support
98 | commit-message: |-
99 | Bump tomcat-lifecycle-support from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }}
100 |
101 | Bumps tomcat-lifecycle-support from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }}.
102 | delete-branch: true
103 | labels: ${{ steps.buildpack.outputs.version-label }}, type:dependency-upgrade
104 | signoff: true
105 | title: Bump tomcat-lifecycle-support from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }}
106 | token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }}
107 |
--------------------------------------------------------------------------------
/.github/workflows/pb-update-tomcat-access-logging-support.yml:
--------------------------------------------------------------------------------
1 | name: Update tomcat-access-logging-support
2 | "on":
3 | schedule:
4 | - cron: 0 5 * * 1-5
5 | workflow_dispatch: {}
6 | jobs:
7 | update:
8 | name: Update Buildpack Dependency
9 | runs-on:
10 | - ubuntu-latest
11 | steps:
12 | - uses: actions/setup-go@v5
13 | with:
14 | go-version: "1.25"
15 | - name: Install update-buildpack-dependency
16 | run: |
17 | #!/usr/bin/env bash
18 |
19 | set -euo pipefail
20 |
21 | go install -ldflags="-s -w" github.com/paketo-buildpacks/libpak/cmd/update-buildpack-dependency@latest
22 | - uses: buildpacks/github-actions/setup-tools@v5.9.7
23 | with:
24 | crane-version: 0.20.3
25 | yj-version: 5.1.0
26 | - uses: actions/checkout@v4
27 | - id: dependency
28 | uses: docker://ghcr.io/paketo-buildpacks/actions/maven-dependency:main
29 | with:
30 | artifact_id: tomcat-access-logging-support
31 | group_id: org.cloudfoundry
32 | source_classifier: sources
33 | uri: https://repo1.maven.org/maven2
34 | version_regex: ^[\d]+\.[\d]+\.[\d]+\.RELEASE$
35 | - name: Update Buildpack Dependency
36 | id: buildpack
37 | run: |
38 | #!/usr/bin/env bash
39 |
40 | set -euo pipefail
41 |
42 | VERSION_DEPS=$(yj -tj < buildpack.toml | jq -r ".metadata.dependencies[] | select( .id == env.ID ) | select( .version | test( env.VERSION_PATTERN ) )")
43 | ARCH=${ARCH:-amd64}
44 | OLD_VERSION=$(echo "$VERSION_DEPS" | jq -r 'select( .purl | contains( env.ARCH ) ) | .version')
45 |
46 | if [ -z "$OLD_VERSION" ]; then
47 | ARCH="" # empty means noarch
48 | OLD_VERSION=$(echo "$VERSION_DEPS" | jq -r ".version")
49 | fi
50 |
51 | update-buildpack-dependency \
52 | --buildpack-toml buildpack.toml \
53 | --id "${ID}" \
54 | --arch "${ARCH}" \
55 | --version-pattern "${VERSION_PATTERN}" \
56 | --version "${VERSION}" \
57 | --cpe-pattern "${CPE_PATTERN:-}" \
58 | --cpe "${CPE:-}" \
59 | --purl-pattern "${PURL_PATTERN:-}" \
60 | --purl "${PURL:-}" \
61 | --uri "${URI}" \
62 | --sha256 "${SHA256}" \
63 | --source "${SOURCE_URI}" \
64 | --source-sha256 "${SOURCE_SHA256}"
65 |
66 | git add buildpack.toml
67 | git checkout -- .
68 |
69 | if [ "$(echo "$OLD_VERSION" | awk -F '.' '{print $1}')" != "$(echo "$VERSION" | awk -F '.' '{print $1}')" ]; then
70 | LABEL="semver:major"
71 | elif [ "$(echo "$OLD_VERSION" | awk -F '.' '{print $2}')" != "$(echo "$VERSION" | awk -F '.' '{print $2}')" ]; then
72 | LABEL="semver:minor"
73 | else
74 | LABEL="semver:patch"
75 | fi
76 |
77 | echo "old-version=${OLD_VERSION}" >> "$GITHUB_OUTPUT"
78 | echo "new-version=${VERSION}" >> "$GITHUB_OUTPUT"
79 | echo "version-label=${LABEL}" >> "$GITHUB_OUTPUT"
80 | env:
81 | ARCH: ""
82 | CPE: ${{ steps.dependency.outputs.cpe }}
83 | CPE_PATTERN: ""
84 | ID: tomcat-access-logging-support
85 | PURL: ${{ steps.dependency.outputs.purl }}
86 | PURL_PATTERN: ""
87 | SHA256: ${{ steps.dependency.outputs.sha256 }}
88 | SOURCE_SHA256: ${{ steps.dependency.outputs.source_sha256 }}
89 | SOURCE_URI: ${{ steps.dependency.outputs.source }}
90 | URI: ${{ steps.dependency.outputs.uri }}
91 | VERSION: ${{ steps.dependency.outputs.version }}
92 | VERSION_PATTERN: '[\d]+\.[\d]+\.[\d]+'
93 | - uses: peter-evans/create-pull-request@v6
94 | with:
95 | author: ${{ secrets.JAVA_GITHUB_USERNAME }} <${{ secrets.JAVA_GITHUB_USERNAME }}@users.noreply.github.com>
96 | body: Bumps `tomcat-access-logging-support` from `${{ steps.buildpack.outputs.old-version }}` to `${{ steps.buildpack.outputs.new-version }}`.
97 | branch: update/buildpack/tomcat-access-logging-support
98 | commit-message: |-
99 | Bump tomcat-access-logging-support from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }}
100 |
101 | Bumps tomcat-access-logging-support from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }}.
102 | delete-branch: true
103 | labels: ${{ steps.buildpack.outputs.version-label }}, type:dependency-upgrade
104 | signoff: true
105 | title: Bump tomcat-access-logging-support from ${{ steps.buildpack.outputs.old-version }} to ${{ steps.buildpack.outputs.new-version }}
106 | token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }}
107 |
--------------------------------------------------------------------------------
/tomcat/detect_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2020 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 |
17 | package tomcat_test
18 |
19 | import (
20 | "io"
21 | "os"
22 | "path/filepath"
23 | "testing"
24 |
25 | "github.com/buildpacks/libcnb"
26 | . "github.com/onsi/gomega"
27 | "github.com/sclevine/spec"
28 |
29 | "github.com/paketo-buildpacks/apache-tomcat/v8/tomcat"
30 | )
31 |
32 | func testDetect(t *testing.T, context spec.G, it spec.S) {
33 | var (
34 | Expect = NewWithT(t).Expect
35 |
36 | ctx libcnb.DetectContext
37 | detect tomcat.Detect
38 | path string
39 | )
40 |
41 | it.Before(func() {
42 | var err error
43 | path, err = os.MkdirTemp("", "tomcat")
44 | Expect(err).NotTo(HaveOccurred())
45 |
46 | ctx.Application.Path = path
47 | })
48 |
49 | it.After(func() {
50 | Expect(os.RemoveAll(path)).To(Succeed())
51 | })
52 |
53 | it("fails with Main-Class", func() {
54 | Expect(os.MkdirAll(filepath.Join(path, "META-INF"), 0755)).To(Succeed())
55 | Expect(os.WriteFile(filepath.Join(path, "META-INF", "MANIFEST.MF"), []byte(`Main-Class: test-main-class`), 0644)).To(Succeed())
56 |
57 | Expect(detect.Detect(ctx)).To(Equal(libcnb.DetectResult{Pass: false}))
58 | })
59 |
60 | context("WEB-INF not found", func() {
61 | it("requires jvm-application-artifact", func() {
62 | Expect(detect.Detect(ctx)).To(Equal(libcnb.DetectResult{
63 | Pass: true,
64 | Plans: []libcnb.BuildPlan{
65 | {
66 | Provides: []libcnb.BuildPlanProvide{
67 | {Name: "jvm-application"},
68 | {Name: "java-app-server"},
69 | },
70 | Requires: []libcnb.BuildPlanRequire{
71 | {Name: "syft"},
72 | {Name: "jre", Metadata: map[string]interface{}{"launch": true}},
73 | {Name: "jvm-application-package"},
74 | {Name: "jvm-application"},
75 | {Name: "java-app-server"},
76 | },
77 | },
78 | },
79 | }))
80 | })
81 | })
82 |
83 | context("WEB-INF found", func() {
84 | it.Before(func() {
85 | Expect(os.MkdirAll(filepath.Join(path, "WEB-INF"), 0755)).To(Succeed())
86 | })
87 |
88 | it("requires and provides jvm-application-artifact", func() {
89 | Expect(detect.Detect(ctx)).To(Equal(libcnb.DetectResult{
90 | Pass: true,
91 | Plans: []libcnb.BuildPlan{
92 | {
93 | Provides: []libcnb.BuildPlanProvide{
94 | {Name: "jvm-application"},
95 | {Name: "java-app-server"},
96 | {Name: "jvm-application-package"},
97 | },
98 | Requires: []libcnb.BuildPlanRequire{
99 | {Name: "syft"},
100 | {Name: "jre", Metadata: map[string]interface{}{"launch": true}},
101 | {Name: "jvm-application-package"},
102 | {Name: "jvm-application"},
103 | {Name: "java-app-server"},
104 | },
105 | },
106 | },
107 | }))
108 | })
109 | })
110 |
111 | context("BP_JAVA_APP_SERVER is set to `tomcat`", func() {
112 | it.Before(func() {
113 | Expect(os.Setenv("BP_JAVA_APP_SERVER", "tomcat")).To(Succeed())
114 | })
115 |
116 | it.After(func() {
117 | Expect(os.Unsetenv("BP_JAVA_APP_SERVER")).To(Succeed())
118 | })
119 |
120 | it("contributes Tomcat", func() {
121 | Expect(detect.Detect(ctx)).To(Equal(libcnb.DetectResult{
122 | Pass: true,
123 | Plans: []libcnb.BuildPlan{
124 | {
125 | Provides: []libcnb.BuildPlanProvide{
126 | {Name: "jvm-application"},
127 | {Name: "java-app-server"},
128 | },
129 | Requires: []libcnb.BuildPlanRequire{
130 | {Name: "syft"},
131 | {Name: "jre", Metadata: map[string]interface{}{"launch": true}},
132 | {Name: "jvm-application-package"},
133 | {Name: "jvm-application"},
134 | {Name: "java-app-server"},
135 | },
136 | },
137 | },
138 | }))
139 | })
140 | })
141 |
142 | context("BP_JAVA_APP_SERVER is set to `foo`", func() {
143 | it.Before(func() {
144 | Expect(os.Setenv("BP_JAVA_APP_SERVER", "foo")).To(Succeed())
145 | })
146 |
147 | it.After(func() {
148 | Expect(os.Unsetenv("BP_JAVA_APP_SERVER")).To(Succeed())
149 | })
150 |
151 | it("fails", func() {
152 | Expect(detect.Detect(ctx)).To(Equal(libcnb.DetectResult{Pass: false}))
153 | })
154 | })
155 |
156 | context("Multiple war files found", func() {
157 | files := []string{"api.war", "ui.war"}
158 |
159 | it.Before(func() {
160 | for _, file := range files {
161 | in, err := os.Open(filepath.Join("testdata", "warfiles", file))
162 | Expect(err).NotTo(HaveOccurred())
163 |
164 | out, err := os.OpenFile(filepath.Join(ctx.Application.Path, file), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
165 | Expect(err).NotTo(HaveOccurred())
166 |
167 | _, err = io.Copy(out, in)
168 | Expect(err).NotTo(HaveOccurred())
169 | Expect(in.Close()).To(Succeed())
170 | Expect(out.Close()).To(Succeed())
171 | }
172 | Expect(os.Setenv("BP_JAVA_APP_SERVER", "tomcat")).To(Succeed())
173 | })
174 |
175 | it.After(func() {
176 | Expect(os.Unsetenv("BP_JAVA_APP_SERVER")).To(Succeed())
177 | for _, file := range files {
178 | os.Remove(filepath.Join(ctx.Application.Path, file))
179 | }
180 | })
181 |
182 | it("contributes Tomcat", func() {
183 | Expect(detect.Detect(ctx)).To(Equal(libcnb.DetectResult{
184 | Pass: true,
185 | Plans: []libcnb.BuildPlan{
186 | {
187 | Provides: []libcnb.BuildPlanProvide{
188 | {Name: "jvm-application"},
189 | {Name: "java-app-server"},
190 | {Name: "jvm-application-package"},
191 | },
192 | Requires: []libcnb.BuildPlanRequire{
193 | {Name: "syft"},
194 | {Name: "jre", Metadata: map[string]interface{}{"launch": true}},
195 | {Name: "jvm-application-package"},
196 | {Name: "jvm-application"},
197 | {Name: "java-app-server"},
198 | },
199 | },
200 | },
201 | }))
202 | })
203 | })
204 | }
205 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg=
2 | github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho=
3 | github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
4 | github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
5 | github.com/buildpacks/libcnb v1.30.4 h1:Jp6cJxYsZQgqix+lpRdSpjHt5bv5yCJqgkw9zWmS6xU=
6 | github.com/buildpacks/libcnb v1.30.4/go.mod h1:vjEDAlK3/Rf67AcmBzphXoqIlbdFgBNUK5d8wjreJbY=
7 | github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s=
8 | github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
9 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
10 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
11 | github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
12 | github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
13 | github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
14 | github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
15 | github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
16 | github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
17 | github.com/google/pprof v0.0.0-20250403155104-27863c87afa6 h1:BHT72Gu3keYf3ZEu2J0b1vyeLSOYI8bm5wbJM/8yDe8=
18 | github.com/google/pprof v0.0.0-20250403155104-27863c87afa6/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
19 | github.com/h2non/filetype v1.1.3 h1:FKkx9QbD7HR/zjK1Ia5XiBsq9zdLi5Kf3zGyFTAFkGg=
20 | github.com/h2non/filetype v1.1.3/go.mod h1:319b3zT68BvV+WRj7cwy856M2ehB3HqNOt6sy1HndBY=
21 | github.com/heroku/color v0.0.6 h1:UTFFMrmMLFcL3OweqP1lAdp8i1y/9oHqkeHjQ/b/Ny0=
22 | github.com/heroku/color v0.0.6/go.mod h1:ZBvOcx7cTF2QKOv4LbmoBtNl5uB17qWxGuzZrsi1wLU=
23 | github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4=
24 | github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY=
25 | github.com/jarcoal/httpmock v1.3.1 h1:iUx3whfZWVf3jT01hQTO/Eo5sAYtB2/rqaUuOtpInww=
26 | github.com/jarcoal/httpmock v1.3.1/go.mod h1:3yb8rc4BI7TCBhFY8ng0gjuLKJNquuDNiPaZjnENuYg=
27 | github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
28 | github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
29 | github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
30 | github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE=
31 | github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8=
32 | github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
33 | github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
34 | github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
35 | github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk=
36 | github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y=
37 | github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4=
38 | github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE=
39 | github.com/onsi/ginkgo/v2 v2.25.3 h1:Ty8+Yi/ayDAGtk4XxmmfUy4GabvM+MegeB4cDLRi6nw=
40 | github.com/onsi/ginkgo/v2 v2.25.3/go.mod h1:43uiyQC4Ed2tkOzLsEYm7hnrb7UJTWHYNsuy3bG/snE=
41 | github.com/onsi/gomega v1.38.3 h1:eTX+W6dobAYfFeGC2PV6RwXRu/MyT+cQguijutvkpSM=
42 | github.com/onsi/gomega v1.38.3/go.mod h1:ZCU1pkQcXDO5Sl9/VVEGlDyp+zm0m1cmeG5TOzLgdh4=
43 | github.com/paketo-buildpacks/libjvm v1.46.0 h1:+mEOsK30a1if0T3ZvSs6di/w5cp/j14uD3DyYUusavI=
44 | github.com/paketo-buildpacks/libjvm v1.46.0/go.mod h1:jNQuS8SQfbbHN9kenMpBhpxaE0uCa9ZkKLrN89IU0VY=
45 | github.com/paketo-buildpacks/libpak v1.73.0 h1:OgdkOn4VLIzRo0WcSx1iRmqeLrcMAZbIk7pOOJSyl5Q=
46 | github.com/paketo-buildpacks/libpak v1.73.0/go.mod h1:EY01BAEtNPT1kI+/OTGTAkitNzKiFzCTGAmxapBUPJ4=
47 | github.com/pavlo-v-chernykh/keystore-go/v4 v4.5.0 h1:2nosf3P75OZv2/ZO/9Px5ZgZ5gbKrzA3joN1QMfOGMQ=
48 | github.com/pavlo-v-chernykh/keystore-go/v4 v4.5.0/go.mod h1:lAVhWwbNaveeJmxrxuSTxMgKpF6DjnuVpn6T8WiBwYQ=
49 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
50 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
51 | github.com/sclevine/spec v1.4.0 h1:z/Q9idDcay5m5irkZ28M7PtQM4aOISzOpj4bUPkDee8=
52 | github.com/sclevine/spec v1.4.0/go.mod h1:LvpgJaFyvQzRvc1kaDs0bulYwzC70PbiYjC4QnFHkOM=
53 | github.com/stretchr/objx v0.5.3 h1:jmXUvGomnU1o3W/V5h2VEradbpJDwGrzugQQvL0POH4=
54 | github.com/stretchr/objx v0.5.3/go.mod h1:rDQraq+vQZU7Fde9LOZLr8Tax6zZvy4kuNKF+QYS+U0=
55 | github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
56 | github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
57 | github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8 h1:nIPpBwaJSVYIxUFsDv3M8ofmx9yWTog9BfvIu0q41lo=
58 | github.com/xi2/xz v0.0.0-20171230120015-48954b6210f8/go.mod h1:HUYIGzjTL3rfEspMxjDjgmT5uz5wzYJKVo23qUhYTos=
59 | go.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=
60 | go.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=
61 | go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
62 | go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
63 | golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
64 | golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
65 | golang.org/x/net v0.48.0 h1:zyQRTTrjc33Lhh0fBgT/H3oZq9WuvRR5gPC70xpDiQU=
66 | golang.org/x/net v0.48.0/go.mod h1:+ndRgGjkh8FGtu1w1FGbEC31if4VrNVMuKTgcAAnQRY=
67 | golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
68 | golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
69 | golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk=
70 | golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
71 | golang.org/x/text v0.32.0 h1:ZD01bjUt1FQ9WJ0ClOL5vxgxOI/sVCNgX1YtKwcY0mU=
72 | golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY=
73 | golang.org/x/tools v0.39.0 h1:ik4ho21kwuQln40uelmciQPp9SipgNDdrafrYA4TmQQ=
74 | golang.org/x/tools v0.39.0/go.mod h1:JnefbkDPyD8UU2kI5fuf8ZX4/yUeh9W877ZeBONxUqQ=
75 | google.golang.org/protobuf v1.36.7 h1:IgrO7UwFQGJdRNXH/sQux4R1Dj1WAKcLElzeeRaXV2A=
76 | google.golang.org/protobuf v1.36.7/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
77 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
78 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
79 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
80 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
81 | software.sslmate.com/src/go-pkcs12 v0.6.0 h1:f3sQittAeF+pao32Vb+mkli+ZyT+VwKaD014qFGq6oU=
82 | software.sslmate.com/src/go-pkcs12 v0.6.0/go.mod h1:Qiz0EyvDRJjjxGyUQa2cCNZn/wMyzrRJ/qcDXOQazLI=
83 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Paketo Buildpack for Apache Tomcat
2 |
3 | ## Buildpack ID: `paketo-buildpacks/apache-tomcat`
4 | ## Registry URLs: `docker.io/paketobuildpacks/apache-tomcat`
5 |
6 | The Paketo Buildpack for Apache Tomcat is a Cloud Native Buildpack that contributes Apache Tomcat and Process Types for WARs.
7 |
8 | ## Behavior
9 |
10 | This buildpack will participate if all of the following conditions are met
11 |
12 | * `$BP_JAVA_APP_SERVER` is `tomcat` or if `$BP_JAVA_APP_SERVER` is unset or empty and this is the first buildpack to provide a Java application server.
13 | * `/WEB-INF` exists
14 | * `Main-Class` is NOT defined in the manifest
15 |
16 | The buildpack will do the following:
17 |
18 | * Requests that a JRE be installed
19 | * Contribute a Tomcat instance to `$CATALINA_HOME`
20 | * Contribute a Tomcat instance to `$CATALINA_BASE`
21 | * Contribute `context.xml`, `logging.properties`, `server.xml`, and `web.xml` to `conf/`
22 | * Contribute [Access Logging Support][als], [Lifecycle Support][lcs], and [Logging Support][lgs]
23 | * Contribute external configuration if available
24 | * Contributes `tomcat`, `task`, and `web` process types
25 |
26 | ### Tiny Stack
27 |
28 | When this buildpack runs on the [Tiny stack](https://paketo.io/docs/concepts/stacks/#tiny), which has no shell, the following notes apply:
29 | * As there is no shell, the `catalina.sh` script cannot be used to start Tomcat
30 | * The Tomcat Buildpack will generate a start command directly. It does not support all the functionality in `catalina.sh`.
31 | * Some configuration options such as `bin/setenv.sh` and setting `CATALINA_*` environment variables, will not be available.
32 | * Tomcat will be run with `umask` set to `0022` instead of the `catalina.sh`provided default of `0027`
33 |
34 | [als]: https://github.com/cloudfoundry/java-buildpack-support/tree/master/tomcat-access-logging-support
35 | [lcs]: https://github.com/cloudfoundry/java-buildpack-support/tree/master/tomcat-lifecycle-support
36 | [lgs]: https://github.com/cloudfoundry/java-buildpack-support/tree/master/tomcat-logging-support
37 |
38 | ## Configuration
39 | | Environment Variable | Description |
40 | | ----------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
41 | | `$BP_JAVA_APP_SERVER` | The application server to use. It defaults to `` (empty string) which means that order dictates which Java application server is installed. The first Java application server buildpack to run will be picked. |
42 | | `$BP_TOMCAT_CONTEXT_PATH` | The context path to mount the application at. Defaults to empty (`ROOT`). |
43 | | `$BP_TOMCAT_EXT_CONF_SHA256` | The SHA256 hash of the external configuration package |
44 | | `$BP_TOMCAT_ENV_PROPERTY_SOURCE_DISABLED` | When true the buildpack will not configure `org.apache.tomcat.util.digester.EnvironmentPropertySource`. This configuration option is added to support loading configuration from environment variables and referencing them in Tomcat configuration files. |
45 | | `$BP_TOMCAT_EXT_CONF_STRIP` | The number of directory levels to strip from the external configuration package. Defaults to `0`. |
46 | | `$BP_TOMCAT_EXT_CONF_URI` | The download URI of the external configuration package |
47 | | `$BP_TOMCAT_EXT_CONF_VERSION` | The version of the external configuration package |
48 | | `$BP_TOMCAT_VERSION` | Configure a specific Tomcat version. This value must _exactly_ match a version available in the buildpack so typically it would configured to a wildcard such as `9.*`. |
49 | | `BPL_TOMCAT_ACCESS_LOGGING_ENABLED` | Whether access logging should be activated. Defaults to inactive. |
50 | | `BPI_TOMCAT_ADDITIONAL_JARS` | This should only be used in other buildpacks to include a `jar` to the tomcat classpath. Several `jars` must be separated by `:`. |
51 | | `BPI_TOMCAT_ADDITIONAL_COMMON_JARS` | This should be used by other buildpacks to include additional locations to be class loaded by the tomcat common classloader. For example a buildpack might contribute resources in its dedicated layer and add the location with this variable to be classloaded additionally by Tomcat. Both folder paths as well as single `jar` file paths can be specified. |
52 |
53 | ### External Configuration Package
54 | The artifacts that the repository provides must be in TAR format and must follow the Tomcat archive structure:
55 |
56 | ```
57 |
58 | └── conf
59 | ├── context.xml
60 | ├── server.xml
61 | ├── web.xml
62 | ├── ...
63 | ```
64 |
65 | ### Environment Property Source
66 | When the Environment Property Source is configured, configuration for Tomcats [configuration files](https://tomcat.apache.org/tomcat-9.0-doc/config/systemprops.html) can be loaded
67 | from environment variables. To use this feature, the name of the environment variable must match the name of the property.
68 |
69 | ## Bindings
70 | The buildpack optionally accepts the following bindings:
71 |
72 | ### Type: `dependency-mapping`
73 | | Key | Value | Description |
74 | | --------------------- | ------- | ------------------------------------------------------------------------------------------------- |
75 | | `` | `` | If needed, the buildpack will fetch the dependency with digest `` from `` |
76 |
77 | ## Providing Additional JARs to Tomcat
78 |
79 | Buildpacks can contribute JARs to the `CLASSPATH` of Tomcat by appending a path to `BPI_TOMCAT_ADDITIONAL_JARS`.
80 |
81 | ```go
82 | func (s) Contribute(layer libcnb.Layer) (libcnb.Layer, error) {
83 | // Copy dependency into the layer
84 | file := filepath.Join(layer.Path, filepath.Base(s.Dependency.URI))
85 |
86 | layer, err := s.LayerContributor.Contribute(layer, func(artifact *os.File) (libcnb.Layer, error) {
87 | if err := sherpa.CopyFile(artifact, file); err != nil {
88 | return libcnb.Layer{}, fmt.Errorf("unable to copy artifact to %s\n%w", file, err)
89 | }
90 | return layer, nil
91 | })
92 |
93 | additionalJars := []string{file}
94 | // Add dependency to BPI_TOMCAT_ADDITIONAL_JARS
95 | layer.LaunchEnvironment.Append("BPI_TOMCAT_ADDITIONAL_JARS", ":", strings.Join(additionalJars, ":"))
96 | return layer, nil
97 | }
98 | ```
99 |
100 | ## License
101 | This buildpack is released under version 2.0 of the [Apache License][a].
102 |
103 | [a]: http://www.apache.org/licenses/LICENSE-2.0
104 |
--------------------------------------------------------------------------------
/tomcat/build.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2020 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 |
17 | package tomcat
18 |
19 | import (
20 | "fmt"
21 | "os"
22 | "path"
23 | "path/filepath"
24 | "strings"
25 | "time"
26 |
27 | "github.com/paketo-buildpacks/libpak/effect"
28 | "github.com/paketo-buildpacks/libpak/sbom"
29 | "github.com/paketo-buildpacks/libpak/sherpa"
30 |
31 | "github.com/heroku/color"
32 |
33 | "github.com/buildpacks/libcnb"
34 | "github.com/paketo-buildpacks/apache-tomcat/v8/internal/util"
35 | "github.com/paketo-buildpacks/libjvm"
36 | "github.com/paketo-buildpacks/libpak"
37 | "github.com/paketo-buildpacks/libpak/bard"
38 | )
39 |
40 | type Build struct {
41 | Logger bard.Logger
42 | SBOMScanner sbom.SBOMScanner
43 | }
44 |
45 | func (b Build) Build(context libcnb.BuildContext) (libcnb.BuildResult, error) {
46 | result := libcnb.NewBuildResult()
47 |
48 | pr := libpak.PlanEntryResolver{Plan: context.Plan}
49 | if _, found, err := pr.Resolve(PlanEntryJavaApplicationServer); err != nil {
50 | return libcnb.BuildResult{}, fmt.Errorf("unable to resolve plan entry\n%w", err)
51 | } else if !found {
52 | return result, nil
53 | }
54 |
55 | warFilesExist, _ := util.ContainsWarFiles(context.Application.Path)
56 | if warFilesExist {
57 | b.Logger.Infof("%s contains war files.", context.Application.Path)
58 | } else {
59 | m, err := libjvm.NewManifest(context.Application.Path)
60 | if err != nil {
61 | return libcnb.BuildResult{}, fmt.Errorf("unable to read manifest\n%w", err)
62 | }
63 |
64 | if _, ok := m.Get("Main-Class"); ok {
65 | for _, entry := range context.Plan.Entries {
66 | result.Unmet = append(result.Unmet, libcnb.UnmetPlanEntry{Name: entry.Name})
67 | }
68 | return result, nil
69 | }
70 |
71 | file := filepath.Join(context.Application.Path, "WEB-INF")
72 | if _, err := os.Stat(file); err != nil && !os.IsNotExist(err) {
73 | return libcnb.BuildResult{}, fmt.Errorf("unable to stat file %s\n%w", file, err)
74 | } else if os.IsNotExist(err) {
75 | for _, entry := range context.Plan.Entries {
76 | result.Unmet = append(result.Unmet, libcnb.UnmetPlanEntry{Name: entry.Name})
77 | }
78 | return result, nil
79 | }
80 | }
81 |
82 | b.Logger.Title(context.Buildpack)
83 |
84 | cr, err := libpak.NewConfigurationResolver(context.Buildpack, &b.Logger)
85 | if err != nil {
86 | return libcnb.BuildResult{}, fmt.Errorf("unable to create configuration resolver\n%w", err)
87 | }
88 |
89 | dr, err := libpak.NewDependencyResolver(context)
90 | if err != nil {
91 | return libcnb.BuildResult{}, fmt.Errorf("unable to create dependency resolver\n%w", err)
92 | }
93 |
94 | dc, err := libpak.NewDependencyCache(context)
95 | if err != nil {
96 | return libcnb.BuildResult{}, fmt.Errorf("unable to create dependency cache\n%w", err)
97 | }
98 | dc.Logger = b.Logger
99 |
100 | v, _ := cr.Resolve("BP_TOMCAT_VERSION")
101 | tomcatDep, err := dr.Resolve("tomcat", v)
102 | if err != nil {
103 | return libcnb.BuildResult{}, fmt.Errorf("unable to find dependency\n%w", err)
104 | }
105 |
106 | home, be := NewHome(tomcatDep, dc)
107 | home.Logger = b.Logger
108 | result.Layers = append(result.Layers, home)
109 | result.BOM.Entries = append(result.BOM.Entries, be)
110 |
111 | h, be := libpak.NewHelperLayer(context.Buildpack, "access-logging-support")
112 | h.Logger = b.Logger
113 | result.Layers = append(result.Layers, h)
114 | result.BOM.Entries = append(result.BOM.Entries, be)
115 |
116 | accessLoggingDependency, err := dr.Resolve("tomcat-access-logging-support", "")
117 | if err != nil {
118 | return libcnb.BuildResult{}, fmt.Errorf("unable to find dependency\n%w", err)
119 | }
120 |
121 | lifecycleDependency, err := dr.Resolve("tomcat-lifecycle-support", "")
122 | if err != nil {
123 | return libcnb.BuildResult{}, fmt.Errorf("unable to find dependency\n%w", err)
124 | }
125 |
126 | loggingDependency, err := dr.Resolve("tomcat-logging-support", "")
127 | if err != nil {
128 | return libcnb.BuildResult{}, fmt.Errorf("unable to find dependency\n%w", err)
129 | }
130 |
131 | var externalConfigurationDependency *libpak.BuildpackDependency
132 | if uri, ok := cr.Resolve("BP_TOMCAT_EXT_CONF_URI"); ok {
133 | v, versionExists := cr.Resolve("BP_TOMCAT_EXT_CONF_VERSION")
134 | s, shaExists := cr.Resolve("BP_TOMCAT_EXT_CONF_SHA256")
135 |
136 | if !versionExists && !shaExists {
137 | v = time.Now().Format(time.RFC3339)
138 | b.Logger.Infof(color.YellowString("WARNING: No BP_TOMCAT_EXT_CONF_VERSION or BP_TOMCAT_EXT_CONF_SHA256 provided, so no layer caching will occur."))
139 | }
140 |
141 | externalConfigurationDependency = &libpak.BuildpackDependency{
142 | ID: "tomcat-external-configuration",
143 | Name: "Tomcat External Configuration",
144 | Version: v,
145 | URI: uri,
146 | SHA256: s,
147 | Stacks: []string{context.StackID},
148 | CPEs: nil,
149 | PURL: "",
150 | }
151 | }
152 |
153 | base, bomEntries := NewBase(context.Application.Path, context.Buildpack.Path, cr, b.ContextPath(cr), accessLoggingDependency, externalConfigurationDependency, lifecycleDependency, loggingDependency, dc, warFilesExist)
154 |
155 | base.Logger = b.Logger
156 | result.Layers = append(result.Layers, base)
157 | if bomEntries != nil {
158 | result.BOM.Entries = append(result.BOM.Entries, bomEntries...)
159 | }
160 |
161 | command := "sh"
162 | arguments := []string{filepath.Join(context.Layers.Path, "tomcat", "bin", "catalina.sh"), "run"}
163 |
164 | if libpak.IsTinyStack(context.StackID) {
165 | command, arguments = b.tinyStartCommand(
166 | filepath.Join(context.Layers.Path, "tomcat"),
167 | filepath.Join(context.Layers.Path, "catalina-base"),
168 | loggingDependency)
169 | }
170 |
171 | result.Processes = append(result.Processes,
172 | libcnb.Process{Type: "task", Command: command, Arguments: arguments, Direct: true},
173 | libcnb.Process{Type: "tomcat", Command: command, Arguments: arguments, Direct: true},
174 | libcnb.Process{Type: "web", Command: command, Arguments: arguments, Direct: true, Default: true},
175 | )
176 |
177 | if b.SBOMScanner == nil {
178 | b.SBOMScanner = sbom.NewSyftCLISBOMScanner(context.Layers, effect.CommandExecutor{}, b.Logger)
179 | }
180 | if err := b.SBOMScanner.ScanLaunch(context.Application.Path, libcnb.SyftJSON, libcnb.CycloneDXJSON); err != nil {
181 | return libcnb.BuildResult{}, fmt.Errorf("unable to create Launch SBoM \n%w", err)
182 | }
183 |
184 | return result, nil
185 | }
186 |
187 | func (b Build) ContextPath(configurationResolver libpak.ConfigurationResolver) string {
188 | cp := "ROOT"
189 | if s, ok := configurationResolver.Resolve("BP_TOMCAT_CONTEXT_PATH"); ok {
190 | cp = s
191 | }
192 | cp = strings.TrimPrefix(cp, "/")
193 | cp = strings.TrimSuffix(cp, "/")
194 | cp = strings.ReplaceAll(cp, "/", "#")
195 |
196 | return cp
197 | }
198 |
199 | func (b Build) tinyStartCommand(homePath, basePath string, loggingDep libpak.BuildpackDependency) (string, []string) {
200 | command := "java"
201 |
202 | arguments := []string{
203 | fmt.Sprintf("-Djava.util.logging.config.file=%s/conf/logging.properties", basePath),
204 | "-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager",
205 | }
206 |
207 | arguments = append(arguments, sherpa.GetEnvWithDefault("JSSE_OPTS", "-Djdk.tls.ephemeralDHKeySize=2048"))
208 |
209 | classpath := []string{
210 | fmt.Sprintf("%s/bin/%s", basePath, path.Base(loggingDep.URI)),
211 | fmt.Sprintf("%s/bin/bootstrap.jar", homePath),
212 | fmt.Sprintf("%s/bin/tomcat-juli.jar", homePath),
213 | }
214 | arguments = append(arguments, "-classpath", strings.Join(classpath, ":"))
215 |
216 | arguments = append(arguments,
217 | fmt.Sprintf("-Dcatalina.home=%s", homePath),
218 | fmt.Sprintf("-Dcatalina.base=%s", basePath),
219 | "org.apache.catalina.startup.Bootstrap", "start",
220 | )
221 |
222 | b.Logger.Header(color.YellowString("WARNING: Tomcat will run on the Tiny stack which has no shell. Due to this, some configuration options such as `bin/setenv.sh` and setting `CATALINA_*` environment variables, will not be available"))
223 |
224 | return command, arguments
225 | }
226 |
--------------------------------------------------------------------------------
/.github/workflows/pb-tests.yml:
--------------------------------------------------------------------------------
1 | name: Tests
2 | "on":
3 | merge_group:
4 | types:
5 | - checks_requested
6 | branches:
7 | - main
8 | pull_request: {}
9 | push:
10 | branches:
11 | - main
12 | jobs:
13 | create-package:
14 | name: Create Package Test
15 | runs-on:
16 | - ubuntu-latest
17 | steps:
18 | - uses: actions/setup-go@v5
19 | with:
20 | go-version: "1.25"
21 | - name: Install create-package
22 | run: |
23 | #!/usr/bin/env bash
24 |
25 | set -euo pipefail
26 |
27 | go install -ldflags="-s -w" github.com/paketo-buildpacks/libpak/cmd/create-package@latest
28 | - uses: buildpacks/github-actions/setup-pack@v5.9.7
29 | with:
30 | pack-version: 0.39.1
31 | - name: Enable pack Experimental
32 | run: |
33 | #!/usr/bin/env bash
34 |
35 | set -euo pipefail
36 |
37 | echo "Enabling pack experimental features"
38 | pack config experimental true
39 | - uses: actions/checkout@v4
40 | - uses: actions/cache@v4
41 | with:
42 | key: ${{ runner.os }}-go-${{ hashFiles('**/buildpack.toml', '**/package.toml') }}
43 | path: |-
44 | ${{ env.HOME }}/.pack
45 | ${{ env.HOME }}/carton-cache
46 | restore-keys: ${{ runner.os }}-go-
47 | - name: Compute Version
48 | id: version
49 | run: |
50 | #!/usr/bin/env bash
51 |
52 | set -euo pipefail
53 |
54 | if [[ ${GITHUB_REF:-} != "refs/"* ]]; then
55 | echo "GITHUB_REF set to [${GITHUB_REF:-}], but that is unexpected. It should start with 'refs/*'"
56 | exit 255
57 | fi
58 |
59 | if [[ ${GITHUB_REF} =~ refs/tags/v([0-9]+\.[0-9]+\.[0-9]+) ]]; then
60 | VERSION=${BASH_REMATCH[1]}
61 |
62 | MAJOR_VERSION="$(echo "${VERSION}" | awk -F '.' '{print $1 }')"
63 | MINOR_VERSION="$(echo "${VERSION}" | awk -F '.' '{print $1 "." $2 }')"
64 |
65 | echo "version-major=${MAJOR_VERSION}" >> "$GITHUB_OUTPUT"
66 | echo "version-minor=${MINOR_VERSION}" >> "$GITHUB_OUTPUT"
67 | elif [[ ${GITHUB_REF} =~ refs/heads/(.+) ]]; then
68 | VERSION=${BASH_REMATCH[1]}
69 | else
70 | VERSION=$(git rev-parse --short HEAD)
71 | fi
72 |
73 | echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
74 | echo "Selected ${VERSION} from
75 | * ref: ${GITHUB_REF}
76 | * sha: ${GITHUB_SHA}
77 | "
78 | - name: Create Package
79 | run: |
80 | #!/usr/bin/env bash
81 |
82 | set -euo pipefail
83 |
84 | # With Go 1.20, we need to set this so that we produce statically compiled binaries
85 | #
86 | # Starting with Go 1.20, Go will produce binaries that are dynamically linked against libc
87 | # which can cause compatibility issues. The compiler links against libc on the build system
88 | # but that may be newer than on the stacks we support.
89 | export CGO_ENABLED=0
90 |
91 | if [[ "${INCLUDE_DEPENDENCIES}" == "true" ]]; then
92 | create-package \
93 | --source "${SOURCE_PATH:-.}" \
94 | --cache-location "${HOME}"/carton-cache \
95 | --destination "${HOME}"/buildpack \
96 | --include-dependencies \
97 | --version "${VERSION}"
98 | else
99 | create-package \
100 | --source "${SOURCE_PATH:-.}" \
101 | --destination "${HOME}"/buildpack \
102 | --version "${VERSION}"
103 | fi
104 |
105 | PACKAGE_FILE="${SOURCE_PATH:-.}/package.toml"
106 | if [ -f "${PACKAGE_FILE}" ]; then
107 | cp "${PACKAGE_FILE}" "${HOME}/buildpack/package.toml"
108 | printf '[buildpack]\nuri = "%s"\n\n[platform]\nos = "%s"\n' "${HOME}/buildpack" "${OS}" >> "${HOME}/buildpack/package.toml"
109 | fi
110 | env:
111 | INCLUDE_DEPENDENCIES: "true"
112 | OS: linux
113 | VERSION: ${{ steps.version.outputs.version }}
114 | - name: Package Buildpack
115 | run: |-
116 | #!/usr/bin/env bash
117 |
118 | set -euo pipefail
119 |
120 | COMPILED_BUILDPACK="${HOME}/buildpack"
121 |
122 | # create-package puts the buildpack here, we need to run from that directory
123 | # for component buildpacks so that pack doesn't need a package.toml
124 | cd "${COMPILED_BUILDPACK}"
125 | CONFIG=""
126 | if [ -f "${COMPILED_BUILDPACK}/package.toml" ]; then
127 | CONFIG="--config ${COMPILED_BUILDPACK}/package.toml --flatten"
128 | fi
129 |
130 | PACKAGE_LIST=($PACKAGES)
131 | # Extract first repo (Docker Hub) as the main to package & register
132 | PACKAGE=${PACKAGE_LIST[0]}
133 |
134 | if [[ "${PUBLISH:-x}" == "true" ]]; then
135 | pack -v buildpack package \
136 | "${PACKAGE}:${VERSION}" ${CONFIG} \
137 | --publish
138 |
139 | if [[ -n ${VERSION_MINOR:-} && -n ${VERSION_MAJOR:-} ]]; then
140 | crane tag "${PACKAGE}:${VERSION}" "${VERSION_MINOR}"
141 | crane tag "${PACKAGE}:${VERSION}" "${VERSION_MAJOR}"
142 | fi
143 | crane tag "${PACKAGE}:${VERSION}" latest
144 | echo "digest=$(crane digest "${PACKAGE}:${VERSION}")" >> "$GITHUB_OUTPUT"
145 |
146 | # copy to other repositories specified
147 | for P in "${PACKAGE_LIST[@]}"
148 | do
149 | if [ "$P" != "$PACKAGE" ]; then
150 | crane copy "${PACKAGE}:${VERSION}" "${P}:${VERSION}"
151 | if [[ -n ${VERSION_MINOR:-} && -n ${VERSION_MAJOR:-} ]]; then
152 | crane tag "${P}:${VERSION}" "${VERSION_MINOR}"
153 | crane tag "${P}:${VERSION}" "${VERSION_MAJOR}"
154 | fi
155 | crane tag "${P}:${VERSION}" latest
156 | fi
157 | done
158 | else
159 | if [ -n "$TTL_SH_PUBLISH" ] && [ "$TTL_SH_PUBLISH" = "true" ]; then
160 | TAG="${PACKAGE}-$(mktemp -u XXXXX | awk '{print tolower($0)}'):${VERSION}"
161 | pack -v buildpack package "${TAG}" ${CONFIG} --format "${FORMAT}" --publish
162 | else
163 | TAG="${PACKAGE}:${VERSION}"
164 | pack -v buildpack package "${TAG}" ${CONFIG} --format "${FORMAT}"
165 | fi
166 |
167 | echo "ttl-image-tag=${TAG:-}" >> "$GITHUB_OUTPUT"
168 | fi
169 | env:
170 | FORMAT: image
171 | PACKAGES: test
172 | TTL_SH_PUBLISH: "false"
173 | VERSION: ${{ steps.version.outputs.version }}
174 | unit:
175 | name: Unit Test
176 | runs-on:
177 | - ubuntu-latest
178 | steps:
179 | - uses: actions/checkout@v4
180 | - uses: actions/cache@v4
181 | with:
182 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
183 | path: ${{ env.HOME }}/go/pkg/mod
184 | restore-keys: ${{ runner.os }}-go-
185 | - uses: actions/setup-go@v5
186 | with:
187 | go-version: "1.25"
188 | - name: Install richgo
189 | run: |
190 | #!/usr/bin/env bash
191 |
192 | set -euo pipefail
193 |
194 | echo "Installing richgo ${RICHGO_VERSION}"
195 |
196 | mkdir -p "${HOME}"/bin
197 | echo "${HOME}/bin" >> "${GITHUB_PATH}"
198 |
199 | curl \
200 | --location \
201 | --show-error \
202 | --silent \
203 | "https://github.com/kyoh86/richgo/releases/download/v${RICHGO_VERSION}/richgo_${RICHGO_VERSION}_linux_amd64.tar.gz" \
204 | | tar -C "${HOME}"/bin -xz richgo
205 | env:
206 | RICHGO_VERSION: 0.3.10
207 | - name: Run Tests
208 | run: |
209 | #!/usr/bin/env bash
210 |
211 | set -euo pipefail
212 |
213 | richgo test ./...
214 | env:
215 | RICHGO_FORCE_COLOR: "1"
216 |
--------------------------------------------------------------------------------
/.github/workflows/pb-create-package.yml:
--------------------------------------------------------------------------------
1 | name: Create Package
2 | "on":
3 | release:
4 | types:
5 | - published
6 | jobs:
7 | create-package:
8 | name: Create Package
9 | runs-on:
10 | - ubuntu-latest
11 | steps:
12 | - name: Docker login docker.io
13 | if: ${{ (github.event_name != 'pull_request' || ! github.event.pull_request.head.repo.fork) && (github.actor != 'dependabot[bot]') }}
14 | uses: docker/login-action@v3
15 | with:
16 | password: ${{ secrets.PAKETO_BUILDPACKS_DOCKERHUB_PASSWORD }}
17 | registry: docker.io
18 | username: ${{ secrets.PAKETO_BUILDPACKS_DOCKERHUB_USERNAME }}
19 | - uses: actions/setup-go@v5
20 | with:
21 | go-version: "1.25"
22 | - name: Install create-package
23 | run: |
24 | #!/usr/bin/env bash
25 |
26 | set -euo pipefail
27 |
28 | go install -ldflags="-s -w" github.com/paketo-buildpacks/libpak/cmd/create-package@latest
29 | - uses: buildpacks/github-actions/setup-tools@v5.9.7
30 | with:
31 | crane-version: 0.20.3
32 | yj-version: 5.1.0
33 | - uses: buildpacks/github-actions/setup-pack@v5.9.7
34 | with:
35 | pack-version: 0.39.1
36 | - name: Enable pack Experimental
37 | run: |
38 | #!/usr/bin/env bash
39 |
40 | set -euo pipefail
41 |
42 | echo "Enabling pack experimental features"
43 | pack config experimental true
44 | - uses: actions/checkout@v4
45 | - if: ${{ false }}
46 | uses: actions/cache@v4
47 | with:
48 | key: ${{ runner.os }}-go-${{ hashFiles('**/buildpack.toml', '**/package.toml') }}
49 | path: |-
50 | ${{ env.HOME }}/.pack
51 | ${{ env.HOME }}/carton-cache
52 | restore-keys: ${{ runner.os }}-go-
53 | - name: Compute Version
54 | id: version
55 | run: |
56 | #!/usr/bin/env bash
57 |
58 | set -euo pipefail
59 |
60 | if [[ ${GITHUB_REF:-} != "refs/"* ]]; then
61 | echo "GITHUB_REF set to [${GITHUB_REF:-}], but that is unexpected. It should start with 'refs/*'"
62 | exit 255
63 | fi
64 |
65 | if [[ ${GITHUB_REF} =~ refs/tags/v([0-9]+\.[0-9]+\.[0-9]+) ]]; then
66 | VERSION=${BASH_REMATCH[1]}
67 |
68 | MAJOR_VERSION="$(echo "${VERSION}" | awk -F '.' '{print $1 }')"
69 | MINOR_VERSION="$(echo "${VERSION}" | awk -F '.' '{print $1 "." $2 }')"
70 |
71 | echo "version-major=${MAJOR_VERSION}" >> "$GITHUB_OUTPUT"
72 | echo "version-minor=${MINOR_VERSION}" >> "$GITHUB_OUTPUT"
73 | elif [[ ${GITHUB_REF} =~ refs/heads/(.+) ]]; then
74 | VERSION=${BASH_REMATCH[1]}
75 | else
76 | VERSION=$(git rev-parse --short HEAD)
77 | fi
78 |
79 | echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
80 | echo "Selected ${VERSION} from
81 | * ref: ${GITHUB_REF}
82 | * sha: ${GITHUB_SHA}
83 | "
84 | - name: Create Package
85 | run: |
86 | #!/usr/bin/env bash
87 |
88 | set -euo pipefail
89 |
90 | # With Go 1.20, we need to set this so that we produce statically compiled binaries
91 | #
92 | # Starting with Go 1.20, Go will produce binaries that are dynamically linked against libc
93 | # which can cause compatibility issues. The compiler links against libc on the build system
94 | # but that may be newer than on the stacks we support.
95 | export CGO_ENABLED=0
96 |
97 | if [[ "${INCLUDE_DEPENDENCIES}" == "true" ]]; then
98 | create-package \
99 | --source "${SOURCE_PATH:-.}" \
100 | --cache-location "${HOME}"/carton-cache \
101 | --destination "${HOME}"/buildpack \
102 | --include-dependencies \
103 | --version "${VERSION}"
104 | else
105 | create-package \
106 | --source "${SOURCE_PATH:-.}" \
107 | --destination "${HOME}"/buildpack \
108 | --version "${VERSION}"
109 | fi
110 |
111 | PACKAGE_FILE="${SOURCE_PATH:-.}/package.toml"
112 | if [ -f "${PACKAGE_FILE}" ]; then
113 | cp "${PACKAGE_FILE}" "${HOME}/buildpack/package.toml"
114 | printf '[buildpack]\nuri = "%s"\n\n[platform]\nos = "%s"\n' "${HOME}/buildpack" "${OS}" >> "${HOME}/buildpack/package.toml"
115 | fi
116 | env:
117 | INCLUDE_DEPENDENCIES: "false"
118 | OS: linux
119 | SOURCE_PATH: ""
120 | VERSION: ${{ steps.version.outputs.version }}
121 | - name: Package Buildpack
122 | id: package
123 | run: |-
124 | #!/usr/bin/env bash
125 |
126 | set -euo pipefail
127 |
128 | COMPILED_BUILDPACK="${HOME}/buildpack"
129 |
130 | # create-package puts the buildpack here, we need to run from that directory
131 | # for component buildpacks so that pack doesn't need a package.toml
132 | cd "${COMPILED_BUILDPACK}"
133 | CONFIG=""
134 | if [ -f "${COMPILED_BUILDPACK}/package.toml" ]; then
135 | CONFIG="--config ${COMPILED_BUILDPACK}/package.toml --flatten"
136 | fi
137 |
138 | PACKAGE_LIST=($PACKAGES)
139 | # Extract first repo (Docker Hub) as the main to package & register
140 | PACKAGE=${PACKAGE_LIST[0]}
141 |
142 | if [[ "${PUBLISH:-x}" == "true" ]]; then
143 | pack -v buildpack package \
144 | "${PACKAGE}:${VERSION}" ${CONFIG} \
145 | --publish
146 |
147 | if [[ -n ${VERSION_MINOR:-} && -n ${VERSION_MAJOR:-} ]]; then
148 | crane tag "${PACKAGE}:${VERSION}" "${VERSION_MINOR}"
149 | crane tag "${PACKAGE}:${VERSION}" "${VERSION_MAJOR}"
150 | fi
151 | crane tag "${PACKAGE}:${VERSION}" latest
152 | echo "digest=$(crane digest "${PACKAGE}:${VERSION}")" >> "$GITHUB_OUTPUT"
153 |
154 | # copy to other repositories specified
155 | for P in "${PACKAGE_LIST[@]}"
156 | do
157 | if [ "$P" != "$PACKAGE" ]; then
158 | crane copy "${PACKAGE}:${VERSION}" "${P}:${VERSION}"
159 | if [[ -n ${VERSION_MINOR:-} && -n ${VERSION_MAJOR:-} ]]; then
160 | crane tag "${P}:${VERSION}" "${VERSION_MINOR}"
161 | crane tag "${P}:${VERSION}" "${VERSION_MAJOR}"
162 | fi
163 | crane tag "${P}:${VERSION}" latest
164 | fi
165 | done
166 | else
167 | if [ -n "$TTL_SH_PUBLISH" ] && [ "$TTL_SH_PUBLISH" = "true" ]; then
168 | TAG="${PACKAGE}-$(mktemp -u XXXXX | awk '{print tolower($0)}'):${VERSION}"
169 | pack -v buildpack package "${TAG}" ${CONFIG} --format "${FORMAT}" --publish
170 | else
171 | TAG="${PACKAGE}:${VERSION}"
172 | pack -v buildpack package "${TAG}" ${CONFIG} --format "${FORMAT}"
173 | fi
174 |
175 | echo "ttl-image-tag=${TAG:-}" >> "$GITHUB_OUTPUT"
176 | fi
177 | env:
178 | PACKAGES: docker.io/paketobuildpacks/apache-tomcat
179 | PUBLISH: "true"
180 | VERSION: ${{ steps.version.outputs.version }}
181 | VERSION_MAJOR: ${{ steps.version.outputs.version-major }}
182 | VERSION_MINOR: ${{ steps.version.outputs.version-minor }}
183 | - name: Update release with digest
184 | run: |
185 | #!/usr/bin/env bash
186 |
187 | set -euo pipefail
188 |
189 | PAYLOAD=$(cat "${GITHUB_EVENT_PATH}")
190 |
191 | RELEASE_ID=$(jq -n -r --argjson PAYLOAD "${PAYLOAD}" '$PAYLOAD.release.id')
192 | RELEASE_TAG_NAME=$(jq -n -r --argjson PAYLOAD "${PAYLOAD}" '$PAYLOAD.release.tag_name')
193 | RELEASE_NAME=$(jq -n -r --argjson PAYLOAD "${PAYLOAD}" '$PAYLOAD.release.name')
194 | RELEASE_BODY=$(jq -n -r --argjson PAYLOAD "${PAYLOAD}" '$PAYLOAD.release.body')
195 |
196 | gh api \
197 | --method PATCH \
198 | "/repos/:owner/:repo/releases/${RELEASE_ID}" \
199 | --field "tag_name=${RELEASE_TAG_NAME}" \
200 | --field "name=${RELEASE_NAME}" \
201 | --field "body=${RELEASE_BODY///\`${DIGEST}\`}"
202 | env:
203 | DIGEST: ${{ steps.package.outputs.digest }}
204 | GITHUB_TOKEN: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }}
205 | - if: ${{ true }}
206 | uses: docker://ghcr.io/buildpacks/actions/registry/request-add-entry:5.9.7
207 | with:
208 | address: docker.io/paketobuildpacks/apache-tomcat@${{ steps.package.outputs.digest }}
209 | id: paketo-buildpacks/apache-tomcat
210 | token: ${{ secrets.PAKETO_BOT_GITHUB_TOKEN }}
211 | version: ${{ steps.version.outputs.version }}
212 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | https://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright [yyyy] [name of copyright owner]
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | https://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/tomcat/base.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2020 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 |
17 | package tomcat
18 |
19 | import (
20 | "errors"
21 | "fmt"
22 | "github.com/paketo-buildpacks/apache-tomcat/v8/internal/util"
23 | "os"
24 | "path/filepath"
25 | "strconv"
26 | "strings"
27 |
28 | "github.com/paketo-buildpacks/libpak/sbom"
29 |
30 | "github.com/buildpacks/libcnb"
31 | "github.com/heroku/color"
32 | "github.com/paketo-buildpacks/libpak"
33 | "github.com/paketo-buildpacks/libpak/bard"
34 | "github.com/paketo-buildpacks/libpak/crush"
35 | "github.com/paketo-buildpacks/libpak/sherpa"
36 | )
37 |
38 | type Base struct {
39 | AccessLoggingDependency libpak.BuildpackDependency
40 | ApplicationPath string
41 | BuildpackPath string
42 | ConfigurationResolver libpak.ConfigurationResolver
43 | ContextPath string
44 | DependencyCache libpak.DependencyCache
45 | ExternalConfigurationDependency *libpak.BuildpackDependency
46 | LayerContributor libpak.LayerContributor
47 | LifecycleDependency libpak.BuildpackDependency
48 | LoggingDependency libpak.BuildpackDependency
49 | Logger bard.Logger
50 | WarFilesExist bool
51 | }
52 |
53 | func NewBase(
54 | applicationPath string,
55 | buildpackPath string,
56 | configurationResolver libpak.ConfigurationResolver,
57 | contextPath string,
58 | accessLoggingDependency libpak.BuildpackDependency,
59 | externalConfigurationDependency *libpak.BuildpackDependency,
60 | lifecycleDependency libpak.BuildpackDependency,
61 | loggingDependency libpak.BuildpackDependency,
62 | cache libpak.DependencyCache,
63 | warFilesExist bool,
64 | ) (Base, []libcnb.BOMEntry) {
65 |
66 | dependencies := []libpak.BuildpackDependency{accessLoggingDependency, lifecycleDependency, loggingDependency}
67 | if externalConfigurationDependency != nil {
68 | dependencies = append(dependencies, *externalConfigurationDependency)
69 | }
70 |
71 | b := Base{
72 | AccessLoggingDependency: accessLoggingDependency,
73 | ApplicationPath: applicationPath,
74 | BuildpackPath: buildpackPath,
75 | ConfigurationResolver: configurationResolver,
76 | ContextPath: contextPath,
77 | DependencyCache: cache,
78 | ExternalConfigurationDependency: externalConfigurationDependency,
79 | LayerContributor: libpak.NewLayerContributor("Apache Tomcat Support", map[string]interface{}{
80 | "context-path": contextPath,
81 | "dependencies": dependencies,
82 | }, libcnb.LayerTypes{
83 | Launch: true,
84 | }),
85 | LifecycleDependency: lifecycleDependency,
86 | LoggingDependency: loggingDependency,
87 | WarFilesExist: warFilesExist,
88 | }
89 |
90 | var bomEntries []libcnb.BOMEntry
91 |
92 | var entry libcnb.BOMEntry
93 | entry = accessLoggingDependency.AsBOMEntry()
94 | entry.Metadata["layer"] = b.Name()
95 | entry.Launch = true
96 | bomEntries = append(bomEntries, entry)
97 |
98 | entry = lifecycleDependency.AsBOMEntry()
99 | entry.Metadata["layer"] = b.Name()
100 | entry.Launch = true
101 | bomEntries = append(bomEntries, entry)
102 |
103 | entry = loggingDependency.AsBOMEntry()
104 | entry.Metadata["layer"] = b.Name()
105 | entry.Launch = true
106 | bomEntries = append(bomEntries, entry)
107 |
108 | if externalConfigurationDependency != nil {
109 | entry = externalConfigurationDependency.AsBOMEntry()
110 | entry.Metadata["layer"] = b.Name()
111 | entry.Launch = true
112 | bomEntries = append(bomEntries, entry)
113 | }
114 |
115 | return b, bomEntries
116 | }
117 |
118 | func (b Base) Contribute(layer libcnb.Layer) (libcnb.Layer, error) {
119 | b.LayerContributor.Logger = b.Logger
120 | var syftArtifacts []sbom.SyftArtifact
121 |
122 | return b.LayerContributor.Contribute(layer, func() (libcnb.Layer, error) {
123 |
124 | if err := b.ContributeConfiguration(layer); err != nil {
125 | return libcnb.Layer{}, fmt.Errorf("unable to contribute configuration\n%w", err)
126 | }
127 |
128 | if err := b.ContributeAccessLogging(layer); err != nil {
129 | return libcnb.Layer{}, fmt.Errorf("unable to contribute access logging\n%w", err)
130 | }
131 | if syftArtifact, err := b.AccessLoggingDependency.AsSyftArtifact(); err != nil {
132 | return libcnb.Layer{}, fmt.Errorf("unable to get Syft Artifact for dependency: %s, \n%w", b.AccessLoggingDependency.Name, err)
133 | } else {
134 | syftArtifacts = append(syftArtifacts, syftArtifact)
135 | }
136 |
137 | if err := b.ContributeLifecycle(layer); err != nil {
138 | return libcnb.Layer{}, fmt.Errorf("unable to contribute lifecycle\n%w", err)
139 | }
140 | if syftArtifact, err := b.LifecycleDependency.AsSyftArtifact(); err != nil {
141 | return libcnb.Layer{}, fmt.Errorf("unable to get Syft Artifact for dependency: %s, \n%w", b.LifecycleDependency.Name, err)
142 | } else {
143 | syftArtifacts = append(syftArtifacts, syftArtifact)
144 | }
145 |
146 | if err := b.ContributeLogging(layer); err != nil {
147 | return libcnb.Layer{}, fmt.Errorf("unable to contribute logging\n%w", err)
148 | }
149 | if syftArtifact, err := b.LoggingDependency.AsSyftArtifact(); err != nil {
150 | return libcnb.Layer{}, fmt.Errorf("unable to get Syft Artifact for dependency: %s, \n%w", b.LoggingDependency.Name, err)
151 | } else {
152 | syftArtifacts = append(syftArtifacts, syftArtifact)
153 | }
154 |
155 | if b.ExternalConfigurationDependency != nil {
156 | if err := b.ContributeExternalConfiguration(layer); err != nil {
157 | return libcnb.Layer{}, fmt.Errorf("unable to contribute external configuration\n%w", err)
158 | }
159 | if syftArtifact, err := b.ExternalConfigurationDependency.AsSyftArtifact(); err != nil {
160 | return libcnb.Layer{}, fmt.Errorf("unable to get Syft Artifact for dependency: %s, \n%w", b.ExternalConfigurationDependency.Name, err)
161 | } else {
162 | syftArtifacts = append(syftArtifacts, syftArtifact)
163 | }
164 | }
165 |
166 | if err := b.ContributeCatalinaProps(layer); err != nil {
167 | return libcnb.Layer{}, fmt.Errorf("unable to contribute configuration\n%w", err)
168 | }
169 |
170 | file := filepath.Join(layer.Path, "temp")
171 | if err := os.MkdirAll(file, 0700); err != nil {
172 | return libcnb.Layer{}, fmt.Errorf("unable to create directory %s\n%w", file, err)
173 | }
174 |
175 | file = filepath.Join(layer.Path, "webapps")
176 | if b.WarFilesExist {
177 | if err := os.Symlink(b.ApplicationPath, file); err != nil {
178 | return libcnb.Layer{}, fmt.Errorf("unable to create symlink from %s to %s\n%w", b.ApplicationPath, file, err)
179 | }
180 | if err := b.explodeWarFiles(); err != nil {
181 | return libcnb.Layer{}, fmt.Errorf("unable to explode war files in %s\n%w", b.ApplicationPath, err)
182 | }
183 | } else {
184 | if err := os.MkdirAll(file, 0755); err != nil {
185 | return libcnb.Layer{}, fmt.Errorf("unable to create directory %s\n%w", file, err)
186 | }
187 |
188 | file = filepath.Join(layer.Path, "webapps", b.ContextPath)
189 | b.Logger.Headerf("Mounting application at %s", b.ContextPath)
190 | if err := os.Symlink(b.ApplicationPath, file); err != nil {
191 | return libcnb.Layer{}, fmt.Errorf("unable to create symlink from %s to %s\n%w", b.ApplicationPath, file, err)
192 | }
193 | }
194 |
195 | catalinaOpts := "-DBPI_TOMCAT_ADDITIONAL_COMMON_JARS=${BPI_TOMCAT_ADDITIONAL_COMMON_JARS}"
196 | environmentPropertySourceDisabled := b.ConfigurationResolver.ResolveBool("BP_TOMCAT_ENV_PROPERTY_SOURCE_DISABLED")
197 | if !environmentPropertySourceDisabled {
198 | catalinaOpts += " -Dorg.apache.tomcat.util.digester.PROPERTY_SOURCE=org.apache.tomcat.util.digester.EnvironmentPropertySource"
199 | }
200 | layer.LaunchEnvironment.Default("CATALINA_OPTS", catalinaOpts)
201 |
202 | layer.LaunchEnvironment.Default("CATALINA_BASE", layer.Path)
203 | layer.LaunchEnvironment.Default("CATALINA_TMPDIR", "/tmp")
204 |
205 | if err := b.writeDependencySBOM(layer, syftArtifacts); err != nil {
206 | return libcnb.Layer{}, err
207 | }
208 |
209 | return layer, nil
210 | })
211 | }
212 |
213 | func (b Base) ContributeAccessLogging(layer libcnb.Layer) error {
214 | b.Logger.Header(color.BlueString("%s %s", b.AccessLoggingDependency.Name, b.AccessLoggingDependency.Version))
215 |
216 | artifact, err := b.DependencyCache.Artifact(b.AccessLoggingDependency)
217 | if err != nil {
218 | return fmt.Errorf("unable to get dependency %s\n%w", b.AccessLoggingDependency.ID, err)
219 | }
220 | defer artifact.Close()
221 |
222 | b.Logger.Bodyf("Copying to %s/lib", layer.Path)
223 |
224 | file := filepath.Join(layer.Path, "lib", filepath.Base(b.AccessLoggingDependency.URI))
225 | if err := sherpa.CopyFile(artifact, file); err != nil {
226 | return fmt.Errorf("unable to copy %s to %s\n%w", filepath.Base(b.AccessLoggingDependency.URI), file, err)
227 | }
228 |
229 | return nil
230 | }
231 |
232 | func (b Base) ContributeConfiguration(layer libcnb.Layer) error {
233 | file := filepath.Join(layer.Path, "conf")
234 | if err := os.MkdirAll(file, 0755); err != nil {
235 | return fmt.Errorf("unable to create directory %s\n%w", file, err)
236 | }
237 |
238 | b.Logger.Bodyf("Copying context.xml to %s/conf", layer.Path)
239 | file = filepath.Join(b.BuildpackPath, "resources", "context.xml")
240 | in, err := os.Open(file)
241 | if err != nil {
242 | return fmt.Errorf("unable to open %s\n%w", file, err)
243 | }
244 | defer in.Close()
245 |
246 | file = filepath.Join(layer.Path, "conf", "context.xml")
247 | if err := sherpa.CopyFile(in, file); err != nil {
248 | return fmt.Errorf("unable to copy %s to %s\n%w", in.Name(), file, err)
249 | }
250 |
251 | b.Logger.Bodyf("Copying logging.properties to %s/conf", layer.Path)
252 | file = filepath.Join(b.BuildpackPath, "resources", "logging.properties")
253 | in, err = os.Open(file)
254 | if err != nil {
255 | return fmt.Errorf("unable to open %s\n%w", file, err)
256 | }
257 | defer in.Close()
258 |
259 | file = filepath.Join(layer.Path, "conf", "logging.properties")
260 | if err := sherpa.CopyFile(in, file); err != nil {
261 | return fmt.Errorf("unable to copy %s to %s\n%w", in.Name(), file, err)
262 | }
263 |
264 | b.Logger.Bodyf("Copying server.xml to %s/conf", layer.Path)
265 | file = filepath.Join(b.BuildpackPath, "resources", "server.xml")
266 | in, err = os.Open(file)
267 | if err != nil {
268 | return fmt.Errorf("unable to open %s\n%w", file, err)
269 | }
270 | defer in.Close()
271 |
272 | file = filepath.Join(layer.Path, "conf", "server.xml")
273 | if err := sherpa.CopyFile(in, file); err != nil {
274 | return fmt.Errorf("unable to copy %s to %s\n%w", in.Name(), file, err)
275 | }
276 |
277 | b.Logger.Bodyf("Copying web.xml to %s/conf", layer.Path)
278 | file = filepath.Join(b.BuildpackPath, "resources", "web.xml")
279 | in, err = os.Open(file)
280 | if err != nil {
281 | return fmt.Errorf("unable to open %s\n%w", file, err)
282 | }
283 | defer in.Close()
284 |
285 | file = filepath.Join(layer.Path, "conf", "web.xml")
286 | if err := sherpa.CopyFile(in, file); err != nil {
287 | return fmt.Errorf("unable to copy %s to %s\n%w", in.Name(), file, err)
288 | }
289 |
290 | return nil
291 | }
292 |
293 | func (b Base) ContributeExternalConfiguration(layer libcnb.Layer) error {
294 | b.Logger.Header(color.BlueString("%s %s", b.ExternalConfigurationDependency.Name, b.ExternalConfigurationDependency.Version))
295 |
296 | artifact, err := b.DependencyCache.Artifact(*b.ExternalConfigurationDependency)
297 | if err != nil {
298 | return fmt.Errorf("unable to get dependency %s\n%w", b.ExternalConfigurationDependency.ID, err)
299 | }
300 | defer artifact.Close()
301 |
302 | b.Logger.Bodyf("Expanding to %s", layer.Path)
303 |
304 | c := 0
305 | if s, ok := b.ConfigurationResolver.Resolve("BP_TOMCAT_EXT_CONF_STRIP"); ok {
306 | if c, err = strconv.Atoi(s); err != nil {
307 | return fmt.Errorf("unable to parse %s to integer\n%w", s, err)
308 | }
309 | }
310 |
311 | if err := crush.ExtractTarGz(artifact, layer.Path, c); err != nil {
312 | return fmt.Errorf("unable to expand external configuration\n%w", err)
313 | }
314 |
315 | return nil
316 | }
317 |
318 | func (b Base) ContributeLifecycle(layer libcnb.Layer) error {
319 | b.Logger.Header(color.BlueString("%s %s", b.LifecycleDependency.Name, b.LifecycleDependency.Version))
320 |
321 | artifact, err := b.DependencyCache.Artifact(b.LifecycleDependency)
322 | if err != nil {
323 | return fmt.Errorf("unable to get dependency %s\n%w", b.LifecycleDependency.ID, err)
324 | }
325 | defer artifact.Close()
326 |
327 | b.Logger.Bodyf("Copying to %s/lib", layer.Path)
328 |
329 | file := filepath.Join(layer.Path, "lib", filepath.Base(b.LifecycleDependency.URI))
330 | if err := sherpa.CopyFile(artifact, file); err != nil {
331 | return fmt.Errorf("unable to copy %s to %s\n%w", filepath.Base(b.LifecycleDependency.URI), file, err)
332 | }
333 |
334 | return nil
335 | }
336 |
337 | func (b Base) ContributeLogging(layer libcnb.Layer) error {
338 | b.Logger.Header(color.BlueString("%s %s", b.LoggingDependency.Name, b.LoggingDependency.Version))
339 |
340 | artifact, err := b.DependencyCache.Artifact(b.LoggingDependency)
341 | if err != nil {
342 | return fmt.Errorf("unable to get dependency %s\n%w", b.LoggingDependency.ID, err)
343 | }
344 | defer artifact.Close()
345 |
346 | b.Logger.Bodyf("Copying to %s/bin", layer.Path)
347 |
348 | file := filepath.Join(layer.Path, "bin", filepath.Base(b.LoggingDependency.URI))
349 | if err := sherpa.CopyFile(artifact, file); err != nil {
350 | return fmt.Errorf("unable to copy %s to %s\n%w", filepath.Base(b.LoggingDependency.URI), file, err)
351 | }
352 |
353 | b.Logger.Bodyf("Writing %s/bin/setenv.sh", layer.Path)
354 |
355 | var s string
356 | additionalJars, ok := os.LookupEnv("BPI_TOMCAT_ADDITIONAL_JARS")
357 | if ok {
358 | b.Logger.Bodyf("found BPI_TOMCAT_ADDITIONAL_JARS %q", additionalJars)
359 | s = fmt.Sprintf(`CLASSPATH="%s:%s"`, file, additionalJars)
360 | } else {
361 | s = fmt.Sprintf(`CLASSPATH="%s"`, file)
362 | }
363 |
364 | file = filepath.Join(layer.Path, "bin", "setenv.sh")
365 | if err = os.WriteFile(file, []byte(s), 0755); err != nil {
366 | return fmt.Errorf("unable to write file %s\n%w", file, err)
367 | }
368 |
369 | return nil
370 | }
371 |
372 | func (b Base) ContributeCatalinaProps(layer libcnb.Layer) error {
373 | b.Logger.Header(color.BlueString("Tomcat catalina.properties with altered common.loader"))
374 |
375 | homeProps := filepath.Join(layer.Path, "..", "tomcat", "conf", "catalina.properties")
376 | baseProps := filepath.Join(layer.Path, "conf", "catalina.properties")
377 |
378 | if _, err := os.Stat(baseProps); errors.Is(err, os.ErrNotExist) {
379 | in, err := os.Open(homeProps)
380 | if err != nil {
381 | b.Logger.Bodyf("Skipping copying of catalina.properties, unable to open %s", homeProps)
382 | return nil
383 | }
384 | defer in.Close()
385 |
386 | b.Logger.Bodyf("Copying catalina.properties to %s/conf", layer.Path)
387 | if err := sherpa.CopyFile(in, baseProps); err != nil {
388 | return fmt.Errorf("unable to copy %s to %s\n%w", in.Name(), baseProps, err)
389 | }
390 | }
391 |
392 | b.Logger.Body("Altering catalina.properties common.loader")
393 | if err := util.ReplaceInCatalinaProps(baseProps); err != nil {
394 | return fmt.Errorf("unable to replace in file %s\n%w", baseProps, err)
395 | }
396 |
397 | return nil
398 | }
399 |
400 | func (b Base) writeDependencySBOM(layer libcnb.Layer, syftArtifacts []sbom.SyftArtifact) error {
401 |
402 | sbomPath := layer.SBOMPath(libcnb.SyftJSON)
403 | dep := sbom.NewSyftDependency(layer.Path, syftArtifacts)
404 | b.Logger.Debugf("Writing Syft SBOM at %s: %+v", sbomPath, dep)
405 | if err := dep.WriteTo(sbomPath); err != nil {
406 | return fmt.Errorf("unable to write SBOM\n%w", err)
407 | }
408 | return nil
409 | }
410 |
411 | func (b Base) explodeWarFiles() error {
412 | warFiles, err := filepath.Glob(filepath.Join(b.ApplicationPath, "*.war"))
413 | if err != nil {
414 | return err
415 | }
416 |
417 | for _, warFilePath := range warFiles {
418 | b.Logger.Debugf("Extracting: %s\n", warFilePath)
419 |
420 | if _, err := os.Stat(warFilePath); err == nil {
421 | in, err := os.Open(warFilePath)
422 | if err != nil {
423 | return fmt.Errorf("An error occurred while extracting %s: %s\n", warFilePath, err)
424 | }
425 | defer in.Close()
426 |
427 | targetDir := strings.TrimSuffix(warFilePath, filepath.Ext(warFilePath))
428 | if err := os.MkdirAll(targetDir, 0755); err != nil {
429 | return fmt.Errorf("An error occurred while extracting %s: %s\n", warFilePath, err)
430 | }
431 |
432 | if err := crush.Extract(in, targetDir, 0); err != nil {
433 | return fmt.Errorf("An error occurred while extracting %s: %s\n", warFilePath, err)
434 | }
435 |
436 | err = os.Remove(warFilePath)
437 | if err != nil {
438 | return fmt.Errorf("An error occurred while removing the .war file: %s\n", err)
439 | }
440 | }
441 | }
442 | return nil
443 | }
444 |
445 | func (Base) Name() string {
446 | return "catalina-base"
447 | }
448 |
--------------------------------------------------------------------------------
/tomcat/build_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2020 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 |
17 | package tomcat_test
18 |
19 | import (
20 | "os"
21 | "path/filepath"
22 | "testing"
23 | "time"
24 |
25 | "github.com/paketo-buildpacks/libpak/sbom/mocks"
26 |
27 | "github.com/buildpacks/libcnb"
28 | . "github.com/onsi/gomega"
29 | "github.com/paketo-buildpacks/libpak"
30 | "github.com/sclevine/spec"
31 |
32 | "github.com/paketo-buildpacks/apache-tomcat/v8/tomcat"
33 | )
34 |
35 | func testBuild(t *testing.T, context spec.G, it spec.S) {
36 | var (
37 | Expect = NewWithT(t).Expect
38 | sbomScanner mocks.SBOMScanner
39 | ctx libcnb.BuildContext
40 | )
41 |
42 | it.Before(func() {
43 | var err error
44 | ctx.Application.Path, err = os.MkdirTemp("", "tomcat-application")
45 | Expect(err).NotTo(HaveOccurred())
46 | ctx.Plan = libcnb.BuildpackPlan{Entries: []libcnb.BuildpackPlanEntry{
47 | {Name: "jvm-application"},
48 | {Name: "java-app-server"},
49 | }}
50 | sbomScanner = mocks.SBOMScanner{}
51 | sbomScanner.On("ScanLaunch", ctx.Application.Path, libcnb.SyftJSON, libcnb.CycloneDXJSON).Return(nil)
52 |
53 | t.Setenv("BP_ARCH", "amd64")
54 | })
55 |
56 | it.After(func() {
57 | Expect(os.RemoveAll(ctx.Application.Path)).To(Succeed())
58 | })
59 |
60 | it("does not contribute Tomcat if no WEB-INF", func() {
61 | result, err := tomcat.Build{SBOMScanner: &sbomScanner}.Build(ctx)
62 | Expect(err).NotTo(HaveOccurred())
63 |
64 | Expect(result.Layers).To(BeEmpty())
65 | Expect(result.Unmet).To(HaveLen(2))
66 | Expect(result.Unmet[0].Name).To(Equal("jvm-application"))
67 | Expect(result.Unmet[1].Name).To(Equal("java-app-server"))
68 | })
69 |
70 | it("does not contribute Tomcat if Main-Class", func() {
71 | Expect(os.MkdirAll(filepath.Join(ctx.Application.Path, "WEB-INF"), 0755)).To(Succeed())
72 | Expect(os.MkdirAll(filepath.Join(ctx.Application.Path, "META-INF"), 0755)).To(Succeed())
73 | Expect(os.WriteFile(filepath.Join(ctx.Application.Path, "META-INF", "MANIFEST.MF"), []byte(`Main-Class: test-main-class`), 0644)).To(Succeed())
74 |
75 | result, err := tomcat.Build{SBOMScanner: &sbomScanner}.Build(ctx)
76 | Expect(err).NotTo(HaveOccurred())
77 |
78 | Expect(result.Layers).To(BeEmpty())
79 | Expect(result.Unmet).To(HaveLen(2))
80 | Expect(result.Unmet[0].Name).To(Equal("jvm-application"))
81 | Expect(result.Unmet[1].Name).To(Equal("java-app-server"))
82 | })
83 |
84 | it("does not contribute Tomcat if java-app-server missing from buildplan", func() {
85 | Expect(os.MkdirAll(filepath.Join(ctx.Application.Path, "WEB-INF"), 0755)).To(Succeed())
86 |
87 | ctx.Buildpack.Metadata = map[string]interface{}{
88 | "dependencies": []map[string]interface{}{
89 | {
90 | "id": "tomcat",
91 | "version": "1.1.1",
92 | "stacks": []interface{}{"test-stack-id"},
93 | "purl": "pkg:generic/tomcat@1.1.1",
94 | "cpes": "cpe:2.3:a:apache:tomcat:1.1.1:*:*:*:*:*:*:*",
95 | },
96 | {
97 | "id": "tomcat-access-logging-support",
98 | "version": "1.1.1",
99 | "stacks": []interface{}{"test-stack-id"},
100 | "purl": "pkg:generic/tomcat-access-logging-support@1.1.1",
101 | "cpes": "cpe:2.3:a:cloudfoundry:tomcat-access-logging-support:1.1.1:*:*:*:*:*:*:*",
102 | },
103 | {
104 | "id": "tomcat-lifecycle-support",
105 | "version": "1.1.1",
106 | "stacks": []interface{}{"test-stack-id"},
107 | "purl": "pkg:generic/tomcat-lifecycle-logging-support@1.1.1",
108 | "cpes": "cpe:2.3:a:cloudfoundry:tomcat-lifecycle-logging-support:1.1.1:*:*:*:*:*:*:*",
109 | },
110 | {
111 | "id": "tomcat-logging-support",
112 | "version": "1.1.1",
113 | "uri": "https://example.com/releases/tomcat-logging-support-1.1.1.RELEASE.jar",
114 | "stacks": []interface{}{"test-stack-id"},
115 | "purl": "pkg:generic/tomcat-logging-support@1.1.1",
116 | "cpes": "cpe:2.3:a:cloudfoundry:tomcat-logging-support:1.1.1:*:*:*:*:*:*:*",
117 | },
118 | },
119 | }
120 | ctx.StackID = "test-stack-id"
121 | ctx.Plan.Entries = ctx.Plan.Entries[0:1] // remove second plan entry, java-app-server
122 |
123 | result, err := tomcat.Build{SBOMScanner: &sbomScanner}.Build(ctx)
124 | Expect(err).NotTo(HaveOccurred())
125 |
126 | Expect(result.Layers).To(BeEmpty())
127 | })
128 |
129 | it("contributes Tomcat", func() {
130 | Expect(os.MkdirAll(filepath.Join(ctx.Application.Path, "WEB-INF"), 0755)).To(Succeed())
131 |
132 | ctx.Buildpack.Metadata = map[string]interface{}{
133 | "dependencies": []map[string]interface{}{
134 | {
135 | "id": "tomcat",
136 | "version": "1.1.1",
137 | "stacks": []interface{}{"test-stack-id"},
138 | "purl": "pkg:generic/tomcat@1.1.1",
139 | "cpes": "cpe:2.3:a:apache:tomcat:1.1.1:*:*:*:*:*:*:*",
140 | },
141 | {
142 | "id": "tomcat-access-logging-support",
143 | "version": "1.1.1",
144 | "stacks": []interface{}{"test-stack-id"},
145 | "purl": "pkg:generic/tomcat-access-logging-support@1.1.1",
146 | "cpes": "cpe:2.3:a:cloudfoundry:tomcat-access-logging-support:1.1.1:*:*:*:*:*:*:*",
147 | },
148 | {
149 | "id": "tomcat-lifecycle-support",
150 | "version": "1.1.1",
151 | "stacks": []interface{}{"test-stack-id"},
152 | "purl": "pkg:generic/tomcat-lifecycle-logging-support@1.1.1",
153 | "cpes": "cpe:2.3:a:cloudfoundry:tomcat-lifecycle-logging-support:1.1.1:*:*:*:*:*:*:*",
154 | },
155 | {
156 | "id": "tomcat-logging-support",
157 | "version": "1.1.1",
158 | "uri": "https://example.com/releases/tomcat-logging-support-1.1.1.RELEASE.jar",
159 | "stacks": []interface{}{"test-stack-id"},
160 | "purl": "pkg:generic/tomcat-logging-support@1.1.1",
161 | "cpes": "cpe:2.3:a:cloudfoundry:tomcat-logging-support:1.1.1:*:*:*:*:*:*:*",
162 | },
163 | },
164 | }
165 | ctx.StackID = "test-stack-id"
166 |
167 | result, err := tomcat.Build{SBOMScanner: &sbomScanner}.Build(ctx)
168 | Expect(err).NotTo(HaveOccurred())
169 |
170 | Expect(result.Processes).To(ContainElements(
171 | libcnb.Process{Type: "task", Command: "sh", Arguments: []string{"tomcat/bin/catalina.sh", "run"}, Direct: true},
172 | libcnb.Process{Type: "tomcat", Command: "sh", Arguments: []string{"tomcat/bin/catalina.sh", "run"}, Direct: true},
173 | libcnb.Process{Type: "web", Command: "sh", Arguments: []string{"tomcat/bin/catalina.sh", "run"}, Direct: true, Default: true},
174 | ))
175 |
176 | Expect(result.Layers).To(HaveLen(3))
177 | Expect(result.Layers[0].Name()).To(Equal("tomcat"))
178 | Expect(result.Layers[1].Name()).To(Equal("helper"))
179 | Expect(result.Layers[1].(libpak.HelperLayerContributor).Names).To(Equal([]string{"access-logging-support"}))
180 | Expect(result.Layers[2].Name()).To(Equal("catalina-base"))
181 |
182 | Expect(result.BOM.Entries).To(HaveLen(5))
183 | Expect(result.BOM.Entries[0].Name).To(Equal("tomcat"))
184 | Expect(result.BOM.Entries[0].Build).To(BeTrue())
185 | Expect(result.BOM.Entries[0].Launch).To(BeTrue())
186 | Expect(result.BOM.Entries[1].Name).To(Equal("helper"))
187 | Expect(result.BOM.Entries[1].Build).To(BeFalse())
188 | Expect(result.BOM.Entries[1].Launch).To(BeTrue())
189 | Expect(result.BOM.Entries[2].Name).To(Equal("tomcat-access-logging-support"))
190 | Expect(result.BOM.Entries[2].Build).To(BeFalse())
191 | Expect(result.BOM.Entries[2].Launch).To(BeTrue())
192 | Expect(result.BOM.Entries[3].Name).To(Equal("tomcat-lifecycle-support"))
193 | Expect(result.BOM.Entries[3].Build).To(BeFalse())
194 | Expect(result.BOM.Entries[3].Launch).To(BeTrue())
195 | Expect(result.BOM.Entries[4].Name).To(Equal("tomcat-logging-support"))
196 | Expect(result.BOM.Entries[4].Build).To(BeFalse())
197 | Expect(result.BOM.Entries[4].Launch).To(BeTrue())
198 |
199 | sbomScanner.AssertCalled(t, "ScanLaunch", ctx.Application.Path, libcnb.SyftJSON, libcnb.CycloneDXJSON)
200 | })
201 |
202 | it("contributes Tomcat on Tiny", func() {
203 | ctx.StackID = libpak.BionicTinyStackID
204 |
205 | Expect(os.MkdirAll(filepath.Join(ctx.Application.Path, "WEB-INF"), 0755)).To(Succeed())
206 |
207 | ctx.Buildpack.Metadata = map[string]interface{}{
208 | "dependencies": []map[string]interface{}{
209 | {
210 | "id": "tomcat",
211 | "version": "1.1.2",
212 | "stacks": []interface{}{libpak.BionicTinyStackID},
213 | "purl": "pkg:generic/tomcat@1.1.1",
214 | "cpes": []interface{}{"cpe:2.3:a:apache:tomcat:1.1.1:*:*:*:*:*:*:*"},
215 | },
216 | {
217 | "id": "tomcat-access-logging-support",
218 | "version": "1.1.1",
219 | "stacks": []interface{}{libpak.BionicTinyStackID},
220 | "purl": "pkg:generic/tomcat-access-logging-support@3.3.0",
221 | "cpes": []interface{}{"cpe:2.3:a:cloudfoundry:tomcat-access-logging-support:3.3.0:*:*:*:*:*:*:*"},
222 | },
223 | {
224 | "id": "tomcat-lifecycle-support",
225 | "version": "1.1.1",
226 | "stacks": []interface{}{libpak.BionicTinyStackID},
227 | "purl": "pkg:generic/tomcat-lifecycle-logging-support@1.1.1",
228 | "cpes": []interface{}{"cpe:2.3:a:cloudfoundry:tomcat-lifecycle-logging-support:1.1.1:*:*:*:*:*:*:*"},
229 | },
230 | {
231 | "id": "tomcat-logging-support",
232 | "version": "1.1.1",
233 | "uri": "https://example.com/releases/tomcat-logging-support-1.1.1.RELEASE.jar",
234 | "stacks": []interface{}{libpak.BionicTinyStackID},
235 | "purl": "pkg:generic/tomcat-logging-support@1.1.1",
236 | "cpes": []interface{}{"cpe:2.3:a:cloudfoundry:tomcat-logging-support:1.1.1:*:*:*:*:*:*:*"},
237 | },
238 | },
239 | }
240 |
241 | result, err := tomcat.Build{SBOMScanner: &sbomScanner}.Build(ctx)
242 | Expect(err).NotTo(HaveOccurred())
243 |
244 | for _, procType := range []string{"task", "tomcat", "web"} {
245 | expectedProcess := libcnb.Process{
246 | Type: procType,
247 | Command: "java",
248 | Arguments: []string{
249 | "-Djava.util.logging.config.file=catalina-base/conf/logging.properties",
250 | "-Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager",
251 | "-Djdk.tls.ephemeralDHKeySize=2048",
252 | "-classpath",
253 | "catalina-base/bin/tomcat-logging-support-1.1.1.RELEASE.jar:tomcat/bin/bootstrap.jar:tomcat/bin/tomcat-juli.jar",
254 | "-Dcatalina.home=tomcat",
255 | "-Dcatalina.base=catalina-base",
256 | "org.apache.catalina.startup.Bootstrap",
257 | "start",
258 | },
259 | Direct: true,
260 | }
261 | if procType == "web" {
262 | expectedProcess.Default = true
263 | }
264 | Expect(result.Processes).To(ContainElement(expectedProcess))
265 | }
266 |
267 | Expect(result.Layers).To(HaveLen(3))
268 | Expect(result.Layers[0].Name()).To(Equal("tomcat"))
269 | Expect(result.Layers[1].Name()).To(Equal("helper"))
270 | Expect(result.Layers[1].(libpak.HelperLayerContributor).Names).To(Equal([]string{"access-logging-support"}))
271 | Expect(result.Layers[2].Name()).To(Equal("catalina-base"))
272 |
273 | Expect(result.BOM.Entries).To(HaveLen(5))
274 | Expect(result.BOM.Entries[0].Name).To(Equal("tomcat"))
275 | Expect(result.BOM.Entries[0].Build).To(BeTrue())
276 | Expect(result.BOM.Entries[0].Launch).To(BeTrue())
277 | Expect(result.BOM.Entries[1].Name).To(Equal("helper"))
278 | Expect(result.BOM.Entries[1].Build).To(BeFalse())
279 | Expect(result.BOM.Entries[1].Launch).To(BeTrue())
280 | Expect(result.BOM.Entries[2].Name).To(Equal("tomcat-access-logging-support"))
281 | Expect(result.BOM.Entries[2].Build).To(BeFalse())
282 | Expect(result.BOM.Entries[2].Launch).To(BeTrue())
283 | Expect(result.BOM.Entries[3].Name).To(Equal("tomcat-lifecycle-support"))
284 | Expect(result.BOM.Entries[3].Build).To(BeFalse())
285 | Expect(result.BOM.Entries[3].Launch).To(BeTrue())
286 | Expect(result.BOM.Entries[4].Name).To(Equal("tomcat-logging-support"))
287 | Expect(result.BOM.Entries[4].Build).To(BeFalse())
288 | Expect(result.BOM.Entries[4].Launch).To(BeTrue())
289 |
290 | sbomScanner.AssertCalled(t, "ScanLaunch", ctx.Application.Path, libcnb.SyftJSON, libcnb.CycloneDXJSON)
291 | })
292 |
293 | context("$BP_TOMCAT_VERSION", func() {
294 | it.Before(func() {
295 | Expect(os.Setenv("BP_TOMCAT_VERSION", "1.1.1")).To(Succeed())
296 | })
297 |
298 | it.After(func() {
299 | Expect(os.Unsetenv("BP_TOMCAT_VERSION")).To(Succeed())
300 | })
301 |
302 | it("selects version based on $BP_TOMCAT_VERSION", func() {
303 | Expect(os.MkdirAll(filepath.Join(ctx.Application.Path, "WEB-INF"), 0755)).To(Succeed())
304 |
305 | ctx.Buildpack.Metadata = map[string]interface{}{
306 | "dependencies": []map[string]interface{}{
307 | {
308 | "id": "tomcat",
309 | "version": "1.1.1",
310 | "stacks": []interface{}{"test-stack-id"},
311 | },
312 | {
313 | "id": "tomcat",
314 | "version": "2.2.2",
315 | "stacks": []interface{}{"test-stack-id"},
316 | },
317 | {
318 | "id": "tomcat-access-logging-support",
319 | "version": "1.1.1",
320 | "stacks": []interface{}{"test-stack-id"},
321 | },
322 | {
323 | "id": "tomcat-lifecycle-support",
324 | "version": "1.1.1",
325 | "stacks": []interface{}{"test-stack-id"},
326 | },
327 | {
328 | "id": "tomcat-logging-support",
329 | "version": "1.1.1",
330 | "stacks": []interface{}{"test-stack-id"},
331 | },
332 | },
333 | }
334 | ctx.StackID = "test-stack-id"
335 |
336 | result, err := tomcat.Build{SBOMScanner: &sbomScanner}.Build(ctx)
337 | Expect(err).NotTo(HaveOccurred())
338 |
339 | Expect(result.Layers[0].(tomcat.Home).LayerContributor.Dependency.Version).To(Equal("1.1.1"))
340 | sbomScanner.AssertCalled(t, "ScanLaunch", ctx.Application.Path, libcnb.SyftJSON, libcnb.CycloneDXJSON)
341 | })
342 | })
343 |
344 | context("$BP_TOMCAT_EXT_CONF_URI", func() {
345 | it.Before(func() {
346 | Expect(os.MkdirAll(filepath.Join(ctx.Application.Path, "WEB-INF"), 0755)).To(Succeed())
347 |
348 | ctx.Buildpack.Metadata = map[string]interface{}{
349 | "dependencies": []map[string]interface{}{
350 | {
351 | "id": "tomcat",
352 | "version": "1.1.1",
353 | "stacks": []interface{}{"test-stack-id"},
354 | },
355 | {
356 | "id": "tomcat-access-logging-support",
357 | "version": "1.1.1",
358 | "stacks": []interface{}{"test-stack-id"},
359 | },
360 | {
361 | "id": "tomcat-lifecycle-support",
362 | "version": "1.1.1",
363 | "stacks": []interface{}{"test-stack-id"},
364 | },
365 | {
366 | "id": "tomcat-logging-support",
367 | "version": "1.1.1",
368 | "stacks": []interface{}{"test-stack-id"},
369 | },
370 | },
371 | }
372 | ctx.StackID = "test-stack-id"
373 |
374 | t.Setenv("BP_TOMCAT_EXT_CONF_SHA256", "test-sha256")
375 | t.Setenv("BP_TOMCAT_EXT_CONF_URI", "test-uri")
376 | t.Setenv("BP_TOMCAT_EXT_CONF_VERSION", "test-version")
377 | })
378 |
379 | it("contributes external configuration when $BP_TOMCAT_EXT_CONF_URI, $BP_TOMCAT_EXT_CONF_VERSION and $BP_TOMCAT_EXT_CONF_SHA256 are set", func() {
380 | result, err := tomcat.Build{SBOMScanner: &sbomScanner}.Build(ctx)
381 | Expect(err).NotTo(HaveOccurred())
382 |
383 | Expect(result.Layers).To(HaveLen(3))
384 | Expect(result.Layers[2].(tomcat.Base).ExternalConfigurationDependency).To(Equal(&libpak.BuildpackDependency{
385 | ID: "tomcat-external-configuration",
386 | Name: "Tomcat External Configuration",
387 | Version: "test-version",
388 | URI: "test-uri",
389 | SHA256: "test-sha256",
390 | Stacks: []string{ctx.StackID},
391 | }))
392 | sbomScanner.AssertCalled(t, "ScanLaunch", ctx.Application.Path, libcnb.SyftJSON, libcnb.CycloneDXJSON)
393 | })
394 |
395 | it("uses time as version if neither $BP_TOMCAT_EXT_CONF_VERSION nor $BP_TOMCAT_EXT_CONF_SHA256 is provided", func() {
396 | Expect(os.Unsetenv("BP_TOMCAT_EXT_CONF_SHA256")).To(Succeed())
397 | Expect(os.Unsetenv("BP_TOMCAT_EXT_CONF_VERSION")).To(Succeed())
398 |
399 | result, err := tomcat.Build{SBOMScanner: &sbomScanner}.Build(ctx)
400 | Expect(err).NotTo(HaveOccurred())
401 |
402 | Expect(result.Layers).To(HaveLen(3))
403 | version := result.Layers[2].(tomcat.Base).ExternalConfigurationDependency.Version
404 | Expect(time.Parse(time.RFC3339, version)).NotTo(BeNil())
405 | })
406 |
407 | it("contributes external configuration when $BP_TOMCAT_EXT_CONF_URI and $BP_TOMCAT_EXT_CONF_VERSION are set", func() {
408 | Expect(os.Unsetenv("BP_TOMCAT_EXT_CONF_SHA256")).To(Succeed())
409 |
410 | result, err := tomcat.Build{SBOMScanner: &sbomScanner}.Build(ctx)
411 | Expect(err).NotTo(HaveOccurred())
412 |
413 | Expect(result.Layers).To(HaveLen(3))
414 | Expect(result.Layers[2].(tomcat.Base).ExternalConfigurationDependency).To(Equal(&libpak.BuildpackDependency{
415 | ID: "tomcat-external-configuration",
416 | Name: "Tomcat External Configuration",
417 | Version: "test-version",
418 | URI: "test-uri",
419 | SHA256: "",
420 | Stacks: []string{ctx.StackID},
421 | }))
422 | sbomScanner.AssertCalled(t, "ScanLaunch", ctx.Application.Path, libcnb.SyftJSON, libcnb.CycloneDXJSON)
423 | })
424 |
425 | it("contributes external configuration when $BP_TOMCAT_EXT_CONF_URI and $BP_TOMCAT_EXT_CONF_SHA256 are set", func() {
426 | Expect(os.Unsetenv("BP_TOMCAT_EXT_CONF_VERSION")).To(Succeed())
427 |
428 | result, err := tomcat.Build{SBOMScanner: &sbomScanner}.Build(ctx)
429 | Expect(err).NotTo(HaveOccurred())
430 |
431 | Expect(result.Layers).To(HaveLen(3))
432 | Expect(result.Layers[2].(tomcat.Base).ExternalConfigurationDependency).To(Equal(&libpak.BuildpackDependency{
433 | ID: "tomcat-external-configuration",
434 | Name: "Tomcat External Configuration",
435 | Version: "",
436 | URI: "test-uri",
437 | SHA256: "test-sha256",
438 | Stacks: []string{ctx.StackID},
439 | }))
440 | sbomScanner.AssertCalled(t, "ScanLaunch", ctx.Application.Path, libcnb.SyftJSON, libcnb.CycloneDXJSON)
441 | })
442 |
443 | })
444 |
445 | it("returns default context path", func() {
446 | Expect(tomcat.Build{SBOMScanner: &sbomScanner}.ContextPath(libpak.ConfigurationResolver{})).To(Equal("ROOT"))
447 | })
448 |
449 | context("$BP_TOMCAT_CONTEXT_PATH", func() {
450 | it.Before(func() {
451 | Expect(os.Setenv("BP_TOMCAT_CONTEXT_PATH", "/alpha/bravo/")).To(Succeed())
452 | })
453 |
454 | it.After(func() {
455 | Expect(os.Unsetenv("BP_TOMCAT_CONTEXT_PATH")).To(Succeed())
456 | })
457 |
458 | it("returns transformed context path", func() {
459 | Expect(tomcat.Build{SBOMScanner: &sbomScanner}.ContextPath(libpak.ConfigurationResolver{})).To(Equal("alpha#bravo"))
460 | })
461 | })
462 |
463 | it("contributes Tomcat with war files", func() {
464 | Expect(os.WriteFile(filepath.Join(ctx.Application.Path, "test.war"), []byte(`test`), 0644)).To(Succeed())
465 |
466 | ctx.Buildpack.Metadata = map[string]interface{}{
467 | "dependencies": []map[string]interface{}{
468 | {
469 | "id": "tomcat",
470 | "version": "1.1.1",
471 | "stacks": []interface{}{"test-stack-id"},
472 | "purl": "pkg:generic/tomcat@1.1.1",
473 | "cpes": "cpe:2.3:a:apache:tomcat:1.1.1:*:*:*:*:*:*:*",
474 | },
475 | {
476 | "id": "tomcat-access-logging-support",
477 | "version": "1.1.1",
478 | "stacks": []interface{}{"test-stack-id"},
479 | "purl": "pkg:generic/tomcat-access-logging-support@1.1.1",
480 | "cpes": "cpe:2.3:a:cloudfoundry:tomcat-access-logging-support:1.1.1:*:*:*:*:*:*:*",
481 | },
482 | {
483 | "id": "tomcat-lifecycle-support",
484 | "version": "1.1.1",
485 | "stacks": []interface{}{"test-stack-id"},
486 | "purl": "pkg:generic/tomcat-lifecycle-logging-support@1.1.1",
487 | "cpes": "cpe:2.3:a:cloudfoundry:tomcat-lifecycle-logging-support:1.1.1:*:*:*:*:*:*:*",
488 | },
489 | {
490 | "id": "tomcat-logging-support",
491 | "version": "1.1.1",
492 | "uri": "https://example.com/releases/tomcat-logging-support-1.1.1.RELEASE.jar",
493 | "stacks": []interface{}{"test-stack-id"},
494 | "purl": "pkg:generic/tomcat-logging-support@1.1.1",
495 | "cpes": "cpe:2.3:a:cloudfoundry:tomcat-logging-support:1.1.1:*:*:*:*:*:*:*",
496 | },
497 | },
498 | }
499 | ctx.StackID = "test-stack-id"
500 |
501 | result, err := tomcat.Build{SBOMScanner: &sbomScanner}.Build(ctx)
502 | Expect(err).NotTo(HaveOccurred())
503 |
504 | Expect(result.Processes).To(ContainElements(
505 | libcnb.Process{Type: "task", Command: "sh", Arguments: []string{"tomcat/bin/catalina.sh", "run"}, Direct: true},
506 | libcnb.Process{Type: "tomcat", Command: "sh", Arguments: []string{"tomcat/bin/catalina.sh", "run"}, Direct: true},
507 | libcnb.Process{Type: "web", Command: "sh", Arguments: []string{"tomcat/bin/catalina.sh", "run"}, Direct: true, Default: true},
508 | ))
509 |
510 | Expect(result.Layers).To(HaveLen(3))
511 | Expect(result.Layers[0].Name()).To(Equal("tomcat"))
512 | Expect(result.Layers[1].Name()).To(Equal("helper"))
513 | Expect(result.Layers[1].(libpak.HelperLayerContributor).Names).To(Equal([]string{"access-logging-support"}))
514 | Expect(result.Layers[2].Name()).To(Equal("catalina-base"))
515 |
516 | Expect(result.BOM.Entries).To(HaveLen(5))
517 | Expect(result.BOM.Entries[0].Name).To(Equal("tomcat"))
518 | Expect(result.BOM.Entries[0].Build).To(BeTrue())
519 | Expect(result.BOM.Entries[0].Launch).To(BeTrue())
520 | Expect(result.BOM.Entries[1].Name).To(Equal("helper"))
521 | Expect(result.BOM.Entries[1].Build).To(BeFalse())
522 | Expect(result.BOM.Entries[1].Launch).To(BeTrue())
523 | Expect(result.BOM.Entries[2].Name).To(Equal("tomcat-access-logging-support"))
524 | Expect(result.BOM.Entries[2].Build).To(BeFalse())
525 | Expect(result.BOM.Entries[2].Launch).To(BeTrue())
526 | Expect(result.BOM.Entries[3].Name).To(Equal("tomcat-lifecycle-support"))
527 | Expect(result.BOM.Entries[3].Build).To(BeFalse())
528 | Expect(result.BOM.Entries[3].Launch).To(BeTrue())
529 | Expect(result.BOM.Entries[4].Name).To(Equal("tomcat-logging-support"))
530 | Expect(result.BOM.Entries[4].Build).To(BeFalse())
531 | Expect(result.BOM.Entries[4].Launch).To(BeTrue())
532 |
533 | sbomScanner.AssertCalled(t, "ScanLaunch", ctx.Application.Path, libcnb.SyftJSON, libcnb.CycloneDXJSON)
534 | })
535 |
536 | }
537 |
--------------------------------------------------------------------------------
/tomcat/base_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2018-2020 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 |
17 | package tomcat_test
18 |
19 | import (
20 | "fmt"
21 | "io"
22 | "os"
23 | "path/filepath"
24 | "strings"
25 | "testing"
26 |
27 | "github.com/buildpacks/libcnb"
28 | . "github.com/onsi/gomega"
29 | "github.com/paketo-buildpacks/libpak"
30 | "github.com/sclevine/spec"
31 |
32 | "github.com/paketo-buildpacks/apache-tomcat/v8/tomcat"
33 | )
34 |
35 | func testBase(t *testing.T, context spec.G, it spec.S) {
36 | var (
37 | Expect = NewWithT(t).Expect
38 |
39 | ctx libcnb.BuildContext
40 | )
41 |
42 | it.Before(func() {
43 | var err error
44 |
45 | ctx.Application.Path, err = os.MkdirTemp("", "base-application")
46 | Expect(err).NotTo(HaveOccurred())
47 |
48 | ctx.Buildpack.Path, err = os.MkdirTemp("", "base-buildpack")
49 | Expect(err).NotTo(HaveOccurred())
50 |
51 | ctx.Layers.Path, err = os.MkdirTemp("", "base-layers")
52 | Expect(err).NotTo(HaveOccurred())
53 |
54 | Expect(os.MkdirAll(filepath.Join(ctx.Layers.Path, "tomcat", "conf"), 0755)).To(Succeed())
55 | commonLoader := "common.loader=\"${catalina.base}/lib\",\"${catalina.base}/lib/*.jar\",\"${catalina.home}/lib\",\"${catalina.home}/lib/*.jar\""
56 | Expect(os.WriteFile(filepath.Join(ctx.Layers.Path, "tomcat", "conf", "catalina.properties"), []byte(commonLoader), 0644)).
57 | To(Succeed())
58 |
59 | Expect(os.MkdirAll(filepath.Join(ctx.Buildpack.Path, "resources"), 0755)).To(Succeed())
60 | Expect(os.WriteFile(filepath.Join(ctx.Buildpack.Path, "resources", "context.xml"), []byte{}, 0644)).
61 | To(Succeed())
62 | Expect(os.WriteFile(filepath.Join(ctx.Buildpack.Path, "resources", "logging.properties"), []byte{}, 0644)).
63 | To(Succeed())
64 | Expect(os.WriteFile(filepath.Join(ctx.Buildpack.Path, "resources", "server.xml"), []byte{}, 0644)).
65 | To(Succeed())
66 | Expect(os.WriteFile(filepath.Join(ctx.Buildpack.Path, "resources", "web.xml"), []byte{}, 0644)).
67 | To(Succeed())
68 | })
69 |
70 | it.After(func() {
71 | Expect(os.RemoveAll(ctx.Application.Path)).To(Succeed())
72 | Expect(os.RemoveAll(ctx.Buildpack.Path)).To(Succeed())
73 | Expect(os.RemoveAll(ctx.Layers.Path)).To(Succeed())
74 | })
75 |
76 | it("contributes catalina base", func() {
77 | accessLoggingDep := libpak.BuildpackDependency{
78 | ID: "tomcat-access-logging-support",
79 | URI: "https://localhost/stub-tomcat-access-logging-support.jar",
80 | SHA256: "d723bfe2ba67dfa92b24e3b6c7b2d0e6a963de7313350e306d470e44e330a5d2",
81 | PURL: "pkg:generic/tomcat-access-logging-support@3.3.0",
82 | CPEs: []string{"cpe:2.3:a:cloudfoundry:tomcat-access-logging-support:3.3.0:*:*:*:*:*:*:*"},
83 | }
84 | lifecycleDep := libpak.BuildpackDependency{
85 | ID: "tomcat-lifecycle-support",
86 | URI: "https://localhost/stub-tomcat-lifecycle-support.jar",
87 | SHA256: "723126712c0b22a7fe409664adf1fbb78cf3040e313a82c06696f5058e190534",
88 | PURL: "pkg:generic/tomcat-lifecycle-support@3.3.0",
89 | CPEs: []string{"cpe:2.3:a:cloudfoundry:tomcat-lifecycle-support:3.3.0:*:*:*:*:*:*:*"},
90 | }
91 | loggingDep := libpak.BuildpackDependency{
92 | ID: "tomcat-logging-support",
93 | URI: "https://localhost/stub-tomcat-logging-support.jar",
94 | SHA256: "e0a7e163cc9f1ffd41c8de3942c7c6b505090b7484c2ba9be846334e31c44a2c",
95 | PURL: "pkg:generic/tomcat-logging-support@3.3.0",
96 | CPEs: []string{"cpe:2.3:a:cloudfoundry:tomcat-logging-support:3.3.0:*:*:*:*:*:*:*"},
97 | }
98 |
99 | dc := libpak.DependencyCache{CachePath: "testdata"}
100 |
101 | contributor, entries := tomcat.NewBase(
102 | ctx.Application.Path,
103 | ctx.Buildpack.Path,
104 | libpak.ConfigurationResolver{},
105 | "test-context-path",
106 | accessLoggingDep,
107 | nil,
108 | lifecycleDep,
109 | loggingDep,
110 | dc,
111 | false,
112 | )
113 |
114 | Expect(entries).To(HaveLen(3))
115 | Expect(entries[0].Name).To(Equal("tomcat-access-logging-support"))
116 | Expect(entries[0].Build).To(BeFalse())
117 | Expect(entries[0].Launch).To(BeTrue())
118 | Expect(entries[1].Name).To(Equal("tomcat-lifecycle-support"))
119 | Expect(entries[1].Build).To(BeFalse())
120 | Expect(entries[1].Launch).To(BeTrue())
121 | Expect(entries[2].Name).To(Equal("tomcat-logging-support"))
122 | Expect(entries[2].Build).To(BeFalse())
123 | Expect(entries[2].Launch).To(BeTrue())
124 |
125 | layer, err := ctx.Layers.Layer("test-layer")
126 | Expect(err).NotTo(HaveOccurred())
127 |
128 | layer, err = contributor.Contribute(layer)
129 | Expect(err).NotTo(HaveOccurred())
130 |
131 | Expect(layer.Launch).To(BeTrue())
132 | Expect(filepath.Join(layer.Path, "conf", "context.xml")).To(BeARegularFile())
133 | Expect(filepath.Join(layer.Path, "conf", "logging.properties")).To(BeARegularFile())
134 | Expect(filepath.Join(layer.Path, "conf", "server.xml")).To(BeARegularFile())
135 | Expect(filepath.Join(layer.Path, "conf", "web.xml")).To(BeARegularFile())
136 | Expect(filepath.Join(layer.Path, "conf", "catalina.properties")).To(BeARegularFile())
137 | Expect(os.ReadFile(filepath.Join(layer.Path, "conf", "catalina.properties"))).To(ContainSubstring("common.loader=${BPI_TOMCAT_ADDITIONAL_COMMON_JARS}"))
138 | Expect(filepath.Join(layer.Path, "lib", "stub-tomcat-access-logging-support.jar")).To(BeARegularFile())
139 | Expect(filepath.Join(layer.Path, "lib", "stub-tomcat-lifecycle-support.jar")).To(BeARegularFile())
140 | Expect(filepath.Join(layer.Path, "bin", "stub-tomcat-logging-support.jar")).To(BeARegularFile())
141 | Expect(os.ReadFile(filepath.Join(layer.Path, "bin", "setenv.sh"))).To(Equal(
142 | []byte(fmt.Sprintf(`CLASSPATH="%s"`, filepath.Join(layer.Path, "bin", "stub-tomcat-logging-support.jar")))))
143 | Expect(layer.LaunchEnvironment["CATALINA_BASE.default"]).To(Equal(layer.Path))
144 | Expect(filepath.Join(layer.Path, "temp")).To(BeADirectory())
145 |
146 | file := filepath.Join(layer.Path, "webapps", "test-context-path")
147 | fi, err := os.Lstat(file)
148 | Expect(err).NotTo(HaveOccurred())
149 | Expect(fi.Mode() & os.ModeSymlink).To(Equal(os.ModeSymlink))
150 | Expect(os.Readlink(file)).To(Equal(ctx.Application.Path))
151 |
152 | Expect(layer.LaunchEnvironment["CATALINA_BASE.default"]).To(Equal(layer.Path))
153 | Expect(layer.LaunchEnvironment["CATALINA_OPTS.default"]).To(Equal("-DBPI_TOMCAT_ADDITIONAL_COMMON_JARS=${BPI_TOMCAT_ADDITIONAL_COMMON_JARS} -Dorg.apache.tomcat.util.digester.PROPERTY_SOURCE=org.apache.tomcat.util.digester.EnvironmentPropertySource"))
154 | })
155 |
156 | it("contributes custom configuration", func() {
157 | externalConfigurationDep := libpak.BuildpackDependency{
158 | ID: "tomcat-external-configuration",
159 | URI: "https://localhost/stub-external-configuration.tar.gz",
160 | SHA256: "22e708cfd301430cbcf8d1c2289503d8288d50df519ff4db7cca0ff9fe83c324",
161 | PURL: "pkg:generic/tomcat@1.1.1",
162 | CPEs: []string{"cpe:2.3:a:apache:tomcat:1.1.1:*:*:*:*:*:*:*"},
163 | }
164 | accessLoggingDep := libpak.BuildpackDependency{
165 | ID: "tomcat-access-logging-support",
166 | URI: "https://localhost/stub-tomcat-access-logging-support.jar",
167 | SHA256: "d723bfe2ba67dfa92b24e3b6c7b2d0e6a963de7313350e306d470e44e330a5d2",
168 | PURL: "pkg:generic/tomcat-access-logging-support@3.3.0",
169 | CPEs: []string{"cpe:2.3:a:cloudfoundry:tomcat-access-logging-support:3.3.0:*:*:*:*:*:*:*"},
170 | }
171 | lifecycleDep := libpak.BuildpackDependency{
172 | ID: "tomcat-lifecycle-support",
173 | URI: "https://localhost/stub-tomcat-lifecycle-support.jar",
174 | SHA256: "723126712c0b22a7fe409664adf1fbb78cf3040e313a82c06696f5058e190534",
175 | PURL: "pkg:generic/tomcat-lifecycle-support@3.3.0",
176 | CPEs: []string{"cpe:2.3:a:cloudfoundry:tomcat-lifecycle-support:3.3.0:*:*:*:*:*:*:*"},
177 | }
178 | loggingDep := libpak.BuildpackDependency{
179 | ID: "tomcat-logging-support",
180 | URI: "https://localhost/stub-tomcat-logging-support.jar",
181 | SHA256: "e0a7e163cc9f1ffd41c8de3942c7c6b505090b7484c2ba9be846334e31c44a2c",
182 | PURL: "pkg:generic/tomcat-logging-support@3.3.0",
183 | CPEs: []string{"cpe:2.3:a:cloudfoundry:tomcat-logging-support:3.3.0:*:*:*:*:*:*:*"},
184 | }
185 |
186 | dc := libpak.DependencyCache{CachePath: "testdata"}
187 |
188 | contrib, entries := tomcat.NewBase(
189 | ctx.Application.Path,
190 | ctx.Buildpack.Path,
191 | libpak.ConfigurationResolver{},
192 | "test-context-path",
193 | accessLoggingDep,
194 | &externalConfigurationDep,
195 | lifecycleDep,
196 | loggingDep,
197 | dc,
198 | false,
199 | )
200 | layer, err := ctx.Layers.Layer("test-layer")
201 | Expect(err).NotTo(HaveOccurred())
202 |
203 | Expect(entries).To(HaveLen(4))
204 | Expect(entries[0].Name).To(Equal("tomcat-access-logging-support"))
205 | Expect(entries[0].Build).To(BeFalse())
206 | Expect(entries[0].Launch).To(BeTrue())
207 | Expect(entries[1].Name).To(Equal("tomcat-lifecycle-support"))
208 | Expect(entries[1].Build).To(BeFalse())
209 | Expect(entries[1].Launch).To(BeTrue())
210 | Expect(entries[2].Name).To(Equal("tomcat-logging-support"))
211 | Expect(entries[2].Build).To(BeFalse())
212 | Expect(entries[2].Launch).To(BeTrue())
213 | Expect(entries[3].Name).To(Equal("tomcat-external-configuration"))
214 | Expect(entries[3].Build).To(BeFalse())
215 | Expect(entries[3].Launch).To(BeTrue())
216 |
217 | layer, err = contrib.Contribute(layer)
218 | Expect(err).NotTo(HaveOccurred())
219 |
220 | Expect(filepath.Join(layer.Path, "fixture-marker")).To(BeARegularFile())
221 | })
222 |
223 | context("$BP_TOMCAT_EXT_CONF_STRIP", func() {
224 | it.Before(func() {
225 | Expect(os.Setenv("BP_TOMCAT_EXT_CONF_STRIP", "1")).To(Succeed())
226 | })
227 |
228 | it.After(func() {
229 | Expect(os.Unsetenv("BP_TOMCAT_EXT_CONF_STRIP")).To(Succeed())
230 | })
231 |
232 | it("contributes custom configuration with directory", func() {
233 | externalConfigurationDep := libpak.BuildpackDependency{
234 | ID: "tomcat-external-configuration",
235 | URI: "https://localhost/stub-external-configuration-with-directory.tar.gz",
236 | SHA256: "060818cbcdc2008563f0f9e2428ecf4a199a5821c5b8b1dcd11a67666c1e2cd6",
237 | PURL: "pkg:generic/tomcat@1.1.1",
238 | CPEs: []string{"cpe:2.3:a:apache:tomcat:1.1.1:*:*:*:*:*:*:*"},
239 | }
240 | accessLoggingDep := libpak.BuildpackDependency{
241 | ID: "tomcat-access-logging-support",
242 | URI: "https://localhost/stub-tomcat-access-logging-support.jar",
243 | SHA256: "d723bfe2ba67dfa92b24e3b6c7b2d0e6a963de7313350e306d470e44e330a5d2",
244 | PURL: "pkg:generic/tomcat-access-logging-support@3.3.0",
245 | CPEs: []string{"cpe:2.3:a:cloudfoundry:tomcat-access-logging-support:3.3.0:*:*:*:*:*:*:*"},
246 | }
247 | lifecycleDep := libpak.BuildpackDependency{
248 | ID: "tomcat-lifecycle-support",
249 | URI: "https://localhost/stub-tomcat-lifecycle-support.jar",
250 | SHA256: "723126712c0b22a7fe409664adf1fbb78cf3040e313a82c06696f5058e190534",
251 | PURL: "pkg:generic/tomcat-lifecycle-support@3.3.0",
252 | CPEs: []string{"cpe:2.3:a:cloudfoundry:tomcat-lifecycle-support:3.3.0:*:*:*:*:*:*:*"},
253 | }
254 | loggingDep := libpak.BuildpackDependency{
255 | ID: "tomcat-logging-support",
256 | URI: "https://localhost/stub-tomcat-logging-support.jar",
257 | SHA256: "e0a7e163cc9f1ffd41c8de3942c7c6b505090b7484c2ba9be846334e31c44a2c",
258 | PURL: "pkg:generic/tomcat-logging-support@3.3.0",
259 | CPEs: []string{"cpe:2.3:a:cloudfoundry:tomcat-logging-support:3.3.0:*:*:*:*:*:*:*"},
260 | }
261 |
262 | dc := libpak.DependencyCache{CachePath: "testdata"}
263 |
264 | contrib, entries := tomcat.NewBase(
265 | ctx.Application.Path,
266 | ctx.Buildpack.Path,
267 | libpak.ConfigurationResolver{},
268 | "test-context-path",
269 | accessLoggingDep,
270 | &externalConfigurationDep,
271 | lifecycleDep,
272 | loggingDep,
273 | dc,
274 | false,
275 | )
276 | Expect(entries).To(HaveLen(4))
277 | Expect(entries[0].Name).To(Equal("tomcat-access-logging-support"))
278 | Expect(entries[0].Build).To(BeFalse())
279 | Expect(entries[0].Launch).To(BeTrue())
280 | Expect(entries[1].Name).To(Equal("tomcat-lifecycle-support"))
281 | Expect(entries[1].Build).To(BeFalse())
282 | Expect(entries[1].Launch).To(BeTrue())
283 | Expect(entries[2].Name).To(Equal("tomcat-logging-support"))
284 | Expect(entries[2].Build).To(BeFalse())
285 | Expect(entries[2].Launch).To(BeTrue())
286 | Expect(entries[3].Name).To(Equal("tomcat-external-configuration"))
287 | Expect(entries[3].Build).To(BeFalse())
288 | Expect(entries[3].Launch).To(BeTrue())
289 |
290 | layer, err := ctx.Layers.Layer("test-layer")
291 | Expect(err).NotTo(HaveOccurred())
292 |
293 | layer, err = contrib.Contribute(layer)
294 | Expect(err).NotTo(HaveOccurred())
295 |
296 | Expect(filepath.Join(layer.Path, "fixture-marker")).To(BeARegularFile())
297 | })
298 | })
299 |
300 | context("$BP_TOMCAT_ENV_PROPERTY_SOURCE_DISABLED", func() {
301 | it.Before(func() {
302 | Expect(os.Setenv("BP_TOMCAT_ENV_PROPERTY_SOURCE_DISABLED", "true")).To(Succeed())
303 | })
304 |
305 | it.After(func() {
306 | Expect(os.Unsetenv("BP_TOMCAT_ENV_PROPERTY_SOURCE_DISABLED")).To(Succeed())
307 | })
308 |
309 | it("environment property source can be disabled", func() {
310 | accessLoggingDep := libpak.BuildpackDependency{
311 | ID: "tomcat-access-logging-support",
312 | URI: "https://localhost/stub-tomcat-access-logging-support.jar",
313 | SHA256: "d723bfe2ba67dfa92b24e3b6c7b2d0e6a963de7313350e306d470e44e330a5d2",
314 | PURL: "pkg:generic/tomcat-access-logging-support@3.3.0",
315 | CPEs: []string{"cpe:2.3:a:cloudfoundry:tomcat-access-logging-support:3.3.0:*:*:*:*:*:*:*"},
316 | }
317 | lifecycleDep := libpak.BuildpackDependency{
318 | ID: "tomcat-lifecycle-support",
319 | URI: "https://localhost/stub-tomcat-lifecycle-support.jar",
320 | SHA256: "723126712c0b22a7fe409664adf1fbb78cf3040e313a82c06696f5058e190534",
321 | PURL: "pkg:generic/tomcat-lifecycle-support@3.3.0",
322 | CPEs: []string{"cpe:2.3:a:cloudfoundry:tomcat-lifecycle-support:3.3.0:*:*:*:*:*:*:*"},
323 | }
324 | loggingDep := libpak.BuildpackDependency{
325 | ID: "tomcat-logging-support",
326 | URI: "https://localhost/stub-tomcat-logging-support.jar",
327 | SHA256: "e0a7e163cc9f1ffd41c8de3942c7c6b505090b7484c2ba9be846334e31c44a2c",
328 | PURL: "pkg:generic/tomcat-logging-support@3.3.0",
329 | CPEs: []string{"cpe:2.3:a:cloudfoundry:tomcat-logging-support:3.3.0:*:*:*:*:*:*:*"},
330 | }
331 |
332 | dc := libpak.DependencyCache{CachePath: "testdata"}
333 |
334 | contributor, entries := tomcat.NewBase(
335 | ctx.Application.Path,
336 | ctx.Buildpack.Path,
337 | libpak.ConfigurationResolver{},
338 | "test-context-path",
339 | accessLoggingDep,
340 | nil,
341 | lifecycleDep,
342 | loggingDep,
343 | dc,
344 | false,
345 | )
346 |
347 | Expect(entries).To(HaveLen(3))
348 | Expect(entries[0].Name).To(Equal("tomcat-access-logging-support"))
349 | Expect(entries[0].Build).To(BeFalse())
350 | Expect(entries[0].Launch).To(BeTrue())
351 | Expect(entries[1].Name).To(Equal("tomcat-lifecycle-support"))
352 | Expect(entries[1].Build).To(BeFalse())
353 | Expect(entries[1].Launch).To(BeTrue())
354 | Expect(entries[2].Name).To(Equal("tomcat-logging-support"))
355 | Expect(entries[2].Build).To(BeFalse())
356 | Expect(entries[2].Launch).To(BeTrue())
357 |
358 | layer, err := ctx.Layers.Layer("test-layer")
359 | Expect(err).NotTo(HaveOccurred())
360 |
361 | layer, err = contributor.Contribute(layer)
362 | Expect(err).NotTo(HaveOccurred())
363 |
364 | Expect(layer.Launch).To(BeTrue())
365 | Expect(filepath.Join(layer.Path, "conf", "context.xml")).To(BeARegularFile())
366 | Expect(filepath.Join(layer.Path, "conf", "logging.properties")).To(BeARegularFile())
367 | Expect(filepath.Join(layer.Path, "conf", "server.xml")).To(BeARegularFile())
368 | Expect(filepath.Join(layer.Path, "conf", "web.xml")).To(BeARegularFile())
369 | Expect(filepath.Join(layer.Path, "conf", "catalina.properties")).To(BeARegularFile())
370 | Expect(os.ReadFile(filepath.Join(layer.Path, "conf", "catalina.properties"))).To(ContainSubstring("common.loader=${BPI_TOMCAT_ADDITIONAL_COMMON_JARS}"))
371 | Expect(filepath.Join(layer.Path, "lib", "stub-tomcat-access-logging-support.jar")).To(BeARegularFile())
372 | Expect(filepath.Join(layer.Path, "lib", "stub-tomcat-lifecycle-support.jar")).To(BeARegularFile())
373 | Expect(filepath.Join(layer.Path, "bin", "stub-tomcat-logging-support.jar")).To(BeARegularFile())
374 | Expect(os.ReadFile(filepath.Join(layer.Path, "bin", "setenv.sh"))).To(Equal(
375 | []byte(fmt.Sprintf(`CLASSPATH="%s"`, filepath.Join(layer.Path, "bin", "stub-tomcat-logging-support.jar")))))
376 | Expect(layer.LaunchEnvironment["CATALINA_BASE.default"]).To(Equal(layer.Path))
377 | Expect(filepath.Join(layer.Path, "temp")).To(BeADirectory())
378 |
379 | file := filepath.Join(layer.Path, "webapps", "test-context-path")
380 | fi, err := os.Lstat(file)
381 | Expect(err).NotTo(HaveOccurred())
382 | Expect(fi.Mode() & os.ModeSymlink).To(Equal(os.ModeSymlink))
383 | Expect(os.Readlink(file)).To(Equal(ctx.Application.Path))
384 |
385 | Expect(layer.LaunchEnvironment["CATALINA_BASE.default"]).To(Equal(layer.Path))
386 | Expect(layer.LaunchEnvironment["CATALINA_OPTS.default"]).To(Equal("-DBPI_TOMCAT_ADDITIONAL_COMMON_JARS=${BPI_TOMCAT_ADDITIONAL_COMMON_JARS}"))
387 | })
388 |
389 | })
390 |
391 | context("$BPI_TOMCAT_ADDITIONAL_JARS is set", func() {
392 | it.Before(func() {
393 | t.Setenv("BPI_TOMCAT_ADDITIONAL_JARS", "/layers/test-buildpack/foo/bar.jar")
394 | })
395 |
396 | it("additional jar is added to classpath", func() {
397 | accessLoggingDep := libpak.BuildpackDependency{
398 | ID: "tomcat-access-logging-support",
399 | URI: "https://localhost/stub-tomcat-access-logging-support.jar",
400 | SHA256: "d723bfe2ba67dfa92b24e3b6c7b2d0e6a963de7313350e306d470e44e330a5d2",
401 | PURL: "pkg:generic/tomcat-access-logging-support@3.3.0",
402 | CPEs: []string{"cpe:2.3:a:cloudfoundry:tomcat-access-logging-support:3.3.0:*:*:*:*:*:*:*"},
403 | }
404 | lifecycleDep := libpak.BuildpackDependency{
405 | ID: "tomcat-lifecycle-support",
406 | URI: "https://localhost/stub-tomcat-lifecycle-support.jar",
407 | SHA256: "723126712c0b22a7fe409664adf1fbb78cf3040e313a82c06696f5058e190534",
408 | PURL: "pkg:generic/tomcat-lifecycle-support@3.3.0",
409 | CPEs: []string{"cpe:2.3:a:cloudfoundry:tomcat-lifecycle-support:3.3.0:*:*:*:*:*:*:*"},
410 | }
411 | loggingDep := libpak.BuildpackDependency{
412 | ID: "tomcat-logging-support",
413 | URI: "https://localhost/stub-tomcat-logging-support.jar",
414 | SHA256: "e0a7e163cc9f1ffd41c8de3942c7c6b505090b7484c2ba9be846334e31c44a2c",
415 | PURL: "pkg:generic/tomcat-logging-support@3.3.0",
416 | CPEs: []string{"cpe:2.3:a:cloudfoundry:tomcat-logging-support:3.3.0:*:*:*:*:*:*:*"},
417 | }
418 |
419 | dc := libpak.DependencyCache{CachePath: "testdata"}
420 |
421 | contributor, entries := tomcat.NewBase(
422 | ctx.Application.Path,
423 | ctx.Buildpack.Path,
424 | libpak.ConfigurationResolver{},
425 | "test-context-path",
426 | accessLoggingDep,
427 | nil,
428 | lifecycleDep,
429 | loggingDep,
430 | dc,
431 | false,
432 | )
433 |
434 | Expect(entries).To(HaveLen(3))
435 | Expect(entries[0].Name).To(Equal("tomcat-access-logging-support"))
436 | Expect(entries[0].Build).To(BeFalse())
437 | Expect(entries[0].Launch).To(BeTrue())
438 | Expect(entries[1].Name).To(Equal("tomcat-lifecycle-support"))
439 | Expect(entries[1].Build).To(BeFalse())
440 | Expect(entries[1].Launch).To(BeTrue())
441 | Expect(entries[2].Name).To(Equal("tomcat-logging-support"))
442 | Expect(entries[2].Build).To(BeFalse())
443 | Expect(entries[2].Launch).To(BeTrue())
444 |
445 | layer, err := ctx.Layers.Layer("test-layer")
446 | Expect(err).NotTo(HaveOccurred())
447 |
448 | layer, err = contributor.Contribute(layer)
449 | Expect(err).NotTo(HaveOccurred())
450 |
451 | Expect(os.ReadFile(filepath.Join(layer.Path, "bin", "setenv.sh"))).To(Equal(
452 | []byte(fmt.Sprintf(`CLASSPATH="%s:%s"`, filepath.Join(layer.Path, "bin", "stub-tomcat-logging-support.jar"), "/layers/test-buildpack/foo/bar.jar"))))
453 | })
454 |
455 | })
456 |
457 | context("Contribute multiple war files", func() {
458 | files := []string{"api.war", "ui.war"}
459 | it.Before(func() {
460 | for _, file := range files {
461 | in, err := os.Open(filepath.Join("testdata", "warfiles", file))
462 | Expect(err).NotTo(HaveOccurred())
463 |
464 | out, err := os.OpenFile(filepath.Join(ctx.Application.Path, file), os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
465 | Expect(err).NotTo(HaveOccurred())
466 |
467 | _, err = io.Copy(out, in)
468 | Expect(err).NotTo(HaveOccurred())
469 | Expect(in.Close()).To(Succeed())
470 | Expect(out.Close()).To(Succeed())
471 | }
472 | })
473 |
474 | it.After(func() {
475 | for _, file := range files {
476 | os.Remove(filepath.Join(ctx.Application.Path, file))
477 | }
478 | })
479 |
480 | it("Multiple war files have been exploded in application path", func() {
481 | accessLoggingDep := libpak.BuildpackDependency{
482 | ID: "tomcat-access-logging-support",
483 | URI: "https://localhost/stub-tomcat-access-logging-support.jar",
484 | SHA256: "d723bfe2ba67dfa92b24e3b6c7b2d0e6a963de7313350e306d470e44e330a5d2",
485 | PURL: "pkg:generic/tomcat-access-logging-support@3.3.0",
486 | CPEs: []string{"cpe:2.3:a:cloudfoundry:tomcat-access-logging-support:3.3.0:*:*:*:*:*:*:*"},
487 | }
488 | lifecycleDep := libpak.BuildpackDependency{
489 | ID: "tomcat-lifecycle-support",
490 | URI: "https://localhost/stub-tomcat-lifecycle-support.jar",
491 | SHA256: "723126712c0b22a7fe409664adf1fbb78cf3040e313a82c06696f5058e190534",
492 | PURL: "pkg:generic/tomcat-lifecycle-support@3.3.0",
493 | CPEs: []string{"cpe:2.3:a:cloudfoundry:tomcat-lifecycle-support:3.3.0:*:*:*:*:*:*:*"},
494 | }
495 | loggingDep := libpak.BuildpackDependency{
496 | ID: "tomcat-logging-support",
497 | URI: "https://localhost/stub-tomcat-logging-support.jar",
498 | SHA256: "e0a7e163cc9f1ffd41c8de3942c7c6b505090b7484c2ba9be846334e31c44a2c",
499 | PURL: "pkg:generic/tomcat-logging-support@3.3.0",
500 | CPEs: []string{"cpe:2.3:a:cloudfoundry:tomcat-logging-support:3.3.0:*:*:*:*:*:*:*"},
501 | }
502 |
503 | dc := libpak.DependencyCache{CachePath: "testdata"}
504 |
505 | contributor, entries := tomcat.NewBase(
506 | ctx.Application.Path,
507 | ctx.Buildpack.Path,
508 | libpak.ConfigurationResolver{},
509 | "test-context-path",
510 | accessLoggingDep,
511 | nil,
512 | lifecycleDep,
513 | loggingDep,
514 | dc,
515 | true,
516 | )
517 |
518 | Expect(entries).To(HaveLen(3))
519 | Expect(entries[0].Name).To(Equal("tomcat-access-logging-support"))
520 | Expect(entries[0].Build).To(BeFalse())
521 | Expect(entries[0].Launch).To(BeTrue())
522 | Expect(entries[1].Name).To(Equal("tomcat-lifecycle-support"))
523 | Expect(entries[1].Build).To(BeFalse())
524 | Expect(entries[1].Launch).To(BeTrue())
525 | Expect(entries[2].Name).To(Equal("tomcat-logging-support"))
526 | Expect(entries[2].Build).To(BeFalse())
527 | Expect(entries[2].Launch).To(BeTrue())
528 |
529 | layer, err := ctx.Layers.Layer("test-layer")
530 | Expect(err).NotTo(HaveOccurred())
531 |
532 | layer, err = contributor.Contribute(layer)
533 | Expect(err).NotTo(HaveOccurred())
534 |
535 | Expect(os.Readlink(filepath.Join(layer.Path, "webapps"))).To(Equal(ctx.Application.Path))
536 | for _, file := range files {
537 | targetDir := strings.TrimSuffix(file, filepath.Ext(file))
538 | Expect(filepath.Join(layer.Path, "webapps", targetDir, "META-INF", "MANIFEST.MF")).To(BeARegularFile())
539 | }
540 | })
541 | })
542 |
543 | }
544 |
--------------------------------------------------------------------------------