mirror of https://github.com/jenkinsci/jenkins.git
Compare commits
261 Commits
jenkins-2.
...
master
| Author | SHA1 | Date |
|---|---|---|
|
|
320a149f76 | |
|
|
d8f229853a | |
|
|
a6e8edc1d4 | |
|
|
205849e0f6 | |
|
|
8f1bf5be69 | |
|
|
005a604d75 | |
|
|
7bc93683f6 | |
|
|
8002bc9b4b | |
|
|
c3b06d5f64 | |
|
|
0ea42364a2 | |
|
|
0166c65acf | |
|
|
0f9e1ff1f9 | |
|
|
cb88428113 | |
|
|
f9b41033b9 | |
|
|
e72d483d46 | |
|
|
b251733b22 | |
|
|
313c6ff590 | |
|
|
25b40a5a0f | |
|
|
d6f1414720 | |
|
|
e5dd70c57b | |
|
|
c41456f3a8 | |
|
|
683acc9b43 | |
|
|
2ef5e40f34 | |
|
|
b3f1b8636b | |
|
|
f66e6167ee | |
|
|
1b3ff8fd10 | |
|
|
18741c1bf0 | |
|
|
1b38672f11 | |
|
|
81955b21ef | |
|
|
68adafca38 | |
|
|
d64432c154 | |
|
|
53cca2985b | |
|
|
c54c0b76d4 | |
|
|
ea15e70e82 | |
|
|
41250abff7 | |
|
|
96424407fd | |
|
|
0d5c550028 | |
|
|
17abe4f60b | |
|
|
45c28429d2 | |
|
|
9f8423c324 | |
|
|
e36b9e5dd0 | |
|
|
bf6b0fa790 | |
|
|
0d022fcb57 | |
|
|
ecd3f883b0 | |
|
|
0dd8222cc7 | |
|
|
b42d07badf | |
|
|
d23ae16fd5 | |
|
|
c24d34eba6 | |
|
|
f9a3156b4c | |
|
|
0d151b8fd9 | |
|
|
5dec84577c | |
|
|
23c6d1b513 | |
|
|
e0a3087426 | |
|
|
8c385f4cc0 | |
|
|
68b2695a90 | |
|
|
bc7ffb8376 | |
|
|
4b08357516 | |
|
|
4702ffe219 | |
|
|
0d5f528442 | |
|
|
72a04cb3a4 | |
|
|
e2dd0466ce | |
|
|
84db49f22a | |
|
|
c4e4ae7635 | |
|
|
7e6c5740ec | |
|
|
ba22ac42b4 | |
|
|
45766b3404 | |
|
|
a65d8b549e | |
|
|
903fcc9ad0 | |
|
|
d582839cdb | |
|
|
7e7093c153 | |
|
|
d107c1c69e | |
|
|
e015b5eda5 | |
|
|
5176dd16ca | |
|
|
62b80a158f | |
|
|
1dcfcea225 | |
|
|
01b479367c | |
|
|
11fdff7b54 | |
|
|
96acb6d8bc | |
|
|
d1faf70731 | |
|
|
d334644dba | |
|
|
c97fecc7ec | |
|
|
bb7d0b5cfb | |
|
|
a2146f3c41 | |
|
|
2b583fb069 | |
|
|
c9eaa5d7aa | |
|
|
389e3df894 | |
|
|
c44796db12 | |
|
|
f9c7d00177 | |
|
|
8fcb74485f | |
|
|
64dd8907c6 | |
|
|
f32456d72d | |
|
|
66ee5c2c62 | |
|
|
d309bd67ac | |
|
|
e8c5532a9b | |
|
|
adacec0947 | |
|
|
5dd67bec8b | |
|
|
e45c6140b8 | |
|
|
fa233a1ba0 | |
|
|
5748da5ecb | |
|
|
1e3b70a999 | |
|
|
22b0b8dc85 | |
|
|
893c4b52b8 | |
|
|
e69069b052 | |
|
|
93c17b07a4 | |
|
|
dc8ee385e0 | |
|
|
17cb4ea041 | |
|
|
e11322769a | |
|
|
96584c3d5a | |
|
|
8e3e7421a9 | |
|
|
be407f2119 | |
|
|
f73f1f034d | |
|
|
d396a24930 | |
|
|
b85a4f959b | |
|
|
88cbd32725 | |
|
|
9ad057fd32 | |
|
|
0e405cc185 | |
|
|
c0bed7aa50 | |
|
|
69ef02102e | |
|
|
0b23eef517 | |
|
|
02e6a9aa61 | |
|
|
903837d94e | |
|
|
ec5c7feb45 | |
|
|
4e76327afe | |
|
|
68e7cdec61 | |
|
|
cb6bc5b1d2 | |
|
|
375220a0c2 | |
|
|
aa51217706 | |
|
|
7bf2969c13 | |
|
|
84b55f3250 | |
|
|
bc629045d2 | |
|
|
0eba2a5684 | |
|
|
b0eafed55e | |
|
|
9f200b11b6 | |
|
|
b1fe658d39 | |
|
|
443160f6c0 | |
|
|
417e7b5f7a | |
|
|
49f837247d | |
|
|
35b6741fcc | |
|
|
69d5a54239 | |
|
|
6ec55c327f | |
|
|
c5b876b582 | |
|
|
6cfd7e6b5e | |
|
|
b0367d4cfb | |
|
|
4c310c5201 | |
|
|
481c7784e6 | |
|
|
f7c1f67dbc | |
|
|
865bff5565 | |
|
|
7beca3db02 | |
|
|
c01b2c4363 | |
|
|
bc8ef8dcca | |
|
|
3bce9e226b | |
|
|
04952cad1c | |
|
|
dd78a6a29c | |
|
|
491be312b0 | |
|
|
349d4fc407 | |
|
|
a68faf9554 | |
|
|
36c17f52f5 | |
|
|
8ced500484 | |
|
|
9238140442 | |
|
|
35e05775f9 | |
|
|
8677d21412 | |
|
|
097a5b2f34 | |
|
|
a7a7a1db2a | |
|
|
55aa5a8a80 | |
|
|
42e259a790 | |
|
|
99d24d4856 | |
|
|
ffae77f94d | |
|
|
1b0b8bfff7 | |
|
|
ebc93434ad | |
|
|
9c79ec92aa | |
|
|
8bb39aef8a | |
|
|
75ed1d18c1 | |
|
|
f1a6d155dd | |
|
|
dd2db990c5 | |
|
|
3f8753817a | |
|
|
6edbcc5ae1 | |
|
|
e7d4e3f4db | |
|
|
271c1489b3 | |
|
|
61f4329500 | |
|
|
f998b55bbd | |
|
|
43bf0ff5f9 | |
|
|
1d8ac5a91f | |
|
|
9657540896 | |
|
|
b2dff282f9 | |
|
|
78c8f15755 | |
|
|
5c034fb2d1 | |
|
|
9e11341372 | |
|
|
668c68604c | |
|
|
b3bd7a6cc2 | |
|
|
7aa344749f | |
|
|
ec972db4d3 | |
|
|
1e7f63b256 | |
|
|
ee6e482c12 | |
|
|
cfebcec2e8 | |
|
|
59691663a5 | |
|
|
aeb95d91e3 | |
|
|
364a955ec9 | |
|
|
ad52f2b908 | |
|
|
f92da7a246 | |
|
|
1c61d50fa6 | |
|
|
7a66756595 | |
|
|
8e2e1ec365 | |
|
|
d420deb74d | |
|
|
05fd38da28 | |
|
|
739cea9679 | |
|
|
7cd0ccbb40 | |
|
|
8c257e5ebb | |
|
|
be904ec7f2 | |
|
|
c23cf20ea2 | |
|
|
1bae0f30f6 | |
|
|
a645acb6ac | |
|
|
4ea4552925 | |
|
|
133bc68195 | |
|
|
02b40b1dd1 | |
|
|
27d2d1166e | |
|
|
cf32065ca6 | |
|
|
791da634b9 | |
|
|
ab342c04e2 | |
|
|
c03fcac9d8 | |
|
|
3f5855c0a8 | |
|
|
32830f9d2c | |
|
|
e1124c1eb0 | |
|
|
5ad08302cd | |
|
|
d01cedafa3 | |
|
|
121e9062b3 | |
|
|
0d3fe0a2d3 | |
|
|
633b9c0975 | |
|
|
c765aa2d7e | |
|
|
45bf79a068 | |
|
|
077acbb3f7 | |
|
|
80cb24beec | |
|
|
de24886700 | |
|
|
ea708724cf | |
|
|
ac4288cd9f | |
|
|
40a12cf136 | |
|
|
4c8ab61c28 | |
|
|
46294a54fb | |
|
|
17a1ca6e51 | |
|
|
69e44255b6 | |
|
|
863a8a46b6 | |
|
|
066f4c2fc2 | |
|
|
88dd0bf930 | |
|
|
d3470113b2 | |
|
|
ef97746eb2 | |
|
|
41d273ccf4 | |
|
|
70e8106888 | |
|
|
39fa1bdd99 | |
|
|
fd37b705b3 | |
|
|
1fbaa7cced | |
|
|
3ef51444c5 | |
|
|
6bdea9f44a | |
|
|
a2d1bce6cc | |
|
|
e5fb48fd04 | |
|
|
c64384a867 | |
|
|
d47192078f | |
|
|
04072f17d1 | |
|
|
ca36dbf98f | |
|
|
486119a10e | |
|
|
c2f47265fe | |
|
|
ae52a30aed | |
|
|
522959977a |
|
|
@ -1,2 +1,4 @@
|
|||
# Whitespace: https://github.com/jenkinsci/jenkins/pull/6149
|
||||
e27b310065b3c036b5fc9d123f1d1d99d3058c00
|
||||
# Reformatted: https://github.com/jenkinsci/jenkins/pull/6863
|
||||
e3fdfa527e4fefb4b37f04c92a2dd87b8a374b75
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ The changelog generator relies on the presence of the upgrade guidelines section
|
|||
- [ ] There is automated testing or an explanation as to why this change has no tests.
|
||||
- [ ] New public classes, fields, and methods are annotated with `@Restricted` or have `@since TODO` Javadocs, as appropriate.
|
||||
- [ ] New deprecations are annotated with `@Deprecated(since = "TODO")` or `@Deprecated(forRemoval = true, since = "TODO")`, if applicable.
|
||||
- [ ] New or substantially changed JavaScript is not defined inline and does not call `eval` to ease future introduction of Content Security Policy (CSP) directives (see [documentation](https://www.jenkins.io/doc/developer/security/csp/)).
|
||||
- [ ] UI changes do not introduce regressions when enforcing the current default rules of [Content Security Policy Plugin](https://plugins.jenkins.io/csp/). In particular, new or substantially changed JavaScript is not defined inline and does not call `eval` to ease future introduction of Content Security Policy (CSP) directives (see [documentation](https://www.jenkins.io/doc/developer/security/csp/)).
|
||||
- [ ] For dependency updates, there are links to external changelogs and, if possible, full differentials.
|
||||
- [ ] For new APIs and extension points, there is a link to at least one consumer.
|
||||
|
||||
|
|
|
|||
|
|
@ -106,16 +106,6 @@
|
|||
],
|
||||
"enabled": false,
|
||||
"description": "maven-metadata.xml is missing for this really old package which is required by renovate"
|
||||
},
|
||||
{
|
||||
"description": "Jackson 2.19.0 causes issues with Kubernetes client. See https://github.com/jenkinsci/bom/pull/5114",
|
||||
"matchManagers": [
|
||||
"maven"
|
||||
],
|
||||
"allowedVersions": "<2.19.0",
|
||||
"matchPackageNames": [
|
||||
"org.jenkins-ci.plugins:jackson2-api"
|
||||
]
|
||||
}
|
||||
],
|
||||
"customManagers": [
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ on:
|
|||
jobs:
|
||||
post:
|
||||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository_owner == 'jenkinsci' }}
|
||||
steps:
|
||||
- name: Post on Discourse
|
||||
uses: roots/discourse-topic-github-release-action@c30dc233349b7c6f24f52fb1c659cc64f13b5474 # v1.0.1
|
||||
|
|
|
|||
|
|
@ -18,24 +18,24 @@ jobs:
|
|||
pull-requests: write # to add label to PR (release-drafter/release-drafter)
|
||||
contents: write # to create a github release (release-drafter/release-drafter)
|
||||
|
||||
if: github.repository_owner == 'jenkinsci'
|
||||
if: ${{ github.repository_owner == 'jenkinsci' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
# Drafts your next Release notes as Pull Requests are merged into "master"
|
||||
- name: Generate GitHub Release Draft
|
||||
id: release-drafter
|
||||
uses: release-drafter/release-drafter@v6
|
||||
uses: release-drafter/release-drafter@b1476f6e6eb133afa41ed8589daba6dc69b4d3f5 # v6.1.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# Generates a YAML changelog file using https://github.com/jenkinsci/jenkins-core-changelog-generator
|
||||
# used by Oleg N in open graph generator experiment for now
|
||||
- name: Generate YAML changelog draft
|
||||
id: jenkins-core-changelog-generator
|
||||
uses: jenkinsci/jenkins-core-changelog-generator@master
|
||||
uses: jenkinsci/core-changelog-generator@feb124ed2262f8586ac4561348436afb965812e1 # v2.2.2
|
||||
env:
|
||||
GITHUB_AUTH: github-actions:${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Upload Changelog YAML
|
||||
uses: actions/upload-artifact@v4
|
||||
uses: actions/upload-artifact@330a01c490aca151604b8cf639adc76d48f6c5d4 # v5.0.0
|
||||
with:
|
||||
name: changelog.yaml
|
||||
path: changelog.yaml
|
||||
|
|
@ -44,17 +44,15 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
if: github.repository_owner == 'jenkinsci'
|
||||
steps:
|
||||
- uses: tibdex/github-app-token@v2
|
||||
- uses: actions/create-github-app-token@67018539274d69449ef7c02e8e71183d1719ab42 # v2.1.4
|
||||
id: generate-token
|
||||
with:
|
||||
app_id: ${{ secrets.JENKINS_CHANGELOG_UPDATER_APP_ID }}
|
||||
installation_retrieval_mode: repository
|
||||
installation_retrieval_payload: jenkins-infra/jenkins.io
|
||||
private_key: ${{ secrets.JENKINS_CHANGELOG_UPDATER_PRIVATE_KEY }}
|
||||
repositories: >-
|
||||
["jenkins.io"]
|
||||
app-id: ${{ secrets.JENKINS_CHANGELOG_UPDATER_APP_ID }}
|
||||
private-key: ${{ secrets.JENKINS_CHANGELOG_UPDATER_PRIVATE_KEY }}
|
||||
owner: jenkins-infra
|
||||
repositories: jenkins.io
|
||||
- name: Check out
|
||||
uses: actions/checkout@v5
|
||||
uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Publish jenkins.io changelog draft
|
||||
|
|
|
|||
|
|
@ -9,11 +9,11 @@ permissions:
|
|||
|
||||
jobs:
|
||||
main:
|
||||
if: github.event.pull_request.user.login != 'dependabot[bot]'
|
||||
if: ${{ github.event.pull_request.user.login != 'dependabot[bot]' && github.repository_owner == 'jenkinsci' }}
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Label conflicting PRs
|
||||
uses: eps1lon/actions-label-merge-conflict@v3.0.3
|
||||
uses: eps1lon/actions-label-merge-conflict@1df065ebe6e3310545d4f4c4e862e43bdca146f0 # v3.0.3
|
||||
with:
|
||||
dirtyLabel: "unresolved-merge-conflict"
|
||||
repoToken: "${{ secrets.GITHUB_TOKEN }}"
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ jobs:
|
|||
steps:
|
||||
- name: Check if PR targets LTS branch
|
||||
if: startsWith(github.event.pull_request.base.ref, 'stable-')
|
||||
uses: actions/github-script@v8
|
||||
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
script: |
|
||||
|
|
|
|||
|
|
@ -9,18 +9,19 @@ permissions:
|
|||
|
||||
jobs:
|
||||
determine-version:
|
||||
if: ${{ github.repository_owner == 'jenkinsci' }}
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
project-version: ${{ steps.set-version.outputs.project-version }}
|
||||
is-lts: ${{ steps.set-version.outputs.is-lts }}
|
||||
is-rc: ${{ steps.set-version.outputs.is-rc }}
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- name: Set up JDK 17
|
||||
uses: actions/setup-java@v5
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
- name: Set up JDK 21
|
||||
uses: actions/setup-java@de5a937a1dc73fbc1a67d7d1aa4bebc1082f3190 #v 5.0.0
|
||||
with:
|
||||
distribution: "temurin"
|
||||
java-version: 17
|
||||
java-version: 21
|
||||
cache: "maven"
|
||||
- name: Set version
|
||||
id: set-version
|
||||
|
|
@ -73,7 +74,7 @@ jobs:
|
|||
wget -q https://get.jenkins.io/${REPO}/${PROJECT_VERSION}/${FILE_NAME}
|
||||
- name: Upload Release Asset
|
||||
id: upload-war
|
||||
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836 # v2.3.3
|
||||
uses: softprops/action-gh-release@5be0e66d93ac7ed76da52eca8bb058f665c3a5fe # v2.4.2
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
|
|
@ -108,7 +109,7 @@ jobs:
|
|||
- name: Upload Release Asset
|
||||
id: upload-deb
|
||||
if: always()
|
||||
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836 # v2.3.3
|
||||
uses: softprops/action-gh-release@5be0e66d93ac7ed76da52eca8bb058f665c3a5fe # v2.4.2
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
|
|
@ -144,7 +145,7 @@ jobs:
|
|||
- name: Upload Release Asset
|
||||
id: upload-rpm
|
||||
if: always()
|
||||
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836 # v2.3.3
|
||||
uses: softprops/action-gh-release@5be0e66d93ac7ed76da52eca8bb058f665c3a5fe # v2.4.2
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
|
|
@ -180,7 +181,7 @@ jobs:
|
|||
- name: Upload Release Asset
|
||||
id: upload-msi
|
||||
if: always()
|
||||
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836 # v2.3.3
|
||||
uses: softprops/action-gh-release@5be0e66d93ac7ed76da52eca8bb058f665c3a5fe # v2.4.2
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
|
|
@ -216,7 +217,7 @@ jobs:
|
|||
- name: Upload Release Asset
|
||||
id: upload-suse-rpm
|
||||
if: always()
|
||||
uses: softprops/action-gh-release@6cbd405e2c4e67a21c47fa9e383d020e4e28b836 # v2.3.3
|
||||
uses: softprops/action-gh-release@5be0e66d93ac7ed76da52eca8bb058f665c3a5fe # v2.4.2
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
with:
|
||||
|
|
|
|||
|
|
@ -6,12 +6,13 @@ on:
|
|||
- "master"
|
||||
jobs:
|
||||
label:
|
||||
if: ${{ github.repository_owner == 'jenkinsci' }}
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- uses: mheap/github-action-required-labels@8afbe8ae6ab7647d0c9f0cfa7c2f939650d22509 # v5
|
||||
- uses: mheap/github-action-required-labels@8afbe8ae6ab7647d0c9f0cfa7c2f939650d22509 # v5.5.1
|
||||
with:
|
||||
mode: minimum
|
||||
count: 1
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
if: ${{ github.repository_owner == 'jenkinsci' }}
|
||||
steps:
|
||||
- uses: actions/checkout@v5
|
||||
- uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0
|
||||
with:
|
||||
fetch-depth: 0
|
||||
- name: Run update-since-todo.py
|
||||
|
|
@ -29,7 +29,7 @@ jobs:
|
|||
id: run_script
|
||||
shell: bash
|
||||
- name: Create Pull Request
|
||||
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7
|
||||
uses: peter-evans/create-pull-request@271a8d0340265f705b14b6d32b9829c1cb33d45e # v7.0.8
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
commit-message: Fill in since annotations
|
||||
|
|
|
|||
|
|
@ -0,0 +1,19 @@
|
|||
cff-version: 1.2.0
|
||||
title: "Jenkins"
|
||||
type: software
|
||||
repository-code: "https://github.com/jenkinsci/jenkins"
|
||||
url: "https://jenkins.io/"
|
||||
authors:
|
||||
- name: "Jenkins contributors"
|
||||
affiliation: "Jenkins project"
|
||||
abstract: >-
|
||||
In a nutshell, Jenkins is the leading open-source automation server.
|
||||
Built with Java, it provides over 2,000 plugins to support automating virtually anything,
|
||||
so that humans can spend their time doing things machines cannot.
|
||||
keywords:
|
||||
- jenkins
|
||||
- continuous-integration
|
||||
- continuous-delivery
|
||||
- pipelines
|
||||
- automation
|
||||
license: MIT
|
||||
|
|
@ -14,7 +14,7 @@ properties([
|
|||
|
||||
def axes = [
|
||||
platforms: ['linux', 'windows'],
|
||||
jdks: [17, 21],
|
||||
jdks: [17, 21, 25],
|
||||
]
|
||||
|
||||
stage('Record build') {
|
||||
|
|
@ -29,14 +29,16 @@ stage('Record build') {
|
|||
/*
|
||||
* TODO Add the commits of the transitive closure of the Jenkins WAR under test to this build.
|
||||
*/
|
||||
sh 'launchable verify && launchable record build --name ${BUILD_TAG} --source jenkinsci/jenkins=.'
|
||||
// Replace URL encoded characters with '-' because Launchable rejects '%2F' in build name
|
||||
def launchableName = env.BUILD_TAG.replaceAll('(%[0-9A-Fa-f]{2})+', '-')
|
||||
sh "launchable verify && launchable record build --name ${launchableName} --source jenkinsci/jenkins=."
|
||||
axes.values().combinations {
|
||||
def (platform, jdk) = it
|
||||
if (platform == 'windows' && jdk != 17) {
|
||||
if (platform == 'windows' && jdk != axes.jdks.last()) {
|
||||
return // unnecessary use of hardware
|
||||
}
|
||||
def sessionFile = "launchable-session-${platform}-jdk${jdk}.txt"
|
||||
sh "launchable record session --build ${env.BUILD_TAG} --flavor platform=${platform} --flavor jdk=${jdk} >${sessionFile}"
|
||||
sh "launchable record session --build ${launchableName} --flavor platform=${platform} --flavor jdk=${jdk} >${sessionFile}"
|
||||
stash name: sessionFile, includes: sessionFile
|
||||
}
|
||||
}
|
||||
|
|
@ -58,7 +60,7 @@ def builds = [:]
|
|||
|
||||
axes.values().combinations {
|
||||
def (platform, jdk) = it
|
||||
if (platform == 'windows' && jdk != 17) {
|
||||
if (platform == 'windows' && jdk != axes.jdks.last()) {
|
||||
return // unnecessary use of hardware
|
||||
}
|
||||
builds["${platform}-jdk${jdk}"] = {
|
||||
|
|
@ -67,7 +69,13 @@ axes.values().combinations {
|
|||
if (platform == 'windows') {
|
||||
agentContainerLabel += '-windows'
|
||||
}
|
||||
int retryCount = 0
|
||||
retry(conditions: [kubernetesAgent(handleNonKubernetes: true), nonresumable()], count: 2) {
|
||||
if (retryCount == 1 && platform == 'windows' ) {
|
||||
agentContainerLabel = agentContainerLabel + '-nonspot'
|
||||
}
|
||||
// Increment before allocating the node in case it fails
|
||||
retryCount++
|
||||
node(agentContainerLabel) {
|
||||
// First stage is actually checking out the source. Since we're using Multibranch
|
||||
// currently, we can use "checkout scm".
|
||||
|
|
@ -210,7 +218,7 @@ axes.values().combinations {
|
|||
|
||||
def athAxes = [
|
||||
platforms: ['linux'],
|
||||
jdks: [17],
|
||||
jdks: [21],
|
||||
browsers: ['firefox'],
|
||||
]
|
||||
athAxes.values().combinations {
|
||||
|
|
|
|||
4
ath.sh
4
ath.sh
|
|
@ -6,10 +6,10 @@ set -o xtrace
|
|||
cd "$(dirname "$0")"
|
||||
|
||||
# https://github.com/jenkinsci/acceptance-test-harness/releases
|
||||
export ATH_VERSION=6361.vcb_036a_7ffb_a_5
|
||||
export ATH_VERSION=6446.v64eb_f0dfb_26d
|
||||
|
||||
if [[ $# -eq 0 ]]; then
|
||||
export JDK=17
|
||||
export JDK=21
|
||||
export BROWSER=firefox
|
||||
else
|
||||
export JDK=$1
|
||||
|
|
|
|||
16
bom/pom.xml
16
bom/pom.xml
|
|
@ -28,7 +28,7 @@ THE SOFTWARE.
|
|||
<parent>
|
||||
<groupId>org.jenkins-ci.main</groupId>
|
||||
<artifactId>jenkins-parent</artifactId>
|
||||
<version>2.527</version>
|
||||
<version>${revision}${changelist}</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>jenkins-bom</artifactId>
|
||||
|
|
@ -41,7 +41,7 @@ THE SOFTWARE.
|
|||
<commons-fileupload2.version>2.0.0-M4</commons-fileupload2.version>
|
||||
<groovy.version>2.4.21</groovy.version>
|
||||
<jelly.version>1.1-jenkins-20250731</jelly.version>
|
||||
<stapler.version>2030.v88a_855365981</stapler.version>
|
||||
<stapler.version>2050.v425108fd5089</stapler.version>
|
||||
</properties>
|
||||
|
||||
<dependencyManagement>
|
||||
|
|
@ -63,7 +63,7 @@ THE SOFTWARE.
|
|||
<dependency>
|
||||
<groupId>org.springframework</groupId>
|
||||
<artifactId>spring-framework-bom</artifactId>
|
||||
<version>6.2.10</version>
|
||||
<version>6.2.12</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
|
@ -71,7 +71,7 @@ THE SOFTWARE.
|
|||
<!-- https://docs.spring.io/spring-security/reference/6.3/getting-spring-security.html#getting-maven-no-boot -->
|
||||
<groupId>org.springframework.security</groupId>
|
||||
<artifactId>spring-security-bom</artifactId>
|
||||
<version>6.5.3</version>
|
||||
<version>6.5.6</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
|
@ -89,7 +89,7 @@ THE SOFTWARE.
|
|||
<dependency>
|
||||
<groupId>com.google.guava</groupId>
|
||||
<artifactId>guava</artifactId>
|
||||
<version>33.4.8-jre</version>
|
||||
<version>33.5.0-jre</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.sun.xml.txw2</groupId>
|
||||
|
|
@ -109,7 +109,7 @@ THE SOFTWARE.
|
|||
<dependency>
|
||||
<groupId>commons-codec</groupId>
|
||||
<artifactId>commons-codec</artifactId>
|
||||
<version>1.19.0</version>
|
||||
<version>1.20.0</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>commons-collections</groupId>
|
||||
|
|
@ -144,7 +144,7 @@ THE SOFTWARE.
|
|||
<dependency>
|
||||
<groupId>net.java.dev.jna</groupId>
|
||||
<artifactId>jna</artifactId>
|
||||
<version>5.17.0</version>
|
||||
<version>5.18.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>net.java.sezpoz</groupId>
|
||||
|
|
@ -326,7 +326,7 @@ THE SOFTWARE.
|
|||
<dependency>
|
||||
<groupId>org.ow2.asm</groupId>
|
||||
<artifactId>asm</artifactId>
|
||||
<version>9.8</version>
|
||||
<version>9.9</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<!-- provided by jcl-over-slf4j -->
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
<parent>
|
||||
<groupId>org.jenkins-ci.main</groupId>
|
||||
<artifactId>jenkins-parent</artifactId>
|
||||
<version>2.527</version>
|
||||
<version>${revision}${changelist}</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>cli</artifactId>
|
||||
|
|
@ -53,7 +53,7 @@
|
|||
<dependency>
|
||||
<groupId>org.bouncycastle</groupId>
|
||||
<artifactId>bcprov-jdk18on</artifactId>
|
||||
<version>1.81</version>
|
||||
<version>1.82</version>
|
||||
<optional>true</optional>
|
||||
</dependency>
|
||||
<dependency>
|
||||
|
|
@ -113,7 +113,7 @@
|
|||
<plugin>
|
||||
<groupId>org.apache.maven.plugins</groupId>
|
||||
<artifactId>maven-shade-plugin</artifactId>
|
||||
<version>3.6.0</version>
|
||||
<version>3.6.1</version>
|
||||
<executions>
|
||||
<execution>
|
||||
<goals>
|
||||
|
|
|
|||
|
|
@ -36,7 +36,6 @@
|
|||
|
||||
package hudson.util;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.NoSuchElementException;
|
||||
|
|
@ -140,9 +139,6 @@ public class QuotedStringTokenizer
|
|||
|
||||
/* ------------------------------------------------------------ */
|
||||
@Override
|
||||
@SuppressFBWarnings(
|
||||
value = {"SF_DEAD_STORE_DUE_TO_SWITCH_FALLTHROUGH", "SF_SWITCH_FALLTHROUGH"},
|
||||
justification = "TODO needs triage")
|
||||
public boolean hasMoreTokens()
|
||||
{
|
||||
// Already found a token
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@ CLI.Usage=Jenkins CLI\n\
|
|||
-noCertificateCheck : contourne entiérement la vérification des certificats HTTPS. A utiliser avec précaution\n\
|
||||
-noKeyAuth : ne charge pas la clé privée d''authentification SSH. En conflit avec -i\n\
|
||||
\n\
|
||||
Les commandes disponibles dépendent du serveur. Lancer la commande 'help' pour\n\
|
||||
Les commandes disponibles dépendent du serveur. Lancer la commande ''help'' pour\n\
|
||||
obtenir la liste.
|
||||
CLI.NoURL=Erreur, ni -s ou la variable d''environnement JENKINS_URL ne sont renseignées.
|
||||
CLI.VersionMismatch=Conflit de versions. Cet outil ne peut fonctionner avec le serveur Jenkins renseigné.
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ THE SOFTWARE.
|
|||
<parent>
|
||||
<groupId>org.jenkins-ci.main</groupId>
|
||||
<artifactId>jenkins-parent</artifactId>
|
||||
<version>2.527</version>
|
||||
<version>${revision}${changelist}</version>
|
||||
</parent>
|
||||
|
||||
<artifactId>jenkins-core</artifactId>
|
||||
|
|
@ -57,7 +57,7 @@ THE SOFTWARE.
|
|||
<dependency>
|
||||
<groupId>org.xmlunit</groupId>
|
||||
<artifactId>xmlunit-bom</artifactId>
|
||||
<version>2.10.3</version>
|
||||
<version>2.11.0</version>
|
||||
<type>pom</type>
|
||||
<scope>import</scope>
|
||||
</dependency>
|
||||
|
|
|
|||
|
|
@ -87,7 +87,10 @@ public @interface Extension {
|
|||
/**
|
||||
* If an extension is optional, don't log any class loading errors when reading it.
|
||||
* @since 1.358
|
||||
* @deprecated This is very difficult to use correctly and rarely what you actually wanted.
|
||||
* Use {@code OptionalExtension} from the {@code variant} plugin instead.
|
||||
*/
|
||||
@Deprecated
|
||||
boolean optional() default false;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -193,6 +193,7 @@ public abstract class ExtensionFinder implements ExtensionPoint {
|
|||
super(Extension.class);
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
@Override
|
||||
protected boolean isOptional(Extension annotation) {
|
||||
return annotation.optional();
|
||||
|
|
@ -779,6 +780,7 @@ public abstract class ExtensionFinder implements ExtensionPoint {
|
|||
}
|
||||
}
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
private Level logLevel(IndexItem<Extension, Object> item) {
|
||||
return item.annotation().optional() ? Level.FINE : Level.WARNING;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,17 +36,14 @@ import jenkins.model.Jenkins;
|
|||
* in Jenkins that can be implemented by plugins.
|
||||
*
|
||||
* <p>
|
||||
* See respective interfaces/classes for more about how to register custom
|
||||
* implementations to Jenkins. See {@link Extension} for how to have
|
||||
* Jenkins auto-discover your implementations.
|
||||
* Use {@link Extension} to register an implementation.
|
||||
* Use {@link ExtensionList} to look for implementations.
|
||||
*
|
||||
* <p>
|
||||
* This interface is used for auto-generating
|
||||
* documentation.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
* @see Plugin
|
||||
* @see Extension
|
||||
*/
|
||||
public interface ExtensionPoint {
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -718,10 +718,14 @@ public final class FilePath implements SerializableOnlyOverRemoting {
|
|||
int mode = e.getUnixMode();
|
||||
if (mode != 0) // Ant returns 0 if the archive doesn't record the access mode
|
||||
target.chmod(mode);
|
||||
} catch (InterruptedException ex) {
|
||||
} catch (InterruptedException | NoSuchFileException ex) {
|
||||
LOGGER.log(Level.WARNING, "unable to set permissions", ex);
|
||||
}
|
||||
Files.setLastModifiedTime(Util.fileToPath(f), e.getLastModifiedTime());
|
||||
try {
|
||||
Files.setLastModifiedTime(Util.fileToPath(f), e.getLastModifiedTime());
|
||||
} catch (NoSuchFileException ex) {
|
||||
LOGGER.log(Level.WARNING, "unable to set last modified time", ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1301,7 +1305,7 @@ public final class FilePath implements SerializableOnlyOverRemoting {
|
|||
|
||||
/**
|
||||
* Takes a {@link FilePath}+{@link FileCallable} pair and returns the equivalent {@link Callable}.
|
||||
* When executing the resulting {@link Callable}, it executes {@link FileCallable#act(FileCallable)}
|
||||
* When executing the resulting {@link Callable}, it executes {@link FilePath#act(FileCallable)}
|
||||
* on this {@link FilePath}.
|
||||
*
|
||||
* @since 1.522
|
||||
|
|
|
|||
|
|
@ -2470,7 +2470,6 @@ public class Functions {
|
|||
* Advertises the minimum set of HTTP headers that assist programmatic
|
||||
* discovery of Jenkins.
|
||||
*/
|
||||
@SuppressFBWarnings(value = "UC_USELESS_VOID_METHOD", justification = "TODO needs triage")
|
||||
public static void advertiseHeaders(HttpServletResponse rsp) {
|
||||
Jenkins j = Jenkins.getInstanceOrNull();
|
||||
if (j != null) {
|
||||
|
|
@ -2621,7 +2620,7 @@ public class Functions {
|
|||
|
||||
for (Map.Entry<DetailGroup, List<Detail>> entry : result.entrySet()) {
|
||||
List<Detail> detailList = entry.getValue();
|
||||
detailList.sort(Comparator.comparingInt(Detail::getOrder));
|
||||
detailList.sort(Comparator.comparingInt(Detail::getOrder).reversed());
|
||||
}
|
||||
|
||||
return result;
|
||||
|
|
|
|||
|
|
@ -50,6 +50,7 @@ import java.util.Base64;
|
|||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import jenkins.AgentProtocol;
|
||||
import jenkins.health.HealthCheck;
|
||||
import jenkins.model.Jenkins;
|
||||
import jenkins.model.identity.InstanceIdentityProvider;
|
||||
import jenkins.security.stapler.StaplerAccessibleType;
|
||||
|
|
@ -445,4 +446,14 @@ public final class TcpSlaveAgentListener extends Thread {
|
|||
@SuppressFBWarnings(value = "MS_SHOULD_BE_FINAL", justification = "Accessible via System Groovy Scripts")
|
||||
@Restricted(NoExternalUse.class)
|
||||
public static Integer CLI_PORT = SystemProperties.getInteger(TcpSlaveAgentListener.class.getName() + ".port");
|
||||
|
||||
@Extension
|
||||
public static final class EnforcedPortHealthCheck implements HealthCheck {
|
||||
@Override
|
||||
public boolean check() {
|
||||
var j = Jenkins.get();
|
||||
return !j.isSlaveAgentPortEnforced() || j.getSlaveAgentPort() <= 0 || j.getTcpSlaveAgentListener() != null;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -94,6 +94,11 @@ public class AnnotatedLargeText<T> extends LargeText {
|
|||
this.context = context;
|
||||
}
|
||||
|
||||
public AnnotatedLargeText(LargeText.Source source, Charset charset, boolean completed, T context) {
|
||||
super(source, charset, completed);
|
||||
this.context = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 2.475
|
||||
*/
|
||||
|
|
@ -236,8 +241,10 @@ public class AnnotatedLargeText<T> extends LargeText {
|
|||
|
||||
@CheckReturnValue
|
||||
public long writeHtmlTo(long start, Writer w) throws IOException {
|
||||
StaplerRequest2 req = Stapler.getCurrentRequest2();
|
||||
StaplerResponse2 rsp = Stapler.getCurrentResponse2();
|
||||
ConsoleAnnotationOutputStream<T> caw = new ConsoleAnnotationOutputStream<>(
|
||||
w, createAnnotator(Stapler.getCurrentRequest2()), context, charset);
|
||||
w, createAnnotator(req), context, charset);
|
||||
long r = super.writeLogTo(start, caw);
|
||||
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
|
@ -246,9 +253,12 @@ public class AnnotatedLargeText<T> extends LargeText {
|
|||
oos.writeLong(System.currentTimeMillis()); // send timestamp to prevent a replay attack
|
||||
oos.writeObject(caw.getConsoleAnnotator());
|
||||
oos.close();
|
||||
StaplerResponse2 rsp = Stapler.getCurrentResponse2();
|
||||
if (rsp != null)
|
||||
rsp.setHeader("X-ConsoleAnnotator", Base64.getEncoder().encodeToString(baos.toByteArray()));
|
||||
String state = Base64.getEncoder().encodeToString(baos.toByteArray());
|
||||
if (isStreamingRequest(req)) {
|
||||
putStreamingMeta("consoleAnnotator", state);
|
||||
} else if (rsp != null) {
|
||||
rsp.setHeader("X-ConsoleAnnotator", state);
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ public class ConsoleAnnotationOutputStream<T> extends LineTransformationOutputSt
|
|||
/**
|
||||
* Reused buffer that stores char representation of a single line.
|
||||
*/
|
||||
private final LineBuffer line = new LineBuffer(256);
|
||||
private final LineBuffer line = new LineBuffer(4096);
|
||||
/**
|
||||
* {@link OutputStream} that writes to {@link #line}.
|
||||
*/
|
||||
|
|
@ -173,8 +173,8 @@ public class ConsoleAnnotationOutputStream<T> extends LineTransformationOutputSt
|
|||
|
||||
private void reset() {
|
||||
StringBuffer buf = getStringBuffer();
|
||||
if (buf.length() > 4096)
|
||||
out = new StringWriter(256);
|
||||
if (buf.length() > 64 * 1024)
|
||||
out = new StringWriter(4096);
|
||||
else
|
||||
buf.setLength(0);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@
|
|||
package hudson.lifecycle;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import hudson.Extension;
|
||||
import hudson.util.BootFailure;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
|
@ -44,7 +43,6 @@ import org.kohsuke.accmod.restrictions.NoExternalUse;
|
|||
* @author Alon Bar-Lev
|
||||
*/
|
||||
@Restricted(NoExternalUse.class)
|
||||
@Extension
|
||||
public class ExitLifecycle extends Lifecycle {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(ExitLifecycle.class.getName());
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ package hudson.lifecycle;
|
|||
|
||||
import edu.umd.cs.findbugs.annotations.CheckForNull;
|
||||
import edu.umd.cs.findbugs.annotations.NonNull;
|
||||
import hudson.ExtensionPoint;
|
||||
import hudson.Functions;
|
||||
import hudson.PluginManager;
|
||||
import hudson.Util;
|
||||
|
|
@ -62,7 +61,7 @@ import org.kohsuke.accmod.restrictions.NoExternalUse;
|
|||
* @author Kohsuke Kawaguchi
|
||||
* @since 1.254
|
||||
*/
|
||||
public abstract class Lifecycle implements ExtensionPoint {
|
||||
public abstract class Lifecycle {
|
||||
private static Lifecycle INSTANCE = null;
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import com.sun.jna.Library;
|
|||
import com.sun.jna.Native;
|
||||
import edu.umd.cs.findbugs.annotations.CheckForNull;
|
||||
import edu.umd.cs.findbugs.annotations.NonNull;
|
||||
import hudson.Extension;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
|
@ -17,7 +16,6 @@ import org.kohsuke.accmod.restrictions.NoExternalUse;
|
|||
* @author Basil Crow
|
||||
*/
|
||||
@Restricted(NoExternalUse.class)
|
||||
@Extension(optional = true)
|
||||
public class SystemdLifecycle extends ExitLifecycle {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(SystemdLifecycle.class.getName());
|
||||
|
|
|
|||
|
|
@ -94,10 +94,6 @@ public abstract class AbstractCIBase extends Node implements ItemGroup<TopLevelI
|
|||
ViewJob.interruptReloadThread();
|
||||
}
|
||||
|
||||
protected void killComputer(Computer c) {
|
||||
c.kill();
|
||||
}
|
||||
|
||||
private final Set<String> disabledAdministrativeMonitors = new HashSet<>();
|
||||
|
||||
/**
|
||||
|
|
@ -267,12 +263,12 @@ public abstract class AbstractCIBase extends Node implements ItemGroup<TopLevelI
|
|||
// we need to start the process of reducing the executors on all computers as distinct
|
||||
// from the killing action which should not excessively use the Queue lock.
|
||||
for (Computer c : old) {
|
||||
c.inflictMortalWound();
|
||||
c.setNumExecutors(0);
|
||||
}
|
||||
});
|
||||
for (Computer c : old) {
|
||||
// when we get to here, the number of executors should be zero so this call should not need the Queue.lock
|
||||
killComputer(c);
|
||||
c.kill();
|
||||
}
|
||||
getQueue().scheduleMaintenance();
|
||||
Listeners.notify(ComputerListener.class, false, ComputerListener::onConfigurationChange);
|
||||
|
|
|
|||
|
|
@ -69,7 +69,6 @@ import jenkins.model.Jenkins;
|
|||
import jenkins.model.Loadable;
|
||||
import jenkins.model.queue.ItemDeletion;
|
||||
import jenkins.security.ExtendedReadRedaction;
|
||||
import jenkins.security.NotReallyRoleSensitiveCallable;
|
||||
import jenkins.security.stapler.StaplerNotDispatchable;
|
||||
import jenkins.util.SystemProperties;
|
||||
import jenkins.util.xml.XMLUtils;
|
||||
|
|
@ -914,6 +913,7 @@ public abstract class AbstractItem extends Actionable implements Loadable, Item,
|
|||
* sources may not be handled.
|
||||
* @since 1.473
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public void updateByXml(Source source) throws IOException {
|
||||
checkPermission(CONFIGURE);
|
||||
XmlFile configXmlFile = getConfigFile();
|
||||
|
|
@ -934,12 +934,7 @@ public abstract class AbstractItem extends Actionable implements Loadable, Item,
|
|||
throw new IOException("Expecting " + this.getClass() + " but got " + o.getClass() + " instead");
|
||||
}
|
||||
|
||||
Items.whileUpdatingByXml(new NotReallyRoleSensitiveCallable<Void, IOException>() {
|
||||
@Override public Void call() throws IOException {
|
||||
onLoad(getParent(), getRootDir().getName());
|
||||
return null;
|
||||
}
|
||||
});
|
||||
Items.runWhileUpdatingByXml(() -> onLoad(getParent(), getRootDir().getName()));
|
||||
Jenkins.get().rebuildDependencyGraphAsync();
|
||||
|
||||
// if everything went well, commit this new version
|
||||
|
|
@ -967,18 +962,13 @@ public abstract class AbstractItem extends Actionable implements Loadable, Item,
|
|||
}
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public void load() throws IOException {
|
||||
checkPermission(CONFIGURE);
|
||||
|
||||
// try to reflect the changes by reloading
|
||||
getConfigFile().unmarshal(this);
|
||||
Items.whileUpdatingByXml(new NotReallyRoleSensitiveCallable<Void, IOException>() {
|
||||
@Override
|
||||
public Void call() throws IOException {
|
||||
onLoad(getParent(), getParent().getItemName(getRootDir(), AbstractItem.this));
|
||||
return null;
|
||||
}
|
||||
});
|
||||
Items.runWhileUpdatingByXml(() -> onLoad(getParent(), getParent().getItemName(getRootDir(), this)));
|
||||
Jenkins.get().rebuildDependencyGraphAsync();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -138,10 +138,11 @@ public abstract class Actionable extends AbstractModelObject implements ModelObj
|
|||
*/
|
||||
@NonNull
|
||||
public <T extends Action> List<T> getActions(Class<T> type) {
|
||||
List<T> _actions = Util.filter(getActions(), type);
|
||||
List<T> _actions = new ArrayList<>();
|
||||
for (TransientActionFactory<?> taf : TransientActionFactory.factoriesFor(getClass(), type)) {
|
||||
_actions.addAll(Util.filter(createFor(taf), type));
|
||||
}
|
||||
_actions.addAll(Util.filter(getActions(), type));
|
||||
return Collections.unmodifiableList(_actions);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@
|
|||
|
||||
package hudson.model;
|
||||
|
||||
import static hudson.Functions.getAvatar;
|
||||
|
||||
import com.thoughtworks.xstream.converters.UnmarshallingContext;
|
||||
import edu.umd.cs.findbugs.annotations.CheckForNull;
|
||||
import edu.umd.cs.findbugs.annotations.NonNull;
|
||||
|
|
@ -445,19 +447,30 @@ public abstract class Cause {
|
|||
return userId != null ? userId : User.getUnknown().getId();
|
||||
}
|
||||
|
||||
private User getUser() {
|
||||
return userId == null ? null : User.getById(userId, false);
|
||||
}
|
||||
|
||||
@Exported(visibility = 3)
|
||||
public String getUserName() {
|
||||
final User user = userId == null ? null : User.getById(userId, false);
|
||||
final User user = getUser();
|
||||
return user == null ? "anonymous" : user.getDisplayName();
|
||||
}
|
||||
|
||||
@Restricted(DoNotUse.class) // for Jelly
|
||||
@CheckForNull
|
||||
public String getUserUrl() {
|
||||
final User user = userId == null ? null : User.getById(userId, false);
|
||||
final User user = getUser();
|
||||
return user != null ? user.getUrl() : null;
|
||||
}
|
||||
|
||||
@Restricted(DoNotUse.class) // for Jelly
|
||||
@CheckForNull
|
||||
public String getUserAvatar() {
|
||||
final User user = getUser();
|
||||
return user != null ? getAvatar(user, "48x48") : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getShortDescription() {
|
||||
return Messages.Cause_UserIdCause_ShortDescription(getUserName());
|
||||
|
|
|
|||
|
|
@ -827,23 +827,6 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
|
|||
setNumExecutors(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by {@link Jenkins#updateComputerList(boolean, Collection)} to notify {@link Computer} that it will be discarded.
|
||||
*
|
||||
* <p>
|
||||
* Note that at this point {@link #getNode()} returns null.
|
||||
*
|
||||
* <p>
|
||||
* Note that the Queue lock is already held when this method is called.
|
||||
*
|
||||
* @see #onRemoved()
|
||||
*/
|
||||
@Restricted(NoExternalUse.class)
|
||||
@GuardedBy("hudson.model.Queue.lock")
|
||||
/*package*/ void inflictMortalWound() {
|
||||
setNumExecutors(0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called by {@link Jenkins} when this computer is removed.
|
||||
*
|
||||
|
|
@ -865,7 +848,7 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
|
|||
* Calling path, *means protected by Queue.withLock
|
||||
*
|
||||
* Computer.doConfigSubmit -> Computer.replaceBy ->Jenkins.setNodes* ->Computer.setNode
|
||||
* AbstractCIBase.updateComputerList->Computer.inflictMortalWound*
|
||||
* AbstractCIBase.updateComputerList->Computer.setNumExecutors*
|
||||
* AbstractCIBase.updateComputerList->AbstractCIBase.updateComputer* ->Computer.setNode
|
||||
* AbstractCIBase.updateComputerList->AbstractCIBase.killComputer->Computer.kill
|
||||
* Computer.constructor->Computer.setNode
|
||||
|
|
@ -873,8 +856,9 @@ public /*transient*/ abstract class Computer extends Actionable implements Acces
|
|||
*
|
||||
* @param n number of executors
|
||||
*/
|
||||
@Restricted(NoExternalUse.class)
|
||||
@GuardedBy("hudson.model.Queue.lock")
|
||||
private void setNumExecutors(int n) {
|
||||
public void setNumExecutors(int n) {
|
||||
this.numExecutors = n;
|
||||
final int diff = executors.size() - n;
|
||||
|
||||
|
|
|
|||
|
|
@ -54,7 +54,6 @@ import java.util.Collections;
|
|||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Vector;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.locks.ReadWriteLock;
|
||||
import java.util.concurrent.locks.ReentrantReadWriteLock;
|
||||
|
|
@ -365,40 +364,37 @@ public class Executor extends Thread implements ModelObject, IExecutor {
|
|||
SubTask task;
|
||||
// transition from idle to building.
|
||||
// perform this state change as an atomic operation wrt other queue operations
|
||||
task = Queue.withLock(new Callable<>() {
|
||||
@Override
|
||||
public SubTask call() throws Exception {
|
||||
if (!(owner instanceof Jenkins.MasterComputer)) {
|
||||
if (!owner.isOnline()) {
|
||||
resetWorkUnit("went off-line before the task's worker thread was ready to execute");
|
||||
return null;
|
||||
}
|
||||
if (owner.getNode() == null) {
|
||||
resetWorkUnit("was removed before the task's worker thread was ready to execute");
|
||||
return null;
|
||||
}
|
||||
task = Queue.callWithLock(() -> {
|
||||
if (!(owner instanceof Jenkins.MasterComputer)) {
|
||||
if (!owner.isOnline()) {
|
||||
resetWorkUnit("went off-line before the task's worker thread was ready to execute");
|
||||
return null;
|
||||
}
|
||||
// after this point we cannot unwind the assignment of the work unit, if the owner
|
||||
// is removed or goes off-line then the build will just have to fail.
|
||||
workUnit.setExecutor(Executor.this);
|
||||
queue.onStartExecuting(Executor.this);
|
||||
if (LOGGER.isLoggable(FINE))
|
||||
LOGGER.log(FINE, getName() + " grabbed " + workUnit + " from queue");
|
||||
SubTask task = workUnit.work;
|
||||
Executable executable = task.createExecutable();
|
||||
if (executable == null) {
|
||||
String displayName = task instanceof Queue.Task ? ((Queue.Task) task).getFullDisplayName() : task.getDisplayName();
|
||||
LOGGER.log(WARNING, "{0} cannot be run (for example because it is disabled)", displayName);
|
||||
if (owner.getNode() == null) {
|
||||
resetWorkUnit("was removed before the task's worker thread was ready to execute");
|
||||
return null;
|
||||
}
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
Executor.this.executable = executable;
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
workUnit.setExecutable(executable);
|
||||
return task;
|
||||
}
|
||||
// after this point we cannot unwind the assignment of the work unit, if the owner
|
||||
// is removed or goes off-line then the build will just have to fail.
|
||||
workUnit.setExecutor(Executor.this);
|
||||
queue.onStartExecuting(Executor.this);
|
||||
if (LOGGER.isLoggable(FINE))
|
||||
LOGGER.log(FINE, getName() + " grabbed " + workUnit + " from queue");
|
||||
SubTask _task = workUnit.work;
|
||||
Executable _executable = _task.createExecutable();
|
||||
if (_executable == null) {
|
||||
String displayName = _task instanceof Queue.Task qt ? qt.getFullDisplayName() : _task.getDisplayName();
|
||||
LOGGER.log(WARNING, "{0} cannot be run (for example because it is disabled)", displayName);
|
||||
}
|
||||
lock.writeLock().lock();
|
||||
try {
|
||||
executable = _executable;
|
||||
} finally {
|
||||
lock.writeLock().unlock();
|
||||
}
|
||||
workUnit.setExecutable(_executable);
|
||||
return _task;
|
||||
});
|
||||
Executable executable;
|
||||
lock.readLock().lock();
|
||||
|
|
|
|||
|
|
@ -26,9 +26,11 @@ package hudson.model;
|
|||
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import hudson.EnvVars;
|
||||
import hudson.Extension;
|
||||
import hudson.FilePath;
|
||||
import hudson.Launcher;
|
||||
import hudson.Util;
|
||||
import hudson.model.queue.QueueListener;
|
||||
import hudson.tasks.BuildWrapper;
|
||||
import hudson.util.VariableResolver;
|
||||
import java.io.File;
|
||||
|
|
@ -39,12 +41,17 @@ import java.io.UnsupportedEncodingException;
|
|||
import java.nio.charset.Charset;
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
import java.util.List;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.regex.Pattern;
|
||||
import jenkins.model.Jenkins;
|
||||
import jenkins.util.SystemProperties;
|
||||
import org.apache.commons.fileupload2.core.FileItem;
|
||||
import org.apache.commons.fileupload2.core.FileItemFactory;
|
||||
import org.apache.commons.fileupload2.core.FileItemHeaders;
|
||||
import org.apache.commons.fileupload2.core.FileItemHeadersProvider;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.apache.commons.io.FilenameUtils;
|
||||
import org.kohsuke.accmod.Restricted;
|
||||
import org.kohsuke.accmod.restrictions.NoExternalUse;
|
||||
|
|
@ -58,6 +65,9 @@ import org.kohsuke.stapler.StaplerResponse2;
|
|||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
public class FileParameterValue extends ParameterValue {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(FileParameterValue.class.getName());
|
||||
|
||||
private static final String FOLDER_NAME = "fileParameters";
|
||||
private static final Pattern PROHIBITED_DOUBLE_DOT = Pattern.compile(".*[\\\\/]\\.\\.[\\\\/].*");
|
||||
private static final long serialVersionUID = -143427023159076073L;
|
||||
|
|
@ -71,13 +81,16 @@ public class FileParameterValue extends ParameterValue {
|
|||
public static /* Script Console modifiable */ boolean ALLOW_FOLDER_TRAVERSAL_OUTSIDE_WORKSPACE =
|
||||
SystemProperties.getBoolean(FileParameterValue.class.getName() + ".allowFolderTraversalOutsideWorkspace");
|
||||
|
||||
private final transient FileItem file;
|
||||
private transient FileItem file;
|
||||
|
||||
/**
|
||||
* The name of the originally uploaded file.
|
||||
*/
|
||||
private final String originalFileName;
|
||||
|
||||
|
||||
private String tmpFileName;
|
||||
|
||||
/**
|
||||
* Overrides the location in the build to place this file. Initially set to {@link #getName()}
|
||||
* The location could be directly the filename or also a hierarchical path.
|
||||
|
|
@ -106,6 +119,16 @@ public class FileParameterValue extends ParameterValue {
|
|||
|
||||
protected FileParameterValue(String name, FileItem file, String originalFileName) {
|
||||
super(name);
|
||||
try {
|
||||
File dir = new File(Jenkins.get().getRootDir(), "fileParameterValueFiles");
|
||||
Files.createDirectories(dir.toPath());
|
||||
File tmp = Files.createTempFile(dir.toPath(), "jenkins", ".tmp").toFile();
|
||||
FileUtils.copyInputStreamToFile(file.getInputStream(), tmp);
|
||||
tmpFileName = tmp.getAbsolutePath();
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
this.file = file;
|
||||
this.originalFileName = originalFileName;
|
||||
setLocation(name);
|
||||
|
|
@ -149,7 +172,17 @@ public class FileParameterValue extends ParameterValue {
|
|||
return originalFileName;
|
||||
}
|
||||
|
||||
private void createFile() {
|
||||
if (file == null && tmpFileName != null) {
|
||||
File tmp = new File(tmpFileName);
|
||||
if (tmp.exists()) {
|
||||
file = new FileItemImpl2(tmp);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public FileItem getFile2() {
|
||||
createFile();
|
||||
return file;
|
||||
}
|
||||
|
||||
|
|
@ -163,31 +196,44 @@ public class FileParameterValue extends ParameterValue {
|
|||
|
||||
@Override
|
||||
public BuildWrapper createBuildWrapper(AbstractBuild<?, ?> build) {
|
||||
createFile();
|
||||
return new BuildWrapper() {
|
||||
@SuppressFBWarnings(value = "NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", justification = "TODO needs triage")
|
||||
@SuppressFBWarnings(value = {"NP_NULL_ON_SOME_PATH_FROM_RETURN_VALUE", "PATH_TRAVERSAL_IN"}, justification = "TODO needs triage, False positive, the path is a temporary file")
|
||||
@Override
|
||||
public Environment setUp(AbstractBuild build, Launcher launcher, BuildListener listener) throws IOException, InterruptedException {
|
||||
if (location != null && !location.isEmpty() && file.getName() != null && !file.getName().isEmpty()) {
|
||||
listener.getLogger().println("Copying file to " + location);
|
||||
FilePath ws = build.getWorkspace();
|
||||
if (ws == null) {
|
||||
throw new IllegalStateException("The workspace should be created when setUp method is called");
|
||||
}
|
||||
if (!ALLOW_FOLDER_TRAVERSAL_OUTSIDE_WORKSPACE && (PROHIBITED_DOUBLE_DOT.matcher(location).matches() || !ws.isDescendant(location))) {
|
||||
listener.error("Rejecting file path escaping base directory with relative path: " + location);
|
||||
// force the build to fail
|
||||
return null;
|
||||
}
|
||||
FilePath locationFilePath = ws.child(location);
|
||||
locationFilePath.getParent().mkdirs();
|
||||
if (location != null && !location.isBlank() && file != null && file.getName() != null && !file.getName().isBlank()) {
|
||||
try {
|
||||
listener.getLogger().println("Copying file to " + location);
|
||||
FilePath ws = build.getWorkspace();
|
||||
if (ws == null) {
|
||||
throw new IllegalStateException("The workspace should be created when setUp method is called");
|
||||
}
|
||||
if (!ALLOW_FOLDER_TRAVERSAL_OUTSIDE_WORKSPACE && (PROHIBITED_DOUBLE_DOT.matcher(location).matches() || !ws.isDescendant(location))) {
|
||||
listener.error("Rejecting file path escaping base directory with relative path: " + location);
|
||||
// force the build to fail
|
||||
return null;
|
||||
}
|
||||
FilePath locationFilePath = ws.child(location);
|
||||
locationFilePath.getParent().mkdirs();
|
||||
|
||||
// TODO Remove this workaround after FILEUPLOAD-293 is resolved.
|
||||
if (locationFilePath.exists() && !locationFilePath.isDirectory()) {
|
||||
locationFilePath.delete();
|
||||
// TODO Remove this workaround after FILEUPLOAD-293 is resolved.
|
||||
if (locationFilePath.exists() && !locationFilePath.isDirectory()) {
|
||||
locationFilePath.delete();
|
||||
}
|
||||
locationFilePath.copyFrom(file);
|
||||
locationFilePath.copyTo(new FilePath(getLocationUnderBuild(build)));
|
||||
} finally {
|
||||
if (tmpFileName != null) {
|
||||
File tmp = new File(tmpFileName);
|
||||
try {
|
||||
Files.deleteIfExists(tmp.toPath());
|
||||
} catch (IOException e) {
|
||||
LOGGER.log(Level.WARNING, "Unable to delete temporary file {0} for parameter {1} of job {2}",
|
||||
new Object[]{tmp.getAbsolutePath(), getName(), build.getParent().getName()});
|
||||
}
|
||||
}
|
||||
tmpFileName = null;
|
||||
}
|
||||
|
||||
locationFilePath.copyFrom(file);
|
||||
locationFilePath.copyTo(new FilePath(getLocationUnderBuild(build)));
|
||||
}
|
||||
return new Environment() {};
|
||||
}
|
||||
|
|
@ -257,6 +303,36 @@ public class FileParameterValue extends ParameterValue {
|
|||
return new File(build.getRootDir(), FOLDER_NAME);
|
||||
}
|
||||
|
||||
@Extension
|
||||
public static class CancelledQueueListener extends QueueListener {
|
||||
|
||||
@Override
|
||||
public void onLeft(Queue.LeftItem li) {
|
||||
if (li.isCancelled()) {
|
||||
List<ParametersAction> actions = li.getActions(ParametersAction.class);
|
||||
actions.forEach(a -> {
|
||||
a.getAllParameters().stream()
|
||||
.filter(p -> p instanceof FileParameterValue)
|
||||
.map(p -> (FileParameterValue) p)
|
||||
.forEach(this::deleteTmpFile);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressFBWarnings(value = "PATH_TRAVERSAL_IN", justification = "False positive, the path is a temporary file")
|
||||
private void deleteTmpFile(FileParameterValue p) {
|
||||
if (p.tmpFileName != null) {
|
||||
File tmp = new File(p.tmpFileName);
|
||||
try {
|
||||
Files.deleteIfExists(tmp.toPath());
|
||||
} catch (IOException e) {
|
||||
LOGGER.log(Level.WARNING, "Unable to delete temporary file {0} for parameter {1}",
|
||||
new Object[]{tmp.getAbsolutePath(), p.getName()});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default implementation from {@link File}.
|
||||
*
|
||||
|
|
|
|||
|
|
@ -48,7 +48,6 @@ import javax.xml.transform.stream.StreamResult;
|
|||
import javax.xml.transform.stream.StreamSource;
|
||||
import jenkins.model.Jenkins;
|
||||
import jenkins.security.ExtendedReadRedaction;
|
||||
import jenkins.security.NotReallyRoleSensitiveCallable;
|
||||
import jenkins.util.xml.XMLUtils;
|
||||
import org.kohsuke.stapler.StaplerRequest;
|
||||
import org.kohsuke.stapler.StaplerRequest2;
|
||||
|
|
@ -264,11 +263,7 @@ public abstract class ItemGroupMixIn {
|
|||
|
||||
// reload from the new config
|
||||
final File rootDir = result.getRootDir();
|
||||
result = Items.whileUpdatingByXml(new NotReallyRoleSensitiveCallable<T, IOException>() {
|
||||
@Override public T call() throws IOException {
|
||||
return (T) Items.load(parent, rootDir);
|
||||
}
|
||||
});
|
||||
result = Items.callWhileUpdatingByXml(() -> (T) Items.load(parent, rootDir));
|
||||
result.onCopiedFrom(src);
|
||||
|
||||
add(result);
|
||||
|
|
@ -294,11 +289,7 @@ public abstract class ItemGroupMixIn {
|
|||
XMLUtils.safeTransform(new StreamSource(xml), new StreamResult(configXml));
|
||||
|
||||
// load it
|
||||
TopLevelItem result = Items.whileUpdatingByXml(new NotReallyRoleSensitiveCallable<TopLevelItem, IOException>() {
|
||||
@Override public TopLevelItem call() throws IOException {
|
||||
return (TopLevelItem) Items.load(parent, dir);
|
||||
}
|
||||
});
|
||||
TopLevelItem result = Items.callWhileUpdatingByXml(() -> (TopLevelItem) Items.load(parent, dir));
|
||||
|
||||
boolean hasCreatePermission = acl.getACL().hasCreatePermission2(Jenkins.getAuthentication2(), parent, result.getDescriptor());
|
||||
boolean applicableIn = result.getDescriptor().isApplicableIn(parent);
|
||||
|
|
|
|||
|
|
@ -53,6 +53,8 @@ import java.util.function.Predicate;
|
|||
import jenkins.model.DirectlyModifiableTopLevelItemGroup;
|
||||
import jenkins.model.Jenkins;
|
||||
import jenkins.util.MemoryReductionUtil;
|
||||
import jenkins.util.ThrowingCallable;
|
||||
import jenkins.util.ThrowingRunnable;
|
||||
import org.apache.commons.io.FileUtils;
|
||||
import org.springframework.security.core.Authentication;
|
||||
|
||||
|
|
@ -116,6 +118,23 @@ public class Items {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Runs a block while making {@link #currentlyUpdatingByXml} be temporarily true.
|
||||
* Use this when you are creating or changing an item.
|
||||
* @param <T> an error type (may be {@link Error})
|
||||
* @param runnable a block, typically running {@link #load} or {@link Item#onLoad}
|
||||
* @throws T anything {@code runnable} throws
|
||||
* @since 2.534
|
||||
*/
|
||||
public static <T extends Throwable> void runWhileUpdatingByXml(ThrowingRunnable<T> runnable) throws T {
|
||||
updatingByXml.set(true);
|
||||
try {
|
||||
runnable.run();
|
||||
} finally {
|
||||
updatingByXml.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a block while making {@link #currentlyUpdatingByXml} be temporarily true.
|
||||
* Use this when you are creating or changing an item.
|
||||
|
|
@ -124,6 +143,19 @@ public class Items {
|
|||
* @param callable a block, typically running {@link #load} or {@link Item#onLoad}
|
||||
* @return whatever {@code callable} returned
|
||||
* @throws T anything {@code callable} throws
|
||||
* @since 2.534
|
||||
*/
|
||||
public static <V, T extends Throwable> V callWhileUpdatingByXml(ThrowingCallable<V, T> callable) throws T {
|
||||
updatingByXml.set(true);
|
||||
try {
|
||||
return callable.call();
|
||||
} finally {
|
||||
updatingByXml.set(false);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link #runWhileUpdatingByXml} or {@link #callWhileUpdatingByXml}.
|
||||
* @since 1.546
|
||||
*/
|
||||
public static <V, T extends Throwable> V whileUpdatingByXml(Callable<V, T> callable) throws T {
|
||||
|
|
@ -138,7 +170,8 @@ public class Items {
|
|||
/**
|
||||
* Checks whether we are in the middle of creating or configuring an item via XML.
|
||||
* Used to determine the {@code newInstance} parameter for {@link Trigger#start}.
|
||||
* @return true if {@link #whileUpdatingByXml} is currently being called, false for example when merely starting Jenkins or reloading from disk
|
||||
* @return true if {@link #runWhileUpdatingByXml} or {@link #callWhileUpdatingByXml} is currently being called,
|
||||
* false for example when merely starting Jenkins or reloading from disk
|
||||
* @since 1.546
|
||||
*/
|
||||
public static boolean currentlyUpdatingByXml() {
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ import java.util.Set;
|
|||
import java.util.SortedMap;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Stream;
|
||||
import jenkins.model.BuildDiscarder;
|
||||
import jenkins.model.BuildDiscarderProperty;
|
||||
import jenkins.model.DirectlyModifiableTopLevelItemGroup;
|
||||
|
|
@ -98,6 +99,12 @@ import jenkins.model.JenkinsLocationConfiguration;
|
|||
import jenkins.model.ModelObjectWithChildren;
|
||||
import jenkins.model.PeepholePermalink;
|
||||
import jenkins.model.ProjectNamingStrategy;
|
||||
import jenkins.model.Tab;
|
||||
import jenkins.model.details.Detail;
|
||||
import jenkins.model.details.DetailFactory;
|
||||
import jenkins.model.details.DownstreamProjectsDetail;
|
||||
import jenkins.model.details.ProjectNameDetail;
|
||||
import jenkins.model.details.UpstreamProjectsDetail;
|
||||
import jenkins.model.lazy.LazyBuildMixIn;
|
||||
import jenkins.scm.RunWithSCM;
|
||||
import jenkins.security.HexStringConfidentialKey;
|
||||
|
|
@ -1689,4 +1696,30 @@ public abstract class Job<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
|
|||
}
|
||||
|
||||
private static final HexStringConfidentialKey SERVER_COOKIE = new HexStringConfidentialKey(Job.class, "serverCookie", 16);
|
||||
|
||||
@Extension
|
||||
public static final class BasicJobDetailFactory extends DetailFactory<Job> {
|
||||
|
||||
@Override
|
||||
public Class<Job> type() {
|
||||
return Job.class;
|
||||
}
|
||||
|
||||
@NonNull @Override public List<? extends Detail> createFor(@NonNull Job target) {
|
||||
return Stream.of(new UpstreamProjectsDetail(target), new DownstreamProjectsDetail(target), new ProjectNameDetail(target)).filter(e -> e.getIconClassName() != null).toList();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the tabs for a given job
|
||||
*/
|
||||
@Restricted(NoExternalUse.class)
|
||||
public List<Tab> getJobTabs() {
|
||||
return getActions(Tab.class).stream().filter(e -> e.getIconFileName() != null).toList();
|
||||
}
|
||||
|
||||
@Restricted(NoExternalUse.class)
|
||||
public final ParametersDefinitionProperty getParametersDefinitionProperty() {
|
||||
return getProperty(ParametersDefinitionProperty.class);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -48,6 +48,7 @@ import java.util.TreeSet;
|
|||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import jenkins.model.RunAction2;
|
||||
import jenkins.model.experimentalflags.NewBuildPageUserExperimentalFlag;
|
||||
import jenkins.util.SystemProperties;
|
||||
import org.kohsuke.accmod.Restricted;
|
||||
import org.kohsuke.accmod.restrictions.NoExternalUse;
|
||||
|
|
@ -208,6 +209,12 @@ public class ParametersAction implements RunAction2, Iterable<ParameterValue>, Q
|
|||
|
||||
@Override
|
||||
public String getIconFileName() {
|
||||
Boolean newBuildPageEnabled = new NewBuildPageUserExperimentalFlag().getFlagValue();
|
||||
|
||||
if (newBuildPageEnabled) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return "symbol-parameters";
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -85,6 +85,7 @@ import java.nio.channels.ClosedByInterruptException;
|
|||
import java.nio.file.Files;
|
||||
import java.nio.file.InvalidPathException;
|
||||
import java.nio.file.StandardCopyOption;
|
||||
import java.time.Duration;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
|
|
@ -122,6 +123,8 @@ import jenkins.security.stapler.StaplerAccessibleType;
|
|||
import jenkins.util.AtmostOneTaskExecutor;
|
||||
import jenkins.util.Listeners;
|
||||
import jenkins.util.SystemProperties;
|
||||
import jenkins.util.ThrowingCallable;
|
||||
import jenkins.util.ThrowingRunnable;
|
||||
import jenkins.util.Timer;
|
||||
import net.jcip.annotations.GuardedBy;
|
||||
import org.jenkinsci.remoting.RoleChecker;
|
||||
|
|
@ -1274,6 +1277,22 @@ public class Queue extends ResourceController implements Saveable {
|
|||
* Some operations require to be performed with the {@link Queue} lock held. Use one of these methods rather
|
||||
* than locking directly on Queue in order to allow for future refactoring.
|
||||
* @param runnable the operation to perform.
|
||||
* @throws T if the runnable throws an exception.
|
||||
* @since 2.534
|
||||
*/
|
||||
public static <T extends Throwable> void runWithLock(ThrowingRunnable<T> runnable) throws T {
|
||||
final Jenkins jenkins = Jenkins.getInstanceOrNull();
|
||||
// TODO confirm safe to assume non-null and use getInstance()
|
||||
final Queue queue = jenkins == null ? null : jenkins.getQueue();
|
||||
if (queue == null) {
|
||||
runnable.run();
|
||||
} else {
|
||||
queue._runWithLock(runnable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link #runWithLock}.
|
||||
* @since 1.592
|
||||
*/
|
||||
public static void withLock(Runnable runnable) {
|
||||
|
|
@ -1296,6 +1315,21 @@ public class Queue extends ResourceController implements Saveable {
|
|||
* @param <T> the type of exception.
|
||||
* @return the result of the callable.
|
||||
* @throws T the exception of the callable
|
||||
* @since 2.534
|
||||
*/
|
||||
public static <V, T extends Throwable> V callWithLock(ThrowingCallable<V, T> callable) throws T {
|
||||
final Jenkins jenkins = Jenkins.getInstanceOrNull();
|
||||
// TODO confirm safe to assume non-null and use getInstance()
|
||||
final Queue queue = jenkins == null ? null : jenkins.getQueue();
|
||||
if (queue == null) {
|
||||
return callable.call();
|
||||
} else {
|
||||
return queue._callWithLock(callable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prefer {@link #callWithLock}.
|
||||
* @since 1.592
|
||||
*/
|
||||
public static <V, T extends Throwable> V withLock(hudson.remoting.Callable<V, T> callable) throws T {
|
||||
|
|
@ -1310,13 +1344,7 @@ public class Queue extends ResourceController implements Saveable {
|
|||
}
|
||||
|
||||
/**
|
||||
* Some operations require to be performed with the {@link Queue} lock held. Use one of these methods rather
|
||||
* than locking directly on Queue in order to allow for future refactoring.
|
||||
*
|
||||
* @param callable the operation to perform.
|
||||
* @param <V> the type of return value
|
||||
* @return the result of the callable.
|
||||
* @throws Exception if the callable throws an exception.
|
||||
* Prefer {@link #callWithLock}.
|
||||
* @since 1.592
|
||||
*/
|
||||
public static <V> V withLock(java.util.concurrent.Callable<V> callable) throws Exception {
|
||||
|
|
@ -1348,6 +1376,26 @@ public class Queue extends ResourceController implements Saveable {
|
|||
return queue._tryWithLock(runnable);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the supplied {@link Runnable} if the {@link Queue} lock was obtained within the given timeout.
|
||||
*
|
||||
* @param runnable the operation to perform.
|
||||
* @return {@code true} if the lock was acquired within the timeout and the operation was performed.
|
||||
* @since 2.529
|
||||
*/
|
||||
public static boolean tryWithLock(Runnable runnable, Duration timeout) throws InterruptedException {
|
||||
final Jenkins jenkins = Jenkins.getInstanceOrNull();
|
||||
// TODO confirm safe to assume non-null and use getInstance()
|
||||
final Queue queue = jenkins == null ? null : jenkins.getQueue();
|
||||
if (queue == null) {
|
||||
runnable.run();
|
||||
return true;
|
||||
} else {
|
||||
return queue._tryWithLock(runnable, timeout);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Wraps a {@link Runnable} with the {@link Queue} lock held.
|
||||
*
|
||||
|
|
@ -1399,6 +1447,25 @@ public class Queue extends ResourceController implements Saveable {
|
|||
condition.signalAll();
|
||||
}
|
||||
|
||||
/**
|
||||
* Some operations require to be performed with the {@link Queue} lock held. Use one of these methods rather
|
||||
* than locking directly on Queue in order to allow for future refactoring.
|
||||
*
|
||||
* @param runnable the operation to perform.
|
||||
* @param <T> the type of exception.
|
||||
* @throws T the exception of the runnable
|
||||
* @since 2.534
|
||||
*/
|
||||
@Override
|
||||
protected <T extends Throwable> void _runWithLock(ThrowingRunnable<T> runnable) throws T {
|
||||
lock.lock();
|
||||
try {
|
||||
runnable.run();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Some operations require to be performed with the {@link Queue} lock held. Use one of these methods rather
|
||||
* than locking directly on Queue in order to allow for future refactoring.
|
||||
|
|
@ -1435,6 +1502,47 @@ public class Queue extends ResourceController implements Saveable {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Invokes the supplied {@link Runnable} if the {@link Queue} lock was obtained within the given timeout
|
||||
*
|
||||
* @param runnable the operation to perform.
|
||||
* @return {@code true} if the lock was acquired within the timeout and the operation was performed.
|
||||
* @since 2.529
|
||||
*/
|
||||
protected boolean _tryWithLock(Runnable runnable, Duration timeout) throws InterruptedException {
|
||||
if (lock.tryLock(timeout.toNanos(), TimeUnit.NANOSECONDS)) {
|
||||
try {
|
||||
runnable.run();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Some operations require to be performed with the {@link Queue} lock held. Use one of these methods rather
|
||||
* than locking directly on Queue in order to allow for future refactoring.
|
||||
*
|
||||
* @param callable the operation to perform.
|
||||
* @param <V> the type of return value
|
||||
* @param <T> the type of exception.
|
||||
* @return the result of the callable.
|
||||
* @throws T the exception of the callable
|
||||
* @since 2.534
|
||||
*/
|
||||
@Override
|
||||
protected <V, T extends Throwable> V _callWithLock(ThrowingCallable<V, T> callable) throws T {
|
||||
lock.lock();
|
||||
try {
|
||||
return callable.call();
|
||||
} finally {
|
||||
lock.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Some operations require to be performed with the {@link Queue} lock held. Use one of these methods rather
|
||||
* than locking directly on Queue in order to allow for future refactoring.
|
||||
|
|
@ -1573,7 +1681,13 @@ public class Queue extends ResourceController implements Saveable {
|
|||
updateSnapshot();
|
||||
}
|
||||
} else {
|
||||
p.setCauseOfBlockage(causeOfBlockage);
|
||||
if (causeOfBlockage.isFatal()) {
|
||||
cancel(p);
|
||||
} else {
|
||||
p.leave(this);
|
||||
new BlockedItem(p, causeOfBlockage).enter(this);
|
||||
updateSnapshot();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1586,10 +1700,9 @@ public class Queue extends ResourceController implements Saveable {
|
|||
LOGGER.log(Level.FINEST, "Finished moving all ready items from queue.");
|
||||
break; // finished moving all ready items from queue
|
||||
}
|
||||
|
||||
top.leave(this);
|
||||
CauseOfBlockage causeOfBlockage = getCauseOfBlockageForItem(top);
|
||||
if (causeOfBlockage == null) {
|
||||
top.leave(this);
|
||||
// ready to be executed immediately
|
||||
Runnable r = makeBuildable(new BuildableItem(top));
|
||||
String topTaskDisplayName = LOGGER.isLoggable(Level.FINEST) ? top.task.getFullDisplayName() : null;
|
||||
|
|
@ -1601,9 +1714,14 @@ public class Queue extends ResourceController implements Saveable {
|
|||
new BlockedItem(top, CauseOfBlockage.fromMessage(Messages._Queue_HudsonIsAboutToShutDown())).enter(this);
|
||||
}
|
||||
} else {
|
||||
// this can't be built now because another build is in progress
|
||||
// set this project aside.
|
||||
new BlockedItem(top, causeOfBlockage).enter(this);
|
||||
if (causeOfBlockage.isFatal()) {
|
||||
cancel(top);
|
||||
} else {
|
||||
top.leave(this);
|
||||
// this can't be built now because another build is in progress
|
||||
// set this project aside.
|
||||
new BlockedItem(top, causeOfBlockage).enter(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1626,12 +1744,16 @@ public class Queue extends ResourceController implements Saveable {
|
|||
// one last check to make sure this build is not blocked.
|
||||
CauseOfBlockage causeOfBlockage = getCauseOfBlockageForItem(p);
|
||||
if (causeOfBlockage != null) {
|
||||
p.leave(this);
|
||||
new BlockedItem(p, causeOfBlockage).enter(this);
|
||||
LOGGER.log(Level.FINE, "Catching that {0} is blocked in the last minute", p);
|
||||
// JENKINS-28926 we have moved an unblocked task into the blocked state, update snapshot
|
||||
// so that other buildables which might have been blocked by this can see the state change
|
||||
updateSnapshot();
|
||||
if (causeOfBlockage.isFatal()) {
|
||||
cancel(p);
|
||||
} else {
|
||||
p.leave(this);
|
||||
new BlockedItem(p, causeOfBlockage).enter(this);
|
||||
LOGGER.log(Level.FINE, "Catching that {0} is blocked in the last minute", p);
|
||||
// JENKINS-28926 we have moved an unblocked task into the blocked state, update snapshot
|
||||
// so that other buildables which might have been blocked by this can see the state change
|
||||
updateSnapshot();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -2617,15 +2739,7 @@ public class Queue extends ResourceController implements Saveable {
|
|||
* {@link Item} in the {@link Queue#blockedProjects} stage.
|
||||
*/
|
||||
public final class BlockedItem extends NotWaitingItem {
|
||||
private transient CauseOfBlockage causeOfBlockage = null;
|
||||
|
||||
public BlockedItem(WaitingItem wi) {
|
||||
this(wi, null);
|
||||
}
|
||||
|
||||
public BlockedItem(NotWaitingItem ni) {
|
||||
this(ni, null);
|
||||
}
|
||||
private final transient CauseOfBlockage causeOfBlockage;
|
||||
|
||||
BlockedItem(WaitingItem wi, CauseOfBlockage causeOfBlockage) {
|
||||
super(wi);
|
||||
|
|
@ -2637,10 +2751,6 @@ public class Queue extends ResourceController implements Saveable {
|
|||
this.causeOfBlockage = causeOfBlockage;
|
||||
}
|
||||
|
||||
void setCauseOfBlockage(CauseOfBlockage causeOfBlockage) {
|
||||
this.causeOfBlockage = causeOfBlockage;
|
||||
}
|
||||
|
||||
@Restricted(NoExternalUse.class)
|
||||
public boolean isCauseOfBlockageNull() {
|
||||
if (causeOfBlockage == null) {
|
||||
|
|
|
|||
|
|
@ -31,9 +31,9 @@ import java.util.AbstractCollection;
|
|||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.Set;
|
||||
import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.CopyOnWriteArraySet;
|
||||
import jenkins.security.NotReallyRoleSensitiveCallable;
|
||||
import jenkins.util.ThrowingCallable;
|
||||
import jenkins.util.ThrowingRunnable;
|
||||
|
||||
/**
|
||||
* Controls mutual exclusion of {@link ResourceList}.
|
||||
|
|
@ -82,32 +82,25 @@ public class ResourceController {
|
|||
*/
|
||||
public void execute(@NonNull Runnable task, final ResourceActivity activity) throws InterruptedException {
|
||||
final ResourceList resources = activity.getResourceList();
|
||||
_withLock(new NotReallyRoleSensitiveCallable<Void, InterruptedException>() {
|
||||
@Override
|
||||
public Void call() throws InterruptedException {
|
||||
while (inUse.isCollidingWith(resources)) {
|
||||
// TODO revalidate the resource list after re-acquiring lock, for now we just let the build fail
|
||||
_await();
|
||||
}
|
||||
|
||||
// we have a go
|
||||
inProgress.add(activity);
|
||||
inUse = ResourceList.union(inUse, resources);
|
||||
return null;
|
||||
_runWithLock(() -> {
|
||||
while (inUse.isCollidingWith(resources)) {
|
||||
// TODO revalidate the resource list after re-acquiring lock, for now we just let the build fail
|
||||
_await();
|
||||
}
|
||||
|
||||
// we have a go
|
||||
inProgress.add(activity);
|
||||
inUse = ResourceList.union(inUse, resources);
|
||||
});
|
||||
|
||||
try {
|
||||
task.run();
|
||||
} finally {
|
||||
// TODO if AsynchronousExecution, do that later
|
||||
_withLock(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
inProgress.remove(activity);
|
||||
inUse = ResourceList.union(resourceView);
|
||||
_signalAll();
|
||||
}
|
||||
_runWithLock(() -> {
|
||||
inProgress.remove(activity);
|
||||
inUse = ResourceList.union(resourceView);
|
||||
_signalAll();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
@ -122,16 +115,7 @@ public class ResourceController {
|
|||
* gets to call {@link #execute(Runnable, ResourceActivity)}.
|
||||
*/
|
||||
public boolean canRun(final ResourceList resources) {
|
||||
try {
|
||||
return _withLock(new Callable<>() {
|
||||
@Override
|
||||
public Boolean call() {
|
||||
return !inUse.isCollidingWith(resources);
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Inner callable does not throw exception", e);
|
||||
}
|
||||
return _callWithLock(() -> !inUse.isCollidingWith(resources));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -143,16 +127,7 @@ public class ResourceController {
|
|||
* This method is used for reporting what's causing the blockage.
|
||||
*/
|
||||
public Resource getMissingResource(final ResourceList resources) {
|
||||
try {
|
||||
return _withLock(new Callable<>() {
|
||||
@Override
|
||||
public Resource call() {
|
||||
return resources.getConflict(inUse);
|
||||
}
|
||||
});
|
||||
} catch (Exception e) {
|
||||
throw new IllegalStateException("Inner callable does not throw exception", e);
|
||||
}
|
||||
return _callWithLock(() -> resources.getConflict(inUse));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -177,12 +152,24 @@ public class ResourceController {
|
|||
notifyAll();
|
||||
}
|
||||
|
||||
protected <T extends Throwable> void _runWithLock(ThrowingRunnable<T> runnable) throws T {
|
||||
synchronized (this) {
|
||||
runnable.run();
|
||||
}
|
||||
}
|
||||
|
||||
protected void _withLock(Runnable runnable) {
|
||||
synchronized (this) {
|
||||
runnable.run();
|
||||
}
|
||||
}
|
||||
|
||||
protected <V, T extends Throwable> V _callWithLock(ThrowingCallable<V, T> callable) throws T {
|
||||
synchronized (this) {
|
||||
return callable.call();
|
||||
}
|
||||
}
|
||||
|
||||
protected <V> V _withLock(java.util.concurrent.Callable<V> callable) throws Exception {
|
||||
synchronized (this) {
|
||||
return callable.call();
|
||||
|
|
|
|||
|
|
@ -120,6 +120,8 @@ import jenkins.model.Jenkins;
|
|||
import jenkins.model.JenkinsLocationConfiguration;
|
||||
import jenkins.model.RunAction2;
|
||||
import jenkins.model.StandardArtifactManager;
|
||||
import jenkins.model.Tab;
|
||||
import jenkins.model.details.CauseDetail;
|
||||
import jenkins.model.details.Detail;
|
||||
import jenkins.model.details.DetailFactory;
|
||||
import jenkins.model.details.DurationDetail;
|
||||
|
|
@ -385,7 +387,7 @@ public abstract class Run<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
|
|||
*/
|
||||
@SuppressWarnings("deprecation")
|
||||
protected void onLoad() {
|
||||
for (Action a : getAllActions()) {
|
||||
for (Action a : getActions()) {
|
||||
if (a instanceof RunAction2) {
|
||||
try {
|
||||
((RunAction2) a).onLoad(this);
|
||||
|
|
@ -2706,7 +2708,15 @@ public abstract class Run<JobT extends Job<JobT, RunT>, RunT extends Run<JobT, R
|
|||
}
|
||||
|
||||
@NonNull @Override public List<? extends Detail> createFor(@NonNull Run target) {
|
||||
return List.of(new TimestampDetail(target), new DurationDetail(target));
|
||||
return List.of(new CauseDetail(target), new TimestampDetail(target), new DurationDetail(target));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the tabs for a given run
|
||||
*/
|
||||
@Restricted(NoExternalUse.class)
|
||||
public List<Tab> getRunTabs() {
|
||||
return getActions(Tab.class).stream().filter(e -> e.getIconFileName() != null).toList();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -114,7 +114,7 @@ public class TimeZoneProperty extends UserProperty {
|
|||
|
||||
@Override
|
||||
public @NonNull UserPropertyCategory getUserPropertyCategory() {
|
||||
return UserPropertyCategory.get(UserPropertyCategory.Account.class);
|
||||
return UserPropertyCategory.get(UserPropertyCategory.Preferences.class);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ import javax.xml.transform.TransformerException;
|
|||
import javax.xml.transform.sax.SAXSource;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
import javax.xml.transform.stream.StreamSource;
|
||||
import jenkins.model.Badgeable;
|
||||
import jenkins.model.Jenkins;
|
||||
import jenkins.model.ModelObjectWithChildren;
|
||||
import jenkins.model.ModelObjectWithContextMenu;
|
||||
|
|
@ -144,7 +145,7 @@ import org.xml.sax.SAXException;
|
|||
* @see ViewGroup
|
||||
*/
|
||||
@ExportedBean
|
||||
public abstract class View extends AbstractModelObject implements AccessControlled, Describable<View>, ExtensionPoint, Saveable, ModelObjectWithChildren, DescriptorByNameOwner, HasWidgets {
|
||||
public abstract class View extends AbstractModelObject implements AccessControlled, Describable<View>, ExtensionPoint, Saveable, ModelObjectWithChildren, DescriptorByNameOwner, HasWidgets, Badgeable {
|
||||
|
||||
/**
|
||||
* Container of this view. Set right after the construction
|
||||
|
|
@ -368,6 +369,19 @@ public abstract class View extends AbstractModelObject implements AccessControll
|
|||
return getViewName();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the icon file name for this action.
|
||||
* <p>
|
||||
* Only displays if {@link jenkins.model.experimentalflags.NewDashboardPageUserExperimentalFlag} is enabled.
|
||||
* <p>
|
||||
* This behaves similarly to {@link Action#getIconFileName()}, except that
|
||||
* returning {@code null} here does not hide the associated view; the view
|
||||
* will still be displayed even when this method returns {@code null}.
|
||||
*/
|
||||
public String getIconFileName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public String getNewPronoun() {
|
||||
return AlternativeUiTextProvider.get(NEW_PRONOUN, this, Messages.AbstractItem_Pronoun());
|
||||
}
|
||||
|
|
@ -1236,6 +1250,12 @@ public abstract class View extends AbstractModelObject implements AccessControll
|
|||
}
|
||||
}
|
||||
|
||||
// for Jelly
|
||||
@Restricted(DoNotUse.class)
|
||||
public boolean isMyViewsProperty() {
|
||||
return getOwner() instanceof MyViewsProperty;
|
||||
}
|
||||
|
||||
public static class PropertyList extends DescribableList<ViewProperty, ViewPropertyDescriptor> {
|
||||
private PropertyList(View owner) {
|
||||
super(owner);
|
||||
|
|
|
|||
|
|
@ -35,6 +35,14 @@ public abstract class CauseOfBlockage {
|
|||
*/
|
||||
public abstract String getShortDescription();
|
||||
|
||||
/**
|
||||
* @return {@code true} if the blockage is fatal and the item should be removed from the queue.
|
||||
* @since 2.532
|
||||
*/
|
||||
public boolean isFatal() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Report a line to the listener about this cause.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -41,7 +41,6 @@ import java.util.function.BiFunction;
|
|||
import java.util.stream.Collectors;
|
||||
import jenkins.model.Jenkins;
|
||||
import jenkins.security.NonSerializableSecurityContext;
|
||||
import jenkins.security.NotReallyRoleSensitiveCallable;
|
||||
import org.acegisecurity.acls.sid.PrincipalSid;
|
||||
import org.acegisecurity.acls.sid.Sid;
|
||||
import org.kohsuke.accmod.Restricted;
|
||||
|
|
@ -438,10 +437,6 @@ public abstract class ACL {
|
|||
}
|
||||
|
||||
/**
|
||||
* Safer variant of {@link #impersonate2(Authentication)} that does not require a finally-block.
|
||||
* @param auth authentication, such as {@link #SYSTEM2}
|
||||
* @param body an action to run with this alternate authentication in effect (try {@link NotReallyRoleSensitiveCallable})
|
||||
* @since 2.266
|
||||
* @deprecated use try with resources and {@link #as2(Authentication)}
|
||||
*/
|
||||
@Deprecated
|
||||
|
|
@ -455,7 +450,7 @@ public abstract class ACL {
|
|||
}
|
||||
|
||||
/**
|
||||
* @deprecated use {@link #impersonate2(Authentication, Callable)}
|
||||
* @deprecated use try with resources and {@link #as2(Authentication)}
|
||||
* @since 1.587
|
||||
*/
|
||||
@Deprecated
|
||||
|
|
|
|||
|
|
@ -67,17 +67,14 @@ public class ComputerRetentionWork extends AperiodicWork {
|
|||
protected void doAperiodicRun() {
|
||||
final long startRun = System.currentTimeMillis();
|
||||
for (final Computer c : Jenkins.get().getComputers()) {
|
||||
Queue.withLock(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Node n = c.getNode();
|
||||
if (n != null && n.isHoldOffLaunchUntilSave())
|
||||
return;
|
||||
if (!nextCheck.containsKey(c) || startRun > nextCheck.get(c)) {
|
||||
// at the moment I don't trust strategies to wait more than 60 minutes
|
||||
final long waitInMins = Math.max(0, Math.min(60, c.getRetentionStrategy().check(c)));
|
||||
nextCheck.put(c, startRun + TimeUnit.MINUTES.toMillis(waitInMins));
|
||||
}
|
||||
Queue.runWithLock(() -> {
|
||||
Node n = c.getNode();
|
||||
if (n != null && n.isHoldOffLaunchUntilSave())
|
||||
return;
|
||||
if (!nextCheck.containsKey(c) || startRun > nextCheck.get(c)) {
|
||||
// at the moment I don't trust strategies to wait more than 60 minutes
|
||||
final long waitInMins = Math.max(0, Math.min(60, c.getRetentionStrategy().check(c)));
|
||||
nextCheck.put(c, startRun + TimeUnit.MINUTES.toMillis(waitInMins));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ public abstract class RetentionStrategy<T extends Computer> implements Describab
|
|||
* @since 1.275
|
||||
*/
|
||||
public void start(final @NonNull T c) {
|
||||
Queue.withLock((Runnable) () -> check(c));
|
||||
Queue.runWithLock(() -> check(c));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -211,27 +211,21 @@ public class SimpleScheduledRetentionStrategy extends RetentionStrategy<SlaveCom
|
|||
new Object[]{c.getName()});
|
||||
return 0;
|
||||
} else if (c.isIdle() && c.isAcceptingTasks()) {
|
||||
Queue.withLock(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (c.isIdle()) {
|
||||
LOGGER.log(INFO, "Disconnecting computer {0} as it has finished its scheduled uptime",
|
||||
new Object[]{c.getName()});
|
||||
c.disconnect(OfflineCause
|
||||
.create(Messages._SimpleScheduledRetentionStrategy_FinishedUpTime()));
|
||||
}
|
||||
Queue.runWithLock(() -> {
|
||||
if (c.isIdle()) {
|
||||
LOGGER.log(INFO, "Disconnecting computer {0} as it has finished its scheduled uptime",
|
||||
new Object[]{c.getName()});
|
||||
c.disconnect(OfflineCause
|
||||
.create(Messages._SimpleScheduledRetentionStrategy_FinishedUpTime()));
|
||||
}
|
||||
});
|
||||
} else if (c.isIdle() && !c.isAcceptingTasks()) {
|
||||
Queue.withLock(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (c.isIdle()) {
|
||||
LOGGER.log(INFO, "Disconnecting computer {0} as it has finished all jobs running when "
|
||||
+ "it completed its scheduled uptime", new Object[]{c.getName()});
|
||||
c.disconnect(OfflineCause
|
||||
.create(Messages._SimpleScheduledRetentionStrategy_FinishedUpTime()));
|
||||
}
|
||||
Queue.runWithLock(() -> {
|
||||
if (c.isIdle()) {
|
||||
LOGGER.log(INFO, "Disconnecting computer {0} as it has finished all jobs running when "
|
||||
+ "it completed its scheduled uptime", new Object[]{c.getName()});
|
||||
c.disconnect(OfflineCause
|
||||
.create(Messages._SimpleScheduledRetentionStrategy_FinishedUpTime()));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -625,6 +625,7 @@ public class SlaveComputer extends Computer {
|
|||
* @param listener Channel event listener to be attached (if not {@code null})
|
||||
* @since 1.444
|
||||
*/
|
||||
@SuppressFBWarnings(value = "NN_NAKED_NOTIFY", justification = "False positive, the warning isn't for this scenario")
|
||||
public void setChannel(@NonNull Channel channel,
|
||||
@CheckForNull OutputStream launchLog,
|
||||
@CheckForNull Channel.Listener listener) throws IOException, InterruptedException {
|
||||
|
|
@ -916,12 +917,7 @@ public class SlaveComputer extends Computer {
|
|||
protected void kill() {
|
||||
super.kill();
|
||||
closeChannel();
|
||||
try {
|
||||
log.close();
|
||||
} catch (IOException x) {
|
||||
LOGGER.log(Level.WARNING, "Failed to close agent log", x);
|
||||
}
|
||||
|
||||
closeLog();
|
||||
try {
|
||||
Util.deleteRecursive(getLogDir());
|
||||
} catch (IOException ex) {
|
||||
|
|
@ -929,6 +925,15 @@ public class SlaveComputer extends Computer {
|
|||
}
|
||||
}
|
||||
|
||||
@Restricted(NoExternalUse.class)
|
||||
public void closeLog() {
|
||||
try {
|
||||
log.close();
|
||||
} catch (IOException x) {
|
||||
LOGGER.log(Level.WARNING, "Failed to close agent log", x);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public RetentionStrategy getRetentionStrategy() {
|
||||
Slave n = getNode();
|
||||
|
|
@ -959,6 +964,7 @@ public class SlaveComputer extends Computer {
|
|||
|
||||
@Override
|
||||
@SuppressFBWarnings(value = "UR_UNINIT_READ_CALLED_FROM_SUPER_CONSTRUCTOR", justification = "TODO needs triage")
|
||||
@SuppressWarnings("unchecked")
|
||||
protected void setNode(final Node node) {
|
||||
super.setNode(node);
|
||||
launcher = grabLauncher(node);
|
||||
|
|
@ -967,13 +973,8 @@ public class SlaveComputer extends Computer {
|
|||
// "constructed==null" test is an ugly hack to avoid launching before the object is fully
|
||||
// constructed.
|
||||
if (constructed != null) {
|
||||
if (node instanceof Slave) {
|
||||
Queue.withLock(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
((Slave) node).getRetentionStrategy().check(SlaveComputer.this);
|
||||
}
|
||||
});
|
||||
if (node instanceof Slave slave) {
|
||||
Queue.runWithLock(() -> slave.getRetentionStrategy().check(SlaveComputer.this));
|
||||
} else {
|
||||
connect(false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,6 @@ import edu.umd.cs.findbugs.annotations.NonNull;
|
|||
import hudson.Extension;
|
||||
import hudson.model.Job;
|
||||
import hudson.model.Run;
|
||||
import hudson.util.RunList;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Calendar;
|
||||
|
|
@ -165,20 +164,12 @@ public class LogRotator extends BuildDiscarder {
|
|||
Run lstb = removeLastBuild ? null : job.getLastStableBuild();
|
||||
|
||||
if (numToKeep != -1) {
|
||||
// Note that RunList.size is deprecated, and indeed here we are loading all the builds of the job.
|
||||
// However we would need to load the first numToKeep anyway, just to skip over them;
|
||||
// and we would need to load the rest anyway, to delete them.
|
||||
// (Using RunMap.headMap would not suffice, since we do not know if some recent builds have been deleted for other reasons,
|
||||
// so simply subtracting numToKeep from the currently last build number might cause us to delete too many.)
|
||||
RunList<? extends Run<?, ?>> builds = job.getBuilds();
|
||||
for (Run r : builds.subList(Math.min(builds.size(), numToKeep), builds.size())) {
|
||||
if (shouldKeepRun(r, lsb, lstb)) {
|
||||
continue;
|
||||
}
|
||||
job.getBuildsAsMap().entrySet().stream().skip(numToKeep).map(Map.Entry::getValue)
|
||||
.filter(r -> !shouldKeepRun(r, lsb, lstb)).forEach(r -> {
|
||||
LOGGER.log(FINE, "{0} is to be removed", r);
|
||||
try { r.delete(); }
|
||||
catch (IOException ex) { exceptionMap.computeIfAbsent(r, key -> new HashSet<>()).add(ex); }
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (daysToKeep != -1) {
|
||||
|
|
@ -199,15 +190,12 @@ public class LogRotator extends BuildDiscarder {
|
|||
}
|
||||
|
||||
if (artifactNumToKeep != null && artifactNumToKeep != -1) {
|
||||
RunList<? extends Run<?, ?>> builds = job.getBuilds();
|
||||
for (Run r : builds.subList(Math.min(builds.size(), artifactNumToKeep), builds.size())) {
|
||||
if (shouldKeepRun(r, lsb, lstb)) {
|
||||
continue;
|
||||
}
|
||||
job.getBuildsAsMap().entrySet().stream().skip(artifactNumToKeep).map(Map.Entry::getValue)
|
||||
.filter(r -> !shouldKeepRun(r, lsb, lstb)).forEach(r -> {
|
||||
LOGGER.log(FINE, "{0} is to be purged of artifacts", r);
|
||||
try { r.deleteArtifacts(); }
|
||||
catch (IOException ex) { exceptionMap.computeIfAbsent(r, key -> new HashSet<>()).add(ex); }
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (artifactDaysToKeep != null && artifactDaysToKeep != -1) {
|
||||
|
|
|
|||
|
|
@ -633,31 +633,4 @@ public abstract class FormFieldValidator {
|
|||
error(errorMessage);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Verifies that the {@code value} parameter is an integer ≥ 0.
|
||||
*
|
||||
* @since 1.282
|
||||
* @deprecated as of 1.294
|
||||
* Use {@link FormValidation#validateNonNegativeInteger(String)}
|
||||
*/
|
||||
@Deprecated
|
||||
public static class NonNegativeInteger extends FormFieldValidator {
|
||||
public NonNegativeInteger() {
|
||||
super(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void check() throws IOException, ServletException {
|
||||
try {
|
||||
String value = request.getParameter("value");
|
||||
if (Integer.parseInt(value) < 0)
|
||||
error(hudson.model.Messages.Hudson_NotAPositiveNumber());
|
||||
else
|
||||
ok();
|
||||
} catch (NumberFormatException e) {
|
||||
error(hudson.model.Messages.Hudson_NotANumber());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@
|
|||
package hudson.util;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.CheckForNull;
|
||||
import hudson.ExtensionPoint;
|
||||
import hudson.security.SecurityRealm;
|
||||
import io.jenkins.servlet.FilterWrapper;
|
||||
import io.jenkins.servlet.ServletExceptionWrapper;
|
||||
|
|
@ -51,19 +50,16 @@ import org.kohsuke.accmod.restrictions.NoExternalUse;
|
|||
import org.kohsuke.stapler.CompatibleFilter;
|
||||
|
||||
/**
|
||||
* Servlet {@link Filter} that chains multiple {@link Filter}s, provided by plugins
|
||||
*
|
||||
* Servlet {@link Filter} that chains multiple {@link Filter}s, provided by plugins.
|
||||
* <p>
|
||||
* While this class by itself is not an extension point, I'm marking this class
|
||||
* as an extension point so that this class will be more discoverable.
|
||||
*
|
||||
* In most cases you should rather use {@link HttpServletFilter}.
|
||||
* <p>
|
||||
* {@link SecurityRealm} that wants to contribute {@link Filter}s should first
|
||||
* check if {@link SecurityRealm#createFilter(FilterConfig)} is more appropriate.
|
||||
*
|
||||
* @see SecurityRealm
|
||||
*/
|
||||
public final class PluginServletFilter implements CompatibleFilter, ExtensionPoint {
|
||||
public final class PluginServletFilter implements CompatibleFilter {
|
||||
private final List<Filter> list = new CopyOnWriteArrayList<>();
|
||||
|
||||
private /*almost final*/ FilterConfig config;
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ Lesser General Public License for more details.
|
|||
package hudson.util.jna;
|
||||
|
||||
import com.sun.jna.ptr.IntByReference;
|
||||
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.Collection;
|
||||
import java.util.TreeMap;
|
||||
|
|
@ -90,7 +89,6 @@ public class RegistryKey implements AutoCloseable {
|
|||
return convertBufferToInt(getValue(valueName));
|
||||
}
|
||||
|
||||
@SuppressFBWarnings(value = "SF_SWITCH_FALLTHROUGH", justification = "TODO needs triage")
|
||||
private byte[] getValue(String valueName) {
|
||||
IntByReference pType, lpcbData;
|
||||
byte[] lpData = new byte[1];
|
||||
|
|
@ -151,7 +149,6 @@ public class RegistryKey implements AutoCloseable {
|
|||
/**
|
||||
* Does a specified value exist?
|
||||
*/
|
||||
@SuppressFBWarnings(value = "SF_SWITCH_FALLTHROUGH", justification = "TODO needs triage")
|
||||
public boolean valueExists(String name) {
|
||||
IntByReference pType, lpcbData;
|
||||
byte[] lpData = new byte[1];
|
||||
|
|
@ -226,7 +223,6 @@ public class RegistryKey implements AutoCloseable {
|
|||
*
|
||||
* @return TreeMap with name and value pairs
|
||||
*/
|
||||
@SuppressFBWarnings(value = "SF_SWITCH_FALLTHROUGH", justification = "TODO needs triage")
|
||||
public TreeMap<String, Object> getValues() {
|
||||
int dwIndex, result;
|
||||
char[] lpValueName;
|
||||
|
|
|
|||
|
|
@ -34,8 +34,7 @@ import org.jenkinsci.Symbol;
|
|||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
@Extension(optional = true) @Symbol("hsErrPid")
|
||||
// TODO why would an extension using a built-in extension point need to be marked optional?
|
||||
@Extension @Symbol("hsErrPid")
|
||||
public class HsErrPidList extends AdministrativeMonitor {
|
||||
/**
|
||||
* hs_err_pid files that we think belong to us.
|
||||
|
|
|
|||
|
|
@ -26,8 +26,19 @@ package jenkins.health;
|
|||
|
||||
import hudson.Extension;
|
||||
import hudson.ExtensionList;
|
||||
import hudson.FilePath;
|
||||
import hudson.model.InvisibleAction;
|
||||
import hudson.model.UnprotectedRootAction;
|
||||
import hudson.util.RemotingDiagnostics;
|
||||
import java.io.IOException;
|
||||
import java.time.Duration;
|
||||
import java.util.Timer;
|
||||
import java.util.TimerTask;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
import java.util.stream.Collectors;
|
||||
import jenkins.util.JenkinsJVM;
|
||||
import jenkins.util.SystemProperties;
|
||||
import net.sf.json.JSONArray;
|
||||
import net.sf.json.JSONObject;
|
||||
import org.kohsuke.accmod.Restricted;
|
||||
|
|
@ -42,6 +53,10 @@ import org.kohsuke.stapler.json.JsonHttpResponse;
|
|||
@Restricted(NoExternalUse.class)
|
||||
public final class HealthCheckAction extends InvisibleAction implements UnprotectedRootAction {
|
||||
|
||||
private static final Logger LOGGER = Logger.getLogger(HealthCheckAction.class.getName());
|
||||
private static final Duration THRESHOLD_TIMEOUT = SystemProperties.getDuration(
|
||||
HealthCheckAction.class.getName() + ".thresholdTimeout", Duration.ofSeconds(10));
|
||||
|
||||
@Override
|
||||
public String getUrlName() {
|
||||
return "health";
|
||||
|
|
@ -50,17 +65,40 @@ public final class HealthCheckAction extends InvisibleAction implements Unprotec
|
|||
public HttpResponse doIndex() {
|
||||
boolean success = true;
|
||||
var failing = new JSONArray();
|
||||
for (var healthCheck : ExtensionList.lookup(HealthCheck.class)) {
|
||||
var check = healthCheck.check();
|
||||
success &= check;
|
||||
if (!check) {
|
||||
failing.add(healthCheck.getName());
|
||||
|
||||
var watchdog = new Timer("HealthCheckActionWatchdog", true);
|
||||
watchdog.schedule(new TimerTask() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (JenkinsJVM.isJenkinsJVM()) {
|
||||
try {
|
||||
var threadDump = RemotingDiagnostics.getThreadDump(FilePath.localChannel);
|
||||
LOGGER.severe(() -> "health check did not complete in timely fashion:\n\n"
|
||||
+ threadDump.values().stream().collect(Collectors.joining()).trim());
|
||||
} catch (IOException e) {
|
||||
LOGGER.log(Level.WARNING, "Failed to get thread dump during slow health check", e);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
}
|
||||
}, THRESHOLD_TIMEOUT.toMillis());
|
||||
|
||||
try {
|
||||
for (var healthCheck : ExtensionList.lookup(HealthCheck.class)) {
|
||||
var check = healthCheck.check();
|
||||
success &= check;
|
||||
if (!check) {
|
||||
failing.add(healthCheck.getName());
|
||||
}
|
||||
}
|
||||
var payload = new JSONObject().element("status", success);
|
||||
if (!success) {
|
||||
payload = payload.element("failures", failing);
|
||||
}
|
||||
return new JsonHttpResponse(payload, success ? 200 : 503);
|
||||
} finally {
|
||||
watchdog.cancel();
|
||||
}
|
||||
var payload = new JSONObject().element("status", success);
|
||||
if (!success) {
|
||||
payload = payload.element("failures", failing);
|
||||
}
|
||||
return new JsonHttpResponse(payload, success ? 200 : 503);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2018 CloudBees, Inc.
|
||||
* Copyright (c) 2025, Jan Faracik
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
|
|
@ -22,28 +22,29 @@
|
|||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package jenkins.util.java;
|
||||
package jenkins.job;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeFalse;
|
||||
import static org.junit.jupiter.api.Assumptions.assumeTrue;
|
||||
import hudson.model.Actionable;
|
||||
import jenkins.model.Tab;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.jvnet.hudson.test.For;
|
||||
public class OverviewTab extends Tab {
|
||||
|
||||
@For(JavaUtils.class)
|
||||
class JavaUtilsTest {
|
||||
|
||||
@Test
|
||||
void verifyJava8() {
|
||||
assumeTrue(System.getProperty("java.version").startsWith("1."), "Test is for Java 8 only");
|
||||
assertFalse(JavaUtils.isRunningWithPostJava8(), "isRunningWithPostJava8() should return false on Java 8 and below");
|
||||
public OverviewTab(Actionable object) {
|
||||
super(object);
|
||||
}
|
||||
|
||||
@Test
|
||||
void verifyPostJava8() {
|
||||
assumeFalse(System.getProperty("java.version").startsWith("1."), "Test is for Java 9+ only");
|
||||
assertTrue(JavaUtils.isRunningWithPostJava8(), "isRunningWithPostJava8() should return true on Java 9 and above");
|
||||
@Override
|
||||
public String getIconFileName() {
|
||||
return "symbol-overview";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "Overview";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrlName() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2025, Jan Faracik
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package jenkins.job;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.NonNull;
|
||||
import hudson.Extension;
|
||||
import hudson.model.Job;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import jenkins.model.Tab;
|
||||
import jenkins.model.TransientActionFactory;
|
||||
import jenkins.model.experimentalflags.NewJobPageUserExperimentalFlag;
|
||||
|
||||
@Extension(ordinal = Integer.MAX_VALUE)
|
||||
public class OverviewTabFactory extends TransientActionFactory<Job> {
|
||||
|
||||
@Override
|
||||
public Class<Job> type() {
|
||||
return Job.class;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Collection<? extends Tab> createFor(@NonNull Job target) {
|
||||
boolean isExperimentalUiEnabled = new NewJobPageUserExperimentalFlag().getFlagValue();
|
||||
|
||||
if (!isExperimentalUiEnabled) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
return Collections.singleton(new OverviewTab(target));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2025, Jan Faracik
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package jenkins.model;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.CheckForNull;
|
||||
import jenkins.management.Badge;
|
||||
|
||||
/**
|
||||
* Represents an entity that can display a {@link Badge}.
|
||||
* <p>
|
||||
* Implementations of this interface may provide a badge
|
||||
* to be shown on an associated action or UI element.
|
||||
* If no badge is provided, {@code null} may be returned.
|
||||
* </p>
|
||||
*
|
||||
* @since 2.532
|
||||
*/
|
||||
public interface Badgeable {
|
||||
|
||||
/**
|
||||
* A {@link Badge} shown on the action.
|
||||
*
|
||||
* @return badge or {@code null} if no badge should be shown.
|
||||
* @since 2.532
|
||||
*/
|
||||
default @CheckForNull Badge getBadge() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,39 +0,0 @@
|
|||
package jenkins.model;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.CheckForNull;
|
||||
import hudson.Extension;
|
||||
import hudson.RestrictedSince;
|
||||
import hudson.model.RootAction;
|
||||
import org.jenkinsci.Symbol;
|
||||
import org.kohsuke.accmod.Restricted;
|
||||
import org.kohsuke.accmod.restrictions.NoExternalUse;
|
||||
|
||||
/**
|
||||
* Redirects from /configureClouds to /cloud/.
|
||||
* Previously was the form for clouds.
|
||||
* @deprecated Replaced by {@link jenkins.agents.CloudsLink} and {@link jenkins.agents.CloudSet}.
|
||||
*/
|
||||
@Extension
|
||||
@Symbol("cloud")
|
||||
@Restricted(NoExternalUse.class)
|
||||
@RestrictedSince("2.205")
|
||||
@Deprecated
|
||||
public class GlobalCloudConfiguration implements RootAction {
|
||||
|
||||
@CheckForNull
|
||||
@Override
|
||||
public String getIconFileName() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@CheckForNull
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return Messages.GlobalCloudConfiguration_DisplayName();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrlName() {
|
||||
return "configureClouds";
|
||||
}
|
||||
}
|
||||
|
|
@ -34,6 +34,7 @@ import static hudson.init.InitMilestone.EXTENSIONS_AUGMENTED;
|
|||
import static hudson.init.InitMilestone.JOB_CONFIG_ADAPTED;
|
||||
import static hudson.init.InitMilestone.JOB_LOADED;
|
||||
import static hudson.init.InitMilestone.PLUGINS_PREPARED;
|
||||
import static hudson.init.InitMilestone.SYSTEM_CONFIG_ADAPTED;
|
||||
import static hudson.init.InitMilestone.SYSTEM_CONFIG_LOADED;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_BAD_REQUEST;
|
||||
import static jakarta.servlet.http.HttpServletResponse.SC_NOT_FOUND;
|
||||
|
|
@ -41,6 +42,7 @@ import static java.util.logging.Level.FINE;
|
|||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.SEVERE;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
import static jenkins.model.Messages.Hudson_Computer_IncorrectNumberOfExecutors;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.inject.Inject;
|
||||
|
|
@ -176,6 +178,7 @@ import hudson.slaves.NodePropertyDescriptor;
|
|||
import hudson.slaves.NodeProvisioner;
|
||||
import hudson.slaves.OfflineCause;
|
||||
import hudson.slaves.RetentionStrategy;
|
||||
import hudson.slaves.SlaveComputer;
|
||||
import hudson.tasks.BuildWrapper;
|
||||
import hudson.tasks.Builder;
|
||||
import hudson.tasks.Publisher;
|
||||
|
|
@ -3496,7 +3499,7 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
|
|||
|
||||
List<Handle> loadJobs = new ArrayList<>();
|
||||
for (final File subdir : subdirs) {
|
||||
loadJobs.add(g.requires(loadJenkins).attains(JOB_LOADED).notFatal().add("Loading item " + subdir.getName(), new Executable() {
|
||||
loadJobs.add(g.requires(loadJenkins).requires(SYSTEM_CONFIG_ADAPTED).attains(JOB_LOADED).notFatal().add("Loading item " + subdir.getName(), new Executable() {
|
||||
@Override
|
||||
public void run(Reactor session) throws Exception {
|
||||
if (!Items.getConfigFile(subdir).exists()) {
|
||||
|
|
@ -3775,7 +3778,10 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
|
|||
for (Computer c : getComputersCollection()) {
|
||||
try {
|
||||
c.interrupt();
|
||||
killComputer(c);
|
||||
c.setNumExecutors(0);
|
||||
if (Main.isUnitTest && c instanceof SlaveComputer sc) {
|
||||
sc.closeLog(); // help TemporaryDirectoryAllocator.dispose esp. on Windows
|
||||
}
|
||||
pending.add(c.disconnect(null));
|
||||
} catch (OutOfMemoryError e) {
|
||||
// we should just propagate this, no point trying to log
|
||||
|
|
@ -3950,9 +3956,15 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
|
|||
if (!pending.isEmpty()) {
|
||||
LOGGER.log(Main.isUnitTest ? Level.FINE : Level.INFO, "Waiting for node disconnection completion");
|
||||
}
|
||||
long end = System.nanoTime() + Duration.ofSeconds(10).toNanos();
|
||||
for (Future<?> f : pending) {
|
||||
try {
|
||||
f.get(10, TimeUnit.SECONDS); // if clean up operation didn't complete in time, we fail the test
|
||||
long remaining = end - System.nanoTime();
|
||||
if (remaining <= 0) {
|
||||
LOGGER.warning("Ran out of time waiting for agents to disconnect");
|
||||
break;
|
||||
}
|
||||
f.get(remaining, TimeUnit.NANOSECONDS);
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
break; // someone wants us to die now. quick!
|
||||
|
|
@ -4090,28 +4102,6 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
|
|||
return d.configure(req, js);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts submission from the node configuration page.
|
||||
*/
|
||||
@POST
|
||||
public synchronized void doConfigExecutorsSubmit(StaplerRequest2 req, StaplerResponse2 rsp) throws IOException, ServletException, FormException {
|
||||
checkPermission(ADMINISTER);
|
||||
|
||||
try (BulkChange bc = new BulkChange(this)) {
|
||||
JSONObject json = req.getSubmittedForm();
|
||||
|
||||
ExtensionList.lookupSingleton(MasterBuildConfiguration.class).configure(req, json);
|
||||
|
||||
getNodeProperties().rebuild(req, json.optJSONObject("nodeProperties"), NodeProperty.all());
|
||||
|
||||
bc.commit();
|
||||
}
|
||||
|
||||
updateComputers(this);
|
||||
|
||||
FormApply.success(req.getContextPath() + '/' + toComputer().getUrl()).generateResponse(req, rsp, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Accepts the new description.
|
||||
*/
|
||||
|
|
@ -5498,7 +5488,44 @@ public class Jenkins extends AbstractCIBase implements DirectlyModifiableTopLeve
|
|||
@Override
|
||||
@POST
|
||||
public void doConfigSubmit(StaplerRequest2 req, StaplerResponse2 rsp) throws IOException, ServletException, FormException {
|
||||
Jenkins.get().doConfigExecutorsSubmit(req, rsp);
|
||||
checkPermission(ADMINISTER);
|
||||
|
||||
Jenkins jenkins = Jenkins.get();
|
||||
|
||||
try (BulkChange bc = new BulkChange(jenkins)) {
|
||||
JSONObject json = req.getSubmittedForm();
|
||||
|
||||
try {
|
||||
// For compatibility reasons, this value is stored in Jenkins
|
||||
String num = json.getString("numExecutors");
|
||||
if (!num.matches("\\d+")) {
|
||||
throw new Descriptor.FormException(Hudson_Computer_IncorrectNumberOfExecutors(), "numExecutors");
|
||||
}
|
||||
|
||||
jenkins.setNumExecutors(json.getInt("numExecutors"));
|
||||
if (req.hasParameter("builtin.mode")) {
|
||||
jenkins.setMode(Mode.valueOf(req.getParameter("builtin.mode")));
|
||||
} else {
|
||||
jenkins.setMode(Mode.NORMAL);
|
||||
}
|
||||
|
||||
jenkins.setLabelString(json.optString("labelString", ""));
|
||||
} catch (IOException e) {
|
||||
throw new Descriptor.FormException(e, "numExecutors");
|
||||
}
|
||||
|
||||
jenkins.getNodeProperties().rebuild(req, json.optJSONObject("nodeProperties"), NodeProperty.all());
|
||||
|
||||
bc.commit();
|
||||
}
|
||||
|
||||
jenkins.updateComputers(jenkins);
|
||||
|
||||
Computer computer = jenkins.toComputer();
|
||||
if (computer == null) {
|
||||
throw new IllegalStateException("Cannot find the computer object for the controller node");
|
||||
}
|
||||
FormApply.success(req.getContextPath() + '/' + computer.getUrl()).generateResponse(req, rsp, null);
|
||||
}
|
||||
|
||||
@WebMethod(name = "config.xml")
|
||||
|
|
|
|||
|
|
@ -1,72 +0,0 @@
|
|||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2011, CloudBees, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package jenkins.model;
|
||||
|
||||
import hudson.Extension;
|
||||
import hudson.model.Node.Mode;
|
||||
import java.io.IOException;
|
||||
import net.sf.json.JSONObject;
|
||||
import org.jenkinsci.Symbol;
|
||||
import org.kohsuke.stapler.StaplerRequest2;
|
||||
|
||||
/**
|
||||
* Adds the configuration regarding building on the built-in node.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
*/
|
||||
@Extension(ordinal = 500) @Symbol({"builtInNode", "masterBuild"})
|
||||
public class MasterBuildConfiguration extends GlobalConfiguration {
|
||||
public int getNumExecutors() {
|
||||
return Jenkins.get().getNumExecutors();
|
||||
}
|
||||
|
||||
public String getLabelString() {
|
||||
return Jenkins.get().getLabelString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean configure(StaplerRequest2 req, JSONObject json) throws FormException {
|
||||
Jenkins j = Jenkins.get();
|
||||
try {
|
||||
// for compatibility reasons, this value is stored in Jenkins
|
||||
String num = json.getString("numExecutors");
|
||||
if (!num.matches("\\d+")) {
|
||||
throw new FormException(Messages.Hudson_Computer_IncorrectNumberOfExecutors(), "numExecutors");
|
||||
}
|
||||
|
||||
j.setNumExecutors(json.getInt("numExecutors"));
|
||||
if (req.hasParameter("builtin.mode"))
|
||||
j.setMode(Mode.valueOf(req.getParameter("builtin.mode")));
|
||||
else
|
||||
j.setMode(Mode.NORMAL);
|
||||
|
||||
j.setLabelString(json.optString("labelString", ""));
|
||||
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
throw new FormException(e, "numExecutors");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -140,6 +140,35 @@ public class Nodes implements PersistenceRoot {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a node if a node with the given name doesn't already exist. This is equivalent to
|
||||
*
|
||||
* <pre>
|
||||
* if (nodes.getNode(node.getNodeName()) == null) {
|
||||
* nodes.addNode(node);
|
||||
* }
|
||||
* </pre>
|
||||
*
|
||||
* except that it happens atomically.
|
||||
*
|
||||
* @param node the new node.
|
||||
* @return True if the node was added. False otherwise (indicating a node with the given name already exists)
|
||||
* @throws IOException if the list of nodes could not be persisted.
|
||||
* @since 2.529
|
||||
*/
|
||||
public boolean addNodeIfAbsent(final @NonNull Node node) throws IOException {
|
||||
if (ENFORCE_NAME_RESTRICTIONS) {
|
||||
Jenkins.checkGoodName(node.getNodeName());
|
||||
}
|
||||
|
||||
Node old = nodes.putIfAbsent(node.getNodeName(), node);
|
||||
if (old == null) {
|
||||
handleAddedNode(node, null);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a node. If a node of the same name already exists then that node will be replaced.
|
||||
*
|
||||
|
|
@ -153,34 +182,35 @@ public class Nodes implements PersistenceRoot {
|
|||
|
||||
Node old = nodes.put(node.getNodeName(), node);
|
||||
if (node != old) {
|
||||
node.onLoad(this, node.getNodeName());
|
||||
jenkins.updateNewComputer(node);
|
||||
jenkins.trimLabels(node, old);
|
||||
// TODO there is a theoretical race whereby the node instance is updated/removed after lock release
|
||||
try {
|
||||
node.save();
|
||||
} catch (IOException | RuntimeException e) {
|
||||
// JENKINS-50599: If persisting the node throws an exception, we need to remove the node from
|
||||
// memory before propagating the exception.
|
||||
Queue.withLock(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
nodes.compute(node.getNodeName(), (ignoredNodeName, ignoredNode) -> old);
|
||||
jenkins.updateComputers(node);
|
||||
if (old != null) {
|
||||
jenkins.trimLabels(node, old);
|
||||
} else {
|
||||
jenkins.trimLabels(node);
|
||||
}
|
||||
}
|
||||
});
|
||||
throw e;
|
||||
}
|
||||
if (old != null) {
|
||||
NodeListener.fireOnUpdated(old, node);
|
||||
} else {
|
||||
NodeListener.fireOnCreated(node);
|
||||
}
|
||||
handleAddedNode(node, old);
|
||||
}
|
||||
}
|
||||
|
||||
private void handleAddedNode(final @NonNull Node node, final Node old) throws IOException {
|
||||
node.onLoad(this, node.getNodeName());
|
||||
jenkins.updateNewComputer(node);
|
||||
jenkins.trimLabels(node, old);
|
||||
// TODO there is a theoretical race whereby the node instance is updated/removed after lock release
|
||||
try {
|
||||
node.save();
|
||||
} catch (IOException | RuntimeException e) {
|
||||
// JENKINS-50599: If persisting the node throws an exception, we need to remove the node from
|
||||
// memory before propagating the exception.
|
||||
Queue.runWithLock(() -> {
|
||||
nodes.compute(node.getNodeName(), (ignoredNodeName, ignoredNode) -> old);
|
||||
jenkins.updateComputers(node);
|
||||
if (old != null) {
|
||||
jenkins.trimLabels(node, old);
|
||||
} else {
|
||||
jenkins.trimLabels(node);
|
||||
}
|
||||
});
|
||||
throw e;
|
||||
}
|
||||
if (old != null) {
|
||||
NodeListener.fireOnUpdated(old, node);
|
||||
} else {
|
||||
NodeListener.fireOnCreated(node);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -250,13 +280,10 @@ public class Nodes implements PersistenceRoot {
|
|||
|
||||
if (oldOne == nodes.get(oldOne.getNodeName())) {
|
||||
// use the queue lock until Nodes has a way of directly modifying a single node.
|
||||
Queue.withLock(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Nodes.this.nodes.remove(oldOne.getNodeName());
|
||||
Nodes.this.nodes.put(newOne.getNodeName(), newOne);
|
||||
newOne.onLoad(Nodes.this, newOne.getNodeName());
|
||||
}
|
||||
Queue.runWithLock(() -> {
|
||||
Nodes.this.nodes.remove(oldOne.getNodeName());
|
||||
Nodes.this.nodes.put(newOne.getNodeName(), newOne);
|
||||
newOne.onLoad(Nodes.this, newOne.getNodeName());
|
||||
});
|
||||
updateNode(newOne, false);
|
||||
if (!newOne.getNodeName().equals(oldOne.getNodeName())) {
|
||||
|
|
@ -285,16 +312,13 @@ public class Nodes implements PersistenceRoot {
|
|||
public void removeNode(final @NonNull Node node) throws IOException {
|
||||
if (node == nodes.get(node.getNodeName())) {
|
||||
AtomicBoolean match = new AtomicBoolean();
|
||||
Queue.withLock(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
Computer c = node.toComputer();
|
||||
if (c != null) {
|
||||
c.recordTermination();
|
||||
c.disconnect(OfflineCause.create(hudson.model.Messages._Hudson_NodeBeingRemoved()));
|
||||
}
|
||||
match.set(node == nodes.remove(node.getNodeName()));
|
||||
Queue.runWithLock(() -> {
|
||||
Computer c = node.toComputer();
|
||||
if (c != null) {
|
||||
c.recordTermination();
|
||||
c.disconnect(OfflineCause.create(hudson.model.Messages._Hudson_NodeBeingRemoved()));
|
||||
}
|
||||
match.set(node == nodes.remove(node.getNodeName()));
|
||||
});
|
||||
// no need for a full save() so we just do the minimum
|
||||
LOGGER.fine(() -> "deleting " + new File(getRootDir(), node.getNodeName()));
|
||||
|
|
@ -354,21 +378,18 @@ public class Nodes implements PersistenceRoot {
|
|||
}
|
||||
}
|
||||
}
|
||||
Queue.withLock(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
newNodes.entrySet().removeIf(stringNodeEntry -> ExtensionList.lookup(NodeListener.class).stream().anyMatch(nodeListener -> {
|
||||
if (!nodeListener.allowLoad(stringNodeEntry.getValue())) {
|
||||
LOGGER.log(Level.FINE, () -> "Loading of node " + stringNodeEntry.getKey() + " vetoed by " + nodeListener);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}));
|
||||
nodes.entrySet().removeIf(stringNodeEntry -> !(stringNodeEntry.getValue() instanceof EphemeralNode));
|
||||
nodes.putAll(newNodes);
|
||||
jenkins.updateComputerList();
|
||||
jenkins.trimLabels();
|
||||
}
|
||||
Queue.runWithLock(() -> {
|
||||
newNodes.entrySet().removeIf(stringNodeEntry -> ExtensionList.lookup(NodeListener.class).stream().anyMatch(nodeListener -> {
|
||||
if (!nodeListener.allowLoad(stringNodeEntry.getValue())) {
|
||||
LOGGER.log(Level.FINE, () -> "Loading of node " + stringNodeEntry.getKey() + " vetoed by " + nodeListener);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}));
|
||||
nodes.entrySet().removeIf(stringNodeEntry -> !(stringNodeEntry.getValue() instanceof EphemeralNode));
|
||||
nodes.putAll(newNodes);
|
||||
jenkins.updateComputerList();
|
||||
jenkins.trimLabels();
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,6 +29,8 @@ import static jakarta.servlet.http.HttpServletResponse.SC_CONFLICT;
|
|||
import static jakarta.servlet.http.HttpServletResponse.SC_CREATED;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.CheckForNull;
|
||||
import edu.umd.cs.findbugs.annotations.NonNull;
|
||||
import hudson.Extension;
|
||||
import hudson.Util;
|
||||
import hudson.cli.declarative.CLIMethod;
|
||||
import hudson.cli.declarative.CLIResolver;
|
||||
|
|
@ -60,6 +62,9 @@ import java.util.Arrays;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import jenkins.model.details.Detail;
|
||||
import jenkins.model.details.DetailFactory;
|
||||
import jenkins.model.details.ParameterizedDetail;
|
||||
import jenkins.model.lazy.LazyBuildMixIn;
|
||||
import jenkins.security.stapler.StaplerNotDispatchable;
|
||||
import jenkins.triggers.SCMTriggerItem;
|
||||
|
|
@ -561,6 +566,25 @@ public abstract class ParameterizedJobMixIn<JobT extends Job<JobT, RunT> & Param
|
|||
return !isDisabled() && !((Job) this).isHoldOffBuildUntilSave();
|
||||
}
|
||||
|
||||
@Extension
|
||||
final class ParameterizedDetailFactory extends DetailFactory<Run> {
|
||||
|
||||
@Override
|
||||
public Class<Run> type() {
|
||||
return Run.class;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override public List<? extends Detail> createFor(@NonNull Run target) {
|
||||
var action = target.getAction(ParametersAction.class);
|
||||
|
||||
if (action == null || action.getParameters().isEmpty()) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
return List.of(new ParameterizedDetail(target));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2025, Jan Faracik
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package jenkins.model;
|
||||
|
||||
import hudson.model.Action;
|
||||
import hudson.model.Actionable;
|
||||
|
||||
/**
|
||||
* Represents a tab element shown on {@link Actionable} views.
|
||||
* <p>
|
||||
* A {@code Tab} is an {@link Action} that can be attached to an {@link Actionable} object
|
||||
* (such as a job or build) and displayed as a separate tab in the UI.
|
||||
* </p>
|
||||
*
|
||||
* <p>
|
||||
* Tabs may also implement {@link Badgeable} to display a visual badge associated
|
||||
* with the tab’s action
|
||||
* </p>
|
||||
*
|
||||
* @since 2.532
|
||||
*/
|
||||
public abstract class Tab implements Action, Badgeable {
|
||||
|
||||
protected transient Actionable object;
|
||||
|
||||
public Tab(Actionable object) {
|
||||
this.object = object;
|
||||
}
|
||||
|
||||
public Actionable getObject() {
|
||||
return object;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,29 @@
|
|||
package jenkins.model.details;
|
||||
|
||||
import hudson.model.Cause;
|
||||
import hudson.model.CauseAction;
|
||||
import hudson.model.Run;
|
||||
import java.util.Map;
|
||||
import org.kohsuke.accmod.Restricted;
|
||||
import org.kohsuke.accmod.restrictions.NoExternalUse;
|
||||
|
||||
/**
|
||||
* Displays the cause for the given run
|
||||
*/
|
||||
public class CauseDetail extends Detail {
|
||||
|
||||
public CauseDetail(Run<?, ?> run) {
|
||||
super(run);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return Integer.MAX_VALUE;
|
||||
}
|
||||
|
||||
@Restricted(NoExternalUse.class)
|
||||
public Map<Cause, Integer> getCauseCounts() {
|
||||
CauseAction causeAction = getObject().getAction(CauseAction.class);
|
||||
return causeAction.getCauseCounts();
|
||||
}
|
||||
}
|
||||
|
|
@ -60,9 +60,9 @@ public abstract class Detail implements ModelObject, IconSpec {
|
|||
}
|
||||
|
||||
/**
|
||||
* @return order in the group, zero is first, MAX_VALUE is any order
|
||||
* @return order in the group, MAX_VALUE is first, zero is any order
|
||||
*/
|
||||
public int getOrder() {
|
||||
return Integer.MAX_VALUE;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
package jenkins.model.details;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.Nullable;
|
||||
import hudson.model.AbstractProject;
|
||||
import hudson.model.Actionable;
|
||||
import hudson.model.Item;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Displays downstream projects of a project (if any)
|
||||
*/
|
||||
public class DownstreamProjectsDetail extends Detail {
|
||||
|
||||
public DownstreamProjectsDetail(Actionable object) {
|
||||
super(object);
|
||||
}
|
||||
|
||||
public @Nullable String getIconClassName() {
|
||||
if (getProjects().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return "symbol-arrow-down-circle-outline plugin-ionicons-api";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getDisplayName() {
|
||||
int projectSize = getProjects().size();
|
||||
|
||||
if (projectSize == 1) {
|
||||
return "1 downstream project";
|
||||
}
|
||||
|
||||
return projectSize + " downstream projects";
|
||||
}
|
||||
|
||||
public List<AbstractProject> getProjects() {
|
||||
if (!(getObject() instanceof AbstractProject)) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
List<AbstractProject> projects = ((AbstractProject) getObject()).getDownstreamProjects();
|
||||
return projects.stream().filter(e -> e.hasPermission(Item.READ)).toList();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
package jenkins.model.details;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.Nullable;
|
||||
import hudson.model.ParametersAction;
|
||||
import hudson.model.Run;
|
||||
|
||||
/**
|
||||
* Displays if a run has parameters
|
||||
*/
|
||||
public class ParameterizedDetail extends Detail {
|
||||
|
||||
public final ParametersAction action;
|
||||
|
||||
public ParameterizedDetail(Run<?, ?> run) {
|
||||
super(run);
|
||||
this.action = getObject().getAction(ParametersAction.class);
|
||||
}
|
||||
|
||||
public @Nullable String getIconClassName() {
|
||||
return "symbol-parameters";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getDisplayName() {
|
||||
return action.getDisplayName();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
package jenkins.model.details;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.Nullable;
|
||||
import hudson.model.Actionable;
|
||||
import hudson.model.Hudson;
|
||||
import hudson.model.Job;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* Displays the full name of a project (if necessary)
|
||||
*/
|
||||
public class ProjectNameDetail extends Detail {
|
||||
|
||||
public ProjectNameDetail(Actionable object) {
|
||||
super(object);
|
||||
}
|
||||
|
||||
public @Nullable String getIconClassName() {
|
||||
if (getDisplayName() == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return "symbol-information-circle";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getDisplayName() {
|
||||
var it = (Job<?, ?>) getObject();
|
||||
|
||||
if (Objects.equals(it.getFullName(), it.getFullDisplayName()) || it.getClass().getName().equals("MatrixConfiguration")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
boolean nested = it.getParent().getClass() != Hudson.class;
|
||||
String label = nested ? "Full project name" : "Project name";
|
||||
|
||||
return label + ": " + it.getFullName();
|
||||
}
|
||||
}
|
||||
|
|
@ -10,4 +10,9 @@ public class TimestampDetail extends Detail {
|
|||
public TimestampDetail(Run<?, ?> run) {
|
||||
super(run);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOrder() {
|
||||
return Integer.MAX_VALUE - 1;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,45 @@
|
|||
package jenkins.model.details;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.Nullable;
|
||||
import hudson.model.AbstractProject;
|
||||
import hudson.model.Actionable;
|
||||
import hudson.model.Item;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Displays upstream projects of a project (if any)
|
||||
*/
|
||||
public class UpstreamProjectsDetail extends Detail {
|
||||
|
||||
public UpstreamProjectsDetail(Actionable object) {
|
||||
super(object);
|
||||
}
|
||||
|
||||
public @Nullable String getIconClassName() {
|
||||
if (getProjects().isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return "symbol-arrow-up-circle-outline plugin-ionicons-api";
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable String getDisplayName() {
|
||||
int projectSize = getProjects().size();
|
||||
|
||||
if (projectSize == 1) {
|
||||
return "1 upstream project";
|
||||
}
|
||||
|
||||
return projectSize + " upstream projects";
|
||||
}
|
||||
|
||||
public List<AbstractProject> getProjects() {
|
||||
if (!(getObject() instanceof AbstractProject)) {
|
||||
return List.of();
|
||||
}
|
||||
|
||||
List<AbstractProject> projects = ((AbstractProject) getObject()).getUpstreamProjects();
|
||||
return projects.stream().filter(e -> e.hasPermission(Item.READ)).toList();
|
||||
}
|
||||
}
|
||||
|
|
@ -25,6 +25,7 @@
|
|||
package jenkins.model.experimentalflags;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.NonNull;
|
||||
import jenkins.util.SystemProperties;
|
||||
|
||||
/**
|
||||
* @since 2.395
|
||||
|
|
@ -36,7 +37,7 @@ public abstract class BooleanUserExperimentalFlag extends UserExperimentalFlag<B
|
|||
|
||||
@Override
|
||||
public @NonNull Boolean getDefaultValue() {
|
||||
return false;
|
||||
return SystemProperties.getBoolean(getFlagKey() + ".defaultValue");
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2025, Jan Faracik
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package jenkins.model.experimentalflags;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.Nullable;
|
||||
import hudson.Extension;
|
||||
import org.kohsuke.accmod.Restricted;
|
||||
import org.kohsuke.accmod.restrictions.NoExternalUse;
|
||||
|
||||
@Extension
|
||||
@Restricted(NoExternalUse.class)
|
||||
public class NewDashboardPageUserExperimentalFlag extends BooleanUserExperimentalFlag {
|
||||
public NewDashboardPageUserExperimentalFlag() {
|
||||
super("new-dashboard-page.flag");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "New dashboard page";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getShortDescription() {
|
||||
return "Enables a revamped dashboard page. This feature is still a work in progress, so some things might not work perfectly yet.";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2025, Jan Faracik
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package jenkins.model.experimentalflags;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.Nullable;
|
||||
import hudson.Extension;
|
||||
import org.kohsuke.accmod.Restricted;
|
||||
import org.kohsuke.accmod.restrictions.NoExternalUse;
|
||||
|
||||
@Extension
|
||||
@Restricted(NoExternalUse.class)
|
||||
public class NewJobPageUserExperimentalFlag extends BooleanUserExperimentalFlag {
|
||||
public NewJobPageUserExperimentalFlag() {
|
||||
super("new-job-page.flag");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "New job page";
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@Override
|
||||
public String getShortDescription() {
|
||||
return "Enables a revamped job page. This feature is still a work in progress, so some things might not work perfectly yet.";
|
||||
}
|
||||
}
|
||||
|
|
@ -82,11 +82,6 @@ import org.kohsuke.accmod.restrictions.NoExternalUse;
|
|||
* from concurrent modifications, where another thread deletes a build while one thread iterates them.
|
||||
*
|
||||
* <p>
|
||||
* Some of the {@link SortedMap} operations are inefficiently implemented, by
|
||||
* loading all the build records eagerly. We hope to replace
|
||||
* these implementations by more efficient lazy-loading ones as we go.
|
||||
*
|
||||
* <p>
|
||||
* Object lock of {@code this} is used to make sure mutation occurs sequentially.
|
||||
* That is, ensure that only one thread is actually calling {@link #retrieve(File)} and
|
||||
* updating {@link jenkins.model.lazy.AbstractLazyLoadRunMap#core}.
|
||||
|
|
@ -116,7 +111,7 @@ public abstract class AbstractLazyLoadRunMap<R> extends AbstractMap<Integer, R>
|
|||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<Integer, R>> entrySet() {
|
||||
public Set<Map.Entry<Integer, R>> entrySet() {
|
||||
assert baseDirInitialized();
|
||||
return adapter.entrySet();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -71,7 +71,7 @@ public final class BuildReference<R> {
|
|||
|
||||
/**
|
||||
* check if reference holder set.
|
||||
* means there war a try to load build object and we have some result of that try
|
||||
* means there was a try to load build object and we have some result of that try
|
||||
*
|
||||
* @return true if there was a try to
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ class BuildReferenceMapAdapter<R> extends AbstractMap<Integer, R> implements Sor
|
|||
}
|
||||
|
||||
@Override
|
||||
public Set<Entry<Integer, R>> entrySet() {
|
||||
public Set<Map.Entry<Integer, R>> entrySet() {
|
||||
return entrySet;
|
||||
}
|
||||
|
||||
|
|
@ -171,7 +171,7 @@ class BuildReferenceMapAdapter<R> extends AbstractMap<Integer, R> implements Sor
|
|||
public Iterator<Integer> iterator() {
|
||||
return new AdaptedIterator<>(BuildReferenceMapAdapter.this.entrySet().iterator()) {
|
||||
@Override
|
||||
protected Integer adapt(Entry<Integer, R> e) {
|
||||
protected Integer adapt(Map.Entry<Integer, R> e) {
|
||||
return e.getKey();
|
||||
}
|
||||
};
|
||||
|
|
@ -227,7 +227,7 @@ class BuildReferenceMapAdapter<R> extends AbstractMap<Integer, R> implements Sor
|
|||
public Iterator<R> iterator() {
|
||||
return new AdaptedIterator<>(BuildReferenceMapAdapter.this.entrySet().iterator()) {
|
||||
@Override
|
||||
protected R adapt(Entry<Integer, R> e) {
|
||||
protected R adapt(Map.Entry<Integer, R> e) {
|
||||
return e.getValue();
|
||||
}
|
||||
};
|
||||
|
|
@ -239,7 +239,7 @@ class BuildReferenceMapAdapter<R> extends AbstractMap<Integer, R> implements Sor
|
|||
}
|
||||
}
|
||||
|
||||
private class EntrySetAdapter extends AbstractSet<Entry<Integer, R>> {
|
||||
private class EntrySetAdapter extends AbstractSet<Map.Entry<Integer, R>> {
|
||||
@Override
|
||||
public int size() {
|
||||
return BuildReferenceMapAdapter.this.core.size();
|
||||
|
|
@ -268,13 +268,24 @@ class BuildReferenceMapAdapter<R> extends AbstractMap<Integer, R> implements Sor
|
|||
}
|
||||
|
||||
@Override
|
||||
public Iterator<Entry<Integer, R>> iterator() {
|
||||
public Iterator<Map.Entry<Integer, R>> iterator() {
|
||||
return new Iterator<>() {
|
||||
private Entry<Integer, R> current;
|
||||
private final Iterator<Entry<Integer, R>> it = Iterators.removeNull(Iterators.map(
|
||||
private Map.Entry<Integer, R> current;
|
||||
private final Iterator<Map.Entry<Integer, R>> it = Iterators.removeNull(Iterators.map(
|
||||
BuildReferenceMapAdapter.this.core.entrySet().iterator(), coreEntry -> {
|
||||
R v = BuildReferenceMapAdapter.this.resolver.resolveBuildRef(coreEntry.getValue());
|
||||
return v == null ? null : new AbstractMap.SimpleEntry<>(coreEntry.getKey(), v);
|
||||
BuildReference<R> ref = coreEntry.getValue();
|
||||
if (!ref.isSet()) {
|
||||
R r = resolver.resolveBuildRef(ref);
|
||||
// load not loaded or unloadable build
|
||||
if (r == null) {
|
||||
return null;
|
||||
}
|
||||
return new EntryAdapter(coreEntry, r);
|
||||
}
|
||||
if (ref.isUnloadable()) {
|
||||
return null;
|
||||
}
|
||||
return new EntryAdapter(coreEntry);
|
||||
}));
|
||||
|
||||
@Override
|
||||
|
|
@ -283,7 +294,7 @@ class BuildReferenceMapAdapter<R> extends AbstractMap<Integer, R> implements Sor
|
|||
}
|
||||
|
||||
@Override
|
||||
public Entry<Integer, R> next() {
|
||||
public Map.Entry<Integer, R> next() {
|
||||
return current = it.next();
|
||||
}
|
||||
|
||||
|
|
@ -303,6 +314,59 @@ class BuildReferenceMapAdapter<R> extends AbstractMap<Integer, R> implements Sor
|
|||
}
|
||||
}
|
||||
|
||||
private class EntryAdapter implements Entry<Integer, R> {
|
||||
private final Map.Entry<Integer, BuildReference<R>> coreEntry;
|
||||
private volatile R resolvedValue;
|
||||
|
||||
EntryAdapter(Map.Entry<Integer, BuildReference<R>> coreEntry) {
|
||||
this(coreEntry, null);
|
||||
}
|
||||
|
||||
EntryAdapter(Map.Entry<Integer, BuildReference<R>> coreEntry, R resolvedValue) {
|
||||
this.coreEntry = coreEntry;
|
||||
this.resolvedValue = resolvedValue;
|
||||
}
|
||||
|
||||
private Map.Entry<Integer, R> getResolvedEntry() {
|
||||
return new AbstractMap.SimpleEntry<>(getKey(), getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Integer getKey() {
|
||||
return coreEntry.getKey();
|
||||
}
|
||||
|
||||
@Override
|
||||
public R getValue() {
|
||||
R value = resolvedValue;
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
return resolvedValue = resolver.resolveBuildRef(coreEntry.getValue());
|
||||
}
|
||||
|
||||
@Override
|
||||
public R setValue(R value) {
|
||||
// BuildReferenceAdapter is read only
|
||||
throw new UnsupportedOperationException();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getResolvedEntry().toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return (o instanceof Map.Entry<?, ?>) && getResolvedEntry().equals(o);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getResolvedEntry().hashCode();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* An interface for resolving build references into actual build instances
|
||||
* and extracting basic metadata from them.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2025, Jan Faracik
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package jenkins.run;
|
||||
|
||||
import hudson.model.Actionable;
|
||||
import jenkins.model.Tab;
|
||||
|
||||
public class ChangesTab extends Tab {
|
||||
|
||||
public ChangesTab(Actionable object) {
|
||||
super(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIconFileName() {
|
||||
return "symbol-changes";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "Changes";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrlName() {
|
||||
return "changes";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2025, Jan Faracik
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package jenkins.run;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.NonNull;
|
||||
import hudson.Extension;
|
||||
import hudson.model.Run;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import jenkins.model.Tab;
|
||||
import jenkins.model.TransientActionFactory;
|
||||
import jenkins.model.experimentalflags.NewBuildPageUserExperimentalFlag;
|
||||
import jenkins.scm.RunWithSCM;
|
||||
|
||||
@Extension(ordinal = Integer.MAX_VALUE - 2)
|
||||
public class ChangesTabFactory extends TransientActionFactory<Run> {
|
||||
|
||||
@Override
|
||||
public Class<Run> type() {
|
||||
return Run.class;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Collection<? extends Tab> createFor(@NonNull Run target) {
|
||||
boolean isExperimentalUiEnabled = new NewBuildPageUserExperimentalFlag().getFlagValue();
|
||||
|
||||
if (!isExperimentalUiEnabled) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
if (target instanceof RunWithSCM<?, ?> targetWithSCM) {
|
||||
var hasChangeSet = !targetWithSCM.getChangeSets().isEmpty();
|
||||
if (hasChangeSet) {
|
||||
return Collections.singleton(new ChangesTab(target));
|
||||
}
|
||||
}
|
||||
|
||||
return Collections.emptySet();
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2025, Jan Faracik
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package jenkins.run;
|
||||
|
||||
import hudson.model.Actionable;
|
||||
import jenkins.model.Tab;
|
||||
|
||||
public class ConsoleTab extends Tab {
|
||||
|
||||
public ConsoleTab(Actionable object) {
|
||||
super(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIconFileName() {
|
||||
return "symbol-terminal";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "Console";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrlName() {
|
||||
return "console";
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2025, Jan Faracik
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package jenkins.run;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.NonNull;
|
||||
import hudson.Extension;
|
||||
import hudson.Functions;
|
||||
import hudson.model.Run;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import jenkins.console.DefaultConsoleUrlProvider;
|
||||
import jenkins.model.Tab;
|
||||
import jenkins.model.TransientActionFactory;
|
||||
import jenkins.model.experimentalflags.NewBuildPageUserExperimentalFlag;
|
||||
|
||||
@Extension(ordinal = Integer.MAX_VALUE - 1)
|
||||
public class ConsoleTabFactory extends TransientActionFactory<Run> {
|
||||
|
||||
@Override
|
||||
public Class<Run> type() {
|
||||
return Run.class;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Collection<? extends Tab> createFor(@NonNull Run target) {
|
||||
var consoleProvider = Functions.getConsoleProviderFor(target);
|
||||
boolean isExperimentalUiEnabled = new NewBuildPageUserExperimentalFlag().getFlagValue();
|
||||
|
||||
if (!consoleProvider.getClass().equals(DefaultConsoleUrlProvider.class) || !isExperimentalUiEnabled) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
return Collections.singleton(new ConsoleTab(target));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2025, Jan Faracik
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package jenkins.run;
|
||||
|
||||
import hudson.model.Actionable;
|
||||
import jenkins.model.Tab;
|
||||
|
||||
public class OverviewTab extends Tab {
|
||||
|
||||
public OverviewTab(Actionable object) {
|
||||
super(object);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getIconFileName() {
|
||||
return "symbol-overview";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDisplayName() {
|
||||
return "Overview";
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getUrlName() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright (c) 2025, Jan Faracik
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package jenkins.run;
|
||||
|
||||
import edu.umd.cs.findbugs.annotations.NonNull;
|
||||
import hudson.Extension;
|
||||
import hudson.model.Run;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import jenkins.model.Tab;
|
||||
import jenkins.model.TransientActionFactory;
|
||||
import jenkins.model.experimentalflags.NewBuildPageUserExperimentalFlag;
|
||||
|
||||
@Extension(ordinal = Integer.MAX_VALUE)
|
||||
public class OverviewTabFactory extends TransientActionFactory<Run> {
|
||||
|
||||
@Override
|
||||
public Class<Run> type() {
|
||||
return Run.class;
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@Override
|
||||
public Collection<? extends Tab> createFor(@NonNull Run target) {
|
||||
boolean isExperimentalUiEnabled = new NewBuildPageUserExperimentalFlag().getFlagValue();
|
||||
|
||||
if (!isExperimentalUiEnabled) {
|
||||
return Collections.emptySet();
|
||||
}
|
||||
|
||||
return Collections.singleton(new OverviewTab(target));
|
||||
}
|
||||
}
|
||||
|
|
@ -1,15 +1,13 @@
|
|||
package jenkins.security;
|
||||
|
||||
import hudson.remoting.Callable;
|
||||
import jenkins.util.ThrowingCallable;
|
||||
import org.jenkinsci.remoting.RoleChecker;
|
||||
|
||||
/**
|
||||
* {@link Callable} adapter for situations where Callable is not used for remoting but
|
||||
* just as a convenient function that has parameterized return value and exception type.
|
||||
*
|
||||
* @author Kohsuke Kawaguchi
|
||||
* @since 1.587 / 1.580.1
|
||||
* @deprecated use {@link ThrowingCallable} instead
|
||||
*/
|
||||
@Deprecated
|
||||
public abstract class NotReallyRoleSensitiveCallable<V, T extends Throwable> implements Callable<V, T> {
|
||||
@Override
|
||||
public void checkRoles(RoleChecker checker) throws SecurityException {
|
||||
|
|
|
|||
|
|
@ -134,7 +134,7 @@ public abstract class ProgressiveRendering {
|
|||
((ScheduledExecutorService) executorService).schedule(new Runnable() {
|
||||
@Override public void run() {
|
||||
LOG.log(Level.FINE, "some time has elapsed since {0} finished, so releasing", boundId);
|
||||
release();
|
||||
boundObjectTable.release(boundId);
|
||||
}
|
||||
}, timeout() /* add some grace period for browser/network overhead */ * 2, TimeUnit.MILLISECONDS);
|
||||
}
|
||||
|
|
@ -142,17 +142,6 @@ public abstract class ProgressiveRendering {
|
|||
});
|
||||
}
|
||||
|
||||
/** {@link BoundObjectTable#releaseMe} just cannot work the way we need it to. */
|
||||
private void release() {
|
||||
try {
|
||||
Method release = BoundObjectTable.Table.class.getDeclaredMethod("release", String.class);
|
||||
release.setAccessible(true);
|
||||
release.invoke(boundObjectTable, boundId);
|
||||
} catch (Exception x) {
|
||||
LOG.log(Level.WARNING, "failed to unbind " + boundId, x);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies important fields from the current HTTP request and makes them available during {@link #compute}.
|
||||
* This is necessary because some model methods such as {@link AbstractItem#getUrl} behave differently when called from a request.
|
||||
|
|
@ -281,7 +270,7 @@ public abstract class ProgressiveRendering {
|
|||
r.put("status", statusJSON);
|
||||
if (statusJSON instanceof String) { // somehow completed
|
||||
LOG.log(Level.FINE, "finished in news so releasing {0}", boundId);
|
||||
release();
|
||||
boundObjectTable.release(boundId);
|
||||
}
|
||||
lastNewsTime = System.currentTimeMillis();
|
||||
LOG.log(Level.FINER, "news from {0}", uri);
|
||||
|
|
|
|||
|
|
@ -0,0 +1,54 @@
|
|||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright 2025 CloudBees, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package jenkins.util;
|
||||
|
||||
import java.io.Serializable;
|
||||
import jenkins.security.NotReallyRoleSensitiveCallable;
|
||||
import org.jenkinsci.remoting.RoleSensitive;
|
||||
|
||||
/**
|
||||
* A task that returns a result and may throw an exception.
|
||||
* Similar to {@link java.util.concurrent.Callable} except that the exception type can be constrained.
|
||||
* Similar to {@link hudson.remoting.Callable} or {@link NotReallyRoleSensitiveCallable} except
|
||||
* <ul>
|
||||
* <li>It is not {@link Serializable}, which would cause SpotBugs to complain about captured local variables.
|
||||
* <li>It does not have the {@link RoleSensitive#checkRoles} so it can be a {@link FunctionalInterface}.
|
||||
* </ul>
|
||||
* Similar to {@link ThrowingRunnable} but returns a value.
|
||||
* @param <V> the return type
|
||||
* @param <T> the checked exception type, or might be {@link RuntimeException}
|
||||
* @since 2.534
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ThrowingCallable<V, T extends Throwable> {
|
||||
|
||||
/**
|
||||
* Computes a result, or throws an exception if unable to do so.
|
||||
* @return computed result
|
||||
* @throws T if unable to compute a result
|
||||
*/
|
||||
V call() throws T;
|
||||
|
||||
}
|
||||
|
|
@ -0,0 +1,42 @@
|
|||
/*
|
||||
* The MIT License
|
||||
*
|
||||
* Copyright 2025 CloudBees, Inc.
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package jenkins.util;
|
||||
|
||||
/**
|
||||
* Represents an operation that does not return a result.
|
||||
* Similar to {@link Runnable} but can throw a checked exception.
|
||||
* Similar to {@link ThrowingCallable} but does not return a value.
|
||||
* @param <T> the checked exception type, or might be {@link RuntimeException}
|
||||
*/
|
||||
@FunctionalInterface
|
||||
public interface ThrowingRunnable<T extends Throwable> {
|
||||
|
||||
/**
|
||||
* Runs this operation.
|
||||
* @throws T if unable to run
|
||||
*/
|
||||
void run() throws T;
|
||||
|
||||
}
|
||||
|
|
@ -40,24 +40,6 @@ public class JavaUtils {
|
|||
// Cannot construct
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the current JVM is running with Java 8 or below
|
||||
* @return {@code true} if it is Java 8 or older version
|
||||
*/
|
||||
public static boolean isRunningWithJava8OrBelow() {
|
||||
String javaVersion = getCurrentRuntimeJavaVersion();
|
||||
return javaVersion.startsWith("1.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Check whether the current JVM is running with Java 9 or above.
|
||||
* @return {@code true} if it is Java 9 or above
|
||||
*/
|
||||
public static boolean isRunningWithPostJava8() {
|
||||
String javaVersion = getCurrentRuntimeJavaVersion();
|
||||
return !javaVersion.startsWith("1.");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the JVM's current version as a {@link VersionNumber} instance.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ PluginWrapper.PluginWrapperAdministrativeMonitor.DisplayName=Plugin-Ladefehler
|
|||
|
||||
ProxyConfiguration.FailedToConnectViaProxy=Konnte nicht mit {0} verbinden.
|
||||
ProxyConfiguration.FailedToConnect=Konnte nicht mit {0} verbinden (code {1}).
|
||||
ProxyConfiguration.NonTLSWarning=Jenkins unterstützt nur die Verwendung einer HTTP-Verbindung zum Proxy. Die Zugangsdaten könnten für andere im selben Netzwerk sichtbar sein.
|
||||
ProxyConfiguration.MalformedTestUrl=Format der Test-URL ungültig
|
||||
ProxyConfiguration.Success=Erfolg (code {0})
|
||||
ProxyConfiguration.TestUrlRequired=Test-URL muss angegeben werden.
|
||||
|
|
|
|||
|
|
@ -131,7 +131,7 @@ THE SOFTWARE.
|
|||
<j:forEach var="label" items="${p.categories}">
|
||||
<j:if test="${!it.isMetaLabel(label)}">
|
||||
<a href="?filter=${app.updateCenter.getCategoryDisplayName(label)}"
|
||||
class="jenkins-table__link jenkins-table__badge">
|
||||
class="jenkins-badge">
|
||||
${app.updateCenter.getCategoryDisplayName(label)}
|
||||
</a>
|
||||
</j:if>
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@ Update=Mettre à jour
|
|||
Name=Nom
|
||||
Released=Publié(e)
|
||||
Installed=Installé
|
||||
Applying\ this\ update\ will\ address\ security\ vulnerabilities\ in\ the\ currently\ installed\ version.=L'application de cette mise à jour corrigera les failles de sécurité de la version actuellement installée.
|
||||
Applying\ this\ update\ will\ address\ security\ vulnerabilities\ in\ the\ currently\ installed\ version.=L''application de cette mise à jour corrigera les failles de sécurité de la version actuellement installée.
|
||||
No\ updates=Aucunes mises à jour
|
||||
Inactive=Inactif
|
||||
No\ updates\ available=Aucune mise à jour disponible
|
||||
|
|
|
|||
|
|
@ -1,32 +0,0 @@
|
|||
package hudson.ProxyConfiguration
|
||||
|
||||
def f=namespace(lib.FormTagLib)
|
||||
def l=namespace(lib.LayoutTagLib)
|
||||
|
||||
set("readOnlyMode", !app.hasPermission(app.ADMINISTER))
|
||||
|
||||
f.entry(title:_("Server"),field:"name") {
|
||||
f.textbox()
|
||||
}
|
||||
f.entry(title:_("Port"),field:"port") {
|
||||
f.number(clazz:"number",min:0,max:65535,step:1)
|
||||
}
|
||||
f.entry(title:_("User name"),field:"userName") {
|
||||
f.textbox()
|
||||
}
|
||||
f.entry(title:_("Password"),field:"secretPassword") {
|
||||
f.password()
|
||||
}
|
||||
f.entry(title:_("No Proxy Host"),field:"noProxyHost") {
|
||||
f.textarea()
|
||||
}
|
||||
|
||||
l.isAdmin() {
|
||||
f.advanced() {
|
||||
f.entry(title: _("Test URL"), field: "testUrl") {
|
||||
f.textbox()
|
||||
}
|
||||
f.validateButton(title:_("Validate Proxy"),
|
||||
method:"validateProxy", with:"testUrl,name,port,userName,secretPassword,noProxyHost")
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
<!--
|
||||
The MIT License
|
||||
Copyright (c) 2025, Stefan Spieker
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
-->
|
||||
|
||||
<?jelly escape-by-default='true'?>
|
||||
<j:jelly xmlns:j="jelly:core" xmlns:f="/lib/form" xmlns:l="/lib/layout">
|
||||
<j:set var="readOnlyMode" value="${!app.hasPermission(app.ADMINISTER)}"/>
|
||||
<f:entry title="${%Server}">
|
||||
<f:textbox field="name"/>
|
||||
</f:entry>
|
||||
<f:entry title="${%Port}">
|
||||
<f:number field="port" min="0" max="65535" step="1"/>
|
||||
</f:entry>
|
||||
<f:entry title="${%User name}">
|
||||
<f:textbox field="userName"/>
|
||||
</f:entry>
|
||||
<f:entry title="${%Password}">
|
||||
<f:password field="secretPassword"/>
|
||||
</f:entry>
|
||||
<f:entry title="${%No Proxy Host}">
|
||||
<f:textarea field="noProxyHost"/>
|
||||
</f:entry>
|
||||
|
||||
<l:isAdmin>
|
||||
<f:advanced>
|
||||
<f:entry title="${%Test URL}">
|
||||
<f:textbox field="testUrl"/>
|
||||
</f:entry>
|
||||
<f:validateButton title="${%Validate Proxy}" method="validateProxy" with="testUrl,name,port,userName,secretPassword,noProxyHost"/>
|
||||
</f:advanced>
|
||||
</l:isAdmin>
|
||||
</j:jelly>
|
||||
|
|
@ -24,5 +24,6 @@ Server=Server
|
|||
Port=Port
|
||||
User\ name=Benutzername
|
||||
Password=Passwort
|
||||
Test\ URL=Test-URL
|
||||
No\ Proxy\ Host=Proxy-Ausnahmen
|
||||
Validate\ Proxy=Proxy-Konfiguration prüfen
|
||||
|
|
|
|||
|
|
@ -22,27 +22,34 @@ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|||
THE SOFTWARE.
|
||||
-->
|
||||
|
||||
<!--
|
||||
Displays the console output
|
||||
-->
|
||||
<?jelly escape-by-default='true'?>
|
||||
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form" xmlns:i="jelly:fmt">
|
||||
<l:layout title="${it.fullDisplayName} ${%Changes}">
|
||||
<st:include page="sidepanel.jelly" />
|
||||
<l:breadcrumb title="${%Changes}" />
|
||||
<l:main-panel>
|
||||
<t:buildCaption>${%Changes}</t:buildCaption>
|
||||
<j:choose>
|
||||
<j:when test="${it.hasChangeSetComputed()}">
|
||||
<st:include page="index.jelly" it="${it.changeSet}" />
|
||||
</j:when>
|
||||
<j:when test="${it.building}">
|
||||
${%Not yet determined}
|
||||
</j:when>
|
||||
<j:otherwise>
|
||||
${%Failed to determine} (<a href="${h.getConsoleUrl(it)}">${%log}</a>)
|
||||
</j:otherwise>
|
||||
</j:choose>
|
||||
</l:main-panel>
|
||||
</l:layout>
|
||||
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:l="/lib/layout">
|
||||
<j:new className="jenkins.run.ChangesTab" var="it">
|
||||
<j:arg value="${it}"/>
|
||||
</j:new>
|
||||
|
||||
<l:run-subpage>
|
||||
<l:app-bar title="${it.displayName}">
|
||||
<l:details-bar it="${it.object}" />
|
||||
</l:app-bar>
|
||||
|
||||
<j:set var="changeSets" value="${it.object.changeSets}" />
|
||||
|
||||
<j:choose>
|
||||
<j:when test="${!empty(changeSets)}">
|
||||
<div class="jenkins-card">
|
||||
<div class="jenkins-card__content">
|
||||
<j:forEach var="changeSet" items="${changeSets}">
|
||||
<st:include page="index.jelly" it="${changeSet}" />
|
||||
</j:forEach>
|
||||
</div>
|
||||
</div>
|
||||
</j:when>
|
||||
<j:otherwise>
|
||||
<l:notice icon="${it.iconFileName}" title="${%Failed to determine}">
|
||||
<a href="${h.getConsoleUrl(it.object)}">${%log}</a>
|
||||
</l:notice>
|
||||
</j:otherwise>
|
||||
</j:choose>
|
||||
</l:run-subpage>
|
||||
</j:jelly>
|
||||
|
|
|
|||
|
|
@ -30,17 +30,29 @@ THE SOFTWARE.
|
|||
<l:header />
|
||||
<l:side-panel>
|
||||
<l:tasks>
|
||||
<l:userExperimentalFlag var="newBuildPage" flagClassName="jenkins.model.experimentalflags.NewBuildPageUserExperimentalFlag" />
|
||||
<j:set var="buildUrl" value="${h.decompose(request2)}" />
|
||||
<st:include page="tasks.jelly"/>
|
||||
<st:include page="delete.jelly" />
|
||||
<st:include page="actions.jelly" />
|
||||
<t:actions actions="${it.transientActions}"/>
|
||||
<j:if test="${it.previousBuild!=null}">
|
||||
<l:task contextMenu="false" href="${buildUrl.previousBuildUrl}" icon="icon-previous icon-md" title="${%Previous Build}"/>
|
||||
</j:if>
|
||||
<j:if test="${it.nextBuild!=null}">
|
||||
<l:task contextMenu="false" href="${buildUrl.nextBuildUrl}" icon="icon-next icon-md" title="${%Next Build}"/>
|
||||
</j:if>
|
||||
|
||||
<j:choose>
|
||||
<j:when test="${newBuildPage}">
|
||||
<st:include page="tasks.jelly"/>
|
||||
<st:include page="actions.jelly" />
|
||||
<t:actions actions="${it.transientActions}"/>
|
||||
<st:include page="delete.jelly" />
|
||||
</j:when>
|
||||
<j:otherwise>
|
||||
<st:include page="tasks.jelly"/>
|
||||
<st:include page="delete.jelly" />
|
||||
<st:include page="actions.jelly" />
|
||||
<t:actions actions="${it.transientActions}"/>
|
||||
<j:if test="${it.previousBuild!=null}">
|
||||
<l:task contextMenu="false" href="${buildUrl.previousBuildUrl}" icon="icon-previous icon-md" title="${%Previous Build}"/>
|
||||
</j:if>
|
||||
<j:if test="${it.nextBuild!=null}">
|
||||
<l:task contextMenu="false" href="${buildUrl.nextBuildUrl}" icon="icon-next icon-md" title="${%Next Build}"/>
|
||||
</j:if>
|
||||
</j:otherwise>
|
||||
</j:choose>
|
||||
</l:tasks>
|
||||
</l:side-panel>
|
||||
</j:jelly>
|
||||
|
|
@ -27,15 +27,29 @@ THE SOFTWARE.
|
|||
-->
|
||||
<?jelly escape-by-default='true'?>
|
||||
<j:jelly xmlns:j="jelly:core" xmlns:l="/lib/layout" xmlns:p="/lib/hudson/project">
|
||||
<j:choose>
|
||||
<j:when test="${newBuildPage}">
|
||||
<j:choose>
|
||||
<j:when test="${h.hasPermission(it,it.UPDATE)}">
|
||||
<l:task icon="symbol-edit-note" href="${buildUrl.baseUrl}/configure" title="${%Edit Build Information}"/>
|
||||
</j:when>
|
||||
<j:otherwise>
|
||||
<l:task icon="symbol-view" href="${buildUrl.baseUrl}/configure" title="${%View Build Information}"/>
|
||||
</j:otherwise>
|
||||
</j:choose>
|
||||
</j:when>
|
||||
<j:otherwise>
|
||||
<l:task contextMenu="false" href="${buildUrl.baseUrl}/" icon="symbol-details" title="${%Status}"/>
|
||||
<l:task href="${buildUrl.baseUrl}/changes" icon="symbol-changes" title="${%Changes}"/>
|
||||
<p:console-link/>
|
||||
<j:choose>
|
||||
<j:when test="${h.hasPermission(it,it.UPDATE)}">
|
||||
<l:task icon="symbol-edit-note" href="${buildUrl.baseUrl}/configure" title="${%Edit Build Information}"/>
|
||||
</j:when>
|
||||
<j:otherwise>
|
||||
<l:task icon="symbol-view" href="${buildUrl.baseUrl}/configure" title="${%View Build Information}"/>
|
||||
</j:otherwise>
|
||||
<j:when test="${h.hasPermission(it,it.UPDATE)}">
|
||||
<l:task icon="symbol-edit-note" href="${buildUrl.baseUrl}/configure" title="${%Edit Build Information}"/>
|
||||
</j:when>
|
||||
<j:otherwise>
|
||||
<l:task icon="symbol-view" href="${buildUrl.baseUrl}/configure" title="${%View Build Information}"/>
|
||||
</j:otherwise>
|
||||
</j:choose>
|
||||
</j:otherwise>
|
||||
</j:choose>
|
||||
</j:jelly>
|
||||
|
|
|
|||
|
|
@ -46,5 +46,8 @@ THE SOFTWARE.
|
|||
<st:include page="jobMain.jelly" it="${a}" optional="true" />
|
||||
</j:forEach>
|
||||
|
||||
<p:upstream-downstream />
|
||||
<l:userExperimentalFlag var="newJobPage" flagClassName="jenkins.model.experimentalflags.NewJobPageUserExperimentalFlag" />
|
||||
<j:if test="${!newJobPage}">
|
||||
<p:upstream-downstream />
|
||||
</j:if>
|
||||
</j:jelly>
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue