Compare commits
251 Commits
Author | SHA1 | Date |
---|---|---|
|
4420c92173 | |
|
da8eac3483 | |
|
cb51a44e2b | |
|
5e04d98260 | |
|
61acc52137 | |
|
f1a3971148 | |
|
dd353034de | |
|
c2e838a603 | |
|
6f1bcc4bfa | |
|
2542430e5b | |
|
43b06ca9b9 | |
|
533544d8fc | |
|
5ec993bb8c | |
|
1ebc557bd5 | |
|
2eaad0585c | |
|
46022d2a4e | |
|
a5dbc5bb22 | |
|
16718f676d | |
|
c1924f2fcd | |
|
473fc1b19e | |
|
a79d0e0d10 | |
|
44099d3d21 | |
|
2440667aab | |
|
7db43ef820 | |
|
d97539bc46 | |
|
8c25063273 | |
|
489336255a | |
|
fde0ae4ac9 | |
|
643f6112c7 | |
|
4bf6fcefab | |
|
9d85c379bd | |
|
9d63a74bbc | |
|
f059a953d3 | |
|
1f293cc653 | |
|
49c7546662 | |
|
c92617a015 | |
|
a9bf744b68 | |
|
b503ad3a9a | |
|
a350ead232 | |
|
55a97aa7ad | |
|
06dd6aec3c | |
|
a1038a5e64 | |
|
99f8c4cd0d | |
|
2edd8f9d62 | |
|
3353090bc6 | |
|
ad22bc033f | |
|
dee42bfe2d | |
|
307dc1c477 | |
|
64aad6d060 | |
|
0f41e906b9 | |
|
c65a259637 | |
|
1b44bafda1 | |
|
e4a58a53d0 | |
|
34a8e1bd82 | |
|
af941e0a9a | |
|
7f225a8df5 | |
|
cae3fb578c | |
|
f175d132b3 | |
|
2ca6703f55 | |
|
f59fc4020a | |
|
80c384e92c | |
|
0634c11eee | |
|
bf075ec89c | |
|
f74ed943e5 | |
|
80436abbb6 | |
|
09311ff44e | |
|
9f1e033caf | |
|
4854fec275 | |
|
e83ad75d3e | |
|
657c537cc2 | |
|
0753e7dbf2 | |
|
1d4b9d372f | |
|
e878db0abf | |
|
58e2f9c872 | |
|
1023ed65a6 | |
|
266f9c44e0 | |
|
3fdc1db7ce | |
|
a111f1f68f | |
|
b10262c9bc | |
|
485180df4f | |
|
94fce6f689 | |
|
761302452b | |
|
3f2e1b2466 | |
|
74daedff76 | |
|
169b1b10b0 | |
|
652414155c | |
|
1e4cc948f2 | |
|
4fd9626854 | |
|
8a9528ebdc | |
|
1ed8eb08e0 | |
|
49ba8aa668 | |
|
53cda6a6a1 | |
|
d5717b71ab | |
|
79ad5e6999 | |
|
871f82d116 | |
|
0187b77761 | |
|
04038101d2 | |
|
45bf83d737 | |
|
f7e7664a5a | |
|
6ee519ec57 | |
|
92fe4c55f7 | |
|
911578e560 | |
|
fbcc1fdec6 | |
|
c280fdefe8 | |
|
319e462cba | |
|
aa06ab1e25 | |
|
09967b1a5d | |
|
5c0d2ee180 | |
|
bb19585601 | |
|
f2c2d19a9c | |
|
3db8b5fb26 | |
|
e313624263 | |
|
af2436de84 | |
|
4a62872d63 | |
|
f5d6bea934 | |
|
419d78b359 | |
|
b6950f8fb0 | |
|
f8ce17c751 | |
|
4a30228978 | |
|
f3df45ace1 | |
|
3aaf4b7b0a | |
|
4af0d6d747 | |
|
3c3726b89e | |
|
92ee73df30 | |
|
418e057afc | |
|
1fda419b17 | |
|
4c8cabcb3a | |
|
dec46b45d7 | |
|
57992512b7 | |
|
b566b73be6 | |
|
532ea9abc1 | |
|
ea05f6df15 | |
|
23c9b6510b | |
|
9bb62a074b | |
|
fe975214cd | |
|
c099462e6f | |
|
ee2730ca8d | |
|
b28d390c69 | |
|
e7ffa33d65 | |
|
dadfd9a35d | |
|
2d312ed5f5 | |
|
ee72caf7dc | |
|
a30e832915 | |
|
0ae23de2b5 | |
|
2a521ce4e9 | |
|
3cced515ce | |
|
279de9e807 | |
|
97c89b480f | |
|
82795f9966 | |
|
7c8a15629b | |
|
ef37765625 | |
|
bba56ffc8b | |
|
6f909114e7 | |
|
79091f926d | |
|
155e3bd5e6 | |
|
8008076e04 | |
|
4b2d358384 | |
|
17e655b7e5 | |
|
2b3a8ff647 | |
|
36732bd74a | |
|
352925bbe8 | |
|
f8f6e59d1a | |
|
08a641c1a2 | |
|
26364c27e2 | |
|
787840735b | |
|
e2ba4dad2a | |
|
7a595f1b83 | |
|
922592938a | |
|
b23d7fe203 | |
|
f9d4589259 | |
|
d4fce9a068 | |
|
cc20b3ec3b | |
|
f28bea16c5 | |
|
4ba14c139e | |
|
ad3a18d086 | |
|
98b4afe527 | |
|
a8caeed578 | |
|
bc968fc671 | |
|
1c1a35f6be | |
|
eb307b91ec | |
|
14791f0593 | |
|
474c9cdeb1 | |
|
cbdd7cf23b | |
|
569fa5fd7d | |
|
5a2f90d1f1 | |
|
5301c8d905 | |
|
4f6bbac13e | |
|
54ffc42309 | |
|
8c5a25ee5c | |
|
30d3889079 | |
|
e8b6ab14bc | |
|
5344fb218a | |
|
bdb05639bc | |
|
db10e3d1ed | |
|
ae86895953 | |
|
93c9413ac0 | |
|
f93c43cb8a | |
|
c3aca4b126 | |
|
b3db525f0f | |
|
52c55788a2 | |
|
990644919a | |
|
2756424035 | |
|
9c727be8f2 | |
|
e4e3988433 | |
|
0959271061 | |
|
8c7e0c675f | |
|
0b601118bd | |
|
5348880b69 | |
|
7979a51f65 | |
|
27715dee20 | |
|
d27aedf92a | |
|
b3c28f1ef3 | |
|
f3b44031a6 | |
|
f6da5e7a5c | |
|
8d63043d9e | |
|
b43d2f2088 | |
|
0b0b742758 | |
|
b61e38bef8 | |
|
6490b749db | |
|
9a1d9f677b | |
|
1cd14c96a5 | |
|
51b606e941 | |
|
c5e28c9133 | |
|
c4cace3f74 | |
|
b7695200a9 | |
|
5cec09dd02 | |
|
cae5a811ad | |
|
0afd40fa40 | |
|
3265a2672d | |
|
b6a1c29763 | |
|
9a3a3b1b5e | |
|
6354d802c0 | |
|
bc46bb2a24 | |
|
3b2c8abf50 | |
|
dc341edfdd | |
|
3a9ab15696 | |
|
8d87586395 | |
|
d3bab5f82d | |
|
862db41134 | |
|
42cc7775f4 | |
|
5c95ee798c | |
|
14703cbafd | |
|
3b98af30f5 | |
|
8d000e008f | |
|
335a0b76b5 | |
|
4a1de8534f | |
|
630bb335ea | |
|
c1d51f8687 | |
|
255ea92a57 | |
|
b01dc92233 | |
|
2cf854c5b6 |
|
@ -23,7 +23,7 @@ inputs:
|
|||
java-version:
|
||||
description: 'Java version to use for the build'
|
||||
required: false
|
||||
default: '24'
|
||||
default: '25'
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
|
@ -42,12 +42,12 @@ runs:
|
|||
${{ inputs.java-toolchain == 'true' && '24' || '' }}
|
||||
- name: Set Up Gradle With Read/Write Cache
|
||||
if: ${{ inputs.cache-read-only == 'false' }}
|
||||
uses: gradle/actions/setup-gradle@ed408507eac070d1f99cc633dbcf757c94c7933a # v4.4.3
|
||||
uses: gradle/actions/setup-gradle@748248ddd2a24f49513d8f472f81c3a07d4d50e1 # v4.4.4
|
||||
with:
|
||||
cache-read-only: false
|
||||
develocity-access-key: ${{ inputs.develocity-access-key }}
|
||||
- name: Set Up Gradle
|
||||
uses: gradle/actions/setup-gradle@ed408507eac070d1f99cc633dbcf757c94c7933a # v4.4.3
|
||||
uses: gradle/actions/setup-gradle@748248ddd2a24f49513d8f472f81c3a07d4d50e1 # v4.4.4
|
||||
with:
|
||||
develocity-access-key: ${{ inputs.develocity-access-key }}
|
||||
develocity-token-expiry: 4
|
||||
|
|
|
@ -21,7 +21,7 @@ runs:
|
|||
using: composite
|
||||
steps:
|
||||
- name: Set Up JFrog CLI
|
||||
uses: jfrog/setup-jfrog-cli@88e9eba31c07e31beefa4cef5c0e93d1af9535d7 # v4.6.1
|
||||
uses: jfrog/setup-jfrog-cli@c32bf10843e4071112c4ea3abf622d3b27cd8c17 # v4.7.0
|
||||
env:
|
||||
JF_ENV_SPRING: ${{ inputs.jfrog-cli-config-token }}
|
||||
- name: Download Artifacts
|
||||
|
|
|
@ -17,7 +17,7 @@ runs:
|
|||
using: composite
|
||||
steps:
|
||||
- name: Set Up JFrog CLI
|
||||
uses: jfrog/setup-jfrog-cli@88e9eba31c07e31beefa4cef5c0e93d1af9535d7 # v4.6.1
|
||||
uses: jfrog/setup-jfrog-cli@c32bf10843e4071112c4ea3abf622d3b27cd8c17 # v4.7.0
|
||||
env:
|
||||
JF_ENV_SPRING: ${{ inputs.jfrog-cli-config-token }}
|
||||
- name: Download Release Artifacts
|
||||
|
|
|
@ -23,16 +23,13 @@ jobs:
|
|||
toolchain: true
|
||||
- version: 21
|
||||
toolchain: true
|
||||
- version: 24
|
||||
toolchain: false
|
||||
- version: 25
|
||||
early-access: true
|
||||
toolchain: true
|
||||
toolchain: false
|
||||
exclude:
|
||||
- os:
|
||||
name: Linux
|
||||
java:
|
||||
version: 24
|
||||
version: 25
|
||||
- os:
|
||||
name: ${{ github.repository == 'spring-projects/spring-boot-commercial' && 'Windows' }}
|
||||
steps:
|
||||
|
|
|
@ -75,7 +75,7 @@ jobs:
|
|||
runs-on: ${{ vars.UBUNTU_SMALL || 'ubuntu-latest' }}
|
||||
steps:
|
||||
- name: Set up JFrog CLI
|
||||
uses: jfrog/setup-jfrog-cli@88e9eba31c07e31beefa4cef5c0e93d1af9535d7 # v4.6.1
|
||||
uses: jfrog/setup-jfrog-cli@c32bf10843e4071112c4ea3abf622d3b27cd8c17 # v4.7.0
|
||||
env:
|
||||
JF_ENV_SPRING: ${{ secrets.JF_ARTIFACTORY_SPRING }}
|
||||
- name: Promote build
|
||||
|
|
|
@ -86,7 +86,7 @@ jobs:
|
|||
runs-on: ${{ vars.UBUNTU_SMALL || 'ubuntu-latest' }}
|
||||
steps:
|
||||
- name: Set up JFrog CLI
|
||||
uses: jfrog/setup-jfrog-cli@88e9eba31c07e31beefa4cef5c0e93d1af9535d7 # v4.6.1
|
||||
uses: jfrog/setup-jfrog-cli@c32bf10843e4071112c4ea3abf622d3b27cd8c17 # v4.7.0
|
||||
env:
|
||||
JF_ENV_SPRING: ${{ vars.COMMERCIAL && secrets.COMMERCIAL_JF_ARTIFACTORY_SPRING || secrets.JF_ARTIFACTORY_SPRING }}
|
||||
- name: Promote open source build
|
||||
|
|
|
@ -59,7 +59,7 @@ jobs:
|
|||
with:
|
||||
stable: true
|
||||
- name: Set Up Gradle
|
||||
uses: gradle/actions/setup-gradle@ed408507eac070d1f99cc633dbcf757c94c7933a # v4.4.3
|
||||
uses: gradle/actions/setup-gradle@748248ddd2a24f49513d8f472f81c3a07d4d50e1 # v4.4.4
|
||||
with:
|
||||
cache-read-only: false
|
||||
- name: Configure Gradle Properties
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
# Enable auto-env through the sdkman_auto_env config
|
||||
# Add key=value pairs of SDKs to use below
|
||||
java=24.0.2-librca
|
||||
java=25-librca
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
"@springio/antora-xref-extension": "1.0.0-alpha.4",
|
||||
"@springio/antora-zip-contents-collector-extension": "1.0.0-alpha.8",
|
||||
"@springio/asciidoctor-extensions": "1.0.0-alpha.17",
|
||||
"patch-package": "^8.0.0"
|
||||
"patch-package": "^8.0.1"
|
||||
}
|
||||
},
|
||||
"node_modules/@antora/asciidoc-loader": {
|
||||
|
@ -575,14 +575,6 @@
|
|||
"resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz",
|
||||
"integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ=="
|
||||
},
|
||||
"node_modules/at-least-node": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
|
||||
"integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
|
||||
"engines": {
|
||||
"node": ">= 4.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/atomic-sleep": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/atomic-sleep/-/atomic-sleep-1.0.0.tgz",
|
||||
|
@ -1406,17 +1398,17 @@
|
|||
"integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow=="
|
||||
},
|
||||
"node_modules/fs-extra": {
|
||||
"version": "9.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz",
|
||||
"integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==",
|
||||
"version": "10.1.0",
|
||||
"resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz",
|
||||
"integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"at-least-node": "^1.0.0",
|
||||
"graceful-fs": "^4.2.0",
|
||||
"jsonfile": "^6.0.1",
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10"
|
||||
"node": ">=12"
|
||||
}
|
||||
},
|
||||
"node_modules/fs-mkdirp-stream": {
|
||||
|
@ -1993,9 +1985,10 @@
|
|||
}
|
||||
},
|
||||
"node_modules/jsonfile": {
|
||||
"version": "6.1.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
|
||||
"integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==",
|
||||
"version": "6.2.0",
|
||||
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz",
|
||||
"integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"universalify": "^2.0.0"
|
||||
},
|
||||
|
@ -2331,38 +2324,30 @@
|
|||
"safe-buffer": "~5.1.0"
|
||||
}
|
||||
},
|
||||
"node_modules/os-tmpdir": {
|
||||
"version": "1.0.2",
|
||||
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
|
||||
"integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==",
|
||||
"engines": {
|
||||
"node": ">=0.10.0"
|
||||
}
|
||||
},
|
||||
"node_modules/pako": {
|
||||
"version": "1.0.11",
|
||||
"resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
|
||||
"integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
|
||||
},
|
||||
"node_modules/patch-package": {
|
||||
"version": "8.0.0",
|
||||
"resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.0.tgz",
|
||||
"integrity": "sha512-da8BVIhzjtgScwDJ2TtKsfT5JFWz1hYoBl9rUQ1f38MC2HwnEIkK8VN3dKMKcP7P7bvvgzNDbfNHtx3MsQb5vA==",
|
||||
"version": "8.0.1",
|
||||
"resolved": "https://registry.npmjs.org/patch-package/-/patch-package-8.0.1.tgz",
|
||||
"integrity": "sha512-VsKRIA8f5uqHQ7NGhwIna6Bx6D9s/1iXlA1hthBVBEbkq+t4kXD0HHt+rJhf/Z+Ci0F/HCB2hvn0qLdLG+Qxlw==",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@yarnpkg/lockfile": "^1.1.0",
|
||||
"chalk": "^4.1.2",
|
||||
"ci-info": "^3.7.0",
|
||||
"cross-spawn": "^7.0.3",
|
||||
"find-yarn-workspace-root": "^2.0.0",
|
||||
"fs-extra": "^9.0.0",
|
||||
"fs-extra": "^10.0.0",
|
||||
"json-stable-stringify": "^1.0.2",
|
||||
"klaw-sync": "^6.0.0",
|
||||
"minimist": "^1.2.6",
|
||||
"open": "^7.4.2",
|
||||
"rimraf": "^2.6.3",
|
||||
"semver": "^7.5.3",
|
||||
"slash": "^2.0.0",
|
||||
"tmp": "^0.0.33",
|
||||
"tmp": "^0.2.4",
|
||||
"yaml": "^2.2.2"
|
||||
},
|
||||
"bin": {
|
||||
|
@ -2750,18 +2735,6 @@
|
|||
"node": ">= 0.10"
|
||||
}
|
||||
},
|
||||
"node_modules/rimraf": {
|
||||
"version": "2.7.1",
|
||||
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
|
||||
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
|
||||
"deprecated": "Rimraf versions prior to v4 are no longer supported",
|
||||
"dependencies": {
|
||||
"glob": "^7.1.3"
|
||||
},
|
||||
"bin": {
|
||||
"rimraf": "bin.js"
|
||||
}
|
||||
},
|
||||
"node_modules/safe-buffer": {
|
||||
"version": "5.2.1",
|
||||
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||
|
@ -3118,14 +3091,12 @@
|
|||
}
|
||||
},
|
||||
"node_modules/tmp": {
|
||||
"version": "0.0.33",
|
||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz",
|
||||
"integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==",
|
||||
"dependencies": {
|
||||
"os-tmpdir": "~1.0.2"
|
||||
},
|
||||
"version": "0.2.5",
|
||||
"resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz",
|
||||
"integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.6.0"
|
||||
"node": ">=14.14"
|
||||
}
|
||||
},
|
||||
"node_modules/to-absolute-glob": {
|
||||
|
@ -3238,6 +3209,7 @@
|
|||
"version": "2.0.1",
|
||||
"resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz",
|
||||
"integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">= 10.0.0"
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
"@springio/antora-zip-contents-collector-extension": "1.0.0-alpha.8",
|
||||
"@asciidoctor/tabs": "1.0.0-beta.6",
|
||||
"@springio/asciidoctor-extensions": "1.0.0-alpha.17",
|
||||
"patch-package": "^8.0.0"
|
||||
"patch-package": "^8.0.1"
|
||||
},
|
||||
"config": {
|
||||
"ui-bundle-url": "https://github.com/spring-io/antora-ui-spring/releases/download/v0.4.18/ui-bundle.zip"
|
||||
|
|
|
@ -80,7 +80,6 @@ tasks.register("integrationTest") {
|
|||
ant.propertyref(name: "ivy.class.path")
|
||||
}
|
||||
plainlistener()
|
||||
file(layout.buildDirectory.dir("test-results/integrationTest")).mkdirs()
|
||||
xmllistener(toDir: resultsDir)
|
||||
fileset(dir: layout.buildDirectory.dir("it").get().asFile.toString(), includes: "**/build.xml")
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ tasks.named("bootRun") {
|
|||
|
||||
tasks.register("configuredSystemProperties") {
|
||||
doLast {
|
||||
bootRun.systemProperties.each { k, v ->
|
||||
bootRun.systemProperties.each { k, v ->
|
||||
println "$k = $v"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -141,9 +141,13 @@ final class ApplicationPluginAction implements PluginApplicationAction {
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private void configureFileMode(CopySpec copySpec, int mode) {
|
||||
copySpec.setFileMode(mode);
|
||||
try {
|
||||
copySpec.getClass().getMethod("setFileMode", Integer.class).invoke(copySpec, Integer.valueOf(mode));
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new RuntimeException("Failed to set file mode on CopySpec", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
package org.springframework.boot.gradle.plugin;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collections;
|
||||
import java.util.Set;
|
||||
import java.util.jar.JarFile;
|
||||
|
||||
|
@ -33,7 +32,7 @@ import org.gradle.api.specs.Spec;
|
|||
*/
|
||||
class JarTypeFileSpec implements Spec<File> {
|
||||
|
||||
private static final Set<String> EXCLUDED_JAR_TYPES = Collections.singleton("dependencies-starter");
|
||||
private static final Set<String> EXCLUDED_JAR_TYPES = Set.of("dependencies-starter", "development-tool");
|
||||
|
||||
@Override
|
||||
public boolean isSatisfiedBy(File file) {
|
||||
|
|
|
@ -285,7 +285,6 @@ final class JavaPluginAction implements PluginApplicationAction {
|
|||
private void configureProductionRuntimeClasspathConfiguration(Project project) {
|
||||
Configuration productionRuntimeClasspath = project.getConfigurations()
|
||||
.create(SpringBootPlugin.PRODUCTION_RUNTIME_CLASSPATH_CONFIGURATION_NAME);
|
||||
productionRuntimeClasspath.setVisible(false);
|
||||
Configuration runtimeClasspath = project.getConfigurations()
|
||||
.getByName(JavaPlugin.RUNTIME_CLASSPATH_CONFIGURATION_NAME);
|
||||
productionRuntimeClasspath.attributes((attributes) -> {
|
||||
|
|
|
@ -104,7 +104,6 @@ class WarPluginAction implements PluginApplicationAction {
|
|||
.set(project.provider(() -> javaPluginExtension(project).getTargetCompatibility()));
|
||||
bootWar.resolvedArtifacts(runtimeClasspath.getIncoming().getArtifacts().getResolvedArtifacts());
|
||||
});
|
||||
bootWarProvider.map(War::getClasspath);
|
||||
return bootWarProvider;
|
||||
}
|
||||
|
||||
|
|
|
@ -43,7 +43,7 @@ import org.springframework.util.Assert;
|
|||
* @since 3.0.0
|
||||
*/
|
||||
@CacheableTask
|
||||
public class ProcessTestAot extends AbstractAot {
|
||||
public abstract class ProcessTestAot extends AbstractAot {
|
||||
|
||||
private @Nullable FileCollection classpathRoots;
|
||||
|
||||
|
|
|
@ -34,8 +34,6 @@ import org.gradle.api.tasks.Nested;
|
|||
import org.gradle.api.tasks.Optional;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
||||
|
||||
/**
|
||||
* A Spring Boot "fat" archive task.
|
||||
*
|
||||
|
@ -137,15 +135,6 @@ public interface BootArchive extends Task {
|
|||
*/
|
||||
void resolvedArtifacts(Provider<Set<ResolvedArtifactResult>> resolvedArtifacts);
|
||||
|
||||
/**
|
||||
* The loader implementation that should be used with the archive.
|
||||
* @return the loader implementation
|
||||
* @since 3.2.0
|
||||
*/
|
||||
@Input
|
||||
@Optional
|
||||
Property<LoaderImplementation> getLoaderImplementation();
|
||||
|
||||
/**
|
||||
* Returns whether the JAR tools should be included as a dependency in the layered
|
||||
* archive.
|
||||
|
|
|
@ -22,32 +22,24 @@ import java.io.IOException;
|
|||
import java.io.InputStream;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeMap;
|
||||
import java.util.function.Function;
|
||||
|
||||
import org.gradle.api.file.ConfigurableFilePermissions;
|
||||
import org.gradle.api.file.CopySpec;
|
||||
import org.gradle.api.file.FileCopyDetails;
|
||||
import org.gradle.api.file.FileTreeElement;
|
||||
import org.gradle.api.file.RelativePath;
|
||||
import org.gradle.api.internal.file.copy.CopyAction;
|
||||
import org.gradle.api.internal.file.copy.CopyActionProcessingStream;
|
||||
import org.gradle.api.internal.file.copy.FileCopyDetailsInternal;
|
||||
import org.gradle.api.java.archives.Attributes;
|
||||
import org.gradle.api.java.archives.Manifest;
|
||||
import org.gradle.api.provider.Property;
|
||||
import org.gradle.api.specs.Spec;
|
||||
import org.gradle.api.specs.Specs;
|
||||
import org.gradle.api.tasks.WorkResult;
|
||||
import org.gradle.api.tasks.bundling.Jar;
|
||||
import org.gradle.api.tasks.util.PatternSet;
|
||||
import org.gradle.util.GradleVersion;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
||||
|
||||
/**
|
||||
* Support class for implementations of {@link BootArchive}.
|
||||
*
|
||||
|
@ -123,13 +115,11 @@ class BootArchiveSupport {
|
|||
return (version != null) ? version : "unknown";
|
||||
}
|
||||
|
||||
CopyAction createCopyAction(Jar jar, ResolvedDependencies resolvedDependencies,
|
||||
LoaderImplementation loaderImplementation, boolean supportsSignatureFile) {
|
||||
return createCopyAction(jar, resolvedDependencies, loaderImplementation, supportsSignatureFile, null, null);
|
||||
CopyAction createCopyAction(Jar jar, ResolvedDependencies resolvedDependencies, boolean supportsSignatureFile) {
|
||||
return createCopyAction(jar, resolvedDependencies, supportsSignatureFile, null, null);
|
||||
}
|
||||
|
||||
CopyAction createCopyAction(Jar jar, ResolvedDependencies resolvedDependencies,
|
||||
LoaderImplementation loaderImplementation, boolean supportsSignatureFile,
|
||||
CopyAction createCopyAction(Jar jar, ResolvedDependencies resolvedDependencies, boolean supportsSignatureFile,
|
||||
@Nullable LayerResolver layerResolver, @Nullable String jarmodeToolsLocation) {
|
||||
File output = jar.getArchiveFile().get().getAsFile();
|
||||
Manifest manifest = jar.getManifest();
|
||||
|
@ -145,9 +135,8 @@ class BootArchiveSupport {
|
|||
String encoding = jar.getMetadataCharset();
|
||||
CopyAction action = new BootZipCopyAction(output, manifest, preserveFileTimestamps, dirPermissions,
|
||||
filePermissions, includeDefaultLoader, jarmodeToolsLocation, requiresUnpack, exclusions, launchScript,
|
||||
librarySpec, compressionResolver, encoding, resolvedDependencies, supportsSignatureFile, layerResolver,
|
||||
loaderImplementation);
|
||||
return jar.isReproducibleFileOrder() ? new ReproducibleOrderingCopyAction(action) : action;
|
||||
librarySpec, compressionResolver, encoding, resolvedDependencies, supportsSignatureFile, layerResolver);
|
||||
return action;
|
||||
}
|
||||
|
||||
private @Nullable Integer getUnixNumericDirPermissions(CopySpec copySpec) {
|
||||
|
@ -164,14 +153,22 @@ class BootArchiveSupport {
|
|||
return permissions.isPresent() ? permissions.get().toUnixNumeric() : null;
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private @Nullable Integer getDirMode(CopySpec copySpec) {
|
||||
return copySpec.getDirMode();
|
||||
try {
|
||||
return (Integer) copySpec.getClass().getMethod("getDirMode").invoke(copySpec);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new RuntimeException("Failed to get dir mode from CopySpec", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private @Nullable Integer getFileMode(CopySpec copySpec) {
|
||||
return copySpec.getFileMode();
|
||||
try {
|
||||
return (Integer) copySpec.getClass().getMethod("getFileMode").invoke(copySpec);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new RuntimeException("Failed to get file mode from CopySpec", ex);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isUsingDefaultLoader(Jar jar) {
|
||||
|
@ -234,26 +231,4 @@ class BootArchiveSupport {
|
|||
details.setRelativePath(details.getRelativeSourcePath());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link CopyAction} variant that sorts entries to ensure reproducible ordering.
|
||||
*/
|
||||
private static final class ReproducibleOrderingCopyAction implements CopyAction {
|
||||
|
||||
private final CopyAction delegate;
|
||||
|
||||
private ReproducibleOrderingCopyAction(CopyAction delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
@Override
|
||||
public WorkResult execute(CopyActionProcessingStream stream) {
|
||||
return this.delegate.execute((action) -> {
|
||||
Map<RelativePath, FileCopyDetailsInternal> detailsByPath = new TreeMap<>();
|
||||
stream.process((details) -> detailsByPath.put(details.getRelativePath(), details));
|
||||
detailsByPath.values().forEach(action::processFile);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -38,8 +38,6 @@ import org.gradle.api.tasks.bundling.Jar;
|
|||
import org.gradle.work.DisableCachingByDefault;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
||||
|
||||
/**
|
||||
* A custom {@link Jar} task that produces a Spring Boot executable jar.
|
||||
*
|
||||
|
@ -145,13 +143,12 @@ public abstract class BootJar extends Jar implements BootArchive {
|
|||
|
||||
@Override
|
||||
protected CopyAction createCopyAction() {
|
||||
LoaderImplementation loaderImplementation = getLoaderImplementation().getOrElse(LoaderImplementation.DEFAULT);
|
||||
LayerResolver layerResolver = null;
|
||||
if (!isLayeredDisabled()) {
|
||||
layerResolver = new LayerResolver(this.resolvedDependencies, this.layered, this::isLibrary);
|
||||
}
|
||||
String jarmodeToolsLocation = isIncludeJarmodeTools() ? LIB_DIRECTORY : null;
|
||||
return this.support.createCopyAction(this, this.resolvedDependencies, loaderImplementation, true, layerResolver,
|
||||
return this.support.createCopyAction(this, this.resolvedDependencies, true, layerResolver,
|
||||
jarmodeToolsLocation);
|
||||
}
|
||||
|
||||
|
|
|
@ -38,8 +38,6 @@ import org.gradle.api.tasks.bundling.War;
|
|||
import org.gradle.work.DisableCachingByDefault;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
||||
|
||||
/**
|
||||
* A custom {@link War} task that produces a Spring Boot executable war.
|
||||
*
|
||||
|
@ -119,14 +117,13 @@ public abstract class BootWar extends War implements BootArchive {
|
|||
|
||||
@Override
|
||||
protected CopyAction createCopyAction() {
|
||||
LoaderImplementation loaderImplementation = getLoaderImplementation().getOrElse(LoaderImplementation.DEFAULT);
|
||||
LayerResolver layerResolver = null;
|
||||
if (!isLayeredDisabled()) {
|
||||
layerResolver = new LayerResolver(this.resolvedDependencies, this.layered, this::isLibrary);
|
||||
}
|
||||
String jarmodeToolsLocation = isIncludeJarmodeTools() ? LIB_DIRECTORY : null;
|
||||
return this.support.createCopyAction(this, this.resolvedDependencies, loaderImplementation, false,
|
||||
layerResolver, jarmodeToolsLocation);
|
||||
return this.support.createCopyAction(this, this.resolvedDependencies, false, layerResolver,
|
||||
jarmodeToolsLocation);
|
||||
}
|
||||
|
||||
private boolean isIncludeJarmodeTools() {
|
||||
|
|
|
@ -23,13 +23,10 @@ import java.io.InputStream;
|
|||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.Collection;
|
||||
import java.util.HashMap;
|
||||
import java.util.HexFormat;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
|
@ -63,7 +60,6 @@ import org.springframework.boot.loader.tools.JarModeLibrary;
|
|||
import org.springframework.boot.loader.tools.Layer;
|
||||
import org.springframework.boot.loader.tools.LayersIndex;
|
||||
import org.springframework.boot.loader.tools.LibraryCoordinates;
|
||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
||||
import org.springframework.boot.loader.tools.NativeImageArgFile;
|
||||
import org.springframework.boot.loader.tools.ReachabilityMetadataProperties;
|
||||
import org.springframework.util.Assert;
|
||||
|
@ -120,15 +116,13 @@ class BootZipCopyAction implements CopyAction {
|
|||
|
||||
private final @Nullable LayerResolver layerResolver;
|
||||
|
||||
private final LoaderImplementation loaderImplementation;
|
||||
|
||||
BootZipCopyAction(File output, Manifest manifest, boolean preserveFileTimestamps, @Nullable Integer dirMode,
|
||||
@Nullable Integer fileMode, boolean includeDefaultLoader, @Nullable String jarmodeToolsLocation,
|
||||
Spec<FileTreeElement> requiresUnpack, Spec<FileTreeElement> exclusions,
|
||||
@Nullable LaunchScriptConfiguration launchScript, Spec<FileCopyDetails> librarySpec,
|
||||
Function<FileCopyDetails, ZipCompression> compressionResolver, @Nullable String encoding,
|
||||
ResolvedDependencies resolvedDependencies, boolean supportsSignatureFile,
|
||||
@Nullable LayerResolver layerResolver, LoaderImplementation loaderImplementation) {
|
||||
@Nullable LayerResolver layerResolver) {
|
||||
this.output = output;
|
||||
this.manifest = manifest;
|
||||
this.preserveFileTimestamps = preserveFileTimestamps;
|
||||
|
@ -145,7 +139,6 @@ class BootZipCopyAction implements CopyAction {
|
|||
this.resolvedDependencies = resolvedDependencies;
|
||||
this.supportsSignatureFile = supportsSignatureFile;
|
||||
this.layerResolver = layerResolver;
|
||||
this.loaderImplementation = loaderImplementation;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -329,8 +322,7 @@ class BootZipCopyAction implements CopyAction {
|
|||
// Always write loader entries after META-INF directory (see gh-16698)
|
||||
return;
|
||||
}
|
||||
LoaderZipEntries loaderEntries = new LoaderZipEntries(getTime(), getDirMode(), getFileMode(),
|
||||
BootZipCopyAction.this.loaderImplementation);
|
||||
LoaderZipEntries loaderEntries = new LoaderZipEntries(getTime(), getDirMode(), getFileMode());
|
||||
this.writtenLoaderEntries = loaderEntries.writeTo(this.out);
|
||||
if (BootZipCopyAction.this.layerResolver != null) {
|
||||
for (String name : this.writtenLoaderEntries.getFiles()) {
|
||||
|
@ -512,9 +504,13 @@ class BootZipCopyAction implements CopyAction {
|
|||
? details.getPermissions().toUnixNumeric() : getMode(details);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private int getMode(FileCopyDetails details) {
|
||||
return details.getMode();
|
||||
try {
|
||||
return (int) details.getClass().getMethod("getMode").invoke(details);
|
||||
}
|
||||
catch (Exception ex) {
|
||||
throw new RuntimeException("Failed to get mode from FileCopyDetails", ex);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -590,36 +586,24 @@ class BootZipCopyAction implements CopyAction {
|
|||
|
||||
private static final int BUFFER_SIZE = 32 * 1024;
|
||||
|
||||
private final @Nullable MessageDigest messageDigest;
|
||||
private final boolean unpack;
|
||||
|
||||
private final CRC32 crc = new CRC32();
|
||||
|
||||
private long size;
|
||||
|
||||
StoredEntryPreparator(InputStream inputStream, boolean unpack) throws IOException {
|
||||
this.messageDigest = (unpack) ? sha1Digest() : null;
|
||||
this.unpack = unpack;
|
||||
try (inputStream) {
|
||||
load(inputStream);
|
||||
}
|
||||
}
|
||||
|
||||
private static MessageDigest sha1Digest() {
|
||||
try {
|
||||
return MessageDigest.getInstance("SHA-1");
|
||||
}
|
||||
catch (NoSuchAlgorithmException ex) {
|
||||
throw new IllegalStateException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void load(InputStream inputStream) throws IOException {
|
||||
byte[] buffer = new byte[BUFFER_SIZE];
|
||||
int bytesRead;
|
||||
while ((bytesRead = inputStream.read(buffer)) != -1) {
|
||||
this.crc.update(buffer, 0, bytesRead);
|
||||
if (this.messageDigest != null) {
|
||||
this.messageDigest.update(buffer, 0, bytesRead);
|
||||
}
|
||||
this.size += bytesRead;
|
||||
}
|
||||
}
|
||||
|
@ -629,8 +613,8 @@ class BootZipCopyAction implements CopyAction {
|
|||
entry.setCompressedSize(this.size);
|
||||
entry.setCrc(this.crc.getValue());
|
||||
entry.setMethod(ZipEntry.STORED);
|
||||
if (this.messageDigest != null) {
|
||||
entry.setComment("UNPACK:" + HexFormat.of().formatHex(this.messageDigest.digest()));
|
||||
if (this.unpack) {
|
||||
entry.setComment("UNPACK");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,6 @@ import org.apache.commons.compress.archivers.zip.ZipArchiveOutputStream;
|
|||
import org.gradle.api.file.FileTreeElement;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StreamUtils;
|
||||
|
||||
|
@ -42,27 +41,22 @@ import org.springframework.util.StreamUtils;
|
|||
*/
|
||||
class LoaderZipEntries {
|
||||
|
||||
private final LoaderImplementation loaderImplementation;
|
||||
|
||||
private final @Nullable Long entryTime;
|
||||
|
||||
private final int dirMode;
|
||||
|
||||
private final int fileMode;
|
||||
|
||||
LoaderZipEntries(@Nullable Long entryTime, int dirMode, int fileMode,
|
||||
@Nullable LoaderImplementation loaderImplementation) {
|
||||
LoaderZipEntries(@Nullable Long entryTime, int dirMode, int fileMode) {
|
||||
this.entryTime = entryTime;
|
||||
this.dirMode = dirMode;
|
||||
this.fileMode = fileMode;
|
||||
this.loaderImplementation = (loaderImplementation != null) ? loaderImplementation
|
||||
: LoaderImplementation.DEFAULT;
|
||||
}
|
||||
|
||||
WrittenEntries writeTo(ZipArchiveOutputStream out) throws IOException {
|
||||
WrittenEntries written = new WrittenEntries();
|
||||
try (ZipInputStream loaderJar = new ZipInputStream(
|
||||
getResourceAsStream("/" + this.loaderImplementation.getJarResourceName()))) {
|
||||
getResourceAsStream("/META-INF/loader/spring-boot-loader.jar"))) {
|
||||
java.util.zip.ZipEntry entry = loaderJar.getNextEntry();
|
||||
while (entry != null) {
|
||||
if (entry.isDirectory() && !entry.getName().equals("META-INF/")) {
|
||||
|
|
|
@ -124,7 +124,7 @@ class PackagingDocumentationTests {
|
|||
try (JarFile jar = new JarFile(file)) {
|
||||
JarEntry entry = jar.getJarEntry("BOOT-INF/lib/jruby-complete-1.7.25.jar");
|
||||
assertThat(entry).isNotNull();
|
||||
assertThat(entry.getComment()).startsWith("UNPACK:");
|
||||
assertThat(entry.getComment()).isEqualTo("UNPACK");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -24,8 +24,6 @@ import java.time.format.DateTimeFormatter;
|
|||
import java.util.Properties;
|
||||
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.internal.project.ProjectInternal;
|
||||
import org.gradle.initialization.GradlePropertiesController;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
|
@ -173,11 +171,7 @@ class BuildInfoTests {
|
|||
|
||||
private Project createProject(String projectName) {
|
||||
File projectDir = new File(this.temp, projectName);
|
||||
Project project = GradleProjectBuilder.builder().withProjectDir(projectDir).withName(projectName).build();
|
||||
((ProjectInternal) project).getServices()
|
||||
.get(GradlePropertiesController.class)
|
||||
.loadGradlePropertiesFrom(projectDir, false);
|
||||
return project;
|
||||
return GradleProjectBuilder.builder().withProjectDir(projectDir).withName(projectName).build();
|
||||
}
|
||||
|
||||
private BuildInfo createTask(Project project) {
|
||||
|
|
|
@ -108,16 +108,6 @@ abstract class AbstractBootArchiveIntegrationTests {
|
|||
assertThat(firstHash).isEqualTo(secondHash);
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void classicLoader() throws IOException {
|
||||
assertThat(this.gradleBuild.build(this.taskName).task(":" + this.taskName).getOutcome())
|
||||
.isEqualTo(TaskOutcome.SUCCESS);
|
||||
File jar = new File(this.gradleBuild.getProjectDir(), "build/libs").listFiles()[0];
|
||||
try (JarFile jarFile = new JarFile(jar)) {
|
||||
assertThat(jarFile.getEntry("org/springframework/boot/loader/LaunchedURLClassLoader.class")).isNotNull();
|
||||
}
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void upToDateWhenBuiltTwice() {
|
||||
assertThat(this.gradleBuild.build(this.taskName).task(":" + this.taskName).getOutcome())
|
||||
|
@ -243,14 +233,8 @@ abstract class AbstractBootArchiveIntegrationTests {
|
|||
.filter((entry) -> !entry.isDirectory())
|
||||
.map(JarEntry::getName)
|
||||
.filter((name) -> name.startsWith(this.libPath));
|
||||
if (this.gradleBuild.gradleVersionIsLessThan("9.0.0-rc-1")) {
|
||||
assertThat(libEntryNames).containsExactly(this.libPath + "two-1.0.jar",
|
||||
this.libPath + "commons-io-2.19.0.jar");
|
||||
}
|
||||
else {
|
||||
assertThat(libEntryNames).containsExactly(this.libPath + "commons-io-2.19.0.jar",
|
||||
this.libPath + "two-1.0.jar");
|
||||
}
|
||||
assertThat(libEntryNames).containsExactly(this.libPath + "two-1.0.jar",
|
||||
this.libPath + "commons-io-2.19.0.jar");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -293,6 +277,7 @@ abstract class AbstractBootArchiveIntegrationTests {
|
|||
void jarTypeFilteringIsApplied() throws IOException {
|
||||
File flatDirRepository = new File(this.gradleBuild.getProjectDir(), "repository");
|
||||
createDependenciesStarterJar(new File(flatDirRepository, "starter.jar"));
|
||||
createDependenciesDeveloperToolsJar(new File(flatDirRepository, "devonly.jar"));
|
||||
createStandardJar(new File(flatDirRepository, "standard.jar"));
|
||||
assertThat(this.gradleBuild.build(this.taskName).task(":" + this.taskName).getOutcome())
|
||||
.isEqualTo(TaskOutcome.SUCCESS);
|
||||
|
@ -669,6 +654,10 @@ abstract class AbstractBootArchiveIntegrationTests {
|
|||
createJar(location, (attributes) -> attributes.putValue("Spring-Boot-Jar-Type", "dependencies-starter"));
|
||||
}
|
||||
|
||||
private void createDependenciesDeveloperToolsJar(File location) throws IOException {
|
||||
createJar(location, (attributes) -> attributes.putValue("Spring-Boot-Jar-Type", "development-tool"));
|
||||
}
|
||||
|
||||
private void createJar(File location, Consumer<Attributes> attributesConfigurer) throws IOException {
|
||||
location.getParentFile().mkdirs();
|
||||
Manifest manifest = new Manifest();
|
||||
|
|
|
@ -27,6 +27,8 @@ import java.nio.file.Files;
|
|||
import java.nio.file.Path;
|
||||
import java.nio.file.StandardOpenOption;
|
||||
import java.nio.file.attribute.PosixFilePermission;
|
||||
import java.time.OffsetDateTime;
|
||||
import java.time.ZoneOffset;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Enumeration;
|
||||
import java.util.HashMap;
|
||||
|
@ -35,6 +37,7 @@ import java.util.List;
|
|||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
import java.util.UUID;
|
||||
import java.util.jar.JarEntry;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.jar.JarOutputStream;
|
||||
|
@ -65,7 +68,6 @@ import org.junit.jupiter.api.io.TempDir;
|
|||
import org.springframework.boot.gradle.junit.GradleProjectBuilder;
|
||||
import org.springframework.boot.loader.tools.DefaultLaunchScript;
|
||||
import org.springframework.boot.loader.tools.JarModeLibrary;
|
||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
@ -283,17 +285,6 @@ abstract class AbstractBootArchiveTests<T extends Jar & BootArchive> {
|
|||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void loaderIsWrittenToTheRootOfTheJarWhenUsingClassicLoader() throws IOException {
|
||||
this.task.getMainClass().set("com.example.Main");
|
||||
this.task.getLoaderImplementation().set(LoaderImplementation.CLASSIC);
|
||||
executeTask();
|
||||
try (JarFile jarFile = new JarFile(this.task.getArchiveFile().get().getAsFile())) {
|
||||
assertThat(jarFile.getEntry("org/springframework/boot/loader/LaunchedURLClassLoader.class")).isNotNull();
|
||||
assertThat(jarFile.getEntry("org/springframework/boot/loader/")).isNotNull();
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
void unpackCommentIsAddedToEntryIdentifiedByAPattern() throws IOException {
|
||||
this.task.getMainClass().set("com.example.Main");
|
||||
|
@ -301,7 +292,7 @@ abstract class AbstractBootArchiveTests<T extends Jar & BootArchive> {
|
|||
this.task.requiresUnpack("**/one.jar");
|
||||
executeTask();
|
||||
try (JarFile jarFile = new JarFile(this.task.getArchiveFile().get().getAsFile())) {
|
||||
assertThat(jarFile.getEntry(this.libPath + "one.jar").getComment()).startsWith("UNPACK:");
|
||||
assertThat(jarFile.getEntry(this.libPath + "one.jar").getComment()).isEqualTo("UNPACK");
|
||||
assertThat(jarFile.getEntry(this.libPath + "two.jar").getComment()).isNull();
|
||||
}
|
||||
}
|
||||
|
@ -313,7 +304,7 @@ abstract class AbstractBootArchiveTests<T extends Jar & BootArchive> {
|
|||
this.task.requiresUnpack((element) -> element.getName().endsWith("two.jar"));
|
||||
executeTask();
|
||||
try (JarFile jarFile = new JarFile(this.task.getArchiveFile().get().getAsFile())) {
|
||||
assertThat(jarFile.getEntry(this.libPath + "two.jar").getComment()).startsWith("UNPACK:");
|
||||
assertThat(jarFile.getEntry(this.libPath + "two.jar").getComment()).isEqualTo("UNPACK");
|
||||
assertThat(jarFile.getEntry(this.libPath + "one.jar").getComment()).isNull();
|
||||
}
|
||||
}
|
||||
|
@ -419,23 +410,46 @@ abstract class AbstractBootArchiveTests<T extends Jar & BootArchive> {
|
|||
}
|
||||
|
||||
@Test
|
||||
void reproducibleOrderingCanBeEnabled() throws IOException {
|
||||
void archiveIsReproducibleByDefault() throws IOException {
|
||||
this.task.getMainClass().set("com.example.Main");
|
||||
this.task.from(newFile("bravo.txt"), newFile("alpha.txt"), newFile("charlie.txt"));
|
||||
this.task.setReproducibleFileOrder(true);
|
||||
this.task.from(newFiles("files/b/bravo.txt", "files/a/alpha.txt", "files/c/charlie.txt"));
|
||||
executeTask();
|
||||
assertThat(this.task.getArchiveFile().get().getAsFile()).exists();
|
||||
List<String> textFiles = new ArrayList<>();
|
||||
List<String> files = new ArrayList<>();
|
||||
try (JarFile jarFile = new JarFile(this.task.getArchiveFile().get().getAsFile())) {
|
||||
Enumeration<JarEntry> entries = jarFile.entries();
|
||||
while (entries.hasMoreElements()) {
|
||||
JarEntry entry = entries.nextElement();
|
||||
if (entry.getName().endsWith(".txt")) {
|
||||
textFiles.add(entry.getName());
|
||||
OffsetDateTime lastModifiedTime = entry.getLastModifiedTime().toInstant().atOffset(ZoneOffset.UTC);
|
||||
assertThat(lastModifiedTime).isEqualTo(OffsetDateTime.of(1980, 2, 1, 0, 0, 0, 0, ZoneOffset.UTC));
|
||||
if (entry.getName().startsWith("files/")) {
|
||||
files.add(entry.getName());
|
||||
}
|
||||
}
|
||||
}
|
||||
assertThat(files).containsExactly("files/", "files/a/", "files/a/alpha.txt", "files/b/", "files/b/bravo.txt",
|
||||
"files/c/", "files/c/charlie.txt");
|
||||
}
|
||||
|
||||
@Test
|
||||
void archiveReproducibilityCanBeDisabled() throws IOException {
|
||||
this.task.getMainClass().set("com.example.Main");
|
||||
this.task.from(newFiles("files/b/bravo.txt", "files/a/alpha.txt", "files/c/charlie.txt"));
|
||||
this.task.setPreserveFileTimestamps(true);
|
||||
this.task.setReproducibleFileOrder(false);
|
||||
executeTask();
|
||||
assertThat(this.task.getArchiveFile().get().getAsFile()).exists();
|
||||
try (JarFile jarFile = new JarFile(this.task.getArchiveFile().get().getAsFile())) {
|
||||
Enumeration<JarEntry> entries = jarFile.entries();
|
||||
while (entries.hasMoreElements()) {
|
||||
JarEntry entry = entries.nextElement();
|
||||
if (entry.getName().endsWith(".txt") || entry.getName().startsWith("BOOT-INF/lib/")) {
|
||||
OffsetDateTime lastModifiedTime = entry.getLastModifiedTime().toInstant().atOffset(ZoneOffset.UTC);
|
||||
assertThat(lastModifiedTime)
|
||||
.isNotEqualTo(OffsetDateTime.of(1980, 2, 1, 0, 0, 0, 0, ZoneOffset.UTC));
|
||||
}
|
||||
}
|
||||
}
|
||||
assertThat(textFiles).containsExactly("alpha.txt", "bravo.txt", "charlie.txt");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -675,6 +689,19 @@ abstract class AbstractBootArchiveTests<T extends Jar & BootArchive> {
|
|||
return entryNames;
|
||||
}
|
||||
|
||||
protected File newFiles(String... names) throws IOException {
|
||||
File dir = new File(this.temp, UUID.randomUUID().toString());
|
||||
dir.mkdir();
|
||||
List<File> files = new ArrayList<>();
|
||||
for (String name : names) {
|
||||
File file = new File(dir, name);
|
||||
file.getParentFile().mkdirs();
|
||||
file.createNewFile();
|
||||
files.add(file);
|
||||
}
|
||||
return dir;
|
||||
}
|
||||
|
||||
protected File newFile(String name) throws IOException {
|
||||
File file = new File(this.temp, name);
|
||||
file.createNewFile();
|
||||
|
|
|
@ -30,8 +30,8 @@ import org.junit.jupiter.api.io.TempDir;
|
|||
import org.springframework.boot.buildpack.platform.build.BuildRequest;
|
||||
import org.springframework.boot.buildpack.platform.build.BuildpackReference;
|
||||
import org.springframework.boot.buildpack.platform.build.PullPolicy;
|
||||
import org.springframework.boot.buildpack.platform.docker.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||
import org.springframework.boot.gradle.junit.GradleProjectBuilder;
|
||||
|
||||
|
|
|
@ -66,18 +66,10 @@ class BootJarIntegrationTests extends AbstractBootArchiveIntegrationTests {
|
|||
copyClasspathApplication();
|
||||
BuildResult result = this.gradleBuild.build("launch");
|
||||
String output = result.getOutput();
|
||||
if (this.gradleBuild.gradleVersionIsLessThan("9.0.0-rc-1")) {
|
||||
assertThat(output).containsPattern("1\\. .*classes");
|
||||
assertThat(output).containsPattern("2\\. .*library-1.0-SNAPSHOT.jar");
|
||||
assertThat(output).containsPattern("3\\. .*commons-lang3-3.9.jar");
|
||||
assertThat(output).containsPattern("4\\. .*spring-boot-jarmode-tools.*.jar");
|
||||
}
|
||||
else {
|
||||
assertThat(output).containsPattern("1\\. .*classes");
|
||||
assertThat(output).containsPattern("2\\. .*commons-lang3-3.9.jar");
|
||||
assertThat(output).containsPattern("3\\. .*library-1.0-SNAPSHOT.jar");
|
||||
assertThat(output).containsPattern("4\\. .*spring-boot-jarmode-tools.*.jar");
|
||||
}
|
||||
assertThat(output).containsPattern("1\\. .*classes");
|
||||
assertThat(output).containsPattern("2\\. .*library-1.0-SNAPSHOT.jar");
|
||||
assertThat(output).containsPattern("3\\. .*commons-lang3-3.9.jar");
|
||||
assertThat(output).containsPattern("4\\. .*spring-boot-jarmode-tools.*.jar");
|
||||
assertThat(output).doesNotContain("5. ");
|
||||
}
|
||||
|
||||
|
@ -86,18 +78,10 @@ class BootJarIntegrationTests extends AbstractBootArchiveIntegrationTests {
|
|||
copyClasspathApplication();
|
||||
BuildResult result = this.gradleBuild.build("launch");
|
||||
String output = result.getOutput();
|
||||
if (this.gradleBuild.gradleVersionIsLessThan("9.0.0-rc-1")) {
|
||||
assertThat(output).containsPattern("1\\. .*classes");
|
||||
assertThat(output).containsPattern("2\\. .*spring-boot-jarmode-tools.*.jar");
|
||||
assertThat(output).containsPattern("3\\. .*library-1.0-SNAPSHOT.jar");
|
||||
assertThat(output).containsPattern("4\\. .*commons-lang3-3.9.jar");
|
||||
}
|
||||
else {
|
||||
assertThat(output).containsPattern("1\\. .*classes");
|
||||
assertThat(output).containsPattern("2\\. .*spring-boot-jarmode-tools.*.jar");
|
||||
assertThat(output).containsPattern("3\\. .*commons-lang3-3.9.jar");
|
||||
assertThat(output).containsPattern("4\\. .*library-1.0-SNAPSHOT.jar");
|
||||
}
|
||||
assertThat(output).containsPattern("1\\. .*classes");
|
||||
assertThat(output).containsPattern("2\\. .*spring-boot-jarmode-tools.*.jar");
|
||||
assertThat(output).containsPattern("3\\. .*library-1.0-SNAPSHOT.jar");
|
||||
assertThat(output).containsPattern("4\\. .*commons-lang3-3.9.jar");
|
||||
assertThat(output).doesNotContain("5. ");
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ bootJar {
|
|||
baseName = 'foo'
|
||||
}
|
||||
else {
|
||||
archiveBaseName = 'foo'
|
||||
archiveBaseName = 'foo'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ bootWar {
|
|||
baseName = 'foo'
|
||||
}
|
||||
else {
|
||||
archiveBaseName = 'foo'
|
||||
archiveBaseName = 'foo'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@ dependencies {
|
|||
}
|
||||
|
||||
task('processTestAotClasspath') {
|
||||
dependsOn configurations.processTestAotClasspath
|
||||
dependsOn configurations.processTestAotClasspath
|
||||
doFirst {
|
||||
configurations.processTestAotClasspath.files.each { println it }
|
||||
}
|
||||
|
|
|
@ -42,7 +42,7 @@ dependencies {
|
|||
}
|
||||
|
||||
task('processTestAotClasspath') {
|
||||
dependsOn configurations.processTestAotClasspath
|
||||
dependsOn configurations.processTestAotClasspath
|
||||
doFirst {
|
||||
configurations.processTestAotClasspath.files.each { println it }
|
||||
}
|
||||
|
|
|
@ -33,7 +33,7 @@ bootJar {
|
|||
include "*:*:*SNAPSHOT"
|
||||
}
|
||||
intoLayer("commons-dependencies") {
|
||||
include "org.apache.commons:*"
|
||||
include "org.apache.commons:*"
|
||||
}
|
||||
intoLayer("dependencies")
|
||||
}
|
||||
|
|
|
@ -48,7 +48,7 @@ bootJar {
|
|||
includeProjectDependencies()
|
||||
}
|
||||
intoLayer("commons-dependencies") {
|
||||
include "org.apache.commons:*"
|
||||
include "org.apache.commons:*"
|
||||
}
|
||||
intoLayer("dependencies")
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@ plugins {
|
|||
|
||||
bootJar {
|
||||
mainClass = 'com.example.Application'
|
||||
preserveFileTimestamps = false
|
||||
reproducibleFileOrder = true
|
||||
if (GradleVersion.current().compareTo(GradleVersion.version("9.0.0-rc-1")) < 0) {
|
||||
preserveFileTimestamps = false
|
||||
reproducibleFileOrder = true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,6 +21,8 @@ plugins {
|
|||
|
||||
bootWar {
|
||||
mainClass = 'com.example.Application'
|
||||
preserveFileTimestamps = false
|
||||
reproducibleFileOrder = true
|
||||
if (GradleVersion.current().compareTo(GradleVersion.version("9.0.0-rc-1")) < 0) {
|
||||
preserveFileTimestamps = false
|
||||
reproducibleFileOrder = true
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
<!-- tag::different-versions[] -->
|
||||
<properties>
|
||||
<slf4j.version>1.7.30</slf4j.version>
|
||||
<spring-data-releasetrain.version>Moore-SR6</spring-data-releasetrain.version>
|
||||
<spring-data-bom.version>2024.1.10</spring-data-bom.version>
|
||||
</properties>
|
||||
<!-- end::different-versions[] -->
|
||||
|
||||
|
|
|
@ -24,8 +24,8 @@
|
|||
<!-- Override Spring Data release train provided by Spring Boot -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.data</groupId>
|
||||
<artifactId>spring-data-releasetrain</artifactId>
|
||||
<version>2020.0.0-SR1</version>
|
||||
<artifactId>spring-data-bom</artifactId>
|
||||
<version>2024.1.10</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
|
|
@ -67,6 +67,9 @@ include::example$using/different-versions-pom.xml[tags=different-versions]
|
|||
|
||||
Browse the xref:appendix:dependency-versions/properties.adoc[Dependency Versions Properties] section in the Spring Boot reference for a complete list of dependency version properties.
|
||||
|
||||
WARNING: Each Spring Boot release is designed and tested against a specific set of third-party dependencies.
|
||||
Overriding versions may cause compatibility issues and should be done with care.
|
||||
|
||||
|
||||
|
||||
[[using.import]]
|
||||
|
|
|
@ -159,7 +159,7 @@ abstract class AbstractArchiveIntegrationTests {
|
|||
Optional<JarEntry> match = entries.filter((entry) -> entry.getName().startsWith(prefix))
|
||||
.findFirst();
|
||||
assertThat(match).as("Name starting with %s", prefix)
|
||||
.hasValueSatisfying((entry) -> assertThat(entry.getComment()).startsWith("UNPACK:"));
|
||||
.hasValueSatisfying((entry) -> assertThat(entry.getComment()).isEqualTo("UNPACK"));
|
||||
});
|
||||
});
|
||||
return this;
|
||||
|
|
|
@ -76,26 +76,6 @@ class JarIntegrationTests extends AbstractArchiveIntegrationTests {
|
|||
});
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void whenJarWithClassicLoaderIsRepackagedInPlaceOnlyRepackagedJarIsInstalled(MavenBuild mavenBuild) {
|
||||
mavenBuild.project("jar-with-classic-loader").goals("install").execute((project) -> {
|
||||
File original = new File(project, "target/jar-with-classic-loader-0.0.1.BUILD-SNAPSHOT.jar.original");
|
||||
assertThat(original).isFile();
|
||||
File repackaged = new File(project, "target/jar-with-classic-loader-0.0.1.BUILD-SNAPSHOT.jar");
|
||||
assertThat(launchScript(repackaged)).isEmpty();
|
||||
assertThat(jar(repackaged)).manifest((manifest) -> {
|
||||
manifest.hasMainClass("org.springframework.boot.loader.launch.JarLauncher");
|
||||
manifest.hasStartClass("some.random.Main");
|
||||
manifest.hasAttribute("Not-Used", "Foo");
|
||||
}).hasEntryWithName("org/springframework/boot/loader/launch/JarLauncher.class");
|
||||
assertThat(buildLog(project))
|
||||
.contains("Replacing main artifact " + repackaged + " with repackaged archive,")
|
||||
.contains("The original artifact has been renamed to " + original)
|
||||
.contains("Installing " + repackaged + " to")
|
||||
.doesNotContain("Installing " + original + " to");
|
||||
});
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void whenAttachIsDisabledOnlyTheOriginalJarIsInstalled(MavenBuild mavenBuild) {
|
||||
mavenBuild.project("jar-attach-disabled").goals("install").execute((project) -> {
|
||||
|
@ -196,7 +176,40 @@ class JarIntegrationTests extends AbstractArchiveIntegrationTests {
|
|||
.hasEntryWithNameStartingWith("BOOT-INF/lib/spring-context")
|
||||
.hasEntryWithNameStartingWith("BOOT-INF/lib/spring-core")
|
||||
.hasEntryWithNameStartingWith("BOOT-INF/lib/commons-logging")
|
||||
.doesNotHaveEntryWithName("BOOT-INF/lib/servlet-api-2.5.jar");
|
||||
.doesNotHaveEntryWithNameStartingWith("BOOT-INF/lib/servlet-api-");
|
||||
});
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void whenAnEntryIsOptionalByDefaultDoesNotAppearInTheRepackagedJar(MavenBuild mavenBuild) {
|
||||
mavenBuild.project("jar-optional-default").goals("install").execute((project) -> {
|
||||
File repackaged = new File(project, "target/jar-optional-default-0.0.1.BUILD-SNAPSHOT.jar");
|
||||
assertThat(jar(repackaged)).hasEntryWithNameStartingWith("BOOT-INF/classes/")
|
||||
.hasEntryWithNameStartingWith("BOOT-INF/lib/spring-context")
|
||||
.hasEntryWithNameStartingWith("BOOT-INF/lib/spring-core")
|
||||
.doesNotHaveEntryWithNameStartingWith("BOOT-INF/lib/log4j-api-");
|
||||
});
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void whenAnEntryIsOptionalAndOptionalsIncludedAppearsInTheRepackagedJar(MavenBuild mavenBuild) {
|
||||
mavenBuild.project("jar-optional-include").goals("install").execute((project) -> {
|
||||
File repackaged = new File(project, "target/jar-optional-include-0.0.1.BUILD-SNAPSHOT.jar");
|
||||
assertThat(jar(repackaged)).hasEntryWithNameStartingWith("BOOT-INF/classes/")
|
||||
.hasEntryWithNameStartingWith("BOOT-INF/lib/spring-context")
|
||||
.hasEntryWithNameStartingWith("BOOT-INF/lib/spring-core")
|
||||
.hasEntryWithNameStartingWith("BOOT-INF/lib/log4j-api-");
|
||||
});
|
||||
}
|
||||
|
||||
@TestTemplate
|
||||
void whenAnEntryIsOptionalAndOptionalsExcludedDoesNotAppearInTheRepackagedJar(MavenBuild mavenBuild) {
|
||||
mavenBuild.project("jar-optional-exclude").goals("install").execute((project) -> {
|
||||
File repackaged = new File(project, "target/jar-optional-exclude-0.0.1.BUILD-SNAPSHOT.jar");
|
||||
assertThat(jar(repackaged)).hasEntryWithNameStartingWith("BOOT-INF/classes/")
|
||||
.hasEntryWithNameStartingWith("BOOT-INF/lib/spring-context")
|
||||
.hasEntryWithNameStartingWith("BOOT-INF/lib/spring-core")
|
||||
.doesNotHaveEntryWithNameStartingWith("BOOT-INF/lib/log4j-api-");
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -248,9 +261,8 @@ class JarIntegrationTests extends AbstractArchiveIntegrationTests {
|
|||
File repackaged = new File(project, "target/jar-exclude-group-0.0.1.BUILD-SNAPSHOT.jar");
|
||||
assertThat(jar(repackaged)).hasEntryWithNameStartingWith("BOOT-INF/classes/")
|
||||
.hasEntryWithNameStartingWith("BOOT-INF/lib/spring-context")
|
||||
.hasEntryWithNameStartingWith("BOOT-INF/lib/spring-core")
|
||||
.hasEntryWithNameStartingWith("BOOT-INF/lib/commons-logging")
|
||||
.doesNotHaveEntryWithName("BOOT-INF/lib/log4j-api-2.4.1.jar");
|
||||
.doesNotHaveEntryWithName("BOOT-INF/lib/log4j-api-");
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -388,7 +400,7 @@ class JarIntegrationTests extends AbstractArchiveIntegrationTests {
|
|||
assertThat(layerIndex.get("application")).contains("BOOT-INF/lib/jar-release-0.0.1.RELEASE.jar",
|
||||
"BOOT-INF/lib/jar-snapshot-0.0.1.BUILD-SNAPSHOT.jar");
|
||||
assertThat(layerIndex.get("dependencies"))
|
||||
.anyMatch((dependency) -> dependency.startsWith("BOOT-INF/lib/log4j-api-2"));
|
||||
.anyMatch((dependency) -> dependency.startsWith("BOOT-INF/lib/log4j-api-"));
|
||||
}
|
||||
catch (IOException ex) {
|
||||
// Ignore
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.springframework.boot.maven.it</groupId>
|
||||
<artifactId>jar-optional-default</artifactId>
|
||||
<version>0.0.1.BUILD-SNAPSHOT</version>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>@java.version@</maven.compiler.source>
|
||||
<maven.compiler.target>@java.version@</maven.compiler.target>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>@project.groupId@</groupId>
|
||||
<artifactId>@project.artifactId@</artifactId>
|
||||
<version>@project.version@</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>@maven-jar-plugin.version@</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<version>@spring-framework.version@</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
<version>@log4j2.version@</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -3,7 +3,7 @@
|
|||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.springframework.boot.maven.it</groupId>
|
||||
<artifactId>jar-with-classic-loader</artifactId>
|
||||
<artifactId>jar-optional-exclude</artifactId>
|
||||
<version>0.0.1.BUILD-SNAPSHOT</version>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
|
@ -22,7 +22,7 @@
|
|||
<goal>repackage</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<loaderImplementation>CLASSIC</loaderImplementation>
|
||||
<includeOptional>false</includeOptional>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
|
@ -31,16 +31,6 @@
|
|||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>@maven-jar-plugin.version@</version>
|
||||
<configuration>
|
||||
<archive>
|
||||
<manifest>
|
||||
<mainClass>some.random.Main</mainClass>
|
||||
</manifest>
|
||||
<manifestEntries>
|
||||
<Not-Used>Foo</Not-Used>
|
||||
</manifestEntries>
|
||||
</archive>
|
||||
</configuration>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
|
@ -51,10 +41,10 @@
|
|||
<version>@spring-framework.version@</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>jakarta.servlet</groupId>
|
||||
<artifactId>jakarta.servlet-api</artifactId>
|
||||
<version>@jakarta-servlet.version@</version>
|
||||
<scope>provided</scope>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
<version>@log4j2.version@</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -14,7 +14,11 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Utilities used by Spring Boot's JAR loading.
|
||||
*/
|
||||
package org.springframework.boot.loader.util;
|
||||
package org.test;
|
||||
|
||||
public class SampleApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.springframework.boot.maven.it</groupId>
|
||||
<artifactId>jar-optional-include</artifactId>
|
||||
<version>0.0.1.BUILD-SNAPSHOT</version>
|
||||
<properties>
|
||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||
<maven.compiler.source>@java.version@</maven.compiler.source>
|
||||
<maven.compiler.target>@java.version@</maven.compiler.target>
|
||||
</properties>
|
||||
<build>
|
||||
<plugins>
|
||||
<plugin>
|
||||
<groupId>@project.groupId@</groupId>
|
||||
<artifactId>@project.artifactId@</artifactId>
|
||||
<version>@project.version@</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
<goal>repackage</goal>
|
||||
</goals>
|
||||
<configuration>
|
||||
<includeOptional>true</includeOptional>
|
||||
</configuration>
|
||||
</execution>
|
||||
</executions>
|
||||
</plugin>
|
||||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-jar-plugin</artifactId>
|
||||
<version>@maven-jar-plugin.version@</version>
|
||||
</plugin>
|
||||
</plugins>
|
||||
</build>
|
||||
<dependencies>
|
||||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-context</artifactId>
|
||||
<version>@spring-framework.version@</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>org.apache.logging.log4j</groupId>
|
||||
<artifactId>log4j-api</artifactId>
|
||||
<version>@log4j2.version@</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
</project>
|
|
@ -14,7 +14,11 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Support for loading and manipulating JAR/WAR files.
|
||||
*/
|
||||
package org.springframework.boot.loader.jar;
|
||||
package org.test;
|
||||
|
||||
public class SampleApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
}
|
||||
|
||||
}
|
|
@ -47,7 +47,6 @@ import org.springframework.boot.loader.tools.Layouts.Jar;
|
|||
import org.springframework.boot.loader.tools.Layouts.None;
|
||||
import org.springframework.boot.loader.tools.Layouts.War;
|
||||
import org.springframework.boot.loader.tools.Libraries;
|
||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
||||
import org.springframework.boot.loader.tools.Packager;
|
||||
import org.springframework.boot.loader.tools.layer.CustomLayers;
|
||||
|
||||
|
@ -114,6 +113,13 @@ public abstract class AbstractPackagerMojo extends AbstractDependencyFilterMojo
|
|||
@Parameter(defaultValue = "false")
|
||||
public boolean includeSystemScope;
|
||||
|
||||
/**
|
||||
* Include optional dependencies.
|
||||
* @since 3.5.7
|
||||
*/
|
||||
@Parameter(defaultValue = "false")
|
||||
public boolean includeOptional;
|
||||
|
||||
/**
|
||||
* Include JAR tools.
|
||||
* @since 3.3.0
|
||||
|
@ -142,15 +148,6 @@ public abstract class AbstractPackagerMojo extends AbstractDependencyFilterMojo
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the loader implementation that should be used.
|
||||
* @return the loader implementation or {@code null}
|
||||
* @since 3.2.0
|
||||
*/
|
||||
protected @Nullable LoaderImplementation getLoaderImplementation() {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the layout factory that will be used to determine the {@link LayoutType} if
|
||||
* no explicit layout is set.
|
||||
|
@ -168,7 +165,6 @@ public abstract class AbstractPackagerMojo extends AbstractDependencyFilterMojo
|
|||
*/
|
||||
protected <P extends Packager> P getConfiguredPackager(Supplier<P> supplier) {
|
||||
P packager = supplier.get();
|
||||
packager.setLoaderImplementation(getLoaderImplementation());
|
||||
packager.setLayoutFactory(getLayoutFactory());
|
||||
packager.addMainClassTimeoutWarningListener(new LoggingMainClassTimeoutWarningListener(this::getLog));
|
||||
packager.setMainClass(this.mainClass);
|
||||
|
@ -231,6 +227,9 @@ public abstract class AbstractPackagerMojo extends AbstractDependencyFilterMojo
|
|||
if (!this.includeSystemScope) {
|
||||
filters.add(new ScopeFilter(null, Artifact.SCOPE_SYSTEM));
|
||||
}
|
||||
if (!this.includeOptional) {
|
||||
filters.add(DependencyFilter.exclude(Artifact::isOptional));
|
||||
}
|
||||
return filters.toArray(new ArtifactsFilter[0]);
|
||||
}
|
||||
|
||||
|
|
|
@ -50,7 +50,6 @@ import org.springframework.boot.loader.tools.EntryWriter;
|
|||
import org.springframework.boot.loader.tools.ImagePackager;
|
||||
import org.springframework.boot.loader.tools.LayoutFactory;
|
||||
import org.springframework.boot.loader.tools.Libraries;
|
||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
|
@ -208,13 +207,6 @@ public abstract class BuildImageMojo extends AbstractPackagerMojo {
|
|||
@Parameter
|
||||
private @Nullable LayoutType layout;
|
||||
|
||||
/**
|
||||
* The loader implementation that should be used.
|
||||
* @since 3.2.0
|
||||
*/
|
||||
@Parameter
|
||||
private @Nullable LoaderImplementation loaderImplementation;
|
||||
|
||||
/**
|
||||
* The layout factory that will be used to create the executable archive if no
|
||||
* explicit layout is set. Alternative layouts implementations can be provided by 3rd
|
||||
|
@ -238,11 +230,6 @@ public abstract class BuildImageMojo extends AbstractPackagerMojo {
|
|||
return this.layout;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable LoaderImplementation getLoaderImplementation() {
|
||||
return this.loaderImplementation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the layout factory that will be used to determine the
|
||||
* {@link AbstractPackagerMojo.LayoutType} if no explicit layout is set.
|
||||
|
|
|
@ -16,9 +16,11 @@
|
|||
|
||||
package org.springframework.boot.maven;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
import org.apache.maven.artifact.Artifact;
|
||||
import org.apache.maven.shared.artifact.filter.collection.AbstractArtifactsFilter;
|
||||
|
@ -81,4 +83,22 @@ public abstract class DependencyFilter extends AbstractArtifactsFilter {
|
|||
return this.filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a new {@link DependencyFilter} the excludes artifacts based on the given
|
||||
* predicate.
|
||||
* @param filter the predicate used to filter the artifacts.
|
||||
* @return a new {@link DependencyFilter} instance
|
||||
* @since 3.5.7
|
||||
*/
|
||||
public static DependencyFilter exclude(Predicate<Artifact> filter) {
|
||||
return new DependencyFilter(Collections.emptyList()) {
|
||||
|
||||
@Override
|
||||
protected boolean filter(Artifact artifact) {
|
||||
return filter.test(artifact);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -34,8 +34,8 @@ import org.apache.maven.artifact.Artifact;
|
|||
*/
|
||||
class JarTypeFilter extends DependencyFilter {
|
||||
|
||||
private static final Set<String> EXCLUDED_JAR_TYPES = Collections
|
||||
.unmodifiableSet(new HashSet<>(Arrays.asList("annotation-processor", "dependencies-starter")));
|
||||
private static final Set<String> EXCLUDED_JAR_TYPES = Collections.unmodifiableSet(
|
||||
new HashSet<>(Arrays.asList("annotation-processor", "dependencies-starter", "development-tool")));
|
||||
|
||||
JarTypeFilter() {
|
||||
super(Collections.emptyList());
|
||||
|
|
|
@ -40,7 +40,6 @@ import org.springframework.boot.loader.tools.DefaultLaunchScript;
|
|||
import org.springframework.boot.loader.tools.LaunchScript;
|
||||
import org.springframework.boot.loader.tools.LayoutFactory;
|
||||
import org.springframework.boot.loader.tools.Libraries;
|
||||
import org.springframework.boot.loader.tools.LoaderImplementation;
|
||||
import org.springframework.boot.loader.tools.Repackager;
|
||||
import org.springframework.lang.Contract;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
@ -170,13 +169,6 @@ public class RepackageMojo extends AbstractPackagerMojo {
|
|||
@Parameter(property = "spring-boot.repackage.layout")
|
||||
private @Nullable LayoutType layout;
|
||||
|
||||
/**
|
||||
* The loader implementation that should be used.
|
||||
* @since 3.2.0
|
||||
*/
|
||||
@Parameter
|
||||
private @Nullable LoaderImplementation loaderImplementation;
|
||||
|
||||
/**
|
||||
* The layout factory that will be used to create the executable archive if no
|
||||
* explicit layout is set. Alternative layouts implementations can be provided by 3rd
|
||||
|
@ -201,11 +193,6 @@ public class RepackageMojo extends AbstractPackagerMojo {
|
|||
return this.layout;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable LoaderImplementation getLoaderImplementation() {
|
||||
return this.loaderImplementation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the layout factory that will be used to determine the
|
||||
* {@link AbstractPackagerMojo.LayoutType} if no explicit layout is set.
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.maven;
|
||||
|
||||
import java.util.Set;
|
||||
|
||||
import org.apache.maven.artifact.Artifact;
|
||||
import org.apache.maven.artifact.DefaultArtifact;
|
||||
import org.apache.maven.artifact.handler.ArtifactHandler;
|
||||
import org.apache.maven.artifact.handler.DefaultArtifactHandler;
|
||||
import org.apache.maven.artifact.versioning.VersionRange;
|
||||
import org.apache.maven.shared.artifact.filter.collection.ArtifactFilterException;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link DependencyFilter}.
|
||||
*
|
||||
* @author Phillip Webb
|
||||
*/
|
||||
class DependencyFilterTests {
|
||||
|
||||
@Test
|
||||
void excludeFiltersBasedOnPredicate() throws ArtifactFilterException {
|
||||
DependencyFilter filter = DependencyFilter.exclude(Artifact::isOptional);
|
||||
ArtifactHandler ah = new DefaultArtifactHandler();
|
||||
VersionRange v = VersionRange.createFromVersion("1.0.0");
|
||||
DefaultArtifact a1 = new DefaultArtifact("com.example", "a1", v, "compile", "jar", null, ah, false);
|
||||
DefaultArtifact a2 = new DefaultArtifact("com.example", "a2", v, "compile", "jar", null, ah, true);
|
||||
DefaultArtifact a3 = new DefaultArtifact("com.example", "a3", v, "compile", "jar", null, ah, false);
|
||||
Set<Artifact> filtered = filter.filter(Set.of(a1, a2, a3));
|
||||
assertThat(filtered).containsExactlyInAnyOrder(a1, a3);
|
||||
}
|
||||
|
||||
}
|
|
@ -31,8 +31,8 @@ import org.springframework.boot.buildpack.platform.build.BuildRequest;
|
|||
import org.springframework.boot.buildpack.platform.build.BuildpackReference;
|
||||
import org.springframework.boot.buildpack.platform.build.Cache;
|
||||
import org.springframework.boot.buildpack.platform.build.PullPolicy;
|
||||
import org.springframework.boot.buildpack.platform.docker.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||
import org.springframework.boot.buildpack.platform.io.Owner;
|
||||
import org.springframework.boot.buildpack.platform.io.TarArchive;
|
||||
|
|
|
@ -60,6 +60,11 @@ class JarTypeFilterTests {
|
|||
assertThat(new JarTypeFilter().filter(createArtifact("annotation-processor"))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenArtifactHasDevelopmentToolJarTypeThenItIsExcluded() {
|
||||
assertThat(new JarTypeFilter().filter(createArtifact("development-tool"))).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
void whenArtifactHasNoManifestFileThenItIsIncluded() {
|
||||
assertThat(new JarTypeFilter().filter(createArtifactWithNoManifest())).isFalse();
|
||||
|
|
|
@ -52,7 +52,7 @@ dependencies {
|
|||
implementation("commons-codec:commons-codec:${commonsCodecVersion}")
|
||||
implementation("de.undercouch.download:de.undercouch.download.gradle.plugin:5.5.0")
|
||||
implementation("dev.adamko.dokkatoo:dokkatoo-plugin:2.3.1")
|
||||
implementation("io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.23.8")
|
||||
implementation("dev.detekt:detekt-gradle-plugin:2.0.0-alpha.0")
|
||||
implementation("io.spring.gradle.antora:spring-antora-plugin:0.0.1")
|
||||
implementation("io.spring.javaformat:spring-javaformat-gradle-plugin:${javaFormatVersion}")
|
||||
implementation("io.spring.nohttp:nohttp-gradle:0.0.11")
|
||||
|
@ -133,10 +133,6 @@ gradlePlugin {
|
|||
id = "org.springframework.boot.integration-test"
|
||||
implementationClass = "org.springframework.boot.build.test.IntegrationTestPlugin"
|
||||
}
|
||||
systemTestPlugin {
|
||||
id = "org.springframework.boot.system-test"
|
||||
implementationClass = "org.springframework.boot.build.test.SystemTestPlugin"
|
||||
}
|
||||
mavenPluginPlugin {
|
||||
id = "org.springframework.boot.maven-plugin"
|
||||
implementationClass = "org.springframework.boot.build.mavenplugin.MavenPluginPlugin"
|
||||
|
@ -153,10 +149,22 @@ gradlePlugin {
|
|||
id = "org.springframework.boot.starter"
|
||||
implementationClass = "org.springframework.boot.build.starters.StarterPlugin"
|
||||
}
|
||||
systemTestPlugin {
|
||||
id = "org.springframework.boot.system-test"
|
||||
implementationClass = "org.springframework.boot.build.test.SystemTestPlugin"
|
||||
}
|
||||
testAutoConfigurationPlugin {
|
||||
id = "org.springframework.boot.test-auto-configuration"
|
||||
implementationClass = "org.springframework.boot.build.test.autoconfigure.TestAutoConfigurationPlugin"
|
||||
}
|
||||
testFailuresPlugin {
|
||||
id = "org.springframework.boot.test-failures"
|
||||
implementationClass = "org.springframework.boot.build.testing.TestFailuresPlugin"
|
||||
}
|
||||
testSlicePlugin {
|
||||
id = "org.springframework.boot.test-slice"
|
||||
implementationClass = "org.springframework.boot.build.test.autoconfigure.TestSlicePlugin"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -20,9 +20,9 @@ import java.net.URI;
|
|||
|
||||
import dev.adamko.dokkatoo.DokkatooExtension;
|
||||
import dev.adamko.dokkatoo.formats.DokkatooHtmlPlugin;
|
||||
import io.gitlab.arturbosch.detekt.Detekt;
|
||||
import io.gitlab.arturbosch.detekt.DetektPlugin;
|
||||
import io.gitlab.arturbosch.detekt.extensions.DetektExtension;
|
||||
import dev.detekt.gradle.Detekt;
|
||||
import dev.detekt.gradle.extensions.DetektExtension;
|
||||
import dev.detekt.gradle.plugin.DetektPlugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.gradle.api.tasks.SourceSetContainer;
|
||||
|
@ -76,6 +76,7 @@ class KotlinConventions {
|
|||
|
||||
private void configureDokkatoo(Project project) {
|
||||
DokkatooExtension dokkatoo = project.getExtensions().getByType(DokkatooExtension.class);
|
||||
dokkatoo.getVersions().getJetbrainsDokka().set("2.1.0-Beta");
|
||||
dokkatoo.getDokkatooSourceSets().configureEach((sourceSet) -> {
|
||||
if (SourceSet.MAIN_SOURCE_SET_NAME.equals(sourceSet.getName())) {
|
||||
sourceSet.getSourceRoots().setFrom(project.file("src/main/kotlin"));
|
||||
|
@ -103,7 +104,9 @@ class KotlinConventions {
|
|||
project.getPlugins().apply(DetektPlugin.class);
|
||||
DetektExtension detekt = project.getExtensions().getByType(DetektExtension.class);
|
||||
detekt.getConfig().setFrom(project.getRootProject().file("config/detekt/config.yml"));
|
||||
project.getTasks().withType(Detekt.class).configureEach((task) -> task.setJvmTarget(JVM_TARGET.getTarget()));
|
||||
project.getTasks()
|
||||
.withType(Detekt.class)
|
||||
.configureEach((task) -> task.getJvmTarget().set(JVM_TARGET.getTarget()));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -80,6 +80,8 @@ final class ArchitectureRules {
|
|||
|
||||
private static final String AUTOCONFIGURATION_ANNOTATION = "org.springframework.boot.autoconfigure.AutoConfiguration";
|
||||
|
||||
private static final String TEST_AUTOCONFIGURATION_ANNOTATION = "org.springframework.boot.test.autoconfigure.TestAutoConfiguration";
|
||||
|
||||
private ArchitectureRules() {
|
||||
}
|
||||
|
||||
|
@ -112,6 +114,7 @@ final class ArchitectureRules {
|
|||
rules.add(allConfigurationPropertiesBindingBeanMethodsShouldBeStatic());
|
||||
rules.add(autoConfigurationClassesShouldBePublicAndFinal());
|
||||
rules.add(autoConfigurationClassesShouldHaveNoPublicMembers());
|
||||
rules.add(testAutoConfigurationClassesShouldBePackagePrivateAndFinal());
|
||||
return List.copyOf(rules);
|
||||
}
|
||||
|
||||
|
@ -130,7 +133,12 @@ final class ArchitectureRules {
|
|||
}
|
||||
|
||||
private static ArchRule allPackagesShouldBeFreeOfTangles() {
|
||||
return SlicesRuleDefinition.slices().matching("(**)").should().beFreeOfCycles();
|
||||
return SlicesRuleDefinition.slices()
|
||||
.matching("(**)")
|
||||
.should()
|
||||
.beFreeOfCycles()
|
||||
.ignoreDependency("org.springframework.boot.env.EnvironmentPostProcessor",
|
||||
"org.springframework.boot.SpringApplication");
|
||||
}
|
||||
|
||||
private static ArchRule allBeanPostProcessorBeanMethodsShouldBeStaticAndNotCausePrematureInitialization() {
|
||||
|
@ -353,8 +361,7 @@ final class ArchitectureRules {
|
|||
|
||||
private static ArchRule autoConfigurationClassesShouldBePublicAndFinal() {
|
||||
return ArchRuleDefinition.classes()
|
||||
.that()
|
||||
.areAnnotatedWith(AUTOCONFIGURATION_ANNOTATION)
|
||||
.that(areRegularAutoConfiguration())
|
||||
.should()
|
||||
.bePublic()
|
||||
.andShould()
|
||||
|
@ -365,8 +372,7 @@ final class ArchitectureRules {
|
|||
private static ArchRule autoConfigurationClassesShouldHaveNoPublicMembers() {
|
||||
return ArchRuleDefinition.members()
|
||||
.that()
|
||||
.areDeclaredInClassesThat()
|
||||
.areAnnotatedWith(AUTOCONFIGURATION_ANNOTATION)
|
||||
.areDeclaredInClassesThat(areRegularAutoConfiguration())
|
||||
.and(areNotDefaultConstructors())
|
||||
.and(areNotConstants())
|
||||
.and(dontOverridePublicMethods())
|
||||
|
@ -375,6 +381,24 @@ final class ArchitectureRules {
|
|||
.allowEmptyShould(true);
|
||||
}
|
||||
|
||||
private static ArchRule testAutoConfigurationClassesShouldBePackagePrivateAndFinal() {
|
||||
return ArchRuleDefinition.classes()
|
||||
.that()
|
||||
.areAnnotatedWith(TEST_AUTOCONFIGURATION_ANNOTATION)
|
||||
.should()
|
||||
.bePackagePrivate()
|
||||
.andShould()
|
||||
.haveModifier(JavaModifier.FINAL)
|
||||
.allowEmptyShould(true);
|
||||
}
|
||||
|
||||
private static DescribedPredicate<JavaClass> areRegularAutoConfiguration() {
|
||||
return DescribedPredicate.describe("Regular @AutoConfiguration",
|
||||
(javaClass) -> javaClass.isMetaAnnotatedWith(AUTOCONFIGURATION_ANNOTATION)
|
||||
&& !javaClass.isMetaAnnotatedWith(TEST_AUTOCONFIGURATION_ANNOTATION)
|
||||
&& !javaClass.isAnnotation());
|
||||
}
|
||||
|
||||
private static DescribedPredicate<? super JavaMember> dontOverridePublicMethods() {
|
||||
OverridesPublicMethod<JavaMember> predicate = new OverridesPublicMethod<>();
|
||||
return DescribedPredicate.describe("don't override public methods", (member) -> !predicate.test(member));
|
||||
|
|
|
@ -19,6 +19,7 @@ package org.springframework.boot.build.autoconfigure;
|
|||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
|
@ -54,8 +55,8 @@ public record AutoConfigurationClass(String name, List<String> before, List<Stri
|
|||
attributes.getOrDefault("afterName", Collections.emptyList()));
|
||||
}
|
||||
|
||||
static AutoConfigurationClass of(File classFile) {
|
||||
try (FileInputStream input = new FileInputStream(classFile)) {
|
||||
public static AutoConfigurationClass of(InputStream input) {
|
||||
try {
|
||||
ClassReader classReader = new ClassReader(input);
|
||||
AutoConfigurationClassVisitor visitor = new AutoConfigurationClassVisitor();
|
||||
classReader.accept(visitor, ClassReader.SKIP_DEBUG | ClassReader.SKIP_CODE | ClassReader.SKIP_FRAMES);
|
||||
|
@ -66,6 +67,15 @@ public record AutoConfigurationClass(String name, List<String> before, List<Stri
|
|||
}
|
||||
}
|
||||
|
||||
static AutoConfigurationClass of(File classFile) {
|
||||
try (InputStream input = new FileInputStream(classFile)) {
|
||||
return of(input);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private static final class AutoConfigurationClassVisitor extends ClassVisitor {
|
||||
|
||||
private AutoConfigurationClass autoConfigurationClass;
|
||||
|
|
|
@ -38,7 +38,10 @@ import org.gradle.api.tasks.SkipWhenEmpty;
|
|||
*/
|
||||
public abstract class AutoConfigurationImportsTask extends DefaultTask {
|
||||
|
||||
static final String IMPORTS_FILE = "META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports";
|
||||
/**
|
||||
* The path of the {@code AutoConfiguration.imports} file.
|
||||
*/
|
||||
public static final String IMPORTS_FILE = "META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports";
|
||||
|
||||
private FileCollection sourceFiles = getProject().getObjects().fileCollection();
|
||||
|
||||
|
|
|
@ -65,7 +65,7 @@ public class ConfigurationPropertiesPlugin implements Plugin<Project> {
|
|||
public static final String CHECK_ADDITIONAL_SPRING_CONFIGURATION_METADATA_TASK_NAME = "checkAdditionalSpringConfigurationMetadata";
|
||||
|
||||
/**
|
||||
* Name of the {@link CheckAdditionalSpringConfigurationMetadata} task.
|
||||
* Name of the {@link CheckSpringConfigurationMetadata} task.
|
||||
*/
|
||||
public static final String CHECK_SPRING_CONFIGURATION_METADATA_TASK_NAME = "checkSpringConfigurationMetadata";
|
||||
|
||||
|
|
|
@ -41,7 +41,7 @@ public class OptionalDependenciesPlugin implements Plugin<Project> {
|
|||
@Override
|
||||
public void apply(Project project) {
|
||||
Configuration optional = project.getConfigurations().create("optional");
|
||||
optional.setCanBeConsumed(false);
|
||||
optional.setCanBeConsumed(true);
|
||||
optional.setCanBeResolved(false);
|
||||
project.getPlugins().withType(JavaPlugin.class, (javaPlugin) -> {
|
||||
SourceSetContainer sourceSets = project.getExtensions()
|
||||
|
|
|
@ -0,0 +1,233 @@
|
|||
/*
|
||||
* Copyright 2025-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.test.autoconfigure;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.nio.file.Files;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.jar.JarFile;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.ZipEntry;
|
||||
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.file.DirectoryProperty;
|
||||
import org.gradle.api.file.FileCollection;
|
||||
import org.gradle.api.file.FileTree;
|
||||
import org.gradle.api.tasks.Classpath;
|
||||
import org.gradle.api.tasks.InputFiles;
|
||||
import org.gradle.api.tasks.OutputDirectory;
|
||||
import org.gradle.api.tasks.PathSensitive;
|
||||
import org.gradle.api.tasks.PathSensitivity;
|
||||
import org.gradle.api.tasks.SkipWhenEmpty;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
import org.gradle.api.tasks.VerificationException;
|
||||
import org.gradle.language.base.plugins.LifecycleBasePlugin;
|
||||
|
||||
import org.springframework.boot.build.autoconfigure.AutoConfigurationClass;
|
||||
|
||||
/**
|
||||
* Task to check the contents of a project's
|
||||
* {@code META-INF/spring/*.AutoConfigure*.imports} files.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public abstract class CheckAutoConfigureImports extends DefaultTask {
|
||||
|
||||
private FileCollection sourceFiles = getProject().getObjects().fileCollection();
|
||||
|
||||
private FileCollection classpath = getProject().getObjects().fileCollection();
|
||||
|
||||
public CheckAutoConfigureImports() {
|
||||
getOutputDirectory().convention(getProject().getLayout().getBuildDirectory().dir(getName()));
|
||||
setGroup(LifecycleBasePlugin.VERIFICATION_GROUP);
|
||||
}
|
||||
|
||||
@InputFiles
|
||||
@SkipWhenEmpty
|
||||
@PathSensitive(PathSensitivity.RELATIVE)
|
||||
public FileTree getSource() {
|
||||
return this.sourceFiles.getAsFileTree()
|
||||
.matching((filter) -> filter.include("META-INF/spring/*.AutoConfigure*.imports"));
|
||||
}
|
||||
|
||||
public void setSource(Object source) {
|
||||
this.sourceFiles = getProject().getObjects().fileCollection().from(source);
|
||||
}
|
||||
|
||||
@Classpath
|
||||
public FileCollection getClasspath() {
|
||||
return this.classpath;
|
||||
}
|
||||
|
||||
public void setClasspath(Object classpath) {
|
||||
this.classpath = getProject().getObjects().fileCollection().from(classpath);
|
||||
}
|
||||
|
||||
@OutputDirectory
|
||||
public abstract DirectoryProperty getOutputDirectory();
|
||||
|
||||
@TaskAction
|
||||
void execute() {
|
||||
Map<String, List<String>> allProblems = new TreeMap<>();
|
||||
for (AutoConfigureImports autoConfigureImports : loadImports()) {
|
||||
List<String> problems = new ArrayList<>();
|
||||
if (!find(autoConfigureImports.annotationName)) {
|
||||
problems.add("Annotation '%s' was not found".formatted(autoConfigureImports.annotationName));
|
||||
}
|
||||
for (String imported : autoConfigureImports.imports) {
|
||||
String importedClassName = imported;
|
||||
if (importedClassName.startsWith("optional:")) {
|
||||
importedClassName = importedClassName.substring("optional:".length());
|
||||
}
|
||||
boolean found = find(importedClassName, (input) -> {
|
||||
if (!correctlyAnnotated(input)) {
|
||||
problems.add("Imported auto-configuration '%s' is not annotated with @AutoConfiguration"
|
||||
.formatted(imported));
|
||||
}
|
||||
});
|
||||
if (!found) {
|
||||
problems.add("Imported auto-configuration '%s' was not found".formatted(importedClassName));
|
||||
}
|
||||
|
||||
}
|
||||
List<String> sortedValues = new ArrayList<>(autoConfigureImports.imports);
|
||||
Collections.sort(sortedValues, (i1, i2) -> {
|
||||
boolean imported1 = i1.startsWith("optional:");
|
||||
boolean imported2 = i2.startsWith("optional:");
|
||||
int comparison = Boolean.compare(imported1, imported2);
|
||||
if (comparison != 0) {
|
||||
return comparison;
|
||||
}
|
||||
return i1.compareTo(i2);
|
||||
});
|
||||
if (!sortedValues.equals(autoConfigureImports.imports)) {
|
||||
File sortedOutputFile = getOutputDirectory().file("sorted-" + autoConfigureImports.fileName)
|
||||
.get()
|
||||
.getAsFile();
|
||||
writeString(sortedOutputFile, sortedValues.stream().collect(Collectors.joining(System.lineSeparator()))
|
||||
+ System.lineSeparator());
|
||||
problems.add(
|
||||
"Entries should be required then optional, each sorted alphabetically (expected content written to '%s')"
|
||||
.formatted(sortedOutputFile.getAbsolutePath()));
|
||||
}
|
||||
if (!problems.isEmpty()) {
|
||||
allProblems.computeIfAbsent(autoConfigureImports.fileName, (unused) -> new ArrayList<>())
|
||||
.addAll(problems);
|
||||
}
|
||||
}
|
||||
File outputFile = getOutputDirectory().file("failure-report.txt").get().getAsFile();
|
||||
writeReport(allProblems, outputFile);
|
||||
if (!allProblems.isEmpty()) {
|
||||
throw new VerificationException(
|
||||
"AutoConfigure….imports checks failed. See '%s' for details".formatted(outputFile));
|
||||
}
|
||||
}
|
||||
|
||||
private List<AutoConfigureImports> loadImports() {
|
||||
return getSource().getFiles().stream().map((file) -> {
|
||||
String fileName = file.getName();
|
||||
String annotationName = fileName.substring(0, fileName.length() - ".imports".length());
|
||||
return new AutoConfigureImports(annotationName, loadImports(file), fileName);
|
||||
}).toList();
|
||||
}
|
||||
|
||||
private List<String> loadImports(File importsFile) {
|
||||
try {
|
||||
return Files.readAllLines(importsFile.toPath());
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private boolean find(String className) {
|
||||
return find(className, (input) -> {
|
||||
});
|
||||
}
|
||||
|
||||
private boolean find(String className, Consumer<InputStream> handler) {
|
||||
for (File root : this.classpath.getFiles()) {
|
||||
String classFilePath = className.replace(".", "/") + ".class";
|
||||
if (root.isDirectory()) {
|
||||
File classFile = new File(root, classFilePath);
|
||||
if (classFile.isFile()) {
|
||||
try (InputStream input = new FileInputStream(classFile)) {
|
||||
handler.accept(input);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else {
|
||||
try (JarFile jar = new JarFile(root)) {
|
||||
ZipEntry entry = jar.getEntry(classFilePath);
|
||||
if (entry != null) {
|
||||
try (InputStream input = jar.getInputStream(entry)) {
|
||||
handler.accept(input);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean correctlyAnnotated(InputStream classFile) {
|
||||
return AutoConfigurationClass.of(classFile) != null;
|
||||
}
|
||||
|
||||
private void writeReport(Map<String, List<String>> allProblems, File outputFile) {
|
||||
outputFile.getParentFile().mkdirs();
|
||||
StringBuilder report = new StringBuilder();
|
||||
if (!allProblems.isEmpty()) {
|
||||
allProblems.forEach((fileName, problems) -> {
|
||||
report.append("Found problems in '%s':%n".formatted(fileName));
|
||||
problems.forEach((problem) -> report.append(" - %s%n".formatted(problem)));
|
||||
});
|
||||
}
|
||||
writeString(outputFile, report.toString());
|
||||
}
|
||||
|
||||
private void writeString(File file, String content) {
|
||||
try {
|
||||
Files.writeString(file.toPath(), content);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new UncheckedIOException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
record AutoConfigureImports(String annotationName, List<String> imports, String fileName) {
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -17,17 +17,14 @@
|
|||
package org.springframework.boot.build.test.autoconfigure;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.Reader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.Enumeration;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.TreeMap;
|
||||
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.Task;
|
||||
|
@ -38,9 +35,9 @@ import org.gradle.api.tasks.OutputFile;
|
|||
import org.gradle.api.tasks.PathSensitive;
|
||||
import org.gradle.api.tasks.PathSensitivity;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
import org.springframework.util.ClassUtils;
|
||||
import org.springframework.util.StringUtils;
|
||||
import org.springframework.boot.build.test.autoconfigure.TestSliceMetadata.TestSlice;
|
||||
|
||||
/**
|
||||
* {@link Task} used to document test slices.
|
||||
|
@ -49,16 +46,16 @@ import org.springframework.util.StringUtils;
|
|||
*/
|
||||
public abstract class DocumentTestSlices extends DefaultTask {
|
||||
|
||||
private FileCollection testSlices;
|
||||
private FileCollection testSliceMetadata;
|
||||
|
||||
@InputFiles
|
||||
@PathSensitive(PathSensitivity.RELATIVE)
|
||||
public FileCollection getTestSlices() {
|
||||
return this.testSlices;
|
||||
return this.testSliceMetadata;
|
||||
}
|
||||
|
||||
public void setTestSlices(FileCollection testSlices) {
|
||||
this.testSlices = testSlices;
|
||||
this.testSliceMetadata = testSlices;
|
||||
}
|
||||
|
||||
@OutputFile
|
||||
|
@ -66,61 +63,42 @@ public abstract class DocumentTestSlices extends DefaultTask {
|
|||
|
||||
@TaskAction
|
||||
void documentTestSlices() throws IOException {
|
||||
Set<TestSlice> testSlices = readTestSlices();
|
||||
Map<String, List<TestSlice>> testSlices = readTestSlices();
|
||||
writeTable(testSlices);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Set<TestSlice> readTestSlices() throws IOException {
|
||||
Set<TestSlice> testSlices = new TreeSet<>();
|
||||
for (File metadataFile : this.testSlices) {
|
||||
Properties metadata = new Properties();
|
||||
try (Reader reader = new FileReader(metadataFile)) {
|
||||
metadata.load(reader);
|
||||
}
|
||||
for (String name : Collections.list((Enumeration<String>) metadata.propertyNames())) {
|
||||
testSlices.add(new TestSlice(name,
|
||||
new TreeSet<>(StringUtils.commaDelimitedListToSet(metadata.getProperty(name)))));
|
||||
}
|
||||
private Map<String, List<TestSlice>> readTestSlices() {
|
||||
Map<String, List<TestSlice>> testSlices = new TreeMap<>();
|
||||
for (File metadataFile : this.testSliceMetadata) {
|
||||
JsonMapper mapper = JsonMapper.builder().build();
|
||||
TestSliceMetadata metadata = mapper.readValue(metadataFile, TestSliceMetadata.class);
|
||||
List<TestSlice> slices = new ArrayList<>(metadata.testSlices());
|
||||
Collections.sort(slices, (s1, s2) -> s1.annotation().compareTo(s2.annotation()));
|
||||
testSlices.put(metadata.module(), slices);
|
||||
}
|
||||
return testSlices;
|
||||
}
|
||||
|
||||
private void writeTable(Set<TestSlice> testSlices) throws IOException {
|
||||
private void writeTable(Map<String, List<TestSlice>> testSlicesByModule) throws IOException {
|
||||
File outputFile = getOutputFile().getAsFile().get();
|
||||
outputFile.getParentFile().mkdirs();
|
||||
try (PrintWriter writer = new PrintWriter(new FileWriter(outputFile))) {
|
||||
writer.println("[cols=\"d,a\"]");
|
||||
writer.println("[cols=\"d,d,a\"]");
|
||||
writer.println("|===");
|
||||
writer.println("| Test slice | Imported auto-configuration");
|
||||
for (TestSlice testSlice : testSlices) {
|
||||
writer.println();
|
||||
writer.printf("| `@%s`%n", testSlice.className);
|
||||
writer.println("| ");
|
||||
for (String importedAutoConfiguration : testSlice.importedAutoConfigurations) {
|
||||
writer.printf("`%s`%n", importedAutoConfiguration);
|
||||
}
|
||||
}
|
||||
writer.println("|Module | Test slice | Imported auto-configuration");
|
||||
testSlicesByModule.forEach((module, testSlices) -> {
|
||||
testSlices.forEach((testSlice) -> {
|
||||
writer.println();
|
||||
writer.printf("| `%s`%n", module);
|
||||
writer.printf("| javadoc:%s[format=annotation]%n", testSlice.annotation());
|
||||
writer.println("| ");
|
||||
for (String importedAutoConfiguration : testSlice.importedAutoConfigurations()) {
|
||||
writer.printf("`%s`%n", importedAutoConfiguration);
|
||||
}
|
||||
});
|
||||
});
|
||||
writer.println("|===");
|
||||
}
|
||||
}
|
||||
|
||||
private static final class TestSlice implements Comparable<TestSlice> {
|
||||
|
||||
private final String className;
|
||||
|
||||
private final SortedSet<String> importedAutoConfigurations;
|
||||
|
||||
private TestSlice(String className, SortedSet<String> importedAutoConfigurations) {
|
||||
this.className = ClassUtils.getShortName(className);
|
||||
this.importedAutoConfigurations = importedAutoConfigurations;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int compareTo(TestSlice other) {
|
||||
return this.className.compareTo(other.className);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,238 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.test.autoconfigure;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.Task;
|
||||
import org.gradle.api.file.FileCollection;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.model.ObjectFactory;
|
||||
import org.gradle.api.tasks.Classpath;
|
||||
import org.gradle.api.tasks.InputFiles;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
import org.gradle.api.tasks.PathSensitive;
|
||||
import org.gradle.api.tasks.PathSensitivity;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
import org.springframework.boot.build.test.autoconfigure.TestSliceMetadata.TestSlice;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
* A {@link Task} for generating metadata describing a project's test slices.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public abstract class GenerateTestSliceMetadata extends DefaultTask {
|
||||
|
||||
private final ObjectFactory objectFactory;
|
||||
|
||||
private FileCollection classpath;
|
||||
|
||||
private FileCollection importsFiles;
|
||||
|
||||
private FileCollection classesDirs;
|
||||
|
||||
@Inject
|
||||
public GenerateTestSliceMetadata(ObjectFactory objectFactory) {
|
||||
this.objectFactory = objectFactory;
|
||||
}
|
||||
|
||||
public void setSourceSet(SourceSet sourceSet) {
|
||||
this.classpath = sourceSet.getRuntimeClasspath();
|
||||
this.importsFiles = this.objectFactory.fileTree()
|
||||
.from(new File(sourceSet.getOutput().getResourcesDir(), "META-INF/spring"));
|
||||
this.importsFiles.filter((file) -> file.getName().endsWith(".imports"));
|
||||
getSpringFactories().set(new File(sourceSet.getOutput().getResourcesDir(), "META-INF/spring.factories"));
|
||||
this.classesDirs = sourceSet.getOutput().getClassesDirs();
|
||||
}
|
||||
|
||||
@OutputFile
|
||||
public abstract RegularFileProperty getOutputFile();
|
||||
|
||||
@InputFiles
|
||||
@PathSensitive(PathSensitivity.RELATIVE)
|
||||
abstract RegularFileProperty getSpringFactories();
|
||||
|
||||
@Classpath
|
||||
FileCollection getClasspath() {
|
||||
return this.classpath;
|
||||
}
|
||||
|
||||
@InputFiles
|
||||
@PathSensitive(PathSensitivity.RELATIVE)
|
||||
FileCollection getImportFiles() {
|
||||
return this.importsFiles;
|
||||
}
|
||||
|
||||
@Classpath
|
||||
FileCollection getClassesDirs() {
|
||||
return this.classesDirs;
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void generateTestSliceMetadata() throws IOException {
|
||||
TestSliceMetadata metadata = readTestSlices();
|
||||
File outputFile = getOutputFile().getAsFile().get();
|
||||
outputFile.getParentFile().mkdirs();
|
||||
metadata.writeTo(outputFile);
|
||||
}
|
||||
|
||||
private TestSliceMetadata readTestSlices() throws IOException {
|
||||
List<TestSlice> testSlices = new ArrayList<>();
|
||||
try (URLClassLoader classLoader = new URLClassLoader(
|
||||
StreamSupport.stream(this.classpath.spliterator(), false).map(this::toURL).toArray(URL[]::new))) {
|
||||
MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory(classLoader);
|
||||
Properties springFactories = readSpringFactories(getSpringFactories().getAsFile().getOrNull());
|
||||
readImportsFiles(springFactories, this.importsFiles);
|
||||
for (File classesDir : this.classesDirs) {
|
||||
testSlices.addAll(readTestSlices(classesDir, metadataReaderFactory, springFactories));
|
||||
}
|
||||
}
|
||||
return new TestSliceMetadata(getProject().getName(), testSlices);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the given imports files and puts them in springFactories. The key is the file
|
||||
* name, the value is the file contents, split by line, delimited with a comma. This
|
||||
* is done to mimic the spring.factories structure.
|
||||
* @param springFactories spring.factories parsed as properties
|
||||
* @param importsFiles the imports files to read
|
||||
*/
|
||||
private void readImportsFiles(Properties springFactories, FileCollection importsFiles) {
|
||||
for (File file : importsFiles.getFiles()) {
|
||||
try {
|
||||
List<String> lines = removeComments(Files.readAllLines(file.toPath()));
|
||||
String fileNameWithoutExtension = file.getName()
|
||||
.substring(0, file.getName().length() - ".imports".length());
|
||||
springFactories.setProperty(fileNameWithoutExtension,
|
||||
StringUtils.collectionToCommaDelimitedString(lines));
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new UncheckedIOException("Failed to read file " + file, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> removeComments(List<String> lines) {
|
||||
List<String> result = new ArrayList<>();
|
||||
for (String line : lines) {
|
||||
int commentIndex = line.indexOf('#');
|
||||
if (commentIndex > -1) {
|
||||
line = line.substring(0, commentIndex);
|
||||
}
|
||||
line = line.trim();
|
||||
if (!line.isEmpty()) {
|
||||
result.add(line);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private URL toURL(File file) {
|
||||
try {
|
||||
return file.toURI().toURL();
|
||||
}
|
||||
catch (MalformedURLException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private Properties readSpringFactories(File file) throws IOException {
|
||||
Properties springFactories = new Properties();
|
||||
if (file.isFile()) {
|
||||
try (Reader in = new FileReader(file)) {
|
||||
springFactories.load(in);
|
||||
}
|
||||
}
|
||||
return springFactories;
|
||||
}
|
||||
|
||||
private List<TestSlice> readTestSlices(File classesDir, MetadataReaderFactory metadataReaderFactory,
|
||||
Properties springFactories) throws IOException {
|
||||
try (Stream<Path> classes = Files.walk(classesDir.toPath())) {
|
||||
return classes.filter((path) -> path.toString().endsWith("Test.class"))
|
||||
.map((path) -> getMetadataReader(path, metadataReaderFactory))
|
||||
.filter((metadataReader) -> metadataReader.getClassMetadata().isAnnotation())
|
||||
.map((metadataReader) -> readTestSlice(metadataReader, springFactories))
|
||||
.toList();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private MetadataReader getMetadataReader(Path path, MetadataReaderFactory metadataReaderFactory) {
|
||||
try {
|
||||
return metadataReaderFactory.getMetadataReader(new FileSystemResource(path));
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private TestSlice readTestSlice(MetadataReader metadataReader, Properties springFactories) {
|
||||
String annotationName = metadataReader.getClassMetadata().getClassName();
|
||||
List<String> importedAutoConfiguration = getImportedAutoConfiguration(springFactories,
|
||||
metadataReader.getAnnotationMetadata());
|
||||
return new TestSlice(annotationName, importedAutoConfiguration);
|
||||
}
|
||||
|
||||
private List<String> getImportedAutoConfiguration(Properties springFactories,
|
||||
AnnotationMetadata annotationMetadata) {
|
||||
Stream<String> importers = findMetaImporters(annotationMetadata);
|
||||
if (annotationMetadata.isAnnotated("org.springframework.boot.autoconfigure.ImportAutoConfiguration")) {
|
||||
importers = Stream.concat(importers, Stream.of(annotationMetadata.getClassName()));
|
||||
}
|
||||
return importers
|
||||
.flatMap((importer) -> StringUtils.commaDelimitedListToSet(springFactories.getProperty(importer)).stream())
|
||||
.toList();
|
||||
}
|
||||
|
||||
private Stream<String> findMetaImporters(AnnotationMetadata annotationMetadata) {
|
||||
return annotationMetadata.getAnnotationTypes()
|
||||
.stream()
|
||||
.filter((annotationType) -> isAutoConfigurationImporter(annotationType, annotationMetadata));
|
||||
}
|
||||
|
||||
private boolean isAutoConfigurationImporter(String annotationType, AnnotationMetadata metadata) {
|
||||
return metadata.getMetaAnnotationTypes(annotationType)
|
||||
.contains("org.springframework.boot.autoconfigure.ImportAutoConfiguration");
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.test.autoconfigure;
|
||||
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.file.ConfigurableFileCollection;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.plugins.JavaPluginExtension;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
|
||||
/**
|
||||
* {@link Plugin} for projects that define test auto-configuration. When the
|
||||
* {@link JavaPlugin} is applied it:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Add checks to ensure AutoConfigure*.import files and related annotations are
|
||||
* correct</li>
|
||||
* </ul>
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class TestAutoConfigurationPlugin implements Plugin<Project> {
|
||||
|
||||
@Override
|
||||
public void apply(Project target) {
|
||||
target.getPlugins().withType(JavaPlugin.class, (plugin) -> {
|
||||
target.getTasks().register("checkAutoConfigureImports", CheckAutoConfigureImports.class, (task) -> {
|
||||
SourceSet mainSourceSet = target.getExtensions()
|
||||
.getByType(JavaPluginExtension.class)
|
||||
.getSourceSets()
|
||||
.getByName(SourceSet.MAIN_SOURCE_SET_NAME);
|
||||
task.setSource(mainSourceSet.getResources());
|
||||
ConfigurableFileCollection classpath = target.files(mainSourceSet.getRuntimeClasspath(),
|
||||
target.getConfigurations().getByName(mainSourceSet.getRuntimeClasspathConfigurationName()));
|
||||
task.setClasspath(classpath);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
}
|
|
@ -17,229 +17,30 @@
|
|||
package org.springframework.boot.build.test.autoconfigure;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.io.FileWriter;
|
||||
import java.io.IOException;
|
||||
import java.io.Reader;
|
||||
import java.io.UncheckedIOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Properties;
|
||||
import java.util.SortedSet;
|
||||
import java.util.TreeSet;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import org.gradle.api.DefaultTask;
|
||||
import org.gradle.api.Task;
|
||||
import org.gradle.api.artifacts.Configuration;
|
||||
import org.gradle.api.file.FileCollection;
|
||||
import org.gradle.api.file.RegularFileProperty;
|
||||
import org.gradle.api.model.ObjectFactory;
|
||||
import org.gradle.api.tasks.Classpath;
|
||||
import org.gradle.api.tasks.InputFile;
|
||||
import org.gradle.api.tasks.InputFiles;
|
||||
import org.gradle.api.tasks.OutputFile;
|
||||
import org.gradle.api.tasks.PathSensitive;
|
||||
import org.gradle.api.tasks.PathSensitivity;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.gradle.api.tasks.TaskAction;
|
||||
|
||||
import org.springframework.core.CollectionFactory;
|
||||
import org.springframework.core.io.FileSystemResource;
|
||||
import org.springframework.core.type.AnnotationMetadata;
|
||||
import org.springframework.core.type.classreading.MetadataReader;
|
||||
import org.springframework.core.type.classreading.MetadataReaderFactory;
|
||||
import org.springframework.core.type.classreading.SimpleMetadataReaderFactory;
|
||||
import org.springframework.util.StringUtils;
|
||||
import tools.jackson.databind.SerializationFeature;
|
||||
import tools.jackson.databind.json.JsonMapper;
|
||||
|
||||
/**
|
||||
* A {@link Task} for generating metadata describing a project's test slices.
|
||||
* Metadata describing a module's test slices.
|
||||
*
|
||||
* @param module the module's name
|
||||
* @param testSlices the module's test slices
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public abstract class TestSliceMetadata extends DefaultTask {
|
||||
record TestSliceMetadata(String module, List<TestSlice> testSlices) {
|
||||
|
||||
private final ObjectFactory objectFactory;
|
||||
|
||||
private FileCollection classpath;
|
||||
|
||||
private FileCollection importsFiles;
|
||||
|
||||
private FileCollection classesDirs;
|
||||
|
||||
@Inject
|
||||
public TestSliceMetadata(ObjectFactory objectFactory) {
|
||||
this.objectFactory = objectFactory;
|
||||
Configuration testSliceMetadata = getProject().getConfigurations().maybeCreate("testSliceMetadata");
|
||||
getProject().afterEvaluate((evaluated) -> evaluated.getArtifacts()
|
||||
.add(testSliceMetadata.getName(), getOutputFile(), (artifact) -> artifact.builtBy(this)));
|
||||
static TestSliceMetadata readFrom(File file) {
|
||||
return JsonMapper.builder().build().readValue(file, TestSliceMetadata.class);
|
||||
}
|
||||
|
||||
public void setSourceSet(SourceSet sourceSet) {
|
||||
this.classpath = sourceSet.getRuntimeClasspath();
|
||||
this.importsFiles = this.objectFactory.fileTree()
|
||||
.from(new File(sourceSet.getOutput().getResourcesDir(), "META-INF/spring"));
|
||||
this.importsFiles.filter((file) -> file.getName().endsWith(".imports"));
|
||||
getSpringFactories().set(new File(sourceSet.getOutput().getResourcesDir(), "META-INF/spring.factories"));
|
||||
this.classesDirs = sourceSet.getOutput().getClassesDirs();
|
||||
void writeTo(File file) {
|
||||
JsonMapper.builder().enable(SerializationFeature.INDENT_OUTPUT).build().writeValue(file, this);
|
||||
}
|
||||
|
||||
@OutputFile
|
||||
public abstract RegularFileProperty getOutputFile();
|
||||
record TestSlice(String annotation, List<String> importedAutoConfigurations) {
|
||||
|
||||
@InputFile
|
||||
@PathSensitive(PathSensitivity.RELATIVE)
|
||||
abstract RegularFileProperty getSpringFactories();
|
||||
|
||||
@Classpath
|
||||
FileCollection getClasspath() {
|
||||
return this.classpath;
|
||||
}
|
||||
|
||||
@InputFiles
|
||||
@PathSensitive(PathSensitivity.RELATIVE)
|
||||
FileCollection getImportFiles() {
|
||||
return this.importsFiles;
|
||||
}
|
||||
|
||||
@Classpath
|
||||
FileCollection getClassesDirs() {
|
||||
return this.classesDirs;
|
||||
}
|
||||
|
||||
@TaskAction
|
||||
void documentTestSlices() throws IOException {
|
||||
Properties testSlices = readTestSlices();
|
||||
File outputFile = getOutputFile().getAsFile().get();
|
||||
outputFile.getParentFile().mkdirs();
|
||||
try (FileWriter writer = new FileWriter(outputFile)) {
|
||||
testSlices.store(writer, null);
|
||||
}
|
||||
}
|
||||
|
||||
private Properties readTestSlices() throws IOException {
|
||||
Properties testSlices = CollectionFactory.createSortedProperties(true);
|
||||
try (URLClassLoader classLoader = new URLClassLoader(
|
||||
StreamSupport.stream(this.classpath.spliterator(), false).map(this::toURL).toArray(URL[]::new))) {
|
||||
MetadataReaderFactory metadataReaderFactory = new SimpleMetadataReaderFactory(classLoader);
|
||||
Properties springFactories = readSpringFactories(getSpringFactories().getAsFile().get());
|
||||
readImportsFiles(springFactories, this.importsFiles);
|
||||
for (File classesDir : this.classesDirs) {
|
||||
addTestSlices(testSlices, classesDir, metadataReaderFactory, springFactories);
|
||||
}
|
||||
}
|
||||
return testSlices;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the given imports files and puts them in springFactories. The key is the file
|
||||
* name, the value is the file contents, split by line, delimited with a comma. This
|
||||
* is done to mimic the spring.factories structure.
|
||||
* @param springFactories spring.factories parsed as properties
|
||||
* @param importsFiles the imports files to read
|
||||
*/
|
||||
private void readImportsFiles(Properties springFactories, FileCollection importsFiles) {
|
||||
for (File file : importsFiles.getFiles()) {
|
||||
try {
|
||||
List<String> lines = removeComments(Files.readAllLines(file.toPath()));
|
||||
String fileNameWithoutExtension = file.getName()
|
||||
.substring(0, file.getName().length() - ".imports".length());
|
||||
springFactories.setProperty(fileNameWithoutExtension,
|
||||
StringUtils.collectionToCommaDelimitedString(lines));
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new UncheckedIOException("Failed to read file " + file, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private List<String> removeComments(List<String> lines) {
|
||||
List<String> result = new ArrayList<>();
|
||||
for (String line : lines) {
|
||||
int commentIndex = line.indexOf('#');
|
||||
if (commentIndex > -1) {
|
||||
line = line.substring(0, commentIndex);
|
||||
}
|
||||
line = line.trim();
|
||||
if (!line.isEmpty()) {
|
||||
result.add(line);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private URL toURL(File file) {
|
||||
try {
|
||||
return file.toURI().toURL();
|
||||
}
|
||||
catch (MalformedURLException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private Properties readSpringFactories(File file) throws IOException {
|
||||
Properties springFactories = new Properties();
|
||||
try (Reader in = new FileReader(file)) {
|
||||
springFactories.load(in);
|
||||
}
|
||||
return springFactories;
|
||||
}
|
||||
|
||||
private void addTestSlices(Properties testSlices, File classesDir, MetadataReaderFactory metadataReaderFactory,
|
||||
Properties springFactories) throws IOException {
|
||||
try (Stream<Path> classes = Files.walk(classesDir.toPath())) {
|
||||
classes.filter((path) -> path.toString().endsWith("Test.class"))
|
||||
.map((path) -> getMetadataReader(path, metadataReaderFactory))
|
||||
.filter((metadataReader) -> metadataReader.getClassMetadata().isAnnotation())
|
||||
.forEach((metadataReader) -> addTestSlice(testSlices, springFactories, metadataReader));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private MetadataReader getMetadataReader(Path path, MetadataReaderFactory metadataReaderFactory) {
|
||||
try {
|
||||
return metadataReaderFactory.getMetadataReader(new FileSystemResource(path));
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new RuntimeException(ex);
|
||||
}
|
||||
}
|
||||
|
||||
private void addTestSlice(Properties testSlices, Properties springFactories, MetadataReader metadataReader) {
|
||||
testSlices.setProperty(metadataReader.getClassMetadata().getClassName(),
|
||||
StringUtils.collectionToCommaDelimitedString(
|
||||
getImportedAutoConfiguration(springFactories, metadataReader.getAnnotationMetadata())));
|
||||
}
|
||||
|
||||
private SortedSet<String> getImportedAutoConfiguration(Properties springFactories,
|
||||
AnnotationMetadata annotationMetadata) {
|
||||
Stream<String> importers = findMetaImporters(annotationMetadata);
|
||||
if (annotationMetadata.isAnnotated("org.springframework.boot.autoconfigure.ImportAutoConfiguration")) {
|
||||
importers = Stream.concat(importers, Stream.of(annotationMetadata.getClassName()));
|
||||
}
|
||||
return importers
|
||||
.flatMap((importer) -> StringUtils.commaDelimitedListToSet(springFactories.getProperty(importer)).stream())
|
||||
.collect(Collectors.toCollection(TreeSet::new));
|
||||
}
|
||||
|
||||
private Stream<String> findMetaImporters(AnnotationMetadata annotationMetadata) {
|
||||
return annotationMetadata.getAnnotationTypes()
|
||||
.stream()
|
||||
.filter((annotationType) -> isAutoConfigurationImporter(annotationType, annotationMetadata));
|
||||
}
|
||||
|
||||
private boolean isAutoConfigurationImporter(String annotationType, AnnotationMetadata metadata) {
|
||||
return metadata.getMetaAnnotationTypes(annotationType)
|
||||
.contains("org.springframework.boot.autoconfigure.ImportAutoConfiguration");
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,78 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.test.autoconfigure;
|
||||
|
||||
import org.gradle.api.Plugin;
|
||||
import org.gradle.api.Project;
|
||||
import org.gradle.api.attributes.Category;
|
||||
import org.gradle.api.attributes.Usage;
|
||||
import org.gradle.api.plugins.JavaPlugin;
|
||||
import org.gradle.api.plugins.JavaPluginExtension;
|
||||
import org.gradle.api.plugins.PluginContainer;
|
||||
import org.gradle.api.tasks.SourceSet;
|
||||
import org.gradle.api.tasks.TaskProvider;
|
||||
|
||||
/**
|
||||
* {@link Plugin} for projects that define one or more test slices. When applied, it:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Applies the {@link TestAutoConfigurationPlugin}
|
||||
* </ul>
|
||||
* Additionally, when the {@link JavaPlugin} is applied it:
|
||||
*
|
||||
* <ul>
|
||||
* <li>Defines a task that produces metadata describing the test slices. The metadata is
|
||||
* made available as an artifact in the {@code testSliceMetadata} configuration
|
||||
* </ul>
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class TestSlicePlugin implements Plugin<Project> {
|
||||
|
||||
private static final String TEST_SLICE_METADATA_CONFIGURATION_NAME = "testSliceMetadata";
|
||||
|
||||
@Override
|
||||
public void apply(Project target) {
|
||||
PluginContainer plugins = target.getPlugins();
|
||||
plugins.apply(TestAutoConfigurationPlugin.class);
|
||||
plugins.withType(JavaPlugin.class, (plugin) -> {
|
||||
TaskProvider<GenerateTestSliceMetadata> generateTestSliceMetadata = target.getTasks()
|
||||
.register("generateTestSliceMetadata", GenerateTestSliceMetadata.class, (task) -> {
|
||||
SourceSet mainSourceSet = target.getExtensions()
|
||||
.getByType(JavaPluginExtension.class)
|
||||
.getSourceSets()
|
||||
.getByName(SourceSet.MAIN_SOURCE_SET_NAME);
|
||||
task.setSourceSet(mainSourceSet);
|
||||
task.getOutputFile().set(target.getLayout().getBuildDirectory().file("test-slice-metadata.json"));
|
||||
});
|
||||
addMetadataArtifact(target, generateTestSliceMetadata);
|
||||
});
|
||||
}
|
||||
|
||||
private void addMetadataArtifact(Project project, TaskProvider<GenerateTestSliceMetadata> task) {
|
||||
project.getConfigurations().consumable(TEST_SLICE_METADATA_CONFIGURATION_NAME, (configuration) -> {
|
||||
configuration.attributes((attributes) -> {
|
||||
attributes.attribute(Category.CATEGORY_ATTRIBUTE,
|
||||
project.getObjects().named(Category.class, Category.DOCUMENTATION));
|
||||
attributes.attribute(Usage.USAGE_ATTRIBUTE,
|
||||
project.getObjects().named(Usage.class, "test-slice-metadata"));
|
||||
});
|
||||
});
|
||||
project.getArtifacts().add(TEST_SLICE_METADATA_CONFIGURATION_NAME, task);
|
||||
}
|
||||
|
||||
}
|
|
@ -116,6 +116,18 @@ apiref-openjdk=https://docs.oracle.com/en/java/javase/17/docs/api
|
|||
# === Code Links ===
|
||||
|
||||
code-spring-boot=https://github.com/{github-repo}/tree/{github-ref}
|
||||
code-spring-boot-autoconfigure-src={code-spring-boot}/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure
|
||||
code-spring-boot-autoconfigure-src={code-spring-boot}/core/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure
|
||||
code-spring-boot-batch-jdbc-src={code-spring-boot}/module/spring-boot-batch-jdbc/src/main/java/org/springframework/boot/batch/jdbc
|
||||
code-spring-boot-batch-src={code-spring-boot}/module/spring-boot-batch/src/main/java/org/springframework/boot/batch
|
||||
code-spring-boot-freemarker-src={code-spring-boot}/module/spring-boot-freemarker/src/main/java/org/springframework/boot/freemarker
|
||||
code-spring-boot-groovy-templates-src={code-spring-boot}/module/spring-boot-groovy-templates/src/main/java/org/springframework/boot/groovy/template
|
||||
code-spring-boot-hibernate-src={code-spring-boot}/module/spring-boot-hibernate/src/main/java/org/springframework/boot/hibernate
|
||||
code-spring-boot-integration-src={code-spring-boot}/module/spring-boot-integration/src/main/java/org/springframework/boot/integration
|
||||
code-spring-boot-jdbc-src={code-spring-boot}/module/spring-boot-jdbc/src/main/java/org/springframework/boot/jdbc
|
||||
code-spring-boot-jooq-src={code-spring-boot}/module/spring-boot-jooq/src/main/java/org/springframework/boot/jooq
|
||||
code-spring-boot-jpa-src={code-spring-boot}/module/spring-boot-jpa/src/main/java/org/springframework/boot/jpa
|
||||
code-spring-boot-latest=https://github.com/{github-repo}/tree/main
|
||||
code-spring-boot-servlet-src={code-spring-boot}/module/spring-boot-servlet/src/main/java/org/springframework/boot/servlet
|
||||
code-spring-boot-thymeleaf-src={code-spring-boot}/module/spring-boot-thymeleaf/src/main/java/org/springframework/boot/thymeleaf
|
||||
code-spring-boot-webmvc-src={code-spring-boot}/module/spring-boot-webmvc/src/main/java/org/springframework/boot/webmvc
|
||||
|
||||
|
|
|
@ -77,7 +77,9 @@ class ConventionsPluginTests {
|
|||
out.println(" id 'org.springframework.boot.conventions'");
|
||||
out.println("}");
|
||||
out.println("version = '1.2.3'");
|
||||
out.println("sourceCompatibility = '17'");
|
||||
out.println("java {");
|
||||
out.println(" sourceCompatibility = '17'");
|
||||
out.println("}");
|
||||
out.println("description 'Test project for manifest customization'");
|
||||
out.println("jar.archiveFileName = 'test.jar'");
|
||||
}
|
||||
|
@ -107,7 +109,9 @@ class ConventionsPluginTests {
|
|||
out.println(" id 'org.springframework.boot.conventions'");
|
||||
out.println("}");
|
||||
out.println("version = '1.2.3'");
|
||||
out.println("sourceCompatibility = '17'");
|
||||
out.println("java {");
|
||||
out.println(" sourceCompatibility = '17'");
|
||||
out.println("}");
|
||||
out.println("description 'Test'");
|
||||
}
|
||||
runGradle("assemble");
|
||||
|
@ -136,7 +140,9 @@ class ConventionsPluginTests {
|
|||
out.println(" id 'org.springframework.boot.conventions'");
|
||||
out.println("}");
|
||||
out.println("version = '1.2.3'");
|
||||
out.println("sourceCompatibility = '17'");
|
||||
out.println("java {");
|
||||
out.println(" sourceCompatibility = '17'");
|
||||
out.println("}");
|
||||
out.println("description 'Test'");
|
||||
}
|
||||
runGradle("assemble");
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.junit.jupiter.params.ParameterizedTest;
|
|||
import org.junit.jupiter.params.provider.Arguments;
|
||||
import org.junit.jupiter.params.provider.ArgumentsProvider;
|
||||
import org.junit.jupiter.params.provider.ArgumentsSource;
|
||||
import org.junit.jupiter.params.support.ParameterDeclarations;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
|
@ -244,7 +245,8 @@ class DependencyVersionUpgradeTests {
|
|||
static class InputProvider implements ArgumentsProvider {
|
||||
|
||||
@Override
|
||||
public Stream<? extends Arguments> provideArguments(ExtensionContext context) {
|
||||
public Stream<? extends Arguments> provideArguments(ParameterDeclarations parameterDeclarations,
|
||||
ExtensionContext context) {
|
||||
Method testMethod = context.getRequiredTestMethod();
|
||||
Stream<Arguments> artifactVersions = artifactVersions(testMethod)
|
||||
.map((artifactVersion) -> Arguments.of(VersionType.ARTIFACT_VERSION.parse(artifactVersion.current()),
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright 2012-present the original author or authors.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.build.test.autoconfigure;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.io.TempDir;
|
||||
|
||||
import org.springframework.boot.build.test.autoconfigure.TestSliceMetadata.TestSlice;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
||||
/**
|
||||
* Tests for {@link TestSliceMetadata}.
|
||||
*
|
||||
* @author Andy Wilkinson
|
||||
*/
|
||||
public class TestSliceMetadataTests {
|
||||
|
||||
@TempDir
|
||||
private File temp;
|
||||
|
||||
@Test
|
||||
void roundtripJson() {
|
||||
TestSliceMetadata source = new TestSliceMetadata("example",
|
||||
List.of(new TestSlice("ExampleOneTest", List.of("com.example.OneAutoConfiguration")),
|
||||
new TestSlice("ExampleTwoTest", List.of("com.example.TwoAutoConfiguration"))));
|
||||
File metadataFile = new File(this.temp, "metadata.json");
|
||||
source.writeTo(metadataFile);
|
||||
TestSliceMetadata readBack = TestSliceMetadata.readFrom(metadataFile);
|
||||
assertThat(source).isEqualTo(readBack);
|
||||
}
|
||||
|
||||
}
|
|
@ -166,6 +166,7 @@ class TestFailuresPluginIntegrationTests {
|
|||
writer.println("dependencies {");
|
||||
writer.println(" testImplementation 'org.junit.jupiter:junit-jupiter:5.6.0'");
|
||||
writer.println(" testImplementation 'org.assertj:assertj-core:3.11.1'");
|
||||
writer.println(" testRuntimeOnly 'org.junit.platform:junit-platform-launcher:1.6.0'");
|
||||
writer.println("}");
|
||||
writer.println();
|
||||
writer.println("test {");
|
||||
|
|
|
@ -21,11 +21,11 @@ import java.util.function.Consumer;
|
|||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.LogUpdateEvent;
|
||||
import org.springframework.boot.buildpack.platform.docker.TotalProgressEvent;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.VolumeName;
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ import java.util.stream.IntStream;
|
|||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ApiVersion;
|
||||
import org.springframework.boot.buildpack.platform.docker.ApiVersion;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
/**
|
||||
|
|
|
@ -21,11 +21,11 @@ import java.util.function.Consumer;
|
|||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.LogUpdateEvent;
|
||||
import org.springframework.boot.buildpack.platform.docker.TotalProgressEvent;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.VolumeName;
|
||||
|
||||
|
|
|
@ -28,8 +28,8 @@ import java.util.function.Function;
|
|||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||
import org.springframework.boot.buildpack.platform.io.Owner;
|
||||
import org.springframework.boot.buildpack.platform.io.TarArchive;
|
||||
|
|
|
@ -24,6 +24,7 @@ import org.jspecify.annotations.Nullable;
|
|||
|
||||
import org.springframework.boot.buildpack.platform.docker.DockerApi;
|
||||
import org.springframework.boot.buildpack.platform.docker.DockerLog;
|
||||
import org.springframework.boot.buildpack.platform.docker.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.TotalProgressEvent;
|
||||
import org.springframework.boot.buildpack.platform.docker.TotalProgressPullListener;
|
||||
import org.springframework.boot.buildpack.platform.docker.TotalProgressPushListener;
|
||||
|
@ -34,7 +35,6 @@ import org.springframework.boot.buildpack.platform.docker.transport.DockerEngine
|
|||
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageArchive;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||
import org.springframework.boot.buildpack.platform.io.IOBiConsumer;
|
||||
import org.springframework.boot.buildpack.platform.io.TarArchive;
|
||||
|
|
|
@ -28,10 +28,10 @@ import com.sun.jna.Platform;
|
|||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.build.Cache.Bind;
|
||||
import org.springframework.boot.buildpack.platform.docker.ApiVersion;
|
||||
import org.springframework.boot.buildpack.platform.docker.DockerApi;
|
||||
import org.springframework.boot.buildpack.platform.docker.LogUpdateEvent;
|
||||
import org.springframework.boot.buildpack.platform.docker.configuration.ResolvedDockerHost;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ApiVersion;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerConfig;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerContent;
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.buildpack.platform.docker.type;
|
||||
package org.springframework.boot.buildpack.platform.docker;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
@ -28,7 +28,7 @@ import org.springframework.util.Assert;
|
|||
*
|
||||
* @author Phillip Webb
|
||||
* @author Scott Frederick
|
||||
* @since 3.4.0
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public final class ApiVersion {
|
||||
|
|
@ -34,14 +34,12 @@ import org.springframework.boot.buildpack.platform.docker.PushImageUpdateEvent.E
|
|||
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConnectionConfiguration;
|
||||
import org.springframework.boot.buildpack.platform.docker.transport.HttpTransport;
|
||||
import org.springframework.boot.buildpack.platform.docker.transport.HttpTransport.Response;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ApiVersion;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerConfig;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerContent;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerReference;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerStatus;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageArchive;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.VolumeName;
|
||||
import org.springframework.boot.buildpack.platform.io.IOBiConsumer;
|
||||
|
|
|
@ -14,19 +14,20 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.buildpack.platform.docker.type;
|
||||
package org.springframework.boot.buildpack.platform.docker;
|
||||
|
||||
import java.util.Objects;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* A platform specification for a Docker image.
|
||||
*
|
||||
* @author Scott Frederick
|
||||
* @since 3.4.0
|
||||
* @since 4.0.0
|
||||
*/
|
||||
public class ImagePlatform {
|
||||
|
|
@ -20,7 +20,7 @@ import java.util.stream.IntStream;
|
|||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ApiVersion;
|
||||
import org.springframework.boot.buildpack.platform.docker.ApiVersion;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.assertj.core.api.Assertions.assertThatIllegalStateException;
|
||||
|
|
|
@ -37,9 +37,9 @@ import org.junit.jupiter.api.io.TempDir;
|
|||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageName;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||
import org.springframework.boot.buildpack.platform.io.Owner;
|
||||
import org.springframework.boot.buildpack.platform.io.TarArchive;
|
||||
|
|
|
@ -31,6 +31,7 @@ import org.springframework.boot.buildpack.platform.docker.DockerApi.ContainerApi
|
|||
import org.springframework.boot.buildpack.platform.docker.DockerApi.ImageApi;
|
||||
import org.springframework.boot.buildpack.platform.docker.DockerApi.VolumeApi;
|
||||
import org.springframework.boot.buildpack.platform.docker.DockerLog;
|
||||
import org.springframework.boot.buildpack.platform.docker.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.TotalProgressPullListener;
|
||||
import org.springframework.boot.buildpack.platform.docker.configuration.DockerRegistryAuthentication;
|
||||
import org.springframework.boot.buildpack.platform.docker.transport.DockerEngineException;
|
||||
|
@ -39,7 +40,6 @@ import org.springframework.boot.buildpack.platform.docker.type.ContainerReferenc
|
|||
import org.springframework.boot.buildpack.platform.docker.type.ContainerStatus;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageArchive;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||
import org.springframework.boot.buildpack.platform.io.TarArchive;
|
||||
|
||||
|
|
|
@ -41,6 +41,7 @@ import org.springframework.boot.buildpack.platform.docker.DockerApi;
|
|||
import org.springframework.boot.buildpack.platform.docker.DockerApi.ContainerApi;
|
||||
import org.springframework.boot.buildpack.platform.docker.DockerApi.ImageApi;
|
||||
import org.springframework.boot.buildpack.platform.docker.DockerApi.VolumeApi;
|
||||
import org.springframework.boot.buildpack.platform.docker.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.configuration.DockerConnectionConfiguration;
|
||||
import org.springframework.boot.buildpack.platform.docker.configuration.ResolvedDockerHost;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Binding;
|
||||
|
@ -48,7 +49,6 @@ import org.springframework.boot.buildpack.platform.docker.type.ContainerConfig;
|
|||
import org.springframework.boot.buildpack.platform.docker.type.ContainerContent;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerReference;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerStatus;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.VolumeName;
|
||||
import org.springframework.boot.buildpack.platform.io.IOConsumer;
|
||||
|
|
|
@ -25,10 +25,10 @@ import java.util.function.Consumer;
|
|||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.LogUpdateEvent;
|
||||
import org.springframework.boot.buildpack.platform.docker.TotalProgressEvent;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.VolumeName;
|
||||
import org.springframework.util.FileCopyUtils;
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.buildpack.platform.docker.type;
|
||||
package org.springframework.boot.buildpack.platform.docker;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
|
@ -45,14 +45,12 @@ import org.springframework.boot.buildpack.platform.docker.DockerApi.SystemApi;
|
|||
import org.springframework.boot.buildpack.platform.docker.DockerApi.VolumeApi;
|
||||
import org.springframework.boot.buildpack.platform.docker.transport.HttpTransport;
|
||||
import org.springframework.boot.buildpack.platform.docker.transport.HttpTransport.Response;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ApiVersion;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerConfig;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerContent;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerReference;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ContainerStatus;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageArchive;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImagePlatform;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.ImageReference;
|
||||
import org.springframework.boot.buildpack.platform.docker.type.VolumeName;
|
||||
import org.springframework.boot.buildpack.platform.io.Content;
|
||||
|
|
|
@ -14,12 +14,13 @@
|
|||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package org.springframework.boot.buildpack.platform.docker.type;
|
||||
package org.springframework.boot.buildpack.platform.docker;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import org.springframework.boot.buildpack.platform.docker.type.Image;
|
||||
import org.springframework.boot.buildpack.platform.json.AbstractJsonTests;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
|
@ -64,7 +65,7 @@ class ImagePlatformTests extends AbstractJsonTests {
|
|||
}
|
||||
|
||||
private Image getImage() throws IOException {
|
||||
return Image.of(getContent("image.json"));
|
||||
return Image.of(getContent("type/image.json"));
|
||||
}
|
||||
|
||||
}
|
|
@ -79,4 +79,7 @@
|
|||
<suppress files="EntityManagerFactoryBuilder\.java" checks="NoWhitespaceBefore" message="'...' is preceded with whitespace"/>
|
||||
<suppress files="DockerApi\.java" checks="NoWhitespaceBefore" message="'...' is preceded with whitespace"/>
|
||||
<suppress files="InvocationContext\.java" checks="NoWhitespaceBefore" message="'...' is preceded with whitespace"/>
|
||||
<!-- https://github.com/apache/logging-log4j2/issues/2769#issuecomment-3049020222 -->
|
||||
<suppress files="SpringProfileArbiter\.java" checks="SpringMethodVisibility"/>
|
||||
<suppress files="StructuredLogLayout\.java" checks="SpringMethodVisibility"/>
|
||||
</suppressions>
|
||||
|
|
|
@ -6,7 +6,10 @@
|
|||
<module name="SuppressionFilter">
|
||||
<property name="file" value="${config_loc}/checkstyle-suppressions.xml"/>
|
||||
</module>
|
||||
<module name="io.spring.javaformat.checkstyle.SpringChecks" />
|
||||
<module name="io.spring.javaformat.checkstyle.SpringChecks">
|
||||
<property name="avoidStaticImportExcludes"
|
||||
value="org.springframework.boot.autoconfigure.AutoConfigurationImportedCondition.importedAutoConfiguration" />
|
||||
</module>
|
||||
<module name="io.spring.javaformat.checkstyle.check.SpringHeaderCheck">
|
||||
<property name="headerFile" value="${config_loc}/checkstyle-header.txt"/>
|
||||
</module>
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
<subpackage name="ansi">
|
||||
<disallow pkg="org.springframework.boot" exact-match="true"/>
|
||||
</subpackage>
|
||||
<subpackage name="avilability">
|
||||
<subpackage name="availability">
|
||||
<disallow pkg="org.springframework.boot" exact-match="true"/>
|
||||
</subpackage>
|
||||
<subpackage name="bootstrap">
|
||||
|
@ -91,8 +91,6 @@
|
|||
<disallow pkg="jakarta.servlet" />
|
||||
|
||||
<!-- Common -->
|
||||
<subpackage name="client">
|
||||
</subpackage>
|
||||
<subpackage name="context">
|
||||
<allow pkg="org.springframework.context" />
|
||||
<subpackage name="servlet">
|
||||
|
@ -111,6 +109,10 @@
|
|||
<allow pkg="org.springframework.boot.web.servlet" />
|
||||
</subpackage>
|
||||
</subpackage>
|
||||
<subpackage name="client">
|
||||
<allow pkg="jakarta.servlet" />
|
||||
<allow pkg="org.springframework.context" />
|
||||
</subpackage>
|
||||
<subpackage name="context">
|
||||
<allow pkg="org.springframework.context" />
|
||||
</subpackage>
|
||||
|
|
|
@ -19,7 +19,7 @@ style:
|
|||
active: true
|
||||
NewLineAtEndOfFile:
|
||||
active: true
|
||||
UnusedImports:
|
||||
UnusedImport:
|
||||
active: true
|
||||
WildcardImport:
|
||||
active: true
|
||||
|
|
|
@ -136,20 +136,25 @@ def dependenciesOf(String version) {
|
|||
modules += [
|
||||
"spring-boot-jersey"
|
||||
]
|
||||
if (version.equals("4.0.0-M1")) {
|
||||
modules += [
|
||||
"spring-boot-metrics",
|
||||
"spring-boot-observation",
|
||||
"spring-boot-tracing"
|
||||
]
|
||||
}
|
||||
else {
|
||||
modules += [
|
||||
"spring-boot-micrometer-metrics",
|
||||
"spring-boot-micrometer-observation",
|
||||
"spring-boot-micrometer-tracing"
|
||||
]
|
||||
}
|
||||
}
|
||||
if (version.equals("4.0.0-M1")) {
|
||||
modules += [
|
||||
"spring-boot-metrics",
|
||||
"spring-boot-observation",
|
||||
"spring-boot-tracing"
|
||||
]
|
||||
}
|
||||
else {
|
||||
modules += [
|
||||
"spring-boot-micrometer-metrics",
|
||||
"spring-boot-micrometer-observation",
|
||||
"spring-boot-micrometer-tracing"
|
||||
]
|
||||
}
|
||||
if (version.equals("4.0.0-RC1")) {
|
||||
modules += [
|
||||
"spring-boot-batch-jdbc"
|
||||
]
|
||||
}
|
||||
return modules
|
||||
}
|
||||
|
|
|
@ -56,3 +56,7 @@ dependencies {
|
|||
test {
|
||||
jvmArgs += "--add-opens=java.base/java.net=ALL-UNNAMED"
|
||||
}
|
||||
|
||||
tasks.named("compileTestJava") {
|
||||
options.nullability.checking = "tests"
|
||||
}
|
||||
|
|
|
@ -19,6 +19,7 @@ package org.springframework.boot.autoconfigure;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
@ -42,7 +43,7 @@ class AutoConfigurationExcludeFilterTests {
|
|||
|
||||
private static final Class<?> FILTERED = ExampleFilteredAutoConfiguration.class;
|
||||
|
||||
private AnnotationConfigApplicationContext context;
|
||||
private @Nullable AnnotationConfigApplicationContext context;
|
||||
|
||||
@AfterEach
|
||||
void cleanUp() {
|
||||
|
@ -56,7 +57,10 @@ class AutoConfigurationExcludeFilterTests {
|
|||
this.context = new AnnotationConfigApplicationContext(Config.class);
|
||||
assertThat(this.context.getBeansOfType(String.class)).hasSize(1);
|
||||
assertThat(this.context.getBean(String.class)).isEqualTo("test");
|
||||
assertThatExceptionOfType(NoSuchBeanDefinitionException.class).isThrownBy(() -> this.context.getBean(FILTERED));
|
||||
assertThatExceptionOfType(NoSuchBeanDefinitionException.class).isThrownBy(() -> {
|
||||
assertThat(this.context).isNotNull();
|
||||
this.context.getBean(FILTERED);
|
||||
});
|
||||
}
|
||||
|
||||
@Configuration(proxyBeanMethods = false)
|
||||
|
|
|
@ -28,6 +28,7 @@ import java.util.Set;
|
|||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
@ -83,39 +84,35 @@ class AutoConfigurationImportSelectorTests {
|
|||
void importsAreSelectedWhenUsingEnableAutoConfiguration() {
|
||||
String[] imports = selectImports(BasicEnableAutoConfiguration.class);
|
||||
assertThat(imports).hasSameSizeAs(getAutoConfigurationClassNames());
|
||||
assertThat(this.importSelector.getLastEvent().getExclusions()).isEmpty();
|
||||
assertThat(getLastEvent().getExclusions()).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void classExclusionsAreApplied() {
|
||||
String[] imports = selectImports(EnableAutoConfigurationWithClassExclusions.class);
|
||||
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 1);
|
||||
assertThat(this.importSelector.getLastEvent().getExclusions())
|
||||
.contains(SeventhAutoConfiguration.class.getName());
|
||||
assertThat(getLastEvent().getExclusions()).contains(SeventhAutoConfiguration.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void classExclusionsAreAppliedWhenUsingSpringBootApplication() {
|
||||
String[] imports = selectImports(SpringBootApplicationWithClassExclusions.class);
|
||||
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 1);
|
||||
assertThat(this.importSelector.getLastEvent().getExclusions())
|
||||
.contains(SeventhAutoConfiguration.class.getName());
|
||||
assertThat(getLastEvent().getExclusions()).contains(SeventhAutoConfiguration.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
void classNamesExclusionsAreApplied() {
|
||||
String[] imports = selectImports(EnableAutoConfigurationWithClassNameExclusions.class);
|
||||
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 1);
|
||||
assertThat(this.importSelector.getLastEvent().getExclusions())
|
||||
.contains("com.example.one.FirstAutoConfiguration");
|
||||
assertThat(getLastEvent().getExclusions()).contains("com.example.one.FirstAutoConfiguration");
|
||||
}
|
||||
|
||||
@Test
|
||||
void classNamesExclusionsAreAppliedWhenUsingSpringBootApplication() {
|
||||
String[] imports = selectImports(SpringBootApplicationWithClassNameExclusions.class);
|
||||
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 1);
|
||||
assertThat(this.importSelector.getLastEvent().getExclusions())
|
||||
.contains("com.example.three.ThirdAutoConfiguration");
|
||||
assertThat(getLastEvent().getExclusions()).contains("com.example.three.ThirdAutoConfiguration");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -123,8 +120,7 @@ class AutoConfigurationImportSelectorTests {
|
|||
this.environment.setProperty("spring.autoconfigure.exclude", "com.example.three.ThirdAutoConfiguration");
|
||||
String[] imports = selectImports(BasicEnableAutoConfiguration.class);
|
||||
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 1);
|
||||
assertThat(this.importSelector.getLastEvent().getExclusions())
|
||||
.contains("com.example.three.ThirdAutoConfiguration");
|
||||
assertThat(getLastEvent().getExclusions()).contains("com.example.three.ThirdAutoConfiguration");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -151,8 +147,8 @@ class AutoConfigurationImportSelectorTests {
|
|||
private void testSeveralPropertyExclusionsAreApplied() {
|
||||
String[] imports = selectImports(BasicEnableAutoConfiguration.class);
|
||||
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 2);
|
||||
assertThat(this.importSelector.getLastEvent().getExclusions())
|
||||
.contains("com.example.two.SecondAutoConfiguration", "com.example.four.FourthAutoConfiguration");
|
||||
assertThat(getLastEvent().getExclusions()).contains("com.example.two.SecondAutoConfiguration",
|
||||
"com.example.four.FourthAutoConfiguration");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -160,9 +156,8 @@ class AutoConfigurationImportSelectorTests {
|
|||
this.environment.setProperty("spring.autoconfigure.exclude", "com.example.one.FirstAutoConfiguration");
|
||||
String[] imports = selectImports(EnableAutoConfigurationWithClassAndClassNameExclusions.class);
|
||||
assertThat(imports).hasSize(getAutoConfigurationClassNames().size() - 3);
|
||||
assertThat(this.importSelector.getLastEvent().getExclusions()).contains(
|
||||
"com.example.one.FirstAutoConfiguration", "com.example.five.FifthAutoConfiguration",
|
||||
SeventhAutoConfiguration.class.getName());
|
||||
assertThat(getLastEvent().getExclusions()).contains("com.example.one.FirstAutoConfiguration",
|
||||
"com.example.five.FifthAutoConfiguration", SeventhAutoConfiguration.class.getName());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -202,7 +197,7 @@ class AutoConfigurationImportSelectorTests {
|
|||
this.environment.setProperty("spring.autoconfigure.exclude",
|
||||
"org.springframework.boot.autoconfigure.DoesNotExist2");
|
||||
selectImports(EnableAutoConfigurationWithAbsentClassNameExclude.class);
|
||||
assertThat(this.importSelector.getLastEvent().getExclusions()).containsExactlyInAnyOrder(
|
||||
assertThat(getLastEvent().getExclusions()).containsExactlyInAnyOrder(
|
||||
"org.springframework.boot.autoconfigure.DoesNotExist1",
|
||||
"org.springframework.boot.autoconfigure.DoesNotExist2");
|
||||
}
|
||||
|
@ -269,11 +264,17 @@ class AutoConfigurationImportSelectorTests {
|
|||
importSelector.setBeanClassLoader(Thread.currentThread().getContextClassLoader());
|
||||
}
|
||||
|
||||
private AutoConfigurationImportEvent getLastEvent() {
|
||||
AutoConfigurationImportEvent result = this.importSelector.getLastEvent();
|
||||
assertThat(result).isNotNull();
|
||||
return result;
|
||||
}
|
||||
|
||||
private final class TestAutoConfigurationImportSelector extends AutoConfigurationImportSelector {
|
||||
|
||||
private AutoConfigurationImportEvent lastEvent;
|
||||
private @Nullable AutoConfigurationImportEvent lastEvent;
|
||||
|
||||
TestAutoConfigurationImportSelector(Class<?> autoConfigurationAnnotation) {
|
||||
TestAutoConfigurationImportSelector(@Nullable Class<?> autoConfigurationAnnotation) {
|
||||
super(autoConfigurationAnnotation);
|
||||
}
|
||||
|
||||
|
@ -287,7 +288,7 @@ class AutoConfigurationImportSelectorTests {
|
|||
return Collections.singletonList((event) -> this.lastEvent = event);
|
||||
}
|
||||
|
||||
AutoConfigurationImportEvent getLastEvent() {
|
||||
@Nullable AutoConfigurationImportEvent getLastEvent() {
|
||||
return this.lastEvent;
|
||||
}
|
||||
|
||||
|
@ -297,6 +298,7 @@ class AutoConfigurationImportSelectorTests {
|
|||
|
||||
private final Set<String> nonMatching = new HashSet<>();
|
||||
|
||||
@SuppressWarnings("NullAway.Init")
|
||||
private BeanFactory beanFactory;
|
||||
|
||||
TestAutoConfigurationImportFilter(String[] configurations, int... nonMatching) {
|
||||
|
@ -306,7 +308,8 @@ class AutoConfigurationImportSelectorTests {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean[] match(String[] autoConfigurationClasses, AutoConfigurationMetadata autoConfigurationMetadata) {
|
||||
public boolean[] match(@Nullable String[] autoConfigurationClasses,
|
||||
AutoConfigurationMetadata autoConfigurationMetadata) {
|
||||
boolean[] result = new boolean[autoConfigurationClasses.length];
|
||||
for (int i = 0; i < result.length; i++) {
|
||||
result[i] = !this.nonMatching.contains(autoConfigurationClasses[i]);
|
||||
|
|
|
@ -26,6 +26,7 @@ import java.util.Properties;
|
|||
import java.util.Set;
|
||||
import java.util.function.UnaryOperator;
|
||||
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
|
@ -251,10 +252,14 @@ class AutoConfigurationSorterTests {
|
|||
}
|
||||
|
||||
private void addAutoConfigureAfter(Properties properties, String className, AnnotationMetadata annotationMetadata) {
|
||||
Map<String, Object> autoConfigureAfter = annotationMetadata
|
||||
Map<String, @Nullable Object> autoConfigureAfter = annotationMetadata
|
||||
.getAnnotationAttributes(AutoConfigureAfter.class.getName(), true);
|
||||
if (autoConfigureAfter != null) {
|
||||
String value = merge((String[]) autoConfigureAfter.get("value"), (String[]) autoConfigureAfter.get("name"));
|
||||
String[] valueAttribute = (String[]) autoConfigureAfter.get("value");
|
||||
String[] nameAttribute = (String[]) autoConfigureAfter.get("name");
|
||||
assertThat(valueAttribute).isNotNull();
|
||||
assertThat(nameAttribute).isNotNull();
|
||||
String value = merge(valueAttribute, nameAttribute);
|
||||
if (!value.isEmpty()) {
|
||||
properties.put(className + ".AutoConfigureAfter", value);
|
||||
}
|
||||
|
@ -263,11 +268,14 @@ class AutoConfigurationSorterTests {
|
|||
|
||||
private void addAutoConfigureBefore(Properties properties, String className,
|
||||
AnnotationMetadata annotationMetadata) {
|
||||
Map<String, Object> autoConfigureBefore = annotationMetadata
|
||||
Map<String, @Nullable Object> autoConfigureBefore = annotationMetadata
|
||||
.getAnnotationAttributes(AutoConfigureBefore.class.getName(), true);
|
||||
if (autoConfigureBefore != null) {
|
||||
String value = merge((String[]) autoConfigureBefore.get("value"),
|
||||
(String[]) autoConfigureBefore.get("name"));
|
||||
String[] valueAttribute = (String[]) autoConfigureBefore.get("value");
|
||||
String[] nameAttribute = (String[]) autoConfigureBefore.get("name");
|
||||
assertThat(valueAttribute).isNotNull();
|
||||
assertThat(nameAttribute).isNotNull();
|
||||
String value = merge(valueAttribute, nameAttribute);
|
||||
if (!value.isEmpty()) {
|
||||
properties.put(className + ".AutoConfigureBefore", value);
|
||||
}
|
||||
|
@ -275,7 +283,7 @@ class AutoConfigurationSorterTests {
|
|||
}
|
||||
|
||||
private void addAutoConfigureOrder(Properties properties, String className, AnnotationMetadata annotationMetadata) {
|
||||
Map<String, Object> autoConfigureOrder = annotationMetadata
|
||||
Map<String, @Nullable Object> autoConfigureOrder = annotationMetadata
|
||||
.getAnnotationAttributes(AutoConfigureOrder.class.getName());
|
||||
if (autoConfigureOrder != null) {
|
||||
Integer order = (Integer) autoConfigureOrder.get("order");
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue