diff --git a/.github/renovate.json b/.github/renovate.json index 90c5a18d9f6d..0b034b3a1827 100644 --- a/.github/renovate.json +++ b/.github/renovate.json @@ -10,6 +10,7 @@ "postUpdateOptions": [ "yarnDedupeHighest" ], + "ignoreDeps": ["softprops/action-gh-release"], "packageRules": [ { "matchDatasources": [ @@ -71,16 +72,6 @@ "org.fusesource.jansi:jansi" ] }, - { - "description": "Depends on commons-lang3 which is in progress for removal from core. See: https://issues.jenkins.io/browse/JENKINS-73355", - "matchManagers": [ - "maven" - ], - "enabled": false, - "matchPackageNames": [ - "org.apache.commons:commons-compress" - ] - }, { "description": "Contains incompatible API changes and needs compatibility work. See: https://github.com/jenkinsci/jenkins/pull/4224", "matchManagers": [ diff --git a/.github/workflows/publish-release-artifact.yml b/.github/workflows/publish-release-artifact.yml index 8b7242136ce9..3bd728e9632b 100644 --- a/.github/workflows/publish-release-artifact.yml +++ b/.github/workflows/publish-release-artifact.yml @@ -73,7 +73,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@c062e08bd532815e2082a85e87e3ef29c3e6d191 # v2.0.8 + uses: softprops/action-gh-release@01570a1f39cb168c169c802c3bceb9e93fb10974 # v2.1.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: @@ -108,7 +108,7 @@ jobs: - name: Upload Release Asset id: upload-deb if: always() - uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 # v2.0.8 + uses: softprops/action-gh-release@01570a1f39cb168c169c802c3bceb9e93fb10974 # v2.1.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: @@ -144,7 +144,7 @@ jobs: - name: Upload Release Asset id: upload-rpm if: always() - uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 # v2.0.8 + uses: softprops/action-gh-release@01570a1f39cb168c169c802c3bceb9e93fb10974 # v2.1.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: @@ -180,7 +180,7 @@ jobs: - name: Upload Release Asset id: upload-msi if: always() - uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 # v2.0.8 + uses: softprops/action-gh-release@01570a1f39cb168c169c802c3bceb9e93fb10974 # v2.1.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: @@ -216,7 +216,7 @@ jobs: - name: Upload Release Asset id: upload-suse-rpm if: always() - uses: softprops/action-gh-release@c062e08bd532815e2082a85e87e3ef29c3e6d191 # v2.0.8 + uses: softprops/action-gh-release@01570a1f39cb168c169c802c3bceb9e93fb10974 # v2.1.0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: diff --git a/.github/workflows/run-since-updater.yml b/.github/workflows/run-since-updater.yml index 6241acdc2dc4..e0986b5e7424 100644 --- a/.github/workflows/run-since-updater.yml +++ b/.github/workflows/run-since-updater.yml @@ -29,7 +29,7 @@ jobs: id: run_script shell: bash - name: Create Pull Request - uses: peter-evans/create-pull-request@5e914681df9dc83aa4e4905692ca88beb2f9e91f # v7 + uses: peter-evans/create-pull-request@67ccf781d68cd99b580ae25a5c18a1cc84ffff1f # v7 with: token: ${{ secrets.GITHUB_TOKEN }} commit-message: Fill in since annotations diff --git a/ath.sh b/ath.sh index 3326a2118260..3f6bca7f61bf 100644 --- a/ath.sh +++ b/ath.sh @@ -6,7 +6,7 @@ set -o xtrace cd "$(dirname "$0")" # https://github.com/jenkinsci/acceptance-test-harness/releases -export ATH_VERSION=6040.v72ed2f5b_59f6 +export ATH_VERSION=6107.v8c73fa_b_8f784 if [[ $# -eq 0 ]]; then export JDK=17 diff --git a/bom/pom.xml b/bom/pom.xml index 0065b595ae9a..8de2f4bc33ad 100644 --- a/bom/pom.xml +++ b/bom/pom.xml @@ -39,7 +39,7 @@ THE SOFTWARE. 2.0.0-M2 - 1903.v994a_db_314d58 + 1928.v9115fe47607f 2.4.21 @@ -62,7 +62,7 @@ THE SOFTWARE. org.springframework spring-framework-bom - 6.1.13 + 6.2.1 pom import @@ -70,7 +70,7 @@ THE SOFTWARE. org.springframework.security spring-security-bom - 6.3.3 + 6.4.2 pom import @@ -88,7 +88,7 @@ THE SOFTWARE. com.google.guava guava - 33.3.1-jre + 33.4.0-jre @@ -109,7 +109,7 @@ THE SOFTWARE. com.thoughtworks.xstream xstream - 1.4.20 + 1.4.21 commons-beanutils @@ -129,7 +129,7 @@ THE SOFTWARE. commons-io commons-io - 2.17.0 + 2.18.0 commons-jelly @@ -169,7 +169,7 @@ THE SOFTWARE. net.java.dev.jna jna - 5.15.0 + 5.16.0 net.java.sezpoz @@ -191,11 +191,6 @@ THE SOFTWARE. ant 1.10.15 - - org.apache.commons - commons-compress - 1.26.1 - org.apache.commons commons-fileupload2 @@ -295,7 +290,7 @@ THE SOFTWARE. org.jvnet.hudson commons-jelly-tags-define - 1.1-jenkins-20240903 + 1.1-jenkins-20241115 org.jvnet.localizer @@ -335,7 +330,7 @@ THE SOFTWARE. org.kohsuke.stapler json-lib - 2.4-jenkins-7 + 2.4-jenkins-8 org.kohsuke.stapler diff --git a/cli/src/main/java/hudson/cli/FlightRecorderInputStream.java b/cli/src/main/java/hudson/cli/FlightRecorderInputStream.java index 5a1167c4fdc5..41d7a719dd09 100644 --- a/cli/src/main/java/hudson/cli/FlightRecorderInputStream.java +++ b/cli/src/main/java/hudson/cli/FlightRecorderInputStream.java @@ -21,7 +21,7 @@ class FlightRecorderInputStream extends InputStream { * Size (in bytes) of the flight recorder ring buffer used for debugging remoting issues. * @since 2.41 */ - static final int BUFFER_SIZE = Integer.getInteger("hudson.remoting.FlightRecorderInputStream.BUFFER_SIZE", 1024 * 1024); + static final int BUFFER_SIZE = Integer.getInteger("hudson.remoting.FlightRecorderInputStream.BUFFER_SIZE", 1024); private final InputStream source; private ByteArrayRingBuffer recorder = new ByteArrayRingBuffer(BUFFER_SIZE); diff --git a/core/pom.xml b/core/pom.xml index 9bff5e5ad0b2..0b9cb30d8763 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -285,17 +285,6 @@ THE SOFTWARE. org.apache.ant ant - - org.apache.commons - commons-compress - - - - org.apache.commons - commons-lang3 - - - org.apache.commons commons-fileupload2-core diff --git a/core/src/main/java/hudson/FilePath.java b/core/src/main/java/hudson/FilePath.java index f5288b26d9d1..e48d7b45108f 100644 --- a/core/src/main/java/hudson/FilePath.java +++ b/core/src/main/java/hudson/FilePath.java @@ -124,7 +124,7 @@ import java.util.zip.GZIPInputStream; import java.util.zip.GZIPOutputStream; import jenkins.MasterToSlaveFileCallable; -import jenkins.SlaveToMasterFileCallable; +import jenkins.agents.ControllerToAgentFileCallable; import jenkins.model.Jenkins; import jenkins.security.MasterToSlaveCallable; import jenkins.util.ContextResettingExecutorService; @@ -520,21 +520,7 @@ public int archive(final ArchiverFactory factory, OutputStream os, final DirScan return act(new Archive(factory, out, scanner, verificationRoot, openOptions)); } - private static class Archive extends MasterToSlaveFileCallable { - private final ArchiverFactory factory; - private final OutputStream out; - private final DirScanner scanner; - private final String verificationRoot; - private OpenOption[] openOptions; - - Archive(ArchiverFactory factory, OutputStream out, DirScanner scanner, String verificationRoot, OpenOption... openOptions) { - this.factory = factory; - this.out = out; - this.scanner = scanner; - this.verificationRoot = verificationRoot; - this.openOptions = openOptions; - } - + private record Archive(ArchiverFactory factory, OutputStream out, DirScanner scanner, String verificationRoot, OpenOption... openOptions) implements ControllerToAgentFileCallable { @Override public Integer invoke(File f, VirtualChannel channel) throws IOException { try (Archiver a = factory.create(out)) { @@ -542,8 +528,6 @@ public Integer invoke(File f, VirtualChannel channel) throws IOException { return a.countEntries(); } } - - private static final long serialVersionUID = 1L; } public int archive(final ArchiverFactory factory, OutputStream os, final FileFilter filter) throws IOException, InterruptedException { @@ -1185,12 +1169,7 @@ public void copyFrom(org.apache.commons.fileupload.FileItem file) throws IOExcep /** * Code that gets executed on the machine where the {@link FilePath} is local. * Used to act on {@link FilePath}. - * Warning: implementations must be serializable, so prefer a static nested class to an inner class. - * - *

- * Subtypes would likely want to extend from either {@link MasterToSlaveCallable} - * or {@link SlaveToMasterFileCallable}. - * + * A typical implementation would be a {@code record} implementing {@link ControllerToAgentFileCallable}. * @see FilePath#act(FileCallable) */ public interface FileCallable extends Serializable, RoleSensitive { diff --git a/core/src/main/java/hudson/Functions.java b/core/src/main/java/hudson/Functions.java index 150b1b658c16..67801475233d 100644 --- a/core/src/main/java/hudson/Functions.java +++ b/core/src/main/java/hudson/Functions.java @@ -62,6 +62,7 @@ import hudson.model.View; import hudson.scm.SCM; import hudson.scm.SCMDescriptor; +import hudson.search.SearchFactory; import hudson.search.SearchableModelObject; import hudson.security.ACL; import hudson.security.AccessControlled; @@ -124,6 +125,7 @@ import java.nio.charset.StandardCharsets; import java.text.DateFormat; import java.text.DecimalFormat; +import java.text.MessageFormat; import java.time.ZoneId; import java.time.ZonedDateTime; import java.time.format.DateTimeFormatter; @@ -2576,4 +2578,34 @@ static String guessIcon(String iconGuess, String rootURL) { public static String generateItemId() { return String.valueOf(Math.floor(Math.random() * 3000)); } + + @Restricted(NoExternalUse.class) + public static ExtensionList getSearchFactories() { + return SearchFactory.all(); + } + + /** + * @param keyboardShortcut the shortcut to be translated + * @return the translated shortcut, e.g. CMD+K to ⌘+K for macOS, CTRL+K for Windows + */ + @Restricted(NoExternalUse.class) + public static String translateModifierKeysForUsersPlatform(String keyboardShortcut) { + StaplerRequest2 currentRequest = Stapler.getCurrentRequest2(); + currentRequest.getWebApp().getDispatchValidator().allowDispatch(currentRequest, Stapler.getCurrentResponse2()); + String userAgent = currentRequest.getHeader("User-Agent"); + + List platformsThatUseCommand = List.of("MAC", "IPHONE", "IPAD"); + boolean useCmdKey = platformsThatUseCommand.stream().anyMatch(e -> userAgent.toUpperCase().contains(e)); + + return keyboardShortcut.replace("CMD", useCmdKey ? "⌘" : "CTRL"); + } + + @Restricted(NoExternalUse.class) + public static String formatMessage(String format, Object args) { + if (format == null) { + return args.toString(); + } + + return MessageFormat.format(format, args); + } } diff --git a/core/src/main/java/hudson/Launcher.java b/core/src/main/java/hudson/Launcher.java index e6ba431691fb..195671b1b42e 100644 --- a/core/src/main/java/hudson/Launcher.java +++ b/core/src/main/java/hudson/Launcher.java @@ -55,6 +55,7 @@ import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; +import jenkins.agents.ControllerToAgentCallable; import jenkins.model.Jenkins; import jenkins.security.MasterToSlaveCallable; import jenkins.tasks.filters.EnvVarsFilterLocalRule; @@ -1114,8 +1115,7 @@ public Proc launch(ProcStarter ps) throws IOException { final String workDir = psPwd == null ? null : psPwd.getRemote(); try { - RemoteLaunchCallable remote = new RemoteLaunchCallable(ps.commands, ps.masks, ps.envs, in, ps.reverseStdin, out, ps.reverseStdout, err, ps.reverseStderr, ps.quiet, workDir, listener, ps.stdoutListener); - remote.setEnvVarsFilterRuleWrapper(envVarsFilterRuleWrapper); + RemoteLaunchCallable remote = new RemoteLaunchCallable(ps.commands, ps.masks, ps.envs, in, ps.reverseStdin, out, ps.reverseStdout, err, ps.reverseStderr, ps.quiet, workDir, listener, ps.stdoutListener, envVarsFilterRuleWrapper); // reset the rules to prevent build step without rules configuration to re-use those envVarsFilterRuleWrapper = null; return new ProcImpl(getChannel().call(remote)); @@ -1334,46 +1334,13 @@ public interface RemoteProcess { IOTriplet getIOtriplet(); } - private static class RemoteLaunchCallable extends MasterToSlaveCallable { - private final @NonNull List cmd; - private final @CheckForNull boolean[] masks; - private final @CheckForNull String[] env; - private final @CheckForNull InputStream in; - private final @CheckForNull OutputStream out; - private final @CheckForNull OutputStream err; - private final @CheckForNull String workDir; - private final @NonNull TaskListener listener; - private final @CheckForNull TaskListener stdoutListener; - private final boolean reverseStdin, reverseStdout, reverseStderr; - private final boolean quiet; - - private EnvVarsFilterRuleWrapper envVarsFilterRuleWrapper; - - RemoteLaunchCallable(@NonNull List cmd, @CheckForNull boolean[] masks, @CheckForNull String[] env, + private record RemoteLaunchCallable(@NonNull List cmd, @CheckForNull boolean[] masks, @CheckForNull String[] env, @CheckForNull InputStream in, boolean reverseStdin, @CheckForNull OutputStream out, boolean reverseStdout, @CheckForNull OutputStream err, boolean reverseStderr, - boolean quiet, @CheckForNull String workDir, @NonNull TaskListener listener, @CheckForNull TaskListener stdoutListener) { - this.cmd = new ArrayList<>(cmd); - this.masks = masks; - this.env = env; - this.in = in; - this.out = out; - this.err = err; - this.workDir = workDir; - this.listener = listener; - this.stdoutListener = stdoutListener; - this.reverseStdin = reverseStdin; - this.reverseStdout = reverseStdout; - this.reverseStderr = reverseStderr; - this.quiet = quiet; - } - - @Restricted(NoExternalUse.class) - public void setEnvVarsFilterRuleWrapper(EnvVarsFilterRuleWrapper envVarsFilterRuleWrapper) { - this.envVarsFilterRuleWrapper = envVarsFilterRuleWrapper; - } - + boolean quiet, @CheckForNull String workDir, + @NonNull TaskListener listener, @CheckForNull TaskListener stdoutListener, + @CheckForNull EnvVarsFilterRuleWrapper envVarsFilterRuleWrapper) implements ControllerToAgentCallable { @Override public RemoteProcess call() throws IOException { final Channel channel = getOpenChannelOrFail(); @@ -1433,8 +1400,6 @@ public IOTriplet getIOtriplet() { } }); } - - private static final long serialVersionUID = 1L; } private static class RemoteChannelLaunchCallable extends MasterToSlaveCallable { diff --git a/core/src/main/java/hudson/TcpSlaveAgentListener.java b/core/src/main/java/hudson/TcpSlaveAgentListener.java index 3936071de2ef..e7f037652c9b 100644 --- a/core/src/main/java/hudson/TcpSlaveAgentListener.java +++ b/core/src/main/java/hudson/TcpSlaveAgentListener.java @@ -271,14 +271,11 @@ public void run() { String protocol = s.substring(9); AgentProtocol p = AgentProtocol.of(protocol); if (p != null) { - if (Jenkins.get().getAgentProtocols().contains(protocol)) { - LOGGER.log(p instanceof PingAgentProtocol ? Level.FINE : Level.INFO, () -> "Accepted " + protocol + " connection " + connectionInfo); - p.handle(this.s); - } else { - error("Disabled protocol:" + s, this.s); - } - } else + LOGGER.log(p instanceof PingAgentProtocol ? Level.FINE : Level.INFO, () -> "Accepted " + protocol + " connection " + connectionInfo); + p.handle(this.s); + } else { error("Unknown protocol:", this.s); + } } else { error("Unrecognized protocol: " + s, this.s); } @@ -364,21 +361,11 @@ public PingAgentProtocol() { ping = "Ping\n".getBytes(StandardCharsets.UTF_8); } - @Override - public boolean isRequired() { - return true; - } - @Override public String getName() { return "Ping"; } - @Override - public String getDisplayName() { - return Messages.TcpSlaveAgentListener_PingAgentProtocol_displayName(); - } - @Override public void handle(Socket socket) throws IOException, InterruptedException { try (socket) { diff --git a/core/src/main/java/hudson/Util.java b/core/src/main/java/hudson/Util.java index b4529a0ea279..07d00a44d006 100644 --- a/core/src/main/java/hudson/Util.java +++ b/core/src/main/java/hudson/Util.java @@ -1525,6 +1525,10 @@ public static Number tryParseNumber(@CheckForNull String numberStr, @CheckForNul * does not contain the specified method. */ public static boolean isOverridden(@NonNull Class base, @NonNull Class derived, @NonNull String methodName, @NonNull Class... types) { + if (base == derived) { + // If base and derived are the same type, the method is not overridden by definition + return false; + } // If derived is not a subclass or implementor of base, it can't override any method // Technically this should also be triggered when base == derived, because it can't override its own method, but // the unit tests explicitly test for that as working. diff --git a/core/src/main/java/hudson/cli/CLIAction.java b/core/src/main/java/hudson/cli/CLIAction.java index a2fc5f590197..a2e756267178 100644 --- a/core/src/main/java/hudson/cli/CLIAction.java +++ b/core/src/main/java/hudson/cli/CLIAction.java @@ -118,12 +118,26 @@ public boolean isWebSocketSupported() { return WebSockets.isSupported(); } + /** + * Unlike {@link HttpResponses#errorWithoutStack} this sends the message in a header rather than the body. + * (Currently the WebSocket CLI is unable to process the body in an error message.) + */ + private static HttpResponse statusWithExplanation(int code, String errorMessage) { + return new HttpResponse() { + @Override + public void generateResponse(StaplerRequest2 req, StaplerResponse2 rsp, Object node) { + rsp.setStatus(code); + rsp.setHeader("X-CLI-Error", errorMessage); + } + }; + } + /** * WebSocket endpoint. */ public HttpResponse doWs(StaplerRequest2 req) { if (!WebSockets.isSupported()) { - return HttpResponses.notFound(); + return statusWithExplanation(HttpServletResponse.SC_NOT_FOUND, "WebSocket is not supported in this servlet container (try the built-in Jetty instead)"); } if (ALLOW_WEBSOCKET == null) { final String actualOrigin = req.getHeader("Origin"); @@ -141,10 +155,10 @@ public HttpResponse doWs(StaplerRequest2 req) { if (actualOrigin == null || !actualOrigin.equals(expectedOrigin)) { LOGGER.log(Level.FINE, () -> "Rejecting origin: " + actualOrigin + "; expected was from request: " + expectedOrigin); - return HttpResponses.forbidden(); + return statusWithExplanation(HttpServletResponse.SC_FORBIDDEN, "Unexpected request origin (check your reverse proxy settings)"); } } else if (!ALLOW_WEBSOCKET) { - return HttpResponses.forbidden(); + return statusWithExplanation(HttpServletResponse.SC_FORBIDDEN, "WebSocket support for CLI disabled for this controller"); } Authentication authentication = Jenkins.getAuthentication2(); return WebSockets.upgrade(new WebSocketSession() { diff --git a/core/src/main/java/hudson/cli/CLICommand.java b/core/src/main/java/hudson/cli/CLICommand.java index f2dc2f8b5fc3..ba7fd4250fdd 100644 --- a/core/src/main/java/hudson/cli/CLICommand.java +++ b/core/src/main/java/hudson/cli/CLICommand.java @@ -242,7 +242,6 @@ public int main(List args, Locale locale, InputStream stdin, PrintStream this.stdout = stdout; this.stderr = stderr; this.locale = locale; - registerOptionHandlers(); CmdLineParser p = getCmdLineParser(); // add options from the authenticator @@ -527,20 +526,6 @@ protected CLICommand createClone() { } } - /** - * Auto-discovers {@link OptionHandler}s and add them to the given command line parser. - */ - protected void registerOptionHandlers() { - try { - for (Class c : Index.list(OptionHandlerExtension.class, Jenkins.get().pluginManager.uberClassLoader, Class.class)) { - Type t = Types.getBaseClass(c, OptionHandler.class); - CmdLineParser.registerHandler(Types.erasure(Types.getTypeArgument(t, 0)), c); - } - } catch (IOException e) { - throw new Error(e); - } - } - /** * Returns all the registered {@link CLICommand}s. */ @@ -577,11 +562,21 @@ public static CLICommand getCurrent() { static { // register option handlers that are defined - ClassLoaders cls = new ClassLoaders(); Jenkins j = Jenkins.getInstanceOrNull(); if (j != null) { // only when running on the controller - cls.put(j.getPluginManager().uberClassLoader); + // Register OptionHandlers through META-INF/services/annotations and Annotation Indexer + try { + for (Class c : Index.list(OptionHandlerExtension.class, Jenkins.get().pluginManager.uberClassLoader, Class.class)) { + Type t = Types.getBaseClass(c, OptionHandler.class); + CmdLineParser.registerHandler(Types.erasure(Types.getTypeArgument(t, 0)), c); + } + } catch (IOException e) { + throw new UncheckedIOException(e); + } + // Register OptionHandlers through META-INF/services and Commons Discovery + ClassLoaders cls = new ClassLoaders(); + cls.put(j.getPluginManager().uberClassLoader); ResourceNameIterator servicesIter = new DiscoverServiceNames(cls).findResourceNames(OptionHandler.class.getName()); final ResourceClassIterator itr = diff --git a/core/src/main/java/hudson/cli/declarative/CLIRegisterer.java b/core/src/main/java/hudson/cli/declarative/CLIRegisterer.java index f5d050174940..76b2c2612c15 100644 --- a/core/src/main/java/hudson/cli/declarative/CLIRegisterer.java +++ b/core/src/main/java/hudson/cli/declarative/CLIRegisterer.java @@ -131,7 +131,6 @@ protected CmdLineParser getCmdLineParser() { private CmdLineParser bindMethod(List binders) { - registerOptionHandlers(); ParserProperties properties = ParserProperties.defaults().withAtSyntax(ALLOW_AT_SYNTAX); CmdLineParser parser = new CmdLineParser(null, properties); diff --git a/core/src/main/java/hudson/cli/declarative/OptionHandlerExtension.java b/core/src/main/java/hudson/cli/declarative/OptionHandlerExtension.java index f009038ed2ac..3972514bc46c 100644 --- a/core/src/main/java/hudson/cli/declarative/OptionHandlerExtension.java +++ b/core/src/main/java/hudson/cli/declarative/OptionHandlerExtension.java @@ -35,7 +35,6 @@ /** * {@link OptionHandler}s that should be auto-discovered. - * TODO is this actually necessary? {@code @MetaInfServices(OptionHandler.class)} seems to work as well. * @author Kohsuke Kawaguchi */ @Indexed diff --git a/core/src/main/java/hudson/cli/handlers/AbstractItemOptionHandler.java b/core/src/main/java/hudson/cli/handlers/AbstractItemOptionHandler.java index c99f3090799e..6919bd8a1ebc 100644 --- a/core/src/main/java/hudson/cli/handlers/AbstractItemOptionHandler.java +++ b/core/src/main/java/hudson/cli/handlers/AbstractItemOptionHandler.java @@ -24,18 +24,18 @@ package hudson.cli.handlers; +import hudson.cli.declarative.OptionHandlerExtension; import hudson.model.AbstractItem; -import org.kohsuke.MetaInfServices; import org.kohsuke.args4j.CmdLineParser; import org.kohsuke.args4j.OptionDef; -import org.kohsuke.args4j.spi.OptionHandler; import org.kohsuke.args4j.spi.Setter; /** * Refers to an {@link AbstractItem} by name. * @since 1.538 */ -@MetaInfServices(OptionHandler.class) public class AbstractItemOptionHandler extends GenericItemOptionHandler { +@OptionHandlerExtension +public class AbstractItemOptionHandler extends GenericItemOptionHandler { public AbstractItemOptionHandler(CmdLineParser parser, OptionDef option, Setter setter) { super(parser, option, setter); diff --git a/core/src/main/java/hudson/cli/handlers/AbstractProjectOptionHandler.java b/core/src/main/java/hudson/cli/handlers/AbstractProjectOptionHandler.java index ab7402de65ae..0efd7fca54f8 100644 --- a/core/src/main/java/hudson/cli/handlers/AbstractProjectOptionHandler.java +++ b/core/src/main/java/hudson/cli/handlers/AbstractProjectOptionHandler.java @@ -24,11 +24,10 @@ package hudson.cli.handlers; +import hudson.cli.declarative.OptionHandlerExtension; import hudson.model.AbstractProject; -import org.kohsuke.MetaInfServices; import org.kohsuke.args4j.CmdLineParser; import org.kohsuke.args4j.OptionDef; -import org.kohsuke.args4j.spi.OptionHandler; import org.kohsuke.args4j.spi.Setter; /** @@ -36,7 +35,7 @@ * * @author Kohsuke Kawaguchi */ -@MetaInfServices(OptionHandler.class) +@OptionHandlerExtension @SuppressWarnings("rawtypes") public class AbstractProjectOptionHandler extends GenericItemOptionHandler { public AbstractProjectOptionHandler(CmdLineParser parser, OptionDef option, Setter setter) { diff --git a/core/src/main/java/hudson/cli/handlers/JobOptionHandler.java b/core/src/main/java/hudson/cli/handlers/JobOptionHandler.java index 519293150341..e0141446cce1 100644 --- a/core/src/main/java/hudson/cli/handlers/JobOptionHandler.java +++ b/core/src/main/java/hudson/cli/handlers/JobOptionHandler.java @@ -24,11 +24,10 @@ package hudson.cli.handlers; +import hudson.cli.declarative.OptionHandlerExtension; import hudson.model.Job; -import org.kohsuke.MetaInfServices; import org.kohsuke.args4j.CmdLineParser; import org.kohsuke.args4j.OptionDef; -import org.kohsuke.args4j.spi.OptionHandler; import org.kohsuke.args4j.spi.Setter; /** @@ -36,7 +35,7 @@ * * @author Kohsuke Kawaguchi */ -@MetaInfServices(OptionHandler.class) +@OptionHandlerExtension @SuppressWarnings("rawtypes") public class JobOptionHandler extends GenericItemOptionHandler { public JobOptionHandler(CmdLineParser parser, OptionDef option, Setter setter) { diff --git a/core/src/main/java/hudson/cli/handlers/NodeOptionHandler.java b/core/src/main/java/hudson/cli/handlers/NodeOptionHandler.java index 45bd0f4e679b..7e4d0367bd9e 100644 --- a/core/src/main/java/hudson/cli/handlers/NodeOptionHandler.java +++ b/core/src/main/java/hudson/cli/handlers/NodeOptionHandler.java @@ -24,9 +24,9 @@ package hudson.cli.handlers; +import hudson.cli.declarative.OptionHandlerExtension; import hudson.model.Node; import jenkins.model.Jenkins; -import org.kohsuke.MetaInfServices; import org.kohsuke.args4j.CmdLineException; import org.kohsuke.args4j.CmdLineParser; import org.kohsuke.args4j.OptionDef; @@ -40,7 +40,7 @@ * @author ogondza * @since 1.526 */ -@MetaInfServices +@OptionHandlerExtension public class NodeOptionHandler extends OptionHandler { public NodeOptionHandler(CmdLineParser parser, OptionDef option, Setter setter) { diff --git a/core/src/main/java/hudson/cli/handlers/ParameterizedJobOptionHandler.java b/core/src/main/java/hudson/cli/handlers/ParameterizedJobOptionHandler.java index a37dbf1790f1..b75369070581 100644 --- a/core/src/main/java/hudson/cli/handlers/ParameterizedJobOptionHandler.java +++ b/core/src/main/java/hudson/cli/handlers/ParameterizedJobOptionHandler.java @@ -24,20 +24,19 @@ package hudson.cli.handlers; +import hudson.cli.declarative.OptionHandlerExtension; import jenkins.model.ParameterizedJobMixIn; -import org.kohsuke.MetaInfServices; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.DoNotUse; import org.kohsuke.args4j.CmdLineParser; import org.kohsuke.args4j.OptionDef; -import org.kohsuke.args4j.spi.OptionHandler; import org.kohsuke.args4j.spi.Setter; /** * Refer to {@link jenkins.model.ParameterizedJobMixIn.ParameterizedJob} by its name. */ @Restricted(DoNotUse.class) -@MetaInfServices(OptionHandler.class) +@OptionHandlerExtension @SuppressWarnings("rawtypes") public class ParameterizedJobOptionHandler extends GenericItemOptionHandler { diff --git a/core/src/main/java/hudson/cli/handlers/TopLevelItemOptionHandler.java b/core/src/main/java/hudson/cli/handlers/TopLevelItemOptionHandler.java index 97fdf4b8c5b6..93f1dd969ba1 100644 --- a/core/src/main/java/hudson/cli/handlers/TopLevelItemOptionHandler.java +++ b/core/src/main/java/hudson/cli/handlers/TopLevelItemOptionHandler.java @@ -1,10 +1,9 @@ package hudson.cli.handlers; +import hudson.cli.declarative.OptionHandlerExtension; import hudson.model.TopLevelItem; -import org.kohsuke.MetaInfServices; import org.kohsuke.args4j.CmdLineParser; import org.kohsuke.args4j.OptionDef; -import org.kohsuke.args4j.spi.OptionHandler; import org.kohsuke.args4j.spi.Setter; /** @@ -12,7 +11,7 @@ * * @author Kohsuke Kawaguchi */ -@MetaInfServices(OptionHandler.class) +@OptionHandlerExtension public class TopLevelItemOptionHandler extends GenericItemOptionHandler { public TopLevelItemOptionHandler(CmdLineParser parser, OptionDef option, Setter setter) { super(parser, option, setter); diff --git a/core/src/main/java/hudson/cli/handlers/ViewOptionHandler.java b/core/src/main/java/hudson/cli/handlers/ViewOptionHandler.java index 65656c15e33c..50101b284908 100644 --- a/core/src/main/java/hudson/cli/handlers/ViewOptionHandler.java +++ b/core/src/main/java/hudson/cli/handlers/ViewOptionHandler.java @@ -25,11 +25,11 @@ package hudson.cli.handlers; import edu.umd.cs.findbugs.annotations.CheckForNull; +import hudson.cli.declarative.OptionHandlerExtension; import hudson.model.View; import hudson.model.ViewGroup; import java.util.StringTokenizer; import jenkins.model.Jenkins; -import org.kohsuke.MetaInfServices; import org.kohsuke.args4j.CmdLineException; import org.kohsuke.args4j.CmdLineParser; import org.kohsuke.args4j.OptionDef; @@ -58,7 +58,7 @@ * @author ogondza * @since 1.538 */ -@MetaInfServices +@OptionHandlerExtension public class ViewOptionHandler extends OptionHandler { public ViewOptionHandler(CmdLineParser parser, OptionDef option, Setter setter) { diff --git a/core/src/main/java/hudson/model/Computer.java b/core/src/main/java/hudson/model/Computer.java index 8c3d814c91fe..0c525dfabe84 100644 --- a/core/src/main/java/hudson/model/Computer.java +++ b/core/src/main/java/hudson/model/Computer.java @@ -355,6 +355,7 @@ public AnnotatedLargeText getLogText() { * null if the system was put offline without given a cause. */ @Exported + @Override public OfflineCause getOfflineCause() { var node = getNode(); if (node != null) { @@ -374,19 +375,7 @@ public boolean hasOfflineCause() { @Exported @Override public String getOfflineCauseReason() { - var offlineCause = getOfflineCause(); - if (offlineCause == null) { - return ""; - } - // fetch the localized string for "Disconnected By" - String gsub_base = hudson.slaves.Messages.SlaveComputer_DisconnectedBy("", ""); - // regex to remove commented reason base string - String gsub1 = "^" + gsub_base + "[\\w\\W]* \\: "; - // regex to remove non-commented reason base string - String gsub2 = "^" + gsub_base + "[\\w\\W]*"; - - String newString = offlineCause.toString().replaceAll(gsub1, ""); - return newString.replaceAll(gsub2, ""); + return IComputer.super.getOfflineCauseReason(); } /** @@ -677,6 +666,14 @@ public boolean isTemporarilyOffline() { return node != null && node.isTemporarilyOffline(); } + /** + * Allows a caller to define an {@link OfflineCause} for a computer that has never been online. + * @since 2.483 + */ + public void setOfflineCause(OfflineCause cause) { + this.offlineCause = cause; + } + /** * @deprecated as of 1.320. * Use {@link #setTemporaryOfflineCause(OfflineCause)} @@ -690,7 +687,7 @@ public void setTemporarilyOffline(boolean temporarilyOffline) { * @deprecated * Use {@link #setTemporaryOfflineCause(OfflineCause)} instead. */ - @Deprecated(since = "TODO") + @Deprecated(since = "2.482") public void setTemporarilyOffline(boolean temporarilyOffline, OfflineCause cause) { if (cause == null) { setTemporarilyOffline(temporarilyOffline); @@ -705,7 +702,7 @@ public void setTemporarilyOffline(boolean temporarilyOffline, OfflineCause cause * * @param temporaryOfflineCause The reason why the node is being put offline. * If null, this cancels the status - * @since TODO + * @since 2.482 */ public void setTemporaryOfflineCause(@CheckForNull OfflineCause temporaryOfflineCause) { var node = getNode(); @@ -716,7 +713,7 @@ public void setTemporaryOfflineCause(@CheckForNull OfflineCause temporaryOffline } /** - * @since TODO + * @since 2.482 * @return If the node is temporarily offline, the reason why. */ @SuppressWarnings("unused") // used by setOfflineCause.jelly @@ -736,22 +733,13 @@ public String getTemporaryOfflineCauseReason() { @Exported @Override public String getIcon() { - // The machine was taken offline by someone - if (isTemporarilyOffline() && getOfflineCause() instanceof OfflineCause.UserCause) return "symbol-computer-disconnected"; - // The computer is not accepting tasks, e.g. because the availability demands it being offline. - if (!isAcceptingTasks()) { - return "symbol-computer-not-accepting"; - } - // The computer is not connected or it is temporarily offline due to a node monitor - if (isOffline()) return "symbol-computer-offline"; - return "symbol-computer"; + return IComputer.super.getIcon(); } /** * {@inheritDoc} * - *

- * It is both the recommended and default implementation to serve different icons based on {@link #isOffline}. + * @see #getIcon() */ @Exported @Override @@ -759,14 +747,6 @@ public String getIconClassName() { return IComputer.super.getIconClassName(); } - public String getIconAltText() { - // The machine was taken offline by someone - if (isTemporarilyOffline() && getOfflineCause() instanceof OfflineCause.UserCause) return "[temporarily offline by user]"; - // There is a "technical" reason the computer will not accept new builds - if (isOffline() || !isAcceptingTasks()) return "[offline]"; - return "[online]"; - } - @Exported @Override public @NonNull String getDisplayName() { return nodeName; diff --git a/core/src/main/java/hudson/model/Descriptor.java b/core/src/main/java/hudson/model/Descriptor.java index 562b96e37b8e..c55b7982695e 100644 --- a/core/src/main/java/hudson/model/Descriptor.java +++ b/core/src/main/java/hudson/model/Descriptor.java @@ -24,7 +24,6 @@ package hudson.model; -import static hudson.util.QuotedStringTokenizer.quote; import static jakarta.servlet.http.HttpServletResponse.SC_NOT_FOUND; import edu.umd.cs.findbugs.annotations.CheckForNull; @@ -475,6 +474,12 @@ public void calcAutoCompleteSettings(String field, Map attribute if (method == null) return; // no auto-completion + // build query parameter line by figuring out what should be submitted + List depends = buildFillDependencies(method, new ArrayList<>()); + if (!depends.isEmpty()) { + attributes.put("fillDependsOn", String.join(" ", depends)); + } + attributes.put("autoCompleteUrl", String.format("%s/%s/autoComplete%s", getCurrentDescriptorByNameUrl(), getDescriptorUrl(), capitalizedFieldName)); } @@ -1306,7 +1311,7 @@ public String getFormField() { @Override public void generateResponse(StaplerRequest2 req, StaplerResponse2 rsp, Object node) throws IOException, ServletException { if (FormApply.isApply(req)) { - FormApply.applyResponse("notificationBar.show(" + quote(getMessage()) + ",notificationBar.ERROR)") + FormApply.showNotification(getMessage(), FormApply.NotificationType.ERROR) .generateResponse(req, rsp, node); } else { // for now, we can't really use the field name that caused the problem. diff --git a/core/src/main/java/hudson/model/DirectoryBrowserSupport.java b/core/src/main/java/hudson/model/DirectoryBrowserSupport.java index 72da0c7514c1..242975e2b13b 100644 --- a/core/src/main/java/hudson/model/DirectoryBrowserSupport.java +++ b/core/src/main/java/hudson/model/DirectoryBrowserSupport.java @@ -58,6 +58,8 @@ import java.util.regex.Pattern; import java.util.stream.Collectors; import java.util.stream.Stream; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; import jenkins.model.Jenkins; import jenkins.security.MasterToSlaveCallable; import jenkins.security.ResourceDomainConfiguration; @@ -65,8 +67,6 @@ import jenkins.util.SystemProperties; import jenkins.util.VirtualFile; import org.apache.commons.io.IOUtils; -import org.apache.tools.zip.ZipEntry; -import org.apache.tools.zip.ZipOutputStream; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; import org.kohsuke.stapler.HttpResponse; @@ -530,8 +530,8 @@ private static String createBackRef(int times) { private static void zip(StaplerResponse2 rsp, VirtualFile root, VirtualFile dir, String glob) throws IOException, InterruptedException { OutputStream outputStream = rsp.getOutputStream(); - try (ZipOutputStream zos = new ZipOutputStream(outputStream)) { - zos.setEncoding(Charset.defaultCharset().displayName()); // TODO JENKINS-20663 make this overridable via query parameter + // TODO JENKINS-20663 make encoding overridable via query parameter + try (ZipOutputStream zos = new ZipOutputStream(outputStream, Charset.defaultCharset())) { // TODO consider using run(Callable) here if (glob.isEmpty()) { diff --git a/core/src/main/java/hudson/model/Job.java b/core/src/main/java/hudson/model/Job.java index e4d54d44b375..1bbf3dad1a24 100644 --- a/core/src/main/java/hudson/model/Job.java +++ b/core/src/main/java/hudson/model/Job.java @@ -34,6 +34,7 @@ import hudson.BulkChange; import hudson.EnvVars; import hudson.Extension; +import hudson.ExtensionList; import hudson.ExtensionPoint; import hudson.FeedAdapter; import hudson.PermalinkList; @@ -221,29 +222,20 @@ public void onLoad(ItemGroup parent, String name) TextFile f = getNextBuildNumberFile(); if (f.exists()) { - // starting 1.28, we store nextBuildNumber in a separate file. - // but old Hudson didn't do it, so if the file doesn't exist, - // assume that nextBuildNumber was read from config.xml try { synchronized (this) { this.nextBuildNumber = Integer.parseInt(f.readTrim()); } } catch (NumberFormatException e) { LOGGER.log(Level.WARNING, "Corruption in {0}: {1}", new Object[] {f, e}); - //noinspection StatementWithEmptyBody - if (this instanceof LazyBuildMixIn.LazyLoadingJob) { - // allow LazyBuildMixIn.onLoad to fix it - } else { - RunT lB = getLastBuild(); - synchronized (this) { - this.nextBuildNumber = lB != null ? lB.getNumber() + 1 : 1; - } - saveNextBuildNumber(); + RunT lB = getLastBuild(); + synchronized (this) { + this.nextBuildNumber = lB != null ? lB.getNumber() + 1 : 1; } + saveNextBuildNumber(); } - } else { - // From the old Hudson, or doCreateItem. Create this file now. - saveNextBuildNumber(); + } else if (nextBuildNumber == 0) { + nextBuildNumber = 1; } if (properties == null) // didn't exist < 1.72 @@ -346,12 +338,42 @@ public boolean isKeepDependencies() { } /** - * Allocates a new buildCommand number. + * Allocates a new build number. + * @see BuildNumberAssigner */ - public synchronized int assignBuildNumber() throws IOException { - int r = nextBuildNumber++; - saveNextBuildNumber(); - return r; + public int assignBuildNumber() throws IOException { + return ExtensionList.lookupFirst(BuildNumberAssigner.class).assignBuildNumber(this, this::saveNextBuildNumber); + } + + /** + * Alternate strategy for assigning build numbers. + */ + @Restricted(Beta.class) + public interface BuildNumberAssigner extends ExtensionPoint { + /** + * Implementation of {@link Job#assignBuildNumber}. + */ + int assignBuildNumber(Job job, SaveNextBuildNumber saveNextBuildNumber) throws IOException; + /** + * Provides an externally accessible alias for {@link Job#saveNextBuildNumber}, which is {@code protected}. + * ({@link #getNextBuildNumber} and {@link #fastUpdateNextBuildNumber} are already accessible.) + */ + interface SaveNextBuildNumber { + void call() throws IOException; + } + } + + @Restricted(DoNotUse.class) + @Extension(ordinal = -1000) + public static final class DefaultBuildNumberAssigner implements BuildNumberAssigner { + @Override + public int assignBuildNumber(Job job, SaveNextBuildNumber saveNextBuildNumber) throws IOException { + synchronized (job) { + int r = job.nextBuildNumber++; + saveNextBuildNumber.call(); + return r; + } + } } /** @@ -497,6 +519,11 @@ public boolean supportsLogRotator() { return true; } + @Override + public String getSearchIcon() { + return "symbol-status-" + this.getIconColor().getIconName(); + } + @Override protected SearchIndexBuilder makeSearchIndex() { return super.makeSearchIndex().add(new SearchIndex() { @@ -519,7 +546,7 @@ public void find(String token, List result) { public void suggest(String token, List result) { find(token, result); } - }).add("configure", "config", "configure"); + }); } @Override @@ -831,7 +858,7 @@ public RunT getBuildForCLI(@Argument(required = true, metaVar = "BUILD#", usage } /** - * Gets the youngest build #m that satisfies {@code n<=m}. + * Gets the oldest build #m that satisfies {@code m ≥ n}. * * This is useful when you'd like to fetch a build but the exact build might * be already gone (deleted, rotated, etc.) @@ -846,7 +873,7 @@ public RunT getNearestBuild(int n) { } /** - * Gets the latest build #m that satisfies {@code m<=n}. + * Gets the newest build #m that satisfies {@code m ≤ n}. * * This is useful when you'd like to fetch a build but the exact build might * be already gone (deleted, rotated, etc.) @@ -955,7 +982,7 @@ public File getBuildDir() { protected abstract void removeRun(RunT run); /** - * Returns the last build. + * Returns the newest build. * @see LazyBuildMixIn#getLastBuild */ @Exported diff --git a/core/src/main/java/hudson/model/ModifiableItemGroup.java b/core/src/main/java/hudson/model/ModifiableItemGroup.java index 35e41305afb5..d0bcda31187c 100644 --- a/core/src/main/java/hudson/model/ModifiableItemGroup.java +++ b/core/src/main/java/hudson/model/ModifiableItemGroup.java @@ -33,6 +33,7 @@ import org.kohsuke.stapler.StaplerRequest2; import org.kohsuke.stapler.StaplerResponse; import org.kohsuke.stapler.StaplerResponse2; +import org.kohsuke.stapler.interceptor.RequirePOST; /** * {@link ItemGroup} that is a general purpose container, which allows users and the rest of the program @@ -50,7 +51,7 @@ public interface ModifiableItemGroup extends ItemGroup { * The request format follows that of {@code <n:form xmlns:n="/lib/form">}. * */ - @StaplerNotDispatchable + @RequirePOST default T doCreateItem(StaplerRequest2 req, StaplerResponse2 rsp) throws IOException, ServletException { if (ReflectionUtils.isOverridden( ModifiableItemGroup.class, diff --git a/core/src/main/java/hudson/model/UpdateSite.java b/core/src/main/java/hudson/model/UpdateSite.java index 0e3cc894148a..22764fd403d0 100644 --- a/core/src/main/java/hudson/model/UpdateSite.java +++ b/core/src/main/java/hudson/model/UpdateSite.java @@ -217,7 +217,7 @@ public long getDataTimestamp() { return updateData(DownloadService.loadJSON(new URL(getUrl() + "?id=" + URLEncoder.encode(getId(), StandardCharsets.UTF_8) + "&version=" + URLEncoder.encode(Jenkins.VERSION, StandardCharsets.UTF_8))), signatureCheck); } - private FormValidation updateData(String json, boolean signatureCheck) + protected FormValidation updateData(String json, boolean signatureCheck) throws IOException { dataTimestamp = System.currentTimeMillis(); diff --git a/core/src/main/java/hudson/model/User.java b/core/src/main/java/hudson/model/User.java index 685a80e540a9..d40664ca2c7a 100644 --- a/core/src/main/java/hudson/model/User.java +++ b/core/src/main/java/hudson/model/User.java @@ -44,6 +44,7 @@ import hudson.security.AccessControlled; import hudson.security.SecurityRealm; import hudson.security.UserMayOrMayNotExistException2; +import hudson.tasks.UserAvatarResolver; import hudson.util.FormValidation; import hudson.util.RunList; import hudson.util.XStream2; @@ -77,6 +78,7 @@ import jenkins.util.SystemProperties; import org.jenkinsci.Symbol; import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.Beta; import org.kohsuke.accmod.restrictions.NoExternalUse; import org.kohsuke.stapler.StaplerProxy; import org.kohsuke.stapler.StaplerRequest2; @@ -277,6 +279,11 @@ public String getId() { return "/user/" + Util.rawEncode(idStrategy().keyFor(id)); } + @Override + public String getSearchIcon() { + return UserAvatarResolver.resolve(this, "48x48"); + } + /** * The URL of the user page. */ @@ -673,9 +680,9 @@ public void doSubmitDescription(StaplerRequest2 req, StaplerResponse2 rsp) throw } /** - * To be called from {@link Jenkins#reload} only. + * Called from {@link Jenkins#reload}. */ - @Restricted(NoExternalUse.class) + @Restricted(Beta.class) public static void reload() throws IOException { UserIdMapper.getInstance().reload(); AllUsers.reload(); diff --git a/core/src/main/java/hudson/model/View.java b/core/src/main/java/hudson/model/View.java index b17077cde44d..722254572c0c 100644 --- a/core/src/main/java/hudson/model/View.java +++ b/core/src/main/java/hudson/model/View.java @@ -560,6 +560,11 @@ public String getSearchUrl() { return getUrl(); } + @Override + public String getSearchIcon() { + return "symbol-jobs"; + } + /** * Returns the transient {@link Action}s associated with the top page. * diff --git a/core/src/main/java/hudson/search/Search.java b/core/src/main/java/hudson/search/Search.java index 7773d9e9d696..97d15b65c2df 100644 --- a/core/src/main/java/hudson/search/Search.java +++ b/core/src/main/java/hudson/search/Search.java @@ -46,6 +46,8 @@ import jenkins.security.stapler.StaplerNotDispatchable; import jenkins.util.MemoryReductionUtil; import jenkins.util.SystemProperties; +import org.jenkins.ui.symbol.Symbol; +import org.jenkins.ui.symbol.SymbolRequest; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; import org.kohsuke.stapler.Ancestor; @@ -56,6 +58,7 @@ import org.kohsuke.stapler.StaplerResponse; import org.kohsuke.stapler.StaplerResponse2; import org.kohsuke.stapler.export.DataWriter; +import org.kohsuke.stapler.export.ExportConfig; import org.kohsuke.stapler.export.Exported; import org.kohsuke.stapler.export.ExportedBean; import org.kohsuke.stapler.export.Flavor; @@ -157,10 +160,23 @@ public void doSuggestOpenSearch(StaplerRequest2 req, StaplerResponse2 rsp, @Quer */ public void doSuggest(StaplerRequest2 req, StaplerResponse2 rsp, @QueryParameter String query) throws IOException, ServletException { Result r = new Result(); - for (SuggestedItem item : getSuggestions(req, query)) - r.suggestions.add(new Item(item.getPath())); + for (SuggestedItem curItem : getSuggestions(req, query)) { + String iconName = curItem.item.getSearchIcon(); - rsp.serveExposedBean(req, r, Flavor.JSON); + if (iconName == null || + (!iconName.startsWith("symbol-") && !iconName.startsWith("http")) + ) { + iconName = "symbol-search"; + } + + if (iconName.startsWith("symbol")) { + r.suggestions.add(new Item(curItem.getPath(), curItem.getUrl(), + Symbol.get(new SymbolRequest.Builder().withRaw(iconName).build()))); + } else { + r.suggestions.add(new Item(curItem.getPath(), curItem.getUrl(), iconName, "image")); + } + } + rsp.serveExposedBean(req, r, new ExportConfig()); } /** @@ -252,12 +268,48 @@ public static class Result { @ExportedBean(defaultVisibility = 999) public static class Item { + @Exported @SuppressFBWarnings(value = "URF_UNREAD_PUBLIC_OR_PROTECTED_FIELD", justification = "read by Stapler") public String name; + private final String url; + + private final String type; + + private final String icon; + public Item(String name) { + this(name, null, null); + } + + public Item(String name, String url, String icon) { + this.name = name; + this.url = url; + this.icon = icon; + this.type = "symbol"; + } + + public Item(String name, String url, String icon, String type) { this.name = name; + this.url = url; + this.icon = icon; + this.type = type; + } + + @Exported + public String getUrl() { + return url; + } + + @Exported + public String getIcon() { + return icon; + } + + @Exported + public String getType() { + return type; } } diff --git a/core/src/main/java/hudson/search/SearchItem.java b/core/src/main/java/hudson/search/SearchItem.java index 02b2921b9741..e64efb0257ed 100644 --- a/core/src/main/java/hudson/search/SearchItem.java +++ b/core/src/main/java/hudson/search/SearchItem.java @@ -25,6 +25,7 @@ package hudson.search; import hudson.model.Build; +import org.jenkins.ui.icon.IconSpec; /** * Represents an item reachable from {@link SearchIndex}. @@ -54,6 +55,14 @@ public interface SearchItem { String getSearchUrl(); + default String getSearchIcon() { + if (this instanceof IconSpec) { + return ((IconSpec) this).getIconClassName(); + } + + return "symbol-search"; + } + /** * Returns the {@link SearchIndex} to further search sub items inside this item. * diff --git a/core/src/main/java/hudson/search/SuggestedItem.java b/core/src/main/java/hudson/search/SuggestedItem.java index 9ba455270e58..6911d002fe50 100644 --- a/core/src/main/java/hudson/search/SuggestedItem.java +++ b/core/src/main/java/hudson/search/SuggestedItem.java @@ -62,7 +62,7 @@ private void getPath(StringBuilder buf) { buf.append(item.getSearchName()); else { parent.getPath(buf); - buf.append(' ').append(item.getSearchName()); + buf.append(" » ").append(item.getSearchName()); } } diff --git a/core/src/main/java/hudson/security/GlobalSecurityConfiguration.java b/core/src/main/java/hudson/security/GlobalSecurityConfiguration.java index a8657df65685..ae90b0428c7b 100644 --- a/core/src/main/java/hudson/security/GlobalSecurityConfiguration.java +++ b/core/src/main/java/hudson/security/GlobalSecurityConfiguration.java @@ -37,15 +37,12 @@ import hudson.util.FormApply; import jakarta.servlet.ServletException; import java.io.IOException; -import java.util.Set; -import java.util.TreeSet; import java.util.function.Predicate; import java.util.logging.Level; import java.util.logging.Logger; import jenkins.model.GlobalConfigurationCategory; import jenkins.model.Jenkins; import jenkins.util.ServerTcpPort; -import net.sf.json.JSONArray; import net.sf.json.JSONException; import net.sf.json.JSONObject; import org.jenkinsci.Symbol; @@ -92,11 +89,6 @@ public boolean isSlaveAgentPortEnforced() { return Jenkins.get().isSlaveAgentPortEnforced(); } - @NonNull - public Set getAgentProtocols() { - return Jenkins.get().getAgentProtocols(); - } - public boolean isDisableRememberMe() { return Jenkins.get().isDisableRememberMe(); } @@ -149,18 +141,6 @@ public boolean configure(StaplerRequest2 req, JSONObject json) throws FormExcept throw new FormException(e, "slaveAgentPortType"); } } - Set agentProtocols = new TreeSet<>(); - if (json.has("agentProtocol")) { - Object protocols = json.get("agentProtocol"); - if (protocols instanceof JSONArray) { - for (int i = 0; i < ((JSONArray) protocols).size(); i++) { - agentProtocols.add(((JSONArray) protocols).getString(i)); - } - } else { - agentProtocols.add(protocols.toString()); - } - } - j.setAgentProtocols(agentProtocols); // persist all the additional security configs boolean result = true; diff --git a/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java b/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java index 1a6ebe66f749..f55576dec4d6 100644 --- a/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java +++ b/core/src/main/java/hudson/security/HudsonPrivateSecurityRealm.java @@ -839,6 +839,10 @@ public Details newInstance(StaplerRequest2 req, JSONObject formData) throws Form throw new FormException("Please confirm the password by typing it twice", "user.password2"); } + if (FIPS140.useCompliantAlgorithms() && pwd.length() < FIPS_PASSWORD_LENGTH) { + throw new FormException(Messages.HudsonPrivateSecurityRealm_CreateAccount_FIPS_PasswordLengthInvalid(), "user.password"); + } + // will be null if it wasn't encrypted String data = Protector.unprotect(pwd); String data2 = Protector.unprotect(pwd2); diff --git a/core/src/main/java/hudson/slaves/OfflineCause.java b/core/src/main/java/hudson/slaves/OfflineCause.java index 2a267ef03a4a..07c2d3af2872 100644 --- a/core/src/main/java/hudson/slaves/OfflineCause.java +++ b/core/src/main/java/hudson/slaves/OfflineCause.java @@ -30,7 +30,7 @@ import hudson.model.User; import java.io.ObjectStreamException; import java.util.Collections; -import java.util.Date; +import jenkins.agents.IOfflineCause; import jenkins.model.Jenkins; import org.jvnet.localizer.Localizable; import org.kohsuke.accmod.Restricted; @@ -51,28 +51,20 @@ * @since 1.320 */ @ExportedBean -public abstract class OfflineCause { +public abstract class OfflineCause implements IOfflineCause { protected final long timestamp = System.currentTimeMillis(); /** - * Timestamp in which the event happened. + * {@inheritDoc} * * @since 1.612 */ @Exported + @Override public long getTimestamp() { return timestamp; } - /** - * Same as {@link #getTimestamp()} but in a different type. - * - * @since 1.612 - */ - public final @NonNull Date getTime() { - return new Date(timestamp); - } - /** * @deprecated Only exists for backward compatibility. * @see Computer#setTemporarilyOffline(boolean) @@ -192,6 +184,24 @@ private Object readResolve() throws ObjectStreamException { } return this; } + + @Override + @NonNull + public String getComputerIcon() { + return "symbol-computer-disconnected"; + } + + @Override + @NonNull + public String getComputerIconAltText() { + return "[temporarily offline by user]"; + } + + @NonNull + @Override + public String getIcon() { + return "symbol-person"; + } } public static class ByCLI extends UserCause { @@ -212,5 +222,28 @@ public static class IdleOfflineCause extends SimpleOfflineCause { public IdleOfflineCause() { super(hudson.slaves.Messages._RetentionStrategy_Demand_OfflineIdle()); } + + @Override + @NonNull + public String getComputerIcon() { + return "symbol-computer-paused"; + } + + @Override + @NonNull + public String getComputerIconAltText() { + return "[will connect automatically whenever needed]"; + } + + @Override + @NonNull + public String getIcon() { + return "symbol-pause"; + } + + @Override + public String getStatusClass() { + return "info"; + } } } diff --git a/core/src/main/java/hudson/slaves/RetentionStrategy.java b/core/src/main/java/hudson/slaves/RetentionStrategy.java index 687e44d5c172..6fe491cdaa87 100644 --- a/core/src/main/java/hudson/slaves/RetentionStrategy.java +++ b/core/src/main/java/hudson/slaves/RetentionStrategy.java @@ -272,6 +272,8 @@ public long check(final SlaveComputer c) { logger.log(Level.INFO, "Launching computer {0} as it has been in demand for {1}", new Object[]{c.getName(), Util.getTimeSpanString(demandMilliseconds)}); c.connect(false); + } else if (c.getOfflineCause() == null) { + c.setOfflineCause(new OfflineCause.IdleOfflineCause()); } } else if (c.isIdle()) { final long idleMilliseconds = System.currentTimeMillis() - c.getIdleStartMilliseconds(); diff --git a/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java b/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java index ea90529b29b4..13a267ace665 100644 --- a/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java +++ b/core/src/main/java/hudson/slaves/SimpleScheduledRetentionStrategy.java @@ -242,6 +242,8 @@ public void run() { c.disconnect(OfflineCause.create(Messages._SimpleScheduledRetentionStrategy_FinishedUpTime())); } } + } else { + c.setOfflineCause(new ScheduledOfflineCause()); } return 0; } @@ -252,6 +254,24 @@ private synchronized boolean isOnlineScheduled() { return (lastStart < now && lastStop > now) || (nextStart < now && nextStop > now); } + public static class ScheduledOfflineCause extends OfflineCause.SimpleOfflineCause { + public ScheduledOfflineCause() { + super(Messages._SimpleScheduledRetentionStrategy_ScheduledOfflineCause_displayName()); + } + + @NonNull + @Override + public String getComputerIcon() { + return "symbol-computer-not-accepting"; + } + + @NonNull + @Override + public String getIcon() { + return "symbol-trigger"; + } + } + @Extension @Symbol("schedule") public static class DescriptorImpl extends Descriptor> { @NonNull diff --git a/core/src/main/java/hudson/tools/ZipExtractionInstaller.java b/core/src/main/java/hudson/tools/ZipExtractionInstaller.java index 1e1c171f6a95..4646980ea8d6 100644 --- a/core/src/main/java/hudson/tools/ZipExtractionInstaller.java +++ b/core/src/main/java/hudson/tools/ZipExtractionInstaller.java @@ -43,6 +43,8 @@ import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; +import java.nio.file.FileSystemNotFoundException; +import java.nio.file.Path; import jenkins.MasterToSlaveFileCallable; import jenkins.model.Jenkins; import org.jenkinsci.Symbol; @@ -117,6 +119,14 @@ public FormValidation doCheckUrl(@QueryParameter String value) throws Interrupte } catch (URISyntaxException e) { return FormValidation.error(e, Messages.ZipExtractionInstaller_malformed_url()); } + if (uri.getScheme() != null && !uri.getScheme().startsWith("http")) { + try { + Path.of(uri); + return FormValidation.ok(); + } catch (FileSystemNotFoundException | IllegalArgumentException e) { + return FormValidation.error(e, Messages.ZipExtractionInstaller_malformed_url()); + } + } HttpClient httpClient = ProxyConfiguration.newHttpClient(); HttpRequest httpRequest; try { diff --git a/core/src/main/java/hudson/util/AtomicFileWriter.java b/core/src/main/java/hudson/util/AtomicFileWriter.java index fabe15c5a6b6..252605c06de7 100644 --- a/core/src/main/java/hudson/util/AtomicFileWriter.java +++ b/core/src/main/java/hudson/util/AtomicFileWriter.java @@ -67,6 +67,11 @@ public class AtomicFileWriter extends Writer { private static /* final */ boolean REQUIRES_DIR_FSYNC = SystemProperties.getBoolean( AtomicFileWriter.class.getName() + ".REQUIRES_DIR_FSYNC", !Functions.isWindows()); + /** + * Whether the platform supports atomic move. + */ + private static boolean atomicMoveSupported = true; + static { if (DISABLE_FORCED_FLUSH) { LOGGER.log(Level.WARNING, "DISABLE_FORCED_FLUSH flag used, this could result in dataloss if failures happen in your storage subsystem."); @@ -149,7 +154,7 @@ public AtomicFileWriter(@NonNull Path destinationPath, @NonNull Charset charset, try { // JENKINS-48407: NIO's createTempFile creates file with 0600 permissions, so we use pre-NIO for this... - tmpPath = File.createTempFile("atomic", "tmp", dir.toFile()).toPath(); + tmpPath = File.createTempFile(destPath.getFileName() + "-atomic", "tmp", dir.toFile()).toPath(); } catch (IOException e) { throw new IOException("Failed to create a temporary file in " + dir, e); } @@ -207,36 +212,14 @@ public void abort() throws IOException { public void commit() throws IOException { close(); try { - // Try to make an atomic move. - Files.move(tmpPath, destPath, StandardCopyOption.ATOMIC_MOVE); - } catch (IOException moveFailed) { - // If it falls here that can mean many things. Either that the atomic move is not supported, - // or something wrong happened. Anyway, let's try to be over-diagnosing - if (moveFailed instanceof AtomicMoveNotSupportedException) { - LOGGER.log(Level.WARNING, "Atomic move not supported. falling back to non-atomic move.", moveFailed); - } else { - LOGGER.log(Level.WARNING, "Unable to move atomically, falling back to non-atomic move.", moveFailed); - } - - if (destPath.toFile().exists()) { - LOGGER.log(Level.INFO, "The target file {0} was already existing", destPath); - } - + move(tmpPath, destPath); + } finally { try { - Files.move(tmpPath, destPath, StandardCopyOption.REPLACE_EXISTING); - } catch (IOException replaceFailed) { - replaceFailed.addSuppressed(moveFailed); - LOGGER.log(Level.WARNING, "Unable to move {0} to {1}. Attempting to delete {0} and abandoning.", - new Path[]{tmpPath, destPath}); - try { - Files.deleteIfExists(tmpPath); - } catch (IOException deleteFailed) { - replaceFailed.addSuppressed(deleteFailed); - LOGGER.log(Level.WARNING, "Unable to delete {0}, good bye then!", tmpPath); - throw replaceFailed; - } - - throw replaceFailed; + // In case of prior failure, the temporary file should be deleted. + // If the operation succeeded, the tmpPath is already deleted. + Files.deleteIfExists(tmpPath); + } catch (IOException e) { + LOGGER.log(Level.WARNING, e, () -> "Failed to delete temporary file " + tmpPath + " for destination file " + destPath); } } @@ -253,6 +236,23 @@ public void commit() throws IOException { } } + private static void move(Path source, Path destination) throws IOException { + if (atomicMoveSupported) { + try { + // Try to make an atomic move. + Files.move(source, destination, StandardCopyOption.ATOMIC_MOVE); + return; + } catch (AtomicMoveNotSupportedException e) { + // Both files are on the same filesystem, so this should not happen. + LOGGER.log(Level.WARNING, e, () -> "Atomic move " + source + " → " + destination + " not supported. Falling back to non-atomic move."); + atomicMoveSupported = false; + } + } + if (!atomicMoveSupported) { + Files.move(source, destination, StandardCopyOption.REPLACE_EXISTING); + } + } + private static final class CleanupChecker implements Runnable { private final FileChannelWriter core; private final Path tmpPath; diff --git a/core/src/main/java/hudson/util/FormApply.java b/core/src/main/java/hudson/util/FormApply.java index 280a3051286a..3d5f458ec126 100644 --- a/core/src/main/java/hudson/util/FormApply.java +++ b/core/src/main/java/hudson/util/FormApply.java @@ -24,8 +24,10 @@ package hudson.util; +import hudson.Functions; import jakarta.servlet.ServletException; import java.io.IOException; +import jenkins.model.Jenkins; import org.kohsuke.stapler.HttpResponses.HttpResponseException; import org.kohsuke.stapler.StaplerRequest; import org.kohsuke.stapler.StaplerRequest2; @@ -51,7 +53,7 @@ public static HttpResponseException success(final String destination) { public void generateResponse(StaplerRequest2 req, StaplerResponse2 rsp, Object node) throws IOException, ServletException { if (isApply(req)) { // if the submission is via 'apply', show a response in the notification bar - applyResponse("notificationBar.show('" + Messages.HttpResponses_Saved() + "',notificationBar.SUCCESS)") + showNotification(Messages.HttpResponses_Saved(), NotificationType.SUCCESS) .generateResponse(req, rsp, node); } else { rsp.sendRedirect(destination); @@ -69,6 +71,7 @@ public static boolean isApply(StaplerRequest2 req) { return Boolean.parseBoolean(req.getParameter("core:apply")); } + /** * @deprecated use {@link #isApply(StaplerRequest2)} */ @@ -82,7 +85,10 @@ public static boolean isApply(StaplerRequest req) { *

* When the response HTML includes a JavaScript function in a pre-determined name, that function gets executed. * This method generates such a response from JavaScript text. + * + * @deprecated use {@link #showNotification(String, NotificationType)} instead, which is CSP compatible version */ + @Deprecated public static HttpResponseException applyResponse(final String script) { return new HttpResponseException() { @Override @@ -98,4 +104,36 @@ public void generateResponse(StaplerRequest2 req, StaplerResponse2 rsp, Object n } }; } + + /** + * Generates the response for the asynchronous background form submission (AKA the Apply button), + * that will show a notification of certain type and with provided message. + * + * @param message a message to display in the popup. Only plain text is supported. + * @param notificationType type of notification. See {@link NotificationType} for supported types. Defines the notification + * color and the icon that will be shown. + * + * @since 2.482 + */ + public static HttpResponseException showNotification(final String message, final NotificationType notificationType) { + return new HttpResponseException() { + @Override + public void generateResponse(StaplerRequest2 req, StaplerResponse2 rsp, Object node) throws IOException { + rsp.setContentType("text/html;charset=UTF-8"); + rsp.getWriter().println(""); + } + }; + } + + + /** + * Corresponds to types declared in index.js + */ + public enum NotificationType { + SUCCESS, + WARNING, + ERROR + } } diff --git a/core/src/main/java/jenkins/AgentProtocol.java b/core/src/main/java/jenkins/AgentProtocol.java index d43398985810..1494dc60b183 100644 --- a/core/src/main/java/jenkins/AgentProtocol.java +++ b/core/src/main/java/jenkins/AgentProtocol.java @@ -7,8 +7,6 @@ import hudson.TcpSlaveAgentListener; import java.io.IOException; import java.net.Socket; -import java.util.Set; -import jenkins.model.Jenkins; /** * Pluggable Jenkins TCP agent protocol handler called from {@link TcpSlaveAgentListener}. @@ -18,57 +16,31 @@ * Implementations of this extension point is singleton, and its {@link #handle(Socket)} method * gets invoked concurrently whenever a new connection comes in. * - *

Extending UI

- *
- *
description.jelly
- *
Optional protocol description
- *
deprecationCause.jelly
- *
Optional. If the protocol is marked as {@link #isDeprecated()}, - * clarifies the deprecation reason and provides extra documentation links
- *
- * * @author Kohsuke Kawaguchi * @since 1.467 * @see TcpSlaveAgentListener */ public abstract class AgentProtocol implements ExtensionPoint { /** - * Allow experimental {@link AgentProtocol} implementations to declare being opt-in. - * Note that {@link Jenkins#setAgentProtocols(Set)} only records the protocols where the admin has made a - * conscious decision thus: - *
    - *
  • if a protocol is opt-in, it records the admin enabling it
  • - *
  • if a protocol is opt-out, it records the admin disabling it
  • - *
- * Implementations should not transition rapidly from {@code opt-in -> opt-out -> opt-in}. - * Implementations should never flip-flop: {@code opt-in -> opt-out -> opt-in -> opt-out} as that will basically - * clear any preference that an admin has set. This latter restriction should be ok as we only ever will be - * adding new protocols and retiring old ones. - * - * @return {@code true} if the protocol requires explicit opt-in. - * @since 2.16 - * @see Jenkins#setAgentProtocols(Set) + * @deprecated no longer used */ + @Deprecated public boolean isOptIn() { return false; } + /** - * Allow essential {@link AgentProtocol} implementations (basically {@link TcpSlaveAgentListener.PingAgentProtocol}) - * to be always enabled. - * - * @return {@code true} if the protocol can never be disabled. - * @since 2.16 + * @deprecated no longer used */ - + @Deprecated public boolean isRequired() { return false; } /** - * Checks if the protocol is deprecated. - * - * @since 2.75 + * @deprecated no longer used */ + @Deprecated public boolean isDeprecated() { return false; } @@ -79,17 +51,15 @@ public boolean isDeprecated() { * This is a short string that consists of printable ASCII chars. Sent by the client to select the protocol. * * @return - * null to be disabled. This is useful for avoiding getting used - * until the protocol is properly configured. + * null to be disabled */ + @CheckForNull public abstract String getName(); /** - * Returns the human readable protocol display name. - * - * @return the human readable protocol display name. - * @since 2.16 + * @deprecated no longer used */ + @Deprecated public String getDisplayName() { return getName(); } diff --git a/core/src/main/java/jenkins/MasterToSlaveFileCallable.java b/core/src/main/java/jenkins/MasterToSlaveFileCallable.java index 34afd3c11ae5..6296c6d65b28 100644 --- a/core/src/main/java/jenkins/MasterToSlaveFileCallable.java +++ b/core/src/main/java/jenkins/MasterToSlaveFileCallable.java @@ -1,26 +1,16 @@ package jenkins; import hudson.FilePath.FileCallable; -import hudson.remoting.VirtualChannel; -import java.io.File; -import jenkins.security.Roles; -import jenkins.slaves.RemotingVersionInfo; -import org.jenkinsci.remoting.RoleChecker; +import jenkins.agents.ControllerToAgentFileCallable; /** - * {@link FileCallable}s that are meant to be only used on the master. - * - * Note that the logic within {@link #invoke(File, VirtualChannel)} should use API of a minimum supported Remoting version. - * See {@link RemotingVersionInfo#getMinimumSupportedVersion()}. - * + * {@link FileCallable}s that could run on an agent. + * For new code, implement {@link ControllerToAgentFileCallable} + * which has the advantage that it can be used on {@code record}s. * @since 1.587 / 1.580.1 - * @param the return type; note that this must either be defined in your plugin or included in the stock JEP-200 whitelist + * @param the return type */ -public abstract class MasterToSlaveFileCallable implements FileCallable { - @Override - public void checkRoles(RoleChecker checker) throws SecurityException { - checker.check(this, Roles.SLAVE); - } +public abstract class MasterToSlaveFileCallable implements ControllerToAgentFileCallable { private static final long serialVersionUID = 1L; } diff --git a/core/src/main/java/jenkins/agents/ControllerToAgentCallable.java b/core/src/main/java/jenkins/agents/ControllerToAgentCallable.java new file mode 100644 index 000000000000..edf057656569 --- /dev/null +++ b/core/src/main/java/jenkins/agents/ControllerToAgentCallable.java @@ -0,0 +1,48 @@ +/* + * The MIT License + * + * Copyright 2024 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.agents; + +import hudson.remoting.Callable; +import jenkins.security.Roles; +import jenkins.slaves.RemotingVersionInfo; +import org.jenkinsci.remoting.RoleChecker; + +/** + * {@link Callable} meant to be serialized then run on an agent. + * A typical implementation will be a {@link Record} + * since instance state merely transfers a set of parameters to an agent JVM. + *

Note that the logic within {@link #call} may not use Remoting APIs + * newer than {@link RemotingVersionInfo#getMinimumSupportedVersion}. + * (Core and plugin APIs will be identical to those run inside the controller.) + * @param the return type; note that this must either be defined in your plugin or included in the stock JEP-200 whitelist + * @since 2.485 + */ +public interface ControllerToAgentCallable extends Callable { + + @Override + default void checkRoles(RoleChecker checker) throws SecurityException { + checker.check(this, Roles.SLAVE); + } +} diff --git a/core/src/main/java/jenkins/agents/ControllerToAgentFileCallable.java b/core/src/main/java/jenkins/agents/ControllerToAgentFileCallable.java new file mode 100644 index 000000000000..bd7f7a86c36d --- /dev/null +++ b/core/src/main/java/jenkins/agents/ControllerToAgentFileCallable.java @@ -0,0 +1,43 @@ +/* + * The MIT License + * + * Copyright 2024 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.agents; + +import hudson.FilePath; +import jenkins.security.Roles; +import org.jenkinsci.remoting.RoleChecker; + +/** + * {@link FilePath.FileCallable} meant to be serialized then run on an agent. + * Like {@link ControllerToAgentCallable} this will typically be a {@link Record}. + * @param the return type; note that this must either be defined in your plugin or included in the stock JEP-200 whitelist + * @since 2.485 + */ +public interface ControllerToAgentFileCallable extends FilePath.FileCallable { + + @Override + default void checkRoles(RoleChecker checker) throws SecurityException { + checker.check(this, Roles.SLAVE); + } +} diff --git a/core/src/main/java/jenkins/agents/IOfflineCause.java b/core/src/main/java/jenkins/agents/IOfflineCause.java new file mode 100644 index 000000000000..b1c32820e848 --- /dev/null +++ b/core/src/main/java/jenkins/agents/IOfflineCause.java @@ -0,0 +1,108 @@ +/* + * The MIT License + * + * Copyright 2024 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.agents; + +import edu.umd.cs.findbugs.annotations.NonNull; +import hudson.Util; +import java.util.Date; +import java.util.Objects; +import jenkins.model.IComputer; + +/** + * Represents a cause that puts a {@linkplain IComputer#isOffline() computer offline}. + * @since 2.483 + */ +public interface IOfflineCause { + /** + * @return The icon to use for the computer that has this offline cause. It will be displayed in the build executor status widget, as well as in nodes list screen. + */ + @NonNull + default String getComputerIcon() { + return "symbol-computer-offline"; + } + + /** + * @return The alt text for the icon returned by {@link #getComputerIcon()}. + */ + @NonNull + default String getComputerIconAltText() { + return "[offline]"; + } + + /** + * @return The icon to render this offline cause. It will be displayed in the build executor status widget. + */ + @NonNull + default String getIcon() { + return "symbol-error"; + } + + /** + * @return The reason why this offline cause exists. + *

+ * For implementers: this can use HTML formatting, so make sure to only include trusted content. + */ + @NonNull + default String getReason() { + // fetch the localized string for "Disconnected By" + String gsub_base = hudson.slaves.Messages.SlaveComputer_DisconnectedBy("", ""); + // regex to remove commented reason base string + String gsub1 = "^" + gsub_base + "[\\w\\W]* \\: "; + // regex to remove non-commented reason base string + String gsub2 = "^" + gsub_base + "[\\w\\W]*"; + return Objects.requireNonNull(Util.escape(toString().replaceAll(gsub1, "").replaceAll(gsub2, ""))); + } + + /** + * @return A short message (one word) that summarizes the offline cause. + * + *

+ * For implementers: this can use HTML formatting, so make sure to only include trusted content. + */ + @NonNull + default String getMessage() { + return Messages.IOfflineCause_offline(); + } + + /** + * @return the CSS class name that should be used to render the status. + */ + @SuppressWarnings("unused") // jelly + default String getStatusClass() { + return "warning"; + } + + /** + * Timestamp in which the event happened. + */ + long getTimestamp(); + + /** + * Same as {@link #getTimestamp()} but in a different type. + */ + @NonNull + default Date getTime() { + return new Date(getTimestamp()); + } +} diff --git a/core/src/main/java/jenkins/model/IComputer.java b/core/src/main/java/jenkins/model/IComputer.java index 0708748f4370..e00ee78bd30f 100644 --- a/core/src/main/java/jenkins/model/IComputer.java +++ b/core/src/main/java/jenkins/model/IComputer.java @@ -32,8 +32,10 @@ import hudson.security.AccessControlled; import java.util.List; import java.util.concurrent.Future; +import jenkins.agents.IOfflineCause; import org.jenkins.ui.icon.Icon; import org.jenkins.ui.icon.IconSet; +import org.jenkins.ui.icon.IconSpec; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.Beta; @@ -43,7 +45,7 @@ * @since 2.480 */ @Restricted(Beta.class) -public interface IComputer extends AccessControlled { +public interface IComputer extends AccessControlled, IconSpec { /** * Returns {@link Node#getNodeName() the name of the node}. */ @@ -91,6 +93,12 @@ default boolean hasOfflineCause() { return Util.fixEmpty(getOfflineCauseReason()) != null; } + /** + * @return the offline cause if the computer is offline. + * @since 2.483 + */ + IOfflineCause getOfflineCause(); + /** * If the computer was offline (either temporarily or not), * this method will return the cause as a string (without user info). @@ -101,7 +109,12 @@ default boolean hasOfflineCause() { * empty string if the system was put offline without given a cause. */ @NonNull - String getOfflineCauseReason(); + default String getOfflineCauseReason() { + if (getOfflineCause() == null) { + return ""; + } + return getOfflineCause().getReason(); + } /** * @return true if the node is currently connecting to the Jenkins controller. @@ -115,12 +128,45 @@ default boolean hasOfflineCause() { * * @see #getIconClassName() */ - String getIcon(); + default String getIcon() { + // The computer is not accepting tasks, e.g. because the availability demands it being offline. + if (!isAcceptingTasks()) { + return "symbol-computer-not-accepting"; + } + var offlineCause = getOfflineCause(); + if (offlineCause != null) { + return offlineCause.getComputerIcon(); + } + // The computer is not connected or it is temporarily offline due to a node monitor + if (isOffline()) return "symbol-computer-offline"; + return "symbol-computer"; + } /** * Returns the alternative text for the computer icon. */ - String getIconAltText(); + @SuppressWarnings("unused") // jelly + default String getIconAltText() { + if (!isAcceptingTasks()) { + return "[suspended]"; + } + var offlineCause = getOfflineCause(); + if (offlineCause != null) { + return offlineCause.getComputerIconAltText(); + } + // There is a "technical" reason the computer will not accept new builds + if (isOffline()) return "[offline]"; + return "[online]"; + } + + default String getTooltip() { + var offlineCause = getOfflineCause(); + if (offlineCause != null) { + return offlineCause.toString(); + } else { + return ""; + } + } /** * Returns the class name that will be used to look up the icon. diff --git a/core/src/main/java/jenkins/model/Jenkins.java b/core/src/main/java/jenkins/model/Jenkins.java index dc2c7926f605..66adc45f82e8 100644 --- a/core/src/main/java/jenkins/model/Jenkins.java +++ b/core/src/main/java/jenkins/model/Jenkins.java @@ -147,6 +147,7 @@ import hudson.scm.RepositoryBrowser; import hudson.scm.SCM; import hudson.search.CollectionSearchIndex; +import hudson.search.SearchIndex; import hudson.search.SearchIndexBuilder; import hudson.search.SearchItem; import hudson.security.ACL; @@ -656,47 +657,6 @@ private static int getSlaveAgentPortInitialValue(int def) { */ private static final boolean SLAVE_AGENT_PORT_ENFORCE = SystemProperties.getBoolean(Jenkins.class.getName() + ".slaveAgentPortEnforce", false); - /** - * The TCP agent protocols that are explicitly disabled (we store the disabled ones so that newer protocols - * are enabled by default). Will be {@code null} instead of empty to simplify XML format. - * - * @since 2.16 - */ - @CheckForNull - @GuardedBy("this") - private List disabledAgentProtocols; - /** - * @deprecated Just a temporary buffer for XSTream migration code from JENKINS-39465, do not use - */ - @Deprecated - private transient String[] _disabledAgentProtocols; - - /** - * The TCP agent protocols that are {@link AgentProtocol#isOptIn()} and explicitly enabled. - * Will be {@code null} instead of empty to simplify XML format. - * - * @since 2.16 - */ - @CheckForNull - @GuardedBy("this") - private List enabledAgentProtocols; - /** - * @deprecated Just a temporary buffer for XSTream migration code from JENKINS-39465, do not use - */ - @Deprecated - private transient String[] _enabledAgentProtocols; - - /** - * The TCP agent protocols that are enabled. Built from {@link #disabledAgentProtocols} and - * {@link #enabledAgentProtocols}. - * - * @since 2.16 - * @see #setAgentProtocols(Set) - * @see #getAgentProtocols() - */ - @GuardedBy("this") - private transient Set agentProtocols; - /** * Whitespace-separated labels assigned to the built-in node as a {@link Node}. */ @@ -1012,6 +972,7 @@ protected Jenkins(File root, ServletContext context, PluginManager pluginManager adjuncts = new AdjunctManager(getServletContext(), pluginManager.uberClassLoader, "adjuncts/" + SESSION_HASH, TimeUnit.DAYS.toMillis(365)); ClassFilterImpl.register(); + LOGGER.info("Starting version " + getVersion()); // initialization consists of ... executeReactor(is, @@ -1095,18 +1056,6 @@ protected Object readResolve() { if (SLAVE_AGENT_PORT_ENFORCE) { slaveAgentPort = getSlaveAgentPortInitialValue(slaveAgentPort); } - synchronized (this) { - if (disabledAgentProtocols == null && _disabledAgentProtocols != null) { - disabledAgentProtocols = Arrays.asList(_disabledAgentProtocols); - _disabledAgentProtocols = null; - } - if (enabledAgentProtocols == null && _enabledAgentProtocols != null) { - enabledAgentProtocols = Arrays.asList(_enabledAgentProtocols); - _enabledAgentProtocols = null; - } - // Invalidate the protocols cache after the reload - agentProtocols = null; - } // no longer persisted installStateName = null; @@ -1281,81 +1230,15 @@ private void forceSetSlaveAgentPort(int port) throws IOException { */ @NonNull public synchronized Set getAgentProtocols() { - if (agentProtocols == null) { - Set result = new TreeSet<>(); - Set disabled = new TreeSet<>(); - for (String p : Util.fixNull(disabledAgentProtocols)) { - disabled.add(p.trim()); - } - Set enabled = new TreeSet<>(); - for (String p : Util.fixNull(enabledAgentProtocols)) { - enabled.add(p.trim()); - } - for (AgentProtocol p : AgentProtocol.all()) { - String name = p.getName(); - if (name != null && (p.isRequired() - || (!disabled.contains(name) && (!p.isOptIn() || enabled.contains(name))))) { - result.add(name); - } - } - /* - * An empty result is almost never valid, but it can happen due to JENKINS-70206. Since we know the result - * is likely incorrect, at least decline to cache it so that a correct result can be computed later on - * rather than continuing to deliver the incorrect result indefinitely. - */ - if (!result.isEmpty()) { - agentProtocols = result; - } - return result; - } - return agentProtocols; + return AgentProtocol.all().stream().map(AgentProtocol::getName).filter(Objects::nonNull).collect(Collectors.toCollection(TreeSet::new)); } /** - * Sets the enabled agent protocols. - * - * @param protocols the enabled agent protocols. - * @since 2.16 + * @deprecated No longer does anything. */ + @Deprecated public synchronized void setAgentProtocols(@NonNull Set protocols) { - Set disabled = new TreeSet<>(); - Set enabled = new TreeSet<>(); - for (AgentProtocol p : AgentProtocol.all()) { - String name = p.getName(); - if (name != null && !p.isRequired()) { - // we want to record the protocols where the admin has made a conscious decision - // thus, if a protocol is opt-in, we record the admin enabling it - // if a protocol is opt-out, we record the admin disabling it - // We should not transition rapidly from opt-in -> opt-out -> opt-in - // the scenario we want to have work is: - // 1. We introduce a new protocol, it starts off as opt-in. Some admins decide to test and opt-in - // 2. We decide that the protocol is ready for general use. It gets marked as opt-out. Any admins - // that took part in early testing now have their config switched to not mention the new protocol - // at all when they save their config as the protocol is now opt-out. Any admins that want to - // disable it can do so and will have their preference recorded. - // 3. We decide that the protocol needs to be retired. It gets switched back to opt-in. At this point - // the initial opt-in admins, assuming they visited an upgrade to a controller with step 2, will - // have the protocol disabled for them. This is what we want. If they didn't upgrade to a controller - // with step 2, well there is not much we can do to differentiate them from somebody who is upgrading - // from a previous step 3 controller and had needed to keep the protocol turned on. - // - // What we should never do is flip-flop: opt-in -> opt-out -> opt-in -> opt-out as that will basically - // clear any preference that an admin has set, but this should be ok as we only ever will be - // adding new protocols and retiring old ones. - if (p.isOptIn()) { - if (protocols.contains(name)) { - enabled.add(name); - } - } else { - if (!protocols.contains(name)) { - disabled.add(name); - } - } - } - } - disabledAgentProtocols = disabled.isEmpty() ? null : new ArrayList<>(disabled); - enabledAgentProtocols = enabled.isEmpty() ? null : new ArrayList<>(enabled); - agentProtocols = null; + LOGGER.log(Level.WARNING, null, new IllegalStateException("Jenkins.agentProtocols no longer configurable")); } private void launchTcpSlaveAgentListener() throws IOException { @@ -2463,11 +2346,29 @@ public String getSearchUrl() { @Override public SearchIndexBuilder makeSearchIndex() { SearchIndexBuilder builder = super.makeSearchIndex(); - if (hasPermission(ADMINISTER)) { - builder.add("configure", "config", "configure") - .add("manage") - .add("log"); - } + + this.actions.stream().filter(e -> e.getIconFileName() != null).forEach(action -> builder.add(new SearchItem() { + @Override + public String getSearchName() { + return action.getDisplayName(); + } + + @Override + public String getSearchUrl() { + return action.getUrlName(); + } + + @Override + public String getSearchIcon() { + return action.getIconFileName(); + } + + @Override + public SearchIndex getSearchIndex() { + return SearchIndex.EMPTY; + } + })); + builder.add(new CollectionSearchIndex() { @Override protected SearchItem get(String key) { return getItemByFullName(key, TopLevelItem.class); } @@ -3417,11 +3318,19 @@ public void load() throws IOException { if (cfg.exists()) { // reset some data that may not exist in the disk file // so that we can take a proper compensation action later. + String originalPrimaryView = primaryView; + List originalViews = new ArrayList<>(views); primaryView = null; views.clear(); - - // load from disk - cfg.unmarshal(Jenkins.this); + try { + // load from disk + cfg.unmarshal(Jenkins.this); + } catch (IOException | RuntimeException x) { + primaryView = originalPrimaryView; + views.clear(); + views.addAll(originalViews); + throw x; + } } // initialize views by inserting the default view if necessary // this is both for clean Jenkins and for backward compatibility. @@ -4667,7 +4576,7 @@ public void generateNotFoundResponse(StaplerRequest2 req, StaplerResponse2 rsp) */ @Deprecated(since = "2.414") public HttpResponse doSafeRestart(StaplerRequest req) throws IOException, ServletException, RestartNotSupportedException { - return doSafeRestart(StaplerRequest.toStaplerRequest2(req), null); + return doSafeRestart(req != null ? StaplerRequest.toStaplerRequest2(req) : null, null); } /** @@ -4700,7 +4609,7 @@ public HttpResponse doSafeRestart(StaplerRequest2 req, @QueryParameter("message" @StaplerNotDispatchable public HttpResponse doSafeRestart(StaplerRequest req, @QueryParameter("message") String message) throws IOException, javax.servlet.ServletException, RestartNotSupportedException { try { - return doSafeRestart(StaplerRequest.toStaplerRequest2(req), message); + return doSafeRestart(req != null ? StaplerRequest.toStaplerRequest2(req) : null, message); } catch (ServletException e) { throw ServletExceptionWrapper.fromJakartaServletException(e); } @@ -5999,8 +5908,6 @@ public boolean shouldShowStackTrace() { // for backward compatibility with <1.75, recognize the tag name "view" as well. XSTREAM.alias("view", ListView.class); XSTREAM.alias("listView", ListView.class); - XSTREAM.addImplicitArray(Jenkins.class, "_disabledAgentProtocols", "disabledAgentProtocol"); - XSTREAM.addImplicitArray(Jenkins.class, "_enabledAgentProtocols", "enabledAgentProtocol"); XSTREAM2.addCriticalField(Jenkins.class, "securityRealm"); XSTREAM2.addCriticalField(Jenkins.class, "authorizationStrategy"); // this seems to be necessary to force registration of converter early enough diff --git a/core/src/main/java/jenkins/model/Nodes.java b/core/src/main/java/jenkins/model/Nodes.java index e15d391c4975..ae78028c2d2f 100644 --- a/core/src/main/java/jenkins/model/Nodes.java +++ b/core/src/main/java/jenkins/model/Nodes.java @@ -43,10 +43,9 @@ import java.io.IOException; import java.util.ArrayList; import java.util.Collection; -import java.util.HashSet; +import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.TreeMap; import java.util.concurrent.Callable; import java.util.concurrent.ConcurrentMap; @@ -114,26 +113,31 @@ public List getNodes() { * @throws IOException if the new list of nodes could not be persisted. */ public void setNodes(final @NonNull Collection nodes) throws IOException { - Set toRemove = new HashSet<>(); - Queue.withLock(new Runnable() { - @Override - public void run() { - toRemove.addAll(Nodes.this.nodes.keySet()); - for (Node n : nodes) { - final String name = n.getNodeName(); + Map toRemove = new HashMap<>(); + Queue.withLock(() -> { + toRemove.putAll(Nodes.this.nodes); + for (var node : nodes) { + final var name = node.getNodeName(); + Nodes.this.nodes.put(name, node); + node.onLoad(Nodes.this, name); + var oldNode = toRemove.get(name); + if (oldNode != null) { + NodeListener.fireOnUpdated(oldNode, node); toRemove.remove(name); - Nodes.this.nodes.put(name, n); - n.onLoad(Nodes.this, name); + } else { + NodeListener.fireOnCreated(node); } - Nodes.this.nodes.keySet().removeAll(toRemove); - jenkins.updateComputerList(); - jenkins.trimLabels(); } + Nodes.this.nodes.keySet().removeAll(toRemove.keySet()); + jenkins.updateComputerList(); + jenkins.trimLabels(); }); save(); - for (String name : toRemove) { - LOGGER.fine(() -> "deleting " + new File(getRootDir(), name)); - Util.deleteRecursive(new File(getRootDir(), name)); + for (var deletedNode : toRemove.values()) { + NodeListener.fireOnDeleted(deletedNode); + var nodeName = deletedNode.getNodeName(); + LOGGER.fine(() -> "deleting " + new File(getRootDir(), nodeName)); + Util.deleteRecursive(new File(getRootDir(), nodeName)); } } diff --git a/core/src/main/java/jenkins/model/PeepholePermalink.java b/core/src/main/java/jenkins/model/PeepholePermalink.java index 62185f39f1ae..de6844acca55 100644 --- a/core/src/main/java/jenkins/model/PeepholePermalink.java +++ b/core/src/main/java/jenkins/model/PeepholePermalink.java @@ -4,6 +4,8 @@ import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.SuppressFBWarnings; import hudson.Extension; +import hudson.ExtensionList; +import hudson.ExtensionPoint; import hudson.Util; import hudson.model.Job; import hudson.model.PermalinkProjectAction.Permalink; @@ -14,6 +16,7 @@ import hudson.util.AtomicFileWriter; import java.io.File; import java.io.IOException; +import java.io.Serializable; import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.util.HashMap; @@ -24,6 +27,7 @@ import java.util.logging.Logger; import java.util.stream.Stream; import org.kohsuke.accmod.Restricted; +import org.kohsuke.accmod.restrictions.Beta; import org.kohsuke.accmod.restrictions.NoExternalUse; /** @@ -63,14 +67,6 @@ */ public abstract class PeepholePermalink extends Permalink implements Predicate> { - /** - * JENKINS-22822: avoids rereading caches. - * Top map keys are {@code builds} directories. - * Inner maps are from permalink name to build number. - * Synchronization is first on the outer map, then on the inner. - */ - private static final Map> caches = new HashMap<>(); - /** * Checks if the given build satisfies the peep-hole criteria. * @@ -94,115 +90,216 @@ protected File getPermalinkFile(Job job) { */ @Override public Run resolve(Job job) { - Map cache = cacheFor(job.getBuildDir()); - int n; - synchronized (cache) { - n = cache.getOrDefault(getId(), 0); + return ExtensionList.lookupFirst(Cache.class).get(job, getId()).resolve(this, job, getId()); + } + + /** + * Start from the build 'b' and locate the build that matches the criteria going back in time + */ + @CheckForNull + private Run find(@CheckForNull Run b) { + while (b != null && !apply(b)) { + b = b.getPreviousBuild(); } - if (n == RESOLVES_TO_NONE) { - return null; + return b; + } + + /** + * Remembers the value 'n' in the cache for future {@link #resolve(Job)}. + */ + protected void updateCache(@NonNull Job job, @CheckForNull Run b) { + ExtensionList.lookupFirst(Cache.class).put(job, getId(), b != null ? new Cache.Some(b.getNumber()) : Cache.NONE); + } + + /** + * Persistable cache of peephole permalink targets. + */ + @Restricted(Beta.class) + public interface Cache extends ExtensionPoint { + + /** Cacheable target of a permalink. */ + sealed interface PermalinkTarget extends Serializable { + + /** + * Implementation of {@link #resolve(Job)}. + * This may update the cache if it was missing or found to be invalid. + */ + @Restricted(NoExternalUse.class) + @CheckForNull + Run resolve(@NonNull PeepholePermalink pp, @NonNull Job job, @NonNull String id); + + /** + * Partial implementation of {@link #resolve(PeepholePermalink, Job, String)} when searching. + * @param b if set, the newest build to even consider when searching + */ + @Restricted(NoExternalUse.class) + @CheckForNull + default Run search(@NonNull PeepholePermalink pp, @NonNull Job job, @NonNull String id, @CheckForNull Run b) { + if (b == null) { + // no cache + b = job.getLastBuild(); + } + // start from the build 'b' and locate the build that matches the criteria going back in time + b = pp.find(b); + pp.updateCache(job, b); + return b; + } + } - Run b; - if (n > 0) { - b = job.getBuildByNumber(n); - if (b != null && apply(b)) { - return b; // found it (in the most efficient way possible) + + /** + * The cache entry for this target is missing. + */ + record Unknown() implements PermalinkTarget { + @Override + public Run resolve(PeepholePermalink pp, Job job, String id) { + return search(pp, job, id, null); } - } else { - b = null; } - // the cache is stale. start the search - if (b == null) { - b = job.getNearestOldBuild(n); + Unknown UNKNOWN = new Unknown(); + + /** + * The cache entry for this target is present. + */ + sealed interface Known extends PermalinkTarget {} + + /** There is known to be no matching build. */ + record None() implements Known { + @Override + public Run resolve(PeepholePermalink pp, Job job, String id) { + return null; + } } - if (b == null) { - // no cache - b = job.getLastBuild(); + /** Singleton of {@link None}. */ + None NONE = new None(); + + /** A matching build, indicated by {@link Run#getNumber}. */ + record Some(int number) implements Known { + @Override + public Run resolve(PeepholePermalink pp, Job job, String id) { + Run b = job.getBuildByNumber(number); + if (b != null && pp.apply(b)) { + return b; // found it (in the most efficient way possible) + } + // the cache is stale. start the search + if (b == null) { + b = job.getNearestOldBuild(number); + } + return search(pp, job, id, b); + } } - // start from the build 'b' and locate the build that matches the criteria going back in time - b = find(b); + /** + * Looks for any existing cache hit. + * @param id {@link #getId} + * @return {@link Some} or {@link #NONE} or {@link #UNKNOWN} + */ + @NonNull PermalinkTarget get(@NonNull Job job, @NonNull String id); - updateCache(job, b); - return b; + /** + * Updates the cache. + * Note that this may be called not just when a build completes or is deleted + * (meaning that the logical value of the cache has changed), + * but also when {@link #resolve} has failed to find a cached value + * (or determined that a previously cached value is in fact invalid). + * @param id {@link #getId} + * @param target {@link Some} or {@link #NONE} + */ + void put(@NonNull Job job, @NonNull String id, @NonNull Known target); } /** - * Start from the build 'b' and locate the build that matches the criteria going back in time + * Default cache based on a {@code permalinks} file in the build directory. + * There is one line per cached permalink, in the format {@code lastStableBuild 123} + * or (for a negative cache) {@code lastFailedBuild -1}. */ - private Run find(Run b) { - //noinspection StatementWithEmptyBody - for ( ; b != null && !apply(b); b = b.getPreviousBuild()) - ; - return b; - } + @Restricted(NoExternalUse.class) + @Extension(ordinal = -1000) + public static final class DefaultCache implements Cache { + + /** + * JENKINS-22822: avoids rereading caches. + * Top map keys are {@code builds} directories. + * Inner maps are from permalink name to target. + * Synchronization is first on the outer map, then on the inner. + */ + private final Map> caches = new HashMap<>(); - private static @NonNull Map cacheFor(@NonNull File buildDir) { - synchronized (caches) { - Map cache = caches.get(buildDir); - if (cache == null) { - cache = load(buildDir); - caches.put(buildDir, cache); + @Override + public PermalinkTarget get(Job job, String id) { + var cache = cacheFor(job.getBuildDir()); + synchronized (cache) { + var cached = cache.get(id); + return cached != null ? cached : UNKNOWN; } - return cache; } - } - private static @NonNull Map load(@NonNull File buildDir) { - Map cache = new TreeMap<>(); - File storage = storageFor(buildDir); - if (storage.isFile()) { - try (Stream lines = Files.lines(storage.toPath(), StandardCharsets.UTF_8)) { - lines.forEach(line -> { - int idx = line.indexOf(' '); - if (idx == -1) { - return; - } + @Override + public void put(Job job, String id, Known target) { + File buildDir = job.getBuildDir(); + var cache = cacheFor(buildDir); + synchronized (cache) { + cache.put(id, target); + File storage = storageFor(buildDir); + LOGGER.fine(() -> "saving to " + storage + ": " + cache); + try (AtomicFileWriter cw = new AtomicFileWriter(storage)) { try { - cache.put(line.substring(0, idx), Integer.parseInt(line.substring(idx + 1))); - } catch (NumberFormatException x) { - LOGGER.log(Level.WARNING, "failed to read " + storage, x); + for (var entry : cache.entrySet()) { + cw.write(entry.getKey()); + cw.write(' '); + cw.write(Integer.toString(entry.getValue() instanceof Cache.Some some ? some.number : -1)); + cw.write('\n'); + } + cw.commit(); + } finally { + cw.abort(); } - }); - } catch (IOException x) { - LOGGER.log(Level.WARNING, "failed to read " + storage, x); + } catch (IOException x) { + LOGGER.log(Level.WARNING, "failed to update " + storage, x); + } } - LOGGER.fine(() -> "loading from " + storage + ": " + cache); } - return cache; - } - static @NonNull File storageFor(@NonNull File buildDir) { - return new File(buildDir, "permalinks"); - } + private @NonNull Map cacheFor(@NonNull File buildDir) { + synchronized (caches) { + var cache = caches.get(buildDir); + if (cache == null) { + cache = load(buildDir); + caches.put(buildDir, cache); + } + return cache; + } + } - /** - * Remembers the value 'n' in the cache for future {@link #resolve(Job)}. - */ - protected void updateCache(@NonNull Job job, @CheckForNull Run b) { - File buildDir = job.getBuildDir(); - Map cache = cacheFor(buildDir); - synchronized (cache) { - cache.put(getId(), b == null ? RESOLVES_TO_NONE : b.getNumber()); + private static @NonNull Map load(@NonNull File buildDir) { + Map cache = new TreeMap<>(); File storage = storageFor(buildDir); - LOGGER.fine(() -> "saving to " + storage + ": " + cache); - try (AtomicFileWriter cw = new AtomicFileWriter(storage)) { - try { - for (Map.Entry entry : cache.entrySet()) { - cw.write(entry.getKey()); - cw.write(' '); - cw.write(Integer.toString(entry.getValue())); - cw.write('\n'); - } - cw.commit(); - } finally { - cw.abort(); + if (storage.isFile()) { + try (Stream lines = Files.lines(storage.toPath(), StandardCharsets.UTF_8)) { + lines.forEach(line -> { + int idx = line.indexOf(' '); + if (idx == -1) { + return; + } + try { + int number = Integer.parseInt(line.substring(idx + 1)); + cache.put(line.substring(0, idx), number == -1 ? Cache.NONE : new Cache.Some(number)); + } catch (NumberFormatException x) { + LOGGER.log(Level.WARNING, "failed to read " + storage, x); + } + }); + } catch (IOException x) { + LOGGER.log(Level.WARNING, "failed to read " + storage, x); } - } catch (IOException x) { - LOGGER.log(Level.WARNING, "failed to update " + storage, x); + LOGGER.fine(() -> "loading from " + storage + ": " + cache); } + return cache; + } + + static @NonNull File storageFor(@NonNull File buildDir) { + return new File(buildDir, "permalinks"); } } @@ -380,7 +477,5 @@ public boolean apply(Run run) { @Restricted(NoExternalUse.class) public static void initialized() {} - private static final int RESOLVES_TO_NONE = -1; - private static final Logger LOGGER = Logger.getLogger(PeepholePermalink.class.getName()); } diff --git a/core/src/main/java/jenkins/model/experimentalflags/RemoveYuiUserExperimentalFlag.java b/core/src/main/java/jenkins/model/experimentalflags/RemoveYuiUserExperimentalFlag.java index e8f8dcc31775..418f9c4b5406 100644 --- a/core/src/main/java/jenkins/model/experimentalflags/RemoveYuiUserExperimentalFlag.java +++ b/core/src/main/java/jenkins/model/experimentalflags/RemoveYuiUserExperimentalFlag.java @@ -24,8 +24,10 @@ package jenkins.model.experimentalflags; +import edu.umd.cs.findbugs.annotations.NonNull; import edu.umd.cs.findbugs.annotations.Nullable; import hudson.Extension; +import jenkins.util.SystemProperties; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -46,4 +48,10 @@ public String getDisplayName() { public String getShortDescription() { return "Remove YUI from all Jenkins UI pages. This will break anything that depends on YUI"; } + + @NonNull + @Override + public Boolean getDefaultValue() { + return SystemProperties.getBoolean(RemoveYuiUserExperimentalFlag.class.getName() + ".defaultValue", true); + } } diff --git a/core/src/main/java/jenkins/model/lazy/LazyBuildMixIn.java b/core/src/main/java/jenkins/model/lazy/LazyBuildMixIn.java index c73c5cbf80f1..d6329ed13ed5 100644 --- a/core/src/main/java/jenkins/model/lazy/LazyBuildMixIn.java +++ b/core/src/main/java/jenkins/model/lazy/LazyBuildMixIn.java @@ -109,8 +109,8 @@ public void onLoad(ItemGroup parent, String name) throws IOExcep int max = _builds.maxNumberOnDisk(); int next = asJob().getNextBuildNumber(); if (next <= max) { - LOGGER.log(Level.WARNING, "JENKINS-27530: improper nextBuildNumber {0} detected in {1} with highest build number {2}; adjusting", new Object[] {next, asJob(), max}); - asJob().updateNextBuildNumber(max + 1); + LOGGER.log(Level.FINE, "nextBuildNumber {0} detected in {1} with highest build number {2}; adjusting", new Object[] {next, asJob(), max}); + asJob().fastUpdateNextBuildNumber(max + 1); } RunMap currentBuilds = this.builds; if (parent != null) { diff --git a/core/src/main/java/jenkins/monitor/OperatingSystemEndOfLifeAdminMonitor.java b/core/src/main/java/jenkins/monitor/OperatingSystemEndOfLifeAdminMonitor.java index 0ff40d018225..50cf3c9da6f0 100644 --- a/core/src/main/java/jenkins/monitor/OperatingSystemEndOfLifeAdminMonitor.java +++ b/core/src/main/java/jenkins/monitor/OperatingSystemEndOfLifeAdminMonitor.java @@ -145,7 +145,7 @@ void readOperatingSystemList(String initialOperatingSystemJson) throws IOExcepti } LOGGER.log(Level.FINE, "Matched operating system {0}", name); - if (startDate.isBefore(LocalDate.now())) { + if (!startDate.isAfter(LocalDate.now())) { this.operatingSystemName = name; this.documentationUrl = buildDocumentationUrl(this.operatingSystemName); this.endOfLifeDate = endOfLife.toString(); diff --git a/core/src/main/java/jenkins/security/MasterToSlaveCallable.java b/core/src/main/java/jenkins/security/MasterToSlaveCallable.java index db0406bcf548..7d3830bb9562 100644 --- a/core/src/main/java/jenkins/security/MasterToSlaveCallable.java +++ b/core/src/main/java/jenkins/security/MasterToSlaveCallable.java @@ -1,25 +1,17 @@ package jenkins.security; import hudson.remoting.Callable; -import jenkins.slaves.RemotingVersionInfo; -import org.jenkinsci.remoting.RoleChecker; +import jenkins.agents.ControllerToAgentCallable; /** - * Convenient {@link Callable} meant to be run on agent. - * - * Note that the logic within {@link #call()} should use API of a minimum supported Remoting version. - * See {@link RemotingVersionInfo#getMinimumSupportedVersion()}. - * + * {@link Callable} meant to be run on agent. + * For new code, implement {@link ControllerToAgentCallable} + * which has the advantage that it can be used on {@code record}s. * @author Kohsuke Kawaguchi * @since 1.587 / 1.580.1 - * @param the return type; note that this must either be defined in your plugin or included in the stock JEP-200 whitelist + * @param the return type */ -public abstract class MasterToSlaveCallable implements Callable { +public abstract class MasterToSlaveCallable implements ControllerToAgentCallable { private static final long serialVersionUID = 1L; - - @Override - public void checkRoles(RoleChecker checker) throws SecurityException { - checker.check(this, Roles.SLAVE); - } } diff --git a/core/src/main/java/jenkins/security/stapler/TypedFilter.java b/core/src/main/java/jenkins/security/stapler/TypedFilter.java index eabce3b2dfc9..5851c5e47269 100644 --- a/core/src/main/java/jenkins/security/stapler/TypedFilter.java +++ b/core/src/main/java/jenkins/security/stapler/TypedFilter.java @@ -5,8 +5,6 @@ import hudson.ExtensionList; import java.lang.annotation.Annotation; import java.lang.reflect.Method; -import java.util.HashMap; -import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import jenkins.util.SystemProperties; @@ -25,8 +23,6 @@ public class TypedFilter implements FieldRef.Filter, FunctionList.Filter { private static final Logger LOGGER = Logger.getLogger(TypedFilter.class.getName()); - private static final Map, Boolean> staplerCache = new HashMap<>(); - private boolean isClassAcceptable(Class clazz) { if (clazz.isArray()) { // special case to allow klass.isArray() dispatcher @@ -46,31 +42,23 @@ private boolean isClassAcceptable(Class clazz) { return false; } } - return SKIP_TYPE_CHECK || isStaplerRelevantCached(clazz); + return SKIP_TYPE_CHECK || isStaplerRelevant.get(clazz); } - private static boolean isStaplerRelevantCached(@NonNull Class clazz) { - if (staplerCache.containsKey(clazz)) { - return staplerCache.get(clazz); + private static final ClassValue isStaplerRelevant = new ClassValue<>() { + @Override + protected Boolean computeValue(Class clazz) { + return isSpecificClassStaplerRelevant(clazz) || isSuperTypesStaplerRelevant(clazz); } - boolean ret = isStaplerRelevant(clazz); - - staplerCache.put(clazz, ret); - return ret; - } - - @Restricted(NoExternalUse.class) - public static boolean isStaplerRelevant(@NonNull Class clazz) { - return isSpecificClassStaplerRelevant(clazz) || isSuperTypesStaplerRelevant(clazz); - } + }; private static boolean isSuperTypesStaplerRelevant(@NonNull Class clazz) { Class superclass = clazz.getSuperclass(); - if (superclass != null && isStaplerRelevantCached(superclass)) { + if (superclass != null && isStaplerRelevant.get(superclass)) { return true; } for (Class interfaceClass : clazz.getInterfaces()) { - if (isStaplerRelevantCached(interfaceClass)) { + if (isStaplerRelevant.get(interfaceClass)) { return true; } } diff --git a/core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol4.java b/core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol4.java index 54c1402927a9..87eb26daeb4c 100644 --- a/core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol4.java +++ b/core/src/main/java/jenkins/slaves/JnlpSlaveAgentProtocol4.java @@ -137,16 +137,6 @@ private char[] constructPassword() { return "password".toCharArray(); } - @Override - public boolean isOptIn() { - return false; - } - - @Override - public String getDisplayName() { - return Messages.JnlpSlaveAgentProtocol4_displayName(); - } - @Override public String getName() { return "JNLP4-connect"; // matches JnlpProtocol4Handler.getName diff --git a/core/src/main/java/jenkins/slaves/RemotingVersionInfo.java b/core/src/main/java/jenkins/slaves/RemotingVersionInfo.java index fb17b7243c9f..d595a21d22df 100644 --- a/core/src/main/java/jenkins/slaves/RemotingVersionInfo.java +++ b/core/src/main/java/jenkins/slaves/RemotingVersionInfo.java @@ -31,6 +31,7 @@ import java.util.Properties; import java.util.logging.Level; import java.util.logging.Logger; +import jenkins.agents.ControllerToAgentCallable; /** * Provides information about Remoting versions used within the core. @@ -100,7 +101,7 @@ public static VersionNumber getEmbeddedVersion() { /** * Gets Remoting version which is supported by the core. - * Jenkins core and plugins make invoke operations on agents (e.g. {@link jenkins.security.MasterToSlaveCallable}) + * Jenkins core and plugins make invoke operations on agents (e.g. {@link ControllerToAgentCallable}) * and use Remoting-internal API within them. * In such case this API should be present on the remote side. * This method defines a minimum expected version, so that all calls should use a compatible API. diff --git a/core/src/main/java/jenkins/telemetry/Telemetry.java b/core/src/main/java/jenkins/telemetry/Telemetry.java index 1f60e319f97d..de081c16954b 100644 --- a/core/src/main/java/jenkins/telemetry/Telemetry.java +++ b/core/src/main/java/jenkins/telemetry/Telemetry.java @@ -130,6 +130,11 @@ public static ExtensionList all() { return ExtensionList.lookup(Telemetry.class); } + @Restricted(NoExternalUse.class) // called by jelly + public static boolean isAnyTrialActive() { + return all().stream().anyMatch(Telemetry::isActivePeriod); + } + /** * @since 2.147 * @return whether to collect telemetry diff --git a/core/src/main/java/jenkins/util/SetContextClassLoader.java b/core/src/main/java/jenkins/util/SetContextClassLoader.java index c486fa4607e1..9e0a4c7dec30 100644 --- a/core/src/main/java/jenkins/util/SetContextClassLoader.java +++ b/core/src/main/java/jenkins/util/SetContextClassLoader.java @@ -62,7 +62,7 @@ public final class SetContextClassLoader implements AutoCloseable { * @since 2.362 */ public SetContextClassLoader() { - this(StackWalker.getInstance().getCallerClass()); + this(StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE).getCallerClass()); } /** diff --git a/core/src/main/java/jenkins/util/VirtualFile.java b/core/src/main/java/jenkins/util/VirtualFile.java index b7456e3a80e3..42f21577ace0 100644 --- a/core/src/main/java/jenkins/util/VirtualFile.java +++ b/core/src/main/java/jenkins/util/VirtualFile.java @@ -60,6 +60,8 @@ import java.util.logging.Level; import java.util.logging.Logger; import java.util.stream.Collectors; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; import jenkins.MasterToSlaveFileCallable; import jenkins.model.ArtifactManager; import jenkins.security.MasterToSlaveCallable; @@ -68,8 +70,6 @@ import org.apache.tools.ant.types.selectors.SelectorUtils; import org.apache.tools.ant.types.selectors.TokenizedPath; import org.apache.tools.ant.types.selectors.TokenizedPattern; -import org.apache.tools.zip.ZipEntry; -import org.apache.tools.zip.ZipOutputStream; import org.kohsuke.accmod.Restricted; import org.kohsuke.accmod.restrictions.NoExternalUse; @@ -374,8 +374,8 @@ public int zip(OutputStream outputStream, String includes, String excludes, bool } Collection files = list(includes, excludes, useDefaultExcludes, openOptions); - try (ZipOutputStream zos = new ZipOutputStream(outputStream)) { - zos.setEncoding(Charset.defaultCharset().displayName()); // TODO JENKINS-20663 make this overridable via query parameter + // TODO JENKINS-20663 make encoding overridable via query parameter + try (ZipOutputStream zos = new ZipOutputStream(outputStream, Charset.defaultCharset())) { for (String relativePath : files) { VirtualFile virtualFile = this.child(relativePath); diff --git a/core/src/main/java/jenkins/widgets/ExecutorsWidget.java b/core/src/main/java/jenkins/widgets/ExecutorsWidget.java index c660d4e03911..3577d21af48b 100644 --- a/core/src/main/java/jenkins/widgets/ExecutorsWidget.java +++ b/core/src/main/java/jenkins/widgets/ExecutorsWidget.java @@ -10,6 +10,7 @@ import java.util.Collection; import java.util.Collections; import java.util.List; +import jenkins.model.IComputer; import jenkins.model.Jenkins; import org.jenkinsci.Symbol; @@ -23,9 +24,9 @@ */ public class ExecutorsWidget extends Widget { private final String ownerUrl; - private final List computers; + private final List computers; - public ExecutorsWidget(@NonNull String ownerUrl, @NonNull List computers) { + public ExecutorsWidget(@NonNull String ownerUrl, @NonNull List computers) { this.ownerUrl = ownerUrl; this.computers = new ArrayList<>(computers); } @@ -35,7 +36,7 @@ protected String getOwnerUrl() { return ownerUrl; } - public List getComputers() { + public List getComputers() { return Collections.unmodifiableList(computers); } @@ -92,7 +93,7 @@ public Class widgetType() { @NonNull @Override public Collection createFor(@NonNull ComputerSet target) { - return List.of(new ExecutorsWidget("computer/", List.of(target.get_all()))); + return List.of(new ExecutorsWidget("computer/", new ArrayList<>(target.getComputers()))); } } } diff --git a/core/src/main/java/org/jenkins/ui/icon/IconSpec.java b/core/src/main/java/org/jenkins/ui/icon/IconSpec.java index c44e577e1ce0..f1bf535b6ca6 100644 --- a/core/src/main/java/org/jenkins/ui/icon/IconSpec.java +++ b/core/src/main/java/org/jenkins/ui/icon/IconSpec.java @@ -27,8 +27,7 @@ /** * Icon Specification. *
- * Plugin extension points that implement/extend Action/ManagementLink should - * also implement this interface. + * If your class provides an icon spec you should implement this interface. * * @author tom.fennelly@gmail.com * @since 2.0 diff --git a/core/src/main/resources/hudson/Messages.properties b/core/src/main/resources/hudson/Messages.properties index 99c77ebbed46..a15f31703cd0 100644 --- a/core/src/main/resources/hudson/Messages.properties +++ b/core/src/main/resources/hudson/Messages.properties @@ -127,7 +127,6 @@ PluginWrapper.Plugin.Has.Dependent=The plugin ''{0}'' has, at least, one depende PluginWrapper.Plugin.Disabled=Plugin ''{0}'' disabled PluginWrapper.NoSuchPlugin=No such plugin found with the name ''{0}'' PluginWrapper.Error.Disabling=There was an error disabling the ''{0}'' plugin. Error: ''{1}'' -TcpSlaveAgentListener.PingAgentProtocol.displayName=Ping protocol ProxyConfigurationManager.DisplayName=Proxy Configuration ProxyConfigurationManager.Description=Configure the http proxy used by Jenkins diff --git a/core/src/main/resources/hudson/Messages_bg.properties b/core/src/main/resources/hudson/Messages_bg.properties index 98b816053e44..e741c7400a06 100644 --- a/core/src/main/resources/hudson/Messages_bg.properties +++ b/core/src/main/resources/hudson/Messages_bg.properties @@ -105,9 +105,6 @@ PluginWrapper.disabledAndObsolete=\ # {0} is disabled. To fix, enable it. PluginWrapper.disabled=\ „{0}“ е изключена. Включете я. -# Ping protocol -TcpSlaveAgentListener.PingAgentProtocol.displayName=\ - Протокол „ping“ # {0} v{1} failed to load. Fix this plugin first. PluginWrapper.failed_to_load_dependency=\ „{0}“, версия {1} не се зареди. Оправете приставката. diff --git a/core/src/main/resources/hudson/Messages_de.properties b/core/src/main/resources/hudson/Messages_de.properties index e4068a20b58a..d524a518cd44 100644 --- a/core/src/main/resources/hudson/Messages_de.properties +++ b/core/src/main/resources/hudson/Messages_de.properties @@ -75,5 +75,3 @@ AboutJenkins.DisplayName=Über Jenkins AboutJenkins.Description=Versions- und Lizenzinformationen anzeigen. Functions.NoExceptionDetails=Keine Details zum Ausnahmefehler - -TcpSlaveAgentListener.PingAgentProtocol.displayName=Ping-Protokoll diff --git a/core/src/main/resources/hudson/Messages_es.properties b/core/src/main/resources/hudson/Messages_es.properties index 298ef23821d8..47d3b400c747 100644 --- a/core/src/main/resources/hudson/Messages_es.properties +++ b/core/src/main/resources/hudson/Messages_es.properties @@ -117,4 +117,3 @@ PluginWrapper.Plugin.Has.Dependant=El plugin {0} tiene, al menos, un plugin depe PluginWrapper.Plugin.Disabled=Plugin {0} deshabilitado PluginWrapper.NoSuchPlugin=No se encuentra un plugin con el nombre {0} PluginWrapper.Error.Disabling=Hubo un error al desactivar el plugin ''{0}''. Error: ''{1}'' -TcpSlaveAgentListener.PingAgentProtocol.displayName=Protocolo ping diff --git a/core/src/main/resources/hudson/Messages_fr.properties b/core/src/main/resources/hudson/Messages_fr.properties index 1e0286deddc5..06e739d3b479 100644 --- a/core/src/main/resources/hudson/Messages_fr.properties +++ b/core/src/main/resources/hudson/Messages_fr.properties @@ -126,4 +126,3 @@ PluginWrapper.Plugin.Has.Dependent=Le plugin "{0}" a au moins un plugin dépenda PluginWrapper.Plugin.Disabled=Plugin "{0}" désactivé PluginWrapper.NoSuchPlugin=Aucun plugin trouvé avec le nom "{0}" PluginWrapper.Error.Disabling=Une erreur a été relevée lors de la désactivation du plugin "{0}". Erreur : "{1}" -TcpSlaveAgentListener.PingAgentProtocol.displayName=Protocole de ping diff --git a/core/src/main/resources/hudson/Messages_it.properties b/core/src/main/resources/hudson/Messages_it.properties index 2747e2d39366..d63200aabd1e 100644 --- a/core/src/main/resources/hudson/Messages_it.properties +++ b/core/src/main/resources/hudson/Messages_it.properties @@ -109,7 +109,6 @@ ProxyConfiguration.MalformedTestUrl=URL di prova {0} malformato. ProxyConfiguration.NonTLSWarning=Jenkins supporta solo l''utilizzo di una connessione http al proxy. Le credenziali potrebbero essere esposte a qualcuno sulla stessa rete. ProxyConfiguration.Success=Connessione riuscita (codice {0}) ProxyConfiguration.TestUrlRequired=È richiesto un URL di prova. -TcpSlaveAgentListener.PingAgentProtocol.displayName=Protocollo ping Util.day={0} g Util.hour={0} h Util.millisecond={0} ms diff --git a/core/src/main/resources/hudson/Messages_pt_BR.properties b/core/src/main/resources/hudson/Messages_pt_BR.properties index 0a809578524e..d67bdcba599c 100644 --- a/core/src/main/resources/hudson/Messages_pt_BR.properties +++ b/core/src/main/resources/hudson/Messages_pt_BR.properties @@ -57,7 +57,6 @@ PluginWrapper.missing=Não foi possível encontrar {0} v{1}. Para corrigir, inst Functions.NoExceptionDetails=Sem detalhes da exception FilePath.validateAntFileMask.whitespaceSeparator=Espaços em branco não podem mais serem utilizados como separador. Por \ favor use ", " como separadores. -TcpSlaveAgentListener.PingAgentProtocol.displayName=Protocolo de ping PluginWrapper.PluginWrapperAdministrativeMonitor.DisplayName=Falha ao carregar a extensão ProxyConfiguration.MalformedTestUrl=URL de teste {0} inválida. FilePath.TildaDoesntWork="~" é suportado apenas em um shell Unix e em nenhum outro lugar. diff --git a/core/src/main/resources/hudson/Messages_sr.properties b/core/src/main/resources/hudson/Messages_sr.properties index 3c266c5d67a3..414a84b3a41b 100644 --- a/core/src/main/resources/hudson/Messages_sr.properties +++ b/core/src/main/resources/hudson/Messages_sr.properties @@ -40,4 +40,3 @@ PluginWrapper.disabledAndObsolete={0}, верзија {1} је онемогућ PluginWrapper.disabled={0}, верзија {1} је онемогућено. Молимо вас, омогућите ову модулу. PluginWrapper.obsolete={0}, верзија {1} је старије него што је подржано. Инсталирајте верзију {2} или новије. PluginWrapper.obsoleteCore=Морате ажурирати Jenkins са верзије {0} на {1} или новије да би могли користити ову модулу. -TcpSlaveAgentListener.PingAgentProtocol.displayName=Протокол 'ping' diff --git a/core/src/main/resources/hudson/Messages_sv_SE.properties b/core/src/main/resources/hudson/Messages_sv_SE.properties index fdfa6ee3a524..f4a977e99098 100644 --- a/core/src/main/resources/hudson/Messages_sv_SE.properties +++ b/core/src/main/resources/hudson/Messages_sv_SE.properties @@ -125,7 +125,6 @@ PluginWrapper.Plugin.Has.Dependent=Insticksprogrammet ''{0}'' har minst ett bero PluginWrapper.Plugin.Disabled=Insticksprogrammet ''{0}'' inaktiverades PluginWrapper.NoSuchPlugin=Inget insticksprogram med namnet ''{0}'' hittades PluginWrapper.Error.Disabling=Ett fel uppstod när insticksprogrammet ''{0}'' inaktiverades. Fel: ''{1}'' -TcpSlaveAgentListener.PingAgentProtocol.displayName=Ping-protokoll ProxyConfigurationManager.DisplayName=Proxykonfiguration ProxyConfigurationManager.Description=Konfigurera http-proxyn som Jenkins använder diff --git a/core/src/main/resources/hudson/Messages_zh_TW.properties b/core/src/main/resources/hudson/Messages_zh_TW.properties index 708613e7e603..cc5fb76cb059 100644 --- a/core/src/main/resources/hudson/Messages_zh_TW.properties +++ b/core/src/main/resources/hudson/Messages_zh_TW.properties @@ -96,7 +96,6 @@ PluginWrapper.Plugin.Has.Dependent=外掛「{0}」有至少一個相依性外掛 PluginWrapper.Plugin.Disabled=已停用外掛「{0}」 PluginWrapper.NoSuchPlugin=找不到名為「{0}」的外掛 PluginWrapper.Error.Disabling=停用外掛「{0}」時發生錯誤,錯誤\: 「{1}」 -TcpSlaveAgentListener.PingAgentProtocol.displayName=Ping 協定 PluginManager.emptyUpdateSiteUrl=更新站台網址不得為空,請輸入網址 PluginManager.connectionFailed=無法連線至這個URL diff --git a/core/src/main/resources/hudson/diagnosis/ReverseProxySetupMonitor/message_tr.properties b/core/src/main/resources/hudson/diagnosis/ReverseProxySetupMonitor/message_tr.properties index 2ac4acc9ebbe..2de2187639d5 100644 --- a/core/src/main/resources/hudson/diagnosis/ReverseProxySetupMonitor/message_tr.properties +++ b/core/src/main/resources/hudson/diagnosis/ReverseProxySetupMonitor/message_tr.properties @@ -1,4 +1,4 @@ # This file is under the MIT License by authors -More\ Info=Daha fazla Bilgi +More\ Info=Daha Fazla Bilgi blurb=Ters vekil sunucu yapılandırmanız bozuk görünüyor. diff --git a/core/src/main/resources/hudson/model/AllView/noJob_tr.properties b/core/src/main/resources/hudson/model/AllView/noJob_tr.properties index f11e7258733e..31dd63cf187c 100644 --- a/core/src/main/resources/hudson/model/AllView/noJob_tr.properties +++ b/core/src/main/resources/hudson/model/AllView/noJob_tr.properties @@ -29,6 +29,7 @@ setUpDistributedBuilds=Dağıtılmış bir yapılandırma kurun setUpAgent=Bir ajan kur setUpCloud=Bir bulut ayarla learnMoreDistributedBuilds=Dağıtılmış yapılandırmalar hakkında daha fazla bilgi edinin +thisFolderIsEmpty=Bu klasör boş startBuilding=Yazılım projenizi yapılandırmaya başlayın diff --git a/core/src/main/resources/hudson/model/Computer/sidepanel_tr.properties b/core/src/main/resources/hudson/model/Computer/sidepanel_tr.properties index a96d29188a81..30694a2d05e4 100644 --- a/core/src/main/resources/hudson/model/Computer/sidepanel_tr.properties +++ b/core/src/main/resources/hudson/model/Computer/sidepanel_tr.properties @@ -23,6 +23,6 @@ Back\ to\ List=Listeye Dön Build\ History=Yapılandırma Geçmişi Configure=Yapılandır -Load\ Statistics=Yüklenme istatistikleri +Load\ Statistics=Yük İstatistikleri Script\ Console=Script Konsolu Status=Durum diff --git a/core/src/main/resources/hudson/model/ComputerSet/_legend.jelly b/core/src/main/resources/hudson/model/ComputerSet/_legend.jelly index 07871a6fe5f1..1668323aa584 100644 --- a/core/src/main/resources/hudson/model/ComputerSet/_legend.jelly +++ b/core/src/main/resources/hudson/model/ComputerSet/_legend.jelly @@ -10,6 +10,13 @@ ${%online} +

+ +
+
+ ${%paused} +
+
diff --git a/core/src/main/resources/hudson/model/ComputerSet/_legend.properties b/core/src/main/resources/hudson/model/ComputerSet/_legend.properties index 075df2f8a1c4..223ac506677e 100644 --- a/core/src/main/resources/hudson/model/ComputerSet/_legend.properties +++ b/core/src/main/resources/hudson/model/ComputerSet/_legend.properties @@ -6,6 +6,7 @@ not-accepting=The agent is not accepting tasks for execution. This usually means the time window. The agent might still execute tasks that have started while it was accepting tasks. Cloud agents that are \ intended to run only a single task might also use this.
\ Agents will be shown as suspended in the Build Executor Status widgets. +paused=The agent has been put offline due to the configured retention strategy. It will be brought back online when needed (e.g. a new task requests it). offline=None of the above conditions applies. The agent is not connected or it is still connected but a node monitor decided \ that the agent is not in a healthy state. An agent in this state should be investigated. Legend=Icon legend diff --git a/core/src/main/resources/hudson/model/ComputerSet/index.jelly b/core/src/main/resources/hudson/model/ComputerSet/index.jelly index c96346ac4949..5e957e2bd9c0 100644 --- a/core/src/main/resources/hudson/model/ComputerSet/index.jelly +++ b/core/src/main/resources/hudson/model/ComputerSet/index.jelly @@ -77,7 +77,7 @@ THE SOFTWARE.
- +
diff --git a/core/src/main/resources/hudson/model/ComputerSet/index_tr.properties b/core/src/main/resources/hudson/model/ComputerSet/index_tr.properties new file mode 100644 index 000000000000..0434bbae3c0e --- /dev/null +++ b/core/src/main/resources/hudson/model/ComputerSet/index_tr.properties @@ -0,0 +1,4 @@ +Nodes=Sunucular +New\ Node=Yeni Sunucu +Name=İsim +Data\ obtained=Son güncelleme diff --git a/core/src/main/resources/hudson/model/ComputerSet/new_tr.properties b/core/src/main/resources/hudson/model/ComputerSet/new_tr.properties new file mode 100644 index 000000000000..921759b8e7ef --- /dev/null +++ b/core/src/main/resources/hudson/model/ComputerSet/new_tr.properties @@ -0,0 +1,2 @@ +New\ node=Yeni sunucu +Node\ name=Sunucu ismi diff --git a/core/src/main/resources/hudson/model/ComputerSet/sidepanel_tr.properties b/core/src/main/resources/hudson/model/ComputerSet/sidepanel_tr.properties index ec93e331bfed..f4761cfdc026 100644 --- a/core/src/main/resources/hudson/model/ComputerSet/sidepanel_tr.properties +++ b/core/src/main/resources/hudson/model/ComputerSet/sidepanel_tr.properties @@ -22,3 +22,5 @@ Back\ to\ Dashboard=Kontrol Merkezi'ne Dön Manage\ Jenkins=Jenkins''i Yönet +Nodes=Sunucular +Clouds=Bulutlar diff --git a/core/src/main/resources/hudson/model/Job/_api.jelly b/core/src/main/resources/hudson/model/Job/_api.jelly index 28a161afbb65..652a44c4ec56 100644 --- a/core/src/main/resources/hudson/model/Job/_api.jelly +++ b/core/src/main/resources/hudson/model/Job/_api.jelly @@ -30,7 +30,7 @@ THE SOFTWARE.

Retrieving all builds

To prevent Jenkins from having to load all builds from disk when someone accesses the job API, the builds - tree only contains the 50 newest builds. If you really need to get all builds, access the allBuilds tree, + tree only contains the 100 newest builds. If you really need to get all builds, access the allBuilds tree, e.g. by fetching …/api/xml?tree=allBuilds[…]. Note that this may result in significant performance degradation if you have a lot of builds in this job.

diff --git a/core/src/main/resources/hudson/model/Messages_tr.properties b/core/src/main/resources/hudson/model/Messages_tr.properties index 75399955f2e6..4d9ae97b7bd8 100644 --- a/core/src/main/resources/hudson/model/Messages_tr.properties +++ b/core/src/main/resources/hudson/model/Messages_tr.properties @@ -48,6 +48,8 @@ BallColor.Pending=Bekliyor BallColor.Success=Başarılı BallColor.Unstable=Dengesiz +ComputerSet.DisplayName=Sunucular + Executor.NotAvailable=Mevcut Değil FreeStyleProject.DisplayName=Serbest stil proje diff --git a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message.jelly b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message.jelly index f11ee8837cdd..efaa6159136e 100644 --- a/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message.jelly +++ b/core/src/main/resources/hudson/model/UpdateCenter/CoreUpdateMonitor/message.jelly @@ -53,11 +53,13 @@ THE SOFTWARE. ${%NewVersionAvailable(ucData.core.version,ucData.core.url,changelog_url)} - -
- - -
+ + +
+ + +
+
diff --git a/core/src/main/resources/hudson/model/UsageStatistics/global.groovy b/core/src/main/resources/hudson/model/UsageStatistics/global.groovy index 5506a37a67d5..7254eb283a60 100644 --- a/core/src/main/resources/hudson/model/UsageStatistics/global.groovy +++ b/core/src/main/resources/hudson/model/UsageStatistics/global.groovy @@ -8,7 +8,7 @@ def f=namespace(lib.FormTagLib) f.section(title: _("Usage Statistics")) { if (UsageStatistics.DISABLED) { - span(class: "jenkins-not-applicable") { + div(class: "jenkins-not-applicable jenkins-description") { raw(_("disabledBySystemProperty")) } } else if (FIPS140.useCompliantAlgorithms()) { diff --git a/core/src/main/resources/hudson/model/UsageStatistics/help-usageStatisticsCollected.jelly b/core/src/main/resources/hudson/model/UsageStatistics/help-usageStatisticsCollected.jelly index a1bef14d4e22..2d01661a175b 100644 --- a/core/src/main/resources/hudson/model/UsageStatistics/help-usageStatisticsCollected.jelly +++ b/core/src/main/resources/hudson/model/UsageStatistics/help-usageStatisticsCollected.jelly @@ -1,14 +1,14 @@ -
+

For any project, it's critical to know how the software is used, but tracking usage data is inherently difficult in open-source projects. Anonymous usage statistics address this need. When enabled, Jenkins periodically sends information to the Jenkins project. The Jenkins project uses this information to set development priorities. -

+

-

General usage statistics

+

General usage statistics

Jenkins reports the following general usage statistics:

    @@ -24,7 +24,7 @@

-

Telemetry collection

+

Telemetry collection

@@ -38,24 +38,34 @@ Each trial has a specific purpose and a defined end date, after which collection stops, independent of the installed versions of Jenkins or plugins. Once a trial is complete, the trial results may be aggregated and shared with the developer community.

-

- The following trials defined on this instance are active now or in the future: -

+ + -
- - -
${collector.displayName}
-
- -

- Start date: ${collector.start}
- End date: ${collector.end} -

-
-
-
-
+ + +

${%There are currently no active trials.}

+
+ +

+ The following trials defined on this instance are active now or in the future: +

+
+ + +
${collector.displayName}
+
+ +

+ Start date: ${collector.start} +
+ End date: ${collector.end} +

+
+
+
+
+
+
diff --git a/core/src/main/resources/hudson/model/userproperty/Messages_tr.properties b/core/src/main/resources/hudson/model/userproperty/Messages_tr.properties new file mode 100644 index 000000000000..8144297c655b --- /dev/null +++ b/core/src/main/resources/hudson/model/userproperty/Messages_tr.properties @@ -0,0 +1,13 @@ +UserPropertyCategory.Account.DisplayName=Hesap +UserPropertyCategory.Appearance.DisplayName=Görünüm +UserPropertyCategory.Experimental.DisplayName=Deneyler +UserPropertyCategory.Invisible.DisplayName=Görünmez +UserPropertyCategory.Preferences.DisplayName=Tercihler +UserPropertyCategory.Unclassified.DisplayName=Sınıflandırılmamış +UserPropertyCategory.Security.DisplayName=Güvenlik + +UserPropertyCategoryAccountAction.DisplayName=Hesap +UserPropertyCategoryAppearanceAction.DisplayName=Görünüm +UserPropertyCategoryExperimentalAction.DisplayName=Deneyler +UserPropertyCategoryPreferencesAction.DisplayName=Tercihler +UserPropertyCategorySecurityAction.DisplayName=Güvenlik diff --git a/core/src/main/resources/hudson/model/userproperty/UserPropertyCategoryAccountAction/index_tr.properties b/core/src/main/resources/hudson/model/userproperty/UserPropertyCategoryAccountAction/index_tr.properties index ead6d67dee06..14b810099914 100644 --- a/core/src/main/resources/hudson/model/userproperty/UserPropertyCategoryAccountAction/index_tr.properties +++ b/core/src/main/resources/hudson/model/userproperty/UserPropertyCategoryAccountAction/index_tr.properties @@ -20,6 +20,7 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. +title=Hesap Full\ name=İsminiz Description=Açıklama Save=Kaydet diff --git a/core/src/main/resources/hudson/model/userproperty/UserPropertyCategoryAppearanceAction/index_tr.properties b/core/src/main/resources/hudson/model/userproperty/UserPropertyCategoryAppearanceAction/index_tr.properties new file mode 100644 index 000000000000..f9e350543d71 --- /dev/null +++ b/core/src/main/resources/hudson/model/userproperty/UserPropertyCategoryAppearanceAction/index_tr.properties @@ -0,0 +1,2 @@ +title=Görünüm +warningNoItems=Yapılandırılacak görünüm öğesi yok. diff --git a/core/src/main/resources/hudson/model/userproperty/UserPropertyCategoryExperimentalAction/index_tr.properties b/core/src/main/resources/hudson/model/userproperty/UserPropertyCategoryExperimentalAction/index_tr.properties new file mode 100644 index 000000000000..b645842a1872 --- /dev/null +++ b/core/src/main/resources/hudson/model/userproperty/UserPropertyCategoryExperimentalAction/index_tr.properties @@ -0,0 +1,2 @@ +title=Deneyler +warningNoItems=Şu anda herhangi bir deney mevcut değil. diff --git a/core/src/main/resources/hudson/model/userproperty/UserPropertyCategoryPreferencesAction/index_tr.properties b/core/src/main/resources/hudson/model/userproperty/UserPropertyCategoryPreferencesAction/index_tr.properties new file mode 100644 index 000000000000..5108f7b2cc0b --- /dev/null +++ b/core/src/main/resources/hudson/model/userproperty/UserPropertyCategoryPreferencesAction/index_tr.properties @@ -0,0 +1,2 @@ +title=Tercihler +warningNoItems=Tercih bulunamadı. diff --git a/core/src/main/resources/hudson/model/userproperty/UserPropertyCategorySecurityAction/index_tr.properties b/core/src/main/resources/hudson/model/userproperty/UserPropertyCategorySecurityAction/index_tr.properties new file mode 100644 index 000000000000..21a255c0268e --- /dev/null +++ b/core/src/main/resources/hudson/model/userproperty/UserPropertyCategorySecurityAction/index_tr.properties @@ -0,0 +1,2 @@ +title=Güvenlik +warningNoItems=Güvenlik yapılandırması mevcut değil. diff --git a/core/src/main/resources/hudson/node_monitors/Messages_tr.properties b/core/src/main/resources/hudson/node_monitors/Messages_tr.properties index a58be1787e59..0ac3946dc2c0 100644 --- a/core/src/main/resources/hudson/node_monitors/Messages_tr.properties +++ b/core/src/main/resources/hudson/node_monitors/Messages_tr.properties @@ -22,4 +22,7 @@ ArchitectureMonitor.DisplayName=Mimari ClockMonitor.DisplayName=Saat Farkı -DiskSpaceMonitor.DisplayName=Diskteki Boş Alan +DiskSpaceMonitor.DisplayName=Boş Disk Alanı +SwapSpaceMonitor.DisplayName=Boş Swap Alanı +TemporarySpaceMonitor.DisplayName=Boş Temp Alanı +ResponseTimeMonitor.DisplayName=Tepki Süresi diff --git a/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/help-agentProtocol.html b/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/help-agentProtocol.html deleted file mode 100644 index 01bcd8d3b24b..000000000000 --- a/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/help-agentProtocol.html +++ /dev/null @@ -1,4 +0,0 @@ -
- Jenkins uses a TCP port to communicate with various remote agents. This option - allows control over which agent protocols are enabled. -
diff --git a/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/help-agentProtocol_bg.html b/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/help-agentProtocol_bg.html deleted file mode 100644 index 41852a0729a8..000000000000 --- a/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/help-agentProtocol_bg.html +++ /dev/null @@ -1,4 +0,0 @@ -
- Jenkins използва порт па TCP, за да комуникира с подчинените компютри. Тази - опция задава кои протоколи за връзка са включени. -
diff --git a/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/help-agentProtocol_it.html b/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/help-agentProtocol_it.html deleted file mode 100644 index 2adbe90a72a1..000000000000 --- a/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/help-agentProtocol_it.html +++ /dev/null @@ -1,4 +0,0 @@ -
- Jenkins utilizza una porta TCP per comunicare con vari agenti remoti. - Quest'opzione consente di controllare i vari protocolli agente abilitati. -
diff --git a/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/help-agentProtocol_sv_SE.html b/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/help-agentProtocol_sv_SE.html deleted file mode 100644 index adf57097bb6f..000000000000 --- a/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/help-agentProtocol_sv_SE.html +++ /dev/null @@ -1,5 +0,0 @@ -
- Jenkins använder en TCP-port för att kommunicera med olika fjärragenter. Det - här alternativet gör det möjligt att kontrollera vilka agentprotokoll som är - aktiverade. -
diff --git a/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/index.groovy b/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/index.groovy index 2abc4e795cc0..9c3c80607ca8 100644 --- a/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/index.groovy +++ b/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/index.groovy @@ -3,7 +3,6 @@ package hudson.security.GlobalSecurityConfiguration import hudson.security.SecurityRealm import hudson.markup.MarkupFormatterDescriptor import hudson.security.AuthorizationStrategy -import jenkins.AgentProtocol import hudson.Functions import hudson.model.Descriptor @@ -51,33 +50,6 @@ l.layout(permission:app.SYSTEM_READ, title:my.displayName, cssclass:request.getP f.serverTcpPort() } } - f.advanced(title: _("Agent protocols"), align:"left") { - f.entry(title: _("Agent protocols"), help: '/descriptor/hudson.security.GlobalSecurityConfiguration/help/agentProtocol') { - def agentProtocols = my.agentProtocols - div() { - for (AgentProtocol p : AgentProtocol.all()) { - if (p.name != null && !p.required) { - f.block() { - f.checkbox(name: "agentProtocol", - title: p.displayName, - checked: agentProtocols.contains(p.name), - json: p.name) - } - div(class: "tr") { - div(class:"setting-description"){ - st.include(from:p, page: "description", optional:true) - if (p.deprecated) { - br() - text(b(_("Deprecated. "))) - st.include(from:p, page: "deprecationCause", optional:true) - } - } - } - } - } - } - } - } } Functions.getSortedDescriptorsForGlobalConfigByDescriptor(my.FILTER).each { Descriptor descriptor -> diff --git a/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/index_it.properties b/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/index_it.properties index 9b343aab6b5e..ba6cf0fe694f 100644 --- a/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/index_it.properties +++ b/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/index_it.properties @@ -25,7 +25,6 @@ Agent\ protocols=Protocolli agente Agents=Agenti Authentication=Autenticazione Authorization=Autorizzazione -Deprecated.\ =Deprecato. Disable\ remember\ me=Disabilita "Ricordami" LOADING=Caricamento in corso Markup\ Formatter=Componente formattazione markup diff --git a/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/index_tr.properties b/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/index_tr.properties index 1b12b9eb167c..7f79912af880 100644 --- a/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/index_tr.properties +++ b/core/src/main/resources/hudson/security/GlobalSecurityConfiguration/index_tr.properties @@ -20,8 +20,9 @@ # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. +Authentication=Kimlik Doğrulama Disable\ remember\ me=Beni hatırla özelliğini devre dışı bırak -Markup\ Formatter= +Markup\ Formatter=Metin Formatlayıcısı Access\ Control=Erişim Kontrolü Security\ Realm=Güvenlik Alanı Authorization=Yetkilendirme diff --git a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/firstUser_tr.properties b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/firstUser_tr.properties new file mode 100644 index 000000000000..73adeed2eeaf --- /dev/null +++ b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/firstUser_tr.properties @@ -0,0 +1 @@ +Create\ First\ Admin\ User=İlk Yönetici Kullanıcıyı Oluştur diff --git a/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/success_tr.properties b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/success_tr.properties new file mode 100644 index 000000000000..2320478a1e6c --- /dev/null +++ b/core/src/main/resources/hudson/security/HudsonPrivateSecurityRealm/success_tr.properties @@ -0,0 +1,2 @@ +Success=Tebrikler +description=Giriş yaptınız. Ana sayfaya geri dönün. diff --git a/core/src/main/resources/hudson/security/Messages_tr.properties b/core/src/main/resources/hudson/security/Messages_tr.properties index eefcbfd2ea1c..b3303fcafc21 100644 --- a/core/src/main/resources/hudson/security/Messages_tr.properties +++ b/core/src/main/resources/hudson/security/Messages_tr.properties @@ -22,7 +22,7 @@ GlobalSecurityConfiguration.DisplayName=Güvenlik GlobalSecurityConfiguration.Description=Jenkins''i güvenli hale getir; sisteme erişim izni olanları belirle. -LegacyAuthorizationStrategy.DisplayName=Miras modu +LegacyAuthorizationStrategy.DisplayName=Eski yöntem HudsonPrivateSecurityRealm.DisplayName=Jenkins''in kendi kullanıcı veritabanı HudsonPrivateSecurityRealm.Details.DisplayName=Şifre @@ -32,7 +32,15 @@ HudsonPrivateSecurityRealm.Details.PasswordError=\ HudsonPrivateSecurityRealm.ManageUserLinks.DisplayName=Kullanıcılar HudsonPrivateSecurityRealm.ManageUserLinks.Description=Jenkins''de oturum açabilecek kullanıcıları oluşturun, silin veya değiştirin. +FullControlOnceLoggedInAuthorizationStrategy.DisplayName=Giriş yapmış kullanıcılar her şeyi yapabilir + +AuthorizationStrategy.DisplayName=Herkes her şeyi yapabilir + +LegacySecurityRealm.Displayname=Servlet konteynerine devredin + UserDetailsServiceProxy.UnableToQuery=Kullanıcı bilgisi alınamıyor: {0} # not in use Permission.Permissions.Title=Mevcut Değil + +NoneSecurityRealm.DisplayName=Hiçbiri diff --git a/core/src/main/resources/hudson/slaves/Messages.properties b/core/src/main/resources/hudson/slaves/Messages.properties index 5c95fd0df998..7dbd6e8a1909 100644 --- a/core/src/main/resources/hudson/slaves/Messages.properties +++ b/core/src/main/resources/hudson/slaves/Messages.properties @@ -42,3 +42,4 @@ ComputerLauncher.JavaVersionResult={0} -version returned {1}. ComputerLauncher.UnknownJavaVersion=Couldn’t figure out the Java version of {0} Cloud.ProvisionPermission.Description=Provision new nodes Cloud.RequiredName=Cloud must have a unique non-empty name. +SimpleScheduledRetentionStrategy.ScheduledOfflineCause.displayName=Offline according to schedule diff --git a/core/src/main/resources/hudson/slaves/Messages_tr.properties b/core/src/main/resources/hudson/slaves/Messages_tr.properties index 3f09ef711a25..061c5c7a2ff7 100644 --- a/core/src/main/resources/hudson/slaves/Messages_tr.properties +++ b/core/src/main/resources/hudson/slaves/Messages_tr.properties @@ -1 +1,2 @@ +DumbSlave.displayName=Kalıcı Ajan EnvironmentVariablesNodeProperty.displayName=Ortam değişkenleri diff --git a/core/src/main/resources/hudson/slaves/OfflineCause/cause.jelly b/core/src/main/resources/hudson/slaves/OfflineCause/cause.jelly index 51a05a144aaf..e2457c8ae2bc 100644 --- a/core/src/main/resources/hudson/slaves/OfflineCause/cause.jelly +++ b/core/src/main/resources/hudson/slaves/OfflineCause/cause.jelly @@ -24,12 +24,12 @@ THE SOFTWARE. -
+
- ${it} +
diff --git a/core/src/main/resources/hudson/util/HudsonIsLoading/index.properties b/core/src/main/resources/hudson/util/HudsonIsLoading/index.properties index aa8045ec46d1..a0e10c536a4a 100644 --- a/core/src/main/resources/hudson/util/HudsonIsLoading/index.properties +++ b/core/src/main/resources/hudson/util/HudsonIsLoading/index.properties @@ -1,4 +1,4 @@ Please\ wait\ while\ Jenkins\ is\ getting\ ready\ to\ work=\ Jenkins is getting ready to work Your\ browser\ will\ reload\ automatically\ when\ Jenkins\ is\ ready=\ - Your browser will reload automatically when Jenkins is ready + Your browser will reload automatically when Jenkins is ready. diff --git a/core/src/main/resources/hudson/util/HudsonIsRestarting/index.properties b/core/src/main/resources/hudson/util/HudsonIsRestarting/index.properties index 6ad13259188f..e8cee2b657d9 100644 --- a/core/src/main/resources/hudson/util/HudsonIsRestarting/index.properties +++ b/core/src/main/resources/hudson/util/HudsonIsRestarting/index.properties @@ -1,2 +1,3 @@ Please\ wait\ while\ Jenkins\ is\ restarting=\ Jenkins is restarting +Your\ browser\ will\ reload\ automatically\ when\ Jenkins\ is\ ready=Your browser will reload automatically when Jenkins is ready. diff --git a/core/src/main/resources/hudson/util/Messages_tr.properties b/core/src/main/resources/hudson/util/Messages_tr.properties new file mode 100644 index 000000000000..1df20672d1a6 --- /dev/null +++ b/core/src/main/resources/hudson/util/Messages_tr.properties @@ -0,0 +1 @@ +ClockDifference.InSync=Senkronize diff --git a/core/src/main/resources/jenkins/agents/Messages.properties b/core/src/main/resources/jenkins/agents/Messages.properties index 11a27f40642c..468a77f88db8 100644 --- a/core/src/main/resources/jenkins/agents/Messages.properties +++ b/core/src/main/resources/jenkins/agents/Messages.properties @@ -4,3 +4,4 @@ CloudSet.SpecifyCloudToCopy=Specify which cloud to copy CloudSet.NoSuchCloud=No such cloud: {0} CloudsLink.DisplayName=Clouds CloudsLink.Description=Add, remove, and configure cloud instances to provision agents on-demand. +IOfflineCause.offline=Offline diff --git a/core/src/main/resources/jenkins/agents/Messages_bg.properties b/core/src/main/resources/jenkins/agents/Messages_bg.properties new file mode 100644 index 000000000000..854f81e155df --- /dev/null +++ b/core/src/main/resources/jenkins/agents/Messages_bg.properties @@ -0,0 +1,2 @@ +IOfflineCause.offline=\ + Извън линия diff --git a/core/src/main/resources/jenkins/agents/Messages_ca.properties b/core/src/main/resources/jenkins/agents/Messages_ca.properties new file mode 100644 index 000000000000..b1dc57237b69 --- /dev/null +++ b/core/src/main/resources/jenkins/agents/Messages_ca.properties @@ -0,0 +1 @@ +IOfflineCause.offline=Fora de línia diff --git a/core/src/main/resources/jenkins/agents/Messages_cs.properties b/core/src/main/resources/jenkins/agents/Messages_cs.properties new file mode 100644 index 000000000000..e8be0867d387 --- /dev/null +++ b/core/src/main/resources/jenkins/agents/Messages_cs.properties @@ -0,0 +1 @@ +IOfflineCause.offline=nedostupný diff --git a/core/src/main/resources/jenkins/agents/Messages_da.properties b/core/src/main/resources/jenkins/agents/Messages_da.properties new file mode 100644 index 000000000000..3eeb9c7db8ac --- /dev/null +++ b/core/src/main/resources/jenkins/agents/Messages_da.properties @@ -0,0 +1 @@ +IOfflineCause.offline=offline diff --git a/core/src/main/resources/jenkins/agents/Messages_de.properties b/core/src/main/resources/jenkins/agents/Messages_de.properties new file mode 100644 index 000000000000..3eeb9c7db8ac --- /dev/null +++ b/core/src/main/resources/jenkins/agents/Messages_de.properties @@ -0,0 +1 @@ +IOfflineCause.offline=offline diff --git a/core/src/main/resources/jenkins/agents/Messages_es.properties b/core/src/main/resources/jenkins/agents/Messages_es.properties new file mode 100644 index 000000000000..227b94579e02 --- /dev/null +++ b/core/src/main/resources/jenkins/agents/Messages_es.properties @@ -0,0 +1 @@ +IOfflineCause.offline=fuera de linea diff --git a/core/src/main/resources/jenkins/agents/Messages_es_AR.properties b/core/src/main/resources/jenkins/agents/Messages_es_AR.properties new file mode 100644 index 000000000000..6876bdefb1c9 --- /dev/null +++ b/core/src/main/resources/jenkins/agents/Messages_es_AR.properties @@ -0,0 +1 @@ +IOfflineCause.offline=desconectado diff --git a/core/src/main/resources/jenkins/agents/Messages_et.properties b/core/src/main/resources/jenkins/agents/Messages_et.properties new file mode 100644 index 000000000000..569f0fb79967 --- /dev/null +++ b/core/src/main/resources/jenkins/agents/Messages_et.properties @@ -0,0 +1 @@ +IOfflineCause.offline=offlain diff --git a/core/src/main/resources/jenkins/agents/Messages_fi.properties b/core/src/main/resources/jenkins/agents/Messages_fi.properties new file mode 100644 index 000000000000..9366da643837 --- /dev/null +++ b/core/src/main/resources/jenkins/agents/Messages_fi.properties @@ -0,0 +1 @@ +IOfflineCause.offline=ei yhteyttä diff --git a/core/src/main/resources/jenkins/agents/Messages_fr.properties b/core/src/main/resources/jenkins/agents/Messages_fr.properties index 4d11e586212e..6b5c376636ef 100644 --- a/core/src/main/resources/jenkins/agents/Messages_fr.properties +++ b/core/src/main/resources/jenkins/agents/Messages_fr.properties @@ -4,3 +4,4 @@ CloudSet.SpecifyCloudToCopy=Spécifier le cloud à copier CloudSet.NoSuchCloud=Aucun cloud : {0} CloudsLink.DisplayName=Clouds CloudsLink.Description=Ajouter, supprimer et configurer les instances de cloud afin de provisionner les agents à la demande. +IOfflineCause.offline=déconnecté diff --git a/core/src/main/resources/jenkins/agents/Messages_he.properties b/core/src/main/resources/jenkins/agents/Messages_he.properties new file mode 100644 index 000000000000..845bb4c14d51 --- /dev/null +++ b/core/src/main/resources/jenkins/agents/Messages_he.properties @@ -0,0 +1 @@ +IOfflineCause.offline=לא מקוון diff --git a/core/src/main/resources/jenkins/agents/Messages_hu.properties b/core/src/main/resources/jenkins/agents/Messages_hu.properties new file mode 100644 index 000000000000..f1d2ee59ca29 --- /dev/null +++ b/core/src/main/resources/jenkins/agents/Messages_hu.properties @@ -0,0 +1 @@ +IOfflineCause.offline=kapcsolat nélkül diff --git a/core/src/main/resources/jenkins/agents/Messages_it.properties b/core/src/main/resources/jenkins/agents/Messages_it.properties new file mode 100644 index 000000000000..67f532b2888e --- /dev/null +++ b/core/src/main/resources/jenkins/agents/Messages_it.properties @@ -0,0 +1 @@ +IOfflineCause.offline=non in linea diff --git a/core/src/main/resources/jenkins/agents/Messages_ja.properties b/core/src/main/resources/jenkins/agents/Messages_ja.properties new file mode 100644 index 000000000000..1dc8b5a9de59 --- /dev/null +++ b/core/src/main/resources/jenkins/agents/Messages_ja.properties @@ -0,0 +1 @@ +IOfflineCause.offline=オフライン diff --git a/core/src/main/resources/jenkins/agents/Messages_ko.properties b/core/src/main/resources/jenkins/agents/Messages_ko.properties new file mode 100644 index 000000000000..b66c4afd873d --- /dev/null +++ b/core/src/main/resources/jenkins/agents/Messages_ko.properties @@ -0,0 +1 @@ +IOfflineCause.offline=오프라인 diff --git a/core/src/main/resources/jenkins/agents/Messages_lt.properties b/core/src/main/resources/jenkins/agents/Messages_lt.properties new file mode 100644 index 000000000000..1dc33ef4d0ce --- /dev/null +++ b/core/src/main/resources/jenkins/agents/Messages_lt.properties @@ -0,0 +1 @@ +IOfflineCause.offline=nepasiekiamas diff --git a/core/src/main/resources/jenkins/agents/Messages_lv.properties b/core/src/main/resources/jenkins/agents/Messages_lv.properties new file mode 100644 index 000000000000..b201d5aa457b --- /dev/null +++ b/core/src/main/resources/jenkins/agents/Messages_lv.properties @@ -0,0 +1 @@ +IOfflineCause.offline=bezsaitē diff --git a/core/src/main/resources/jenkins/agents/Messages_nb_NO.properties b/core/src/main/resources/jenkins/agents/Messages_nb_NO.properties new file mode 100644 index 000000000000..e4c6f4d4c1cf --- /dev/null +++ b/core/src/main/resources/jenkins/agents/Messages_nb_NO.properties @@ -0,0 +1 @@ +IOfflineCause.offline=ikke tilgjengelig diff --git a/core/src/main/resources/jenkins/agents/Messages_nl.properties b/core/src/main/resources/jenkins/agents/Messages_nl.properties new file mode 100644 index 000000000000..db8aa00bc17e --- /dev/null +++ b/core/src/main/resources/jenkins/agents/Messages_nl.properties @@ -0,0 +1 @@ +IOfflineCause.offline=niet verbonden diff --git a/core/src/main/resources/jenkins/agents/Messages_pl.properties b/core/src/main/resources/jenkins/agents/Messages_pl.properties new file mode 100644 index 000000000000..d077ba98ac3d --- /dev/null +++ b/core/src/main/resources/jenkins/agents/Messages_pl.properties @@ -0,0 +1 @@ +IOfflineCause.offline=rozłączony diff --git a/core/src/main/resources/jenkins/agents/Messages_pt_BR.properties b/core/src/main/resources/jenkins/agents/Messages_pt_BR.properties new file mode 100644 index 000000000000..6876bdefb1c9 --- /dev/null +++ b/core/src/main/resources/jenkins/agents/Messages_pt_BR.properties @@ -0,0 +1 @@ +IOfflineCause.offline=desconectado diff --git a/core/src/main/resources/jenkins/agents/Messages_pt_PT.properties b/core/src/main/resources/jenkins/agents/Messages_pt_PT.properties new file mode 100644 index 000000000000..1b4e4281c66a --- /dev/null +++ b/core/src/main/resources/jenkins/agents/Messages_pt_PT.properties @@ -0,0 +1 @@ +IOfflineCause.offline=desligado diff --git a/core/src/main/resources/jenkins/agents/Messages_ro.properties b/core/src/main/resources/jenkins/agents/Messages_ro.properties new file mode 100644 index 000000000000..003d60708ddd --- /dev/null +++ b/core/src/main/resources/jenkins/agents/Messages_ro.properties @@ -0,0 +1 @@ +IOfflineCause.offline=oprit diff --git a/core/src/main/resources/jenkins/agents/Messages_ru.properties b/core/src/main/resources/jenkins/agents/Messages_ru.properties new file mode 100644 index 000000000000..6b7d6b72f9bc --- /dev/null +++ b/core/src/main/resources/jenkins/agents/Messages_ru.properties @@ -0,0 +1 @@ +IOfflineCause.offline=выключен diff --git a/core/src/main/resources/jenkins/agents/Messages_sr.properties b/core/src/main/resources/jenkins/agents/Messages_sr.properties new file mode 100644 index 000000000000..33944f0fed07 --- /dev/null +++ b/core/src/main/resources/jenkins/agents/Messages_sr.properties @@ -0,0 +1 @@ +IOfflineCause.offline=Ван мреже diff --git a/core/src/main/resources/jenkins/agents/Messages_sv_SE.properties b/core/src/main/resources/jenkins/agents/Messages_sv_SE.properties index 38cc64867d62..51ede868f109 100644 --- a/core/src/main/resources/jenkins/agents/Messages_sv_SE.properties +++ b/core/src/main/resources/jenkins/agents/Messages_sv_SE.properties @@ -4,3 +4,4 @@ CloudSet.SpecifyCloudToCopy=Ange vilket moln som ska kopieras CloudSet.NoSuchCloud=Det finns inget sådant moln: {0} CloudsLink.DisplayName=Moln CloudsLink.Description=Lägg till, ta bort och konfigurera molninstanser för att tillhandahålla agenter på begäran. +IOfflineCause.offline=frånkopplad diff --git a/core/src/main/resources/jenkins/agents/Messages_tr.properties b/core/src/main/resources/jenkins/agents/Messages_tr.properties index 5265f554c784..2ae6997d3203 100644 --- a/core/src/main/resources/jenkins/agents/Messages_tr.properties +++ b/core/src/main/resources/jenkins/agents/Messages_tr.properties @@ -4,3 +4,4 @@ CloudSet.SpecifyCloudToCopy=Hangi bulutun kopyalanacağını belirtin CloudSet.NoSuchCloud=Böyle bir bulut yok: {0} CloudsLink.DisplayName=Bulutlar CloudsLink.Description=İsteğe bağlı olarak ajan hazırlamak için kullanılmak üzere bulut tanımları ekleyin, kaldırın ve düzenleyin. +IOfflineCause.offline=Çevrimdışı diff --git a/core/src/main/resources/jenkins/agents/Messages_uk.properties b/core/src/main/resources/jenkins/agents/Messages_uk.properties new file mode 100644 index 000000000000..1138609d6943 --- /dev/null +++ b/core/src/main/resources/jenkins/agents/Messages_uk.properties @@ -0,0 +1 @@ +IOfflineCause.offline=поза мережею diff --git a/core/src/main/resources/jenkins/agents/Messages_zh_TW.properties b/core/src/main/resources/jenkins/agents/Messages_zh_TW.properties new file mode 100644 index 000000000000..8f59605265aa --- /dev/null +++ b/core/src/main/resources/jenkins/agents/Messages_zh_TW.properties @@ -0,0 +1 @@ +IOfflineCause.offline=離線 diff --git a/core/src/main/resources/jenkins/install/SetupWizard/setupWizardFirstUser_tr.properties b/core/src/main/resources/jenkins/install/SetupWizard/setupWizardFirstUser_tr.properties new file mode 100644 index 000000000000..73adeed2eeaf --- /dev/null +++ b/core/src/main/resources/jenkins/install/SetupWizard/setupWizardFirstUser_tr.properties @@ -0,0 +1 @@ +Create\ First\ Admin\ User=İlk Yönetici Kullanıcıyı Oluştur diff --git a/core/src/main/resources/jenkins/model/Jenkins/sidepanel.jelly b/core/src/main/resources/jenkins/model/Jenkins/sidepanel.jelly index 9ddff73647d6..e413712d6e7c 100644 --- a/core/src/main/resources/jenkins/model/Jenkins/sidepanel.jelly +++ b/core/src/main/resources/jenkins/model/Jenkins/sidepanel.jelly @@ -23,4 +23,9 @@ THE SOFTWARE. --> - \ No newline at end of file + + + + + + diff --git a/core/src/main/resources/jenkins/monitor/OperatingSystemEndOfLifeAdminMonitor/end-of-life-data.json b/core/src/main/resources/jenkins/monitor/OperatingSystemEndOfLifeAdminMonitor/end-of-life-data.json index 7dfa00bc6772..4eb62c4b49d9 100644 --- a/core/src/main/resources/jenkins/monitor/OperatingSystemEndOfLifeAdminMonitor/end-of-life-data.json +++ b/core/src/main/resources/jenkins/monitor/OperatingSystemEndOfLifeAdminMonitor/end-of-life-data.json @@ -83,6 +83,10 @@ "pattern": "Fedora.* 40.*", "endOfLife": "2025-05-13" }, + { + "pattern": "Fedora.* 41.*", + "endOfLife": "2025-11-19" + }, { "pattern": "Oracle Linux.* 7.*", "endOfLife": "2023-11-16" diff --git a/core/src/main/resources/jenkins/security/UpdateSiteWarningsMonitor/message.groovy b/core/src/main/resources/jenkins/security/UpdateSiteWarningsMonitor/message.groovy index 56cbb0bb4ddd..89b634002006 100644 --- a/core/src/main/resources/jenkins/security/UpdateSiteWarningsMonitor/message.groovy +++ b/core/src/main/resources/jenkins/security/UpdateSiteWarningsMonitor/message.groovy @@ -42,7 +42,7 @@ def listWarnings(warnings, boolean core) { } } } - if (fixables == warnings.size) { + if (fixables == warnings.size()) { dd { if (fixables == 1) { raw(_(core ? "allFixable1Core" : "allFixable1", rootURL)) diff --git a/core/src/main/resources/jenkins/security/whitelisted-classes.txt b/core/src/main/resources/jenkins/security/whitelisted-classes.txt index b1805a6bd387..f3f9080bde81 100644 --- a/core/src/main/resources/jenkins/security/whitelisted-classes.txt +++ b/core/src/main/resources/jenkins/security/whitelisted-classes.txt @@ -105,6 +105,7 @@ java.util.HashMap java.util.HashSet java.util.Hashtable java.util.ImmutableCollections$List12 +java.util.ImmutableCollections$ListN java.util.LinkedHashMap java.util.LinkedHashSet java.util.LinkedList diff --git a/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol4/description.jelly b/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol4/description.jelly deleted file mode 100644 index 55e2c974b5ba..000000000000 --- a/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol4/description.jelly +++ /dev/null @@ -1,4 +0,0 @@ - - - ${%summary} - diff --git a/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol4/description.properties b/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol4/description.properties deleted file mode 100644 index 4ecb60357fd6..000000000000 --- a/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol4/description.properties +++ /dev/null @@ -1 +0,0 @@ -summary=A TLS secured connection between the controller and the agent performed by TLS upgrade of the socket. diff --git a/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol4/description_bg.properties b/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol4/description_bg.properties deleted file mode 100644 index 4d13804ebdad..000000000000 --- a/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol4/description_bg.properties +++ /dev/null @@ -1,26 +0,0 @@ -# The MIT License -# -# Bulgarian translation: Copyright (c) 2016, 2017, Alexander Shopov -# -# 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. - -# A TLS secured connection between the master and the agent performed by TLS upgrade of the socket. -summary=\ - Защитена връзка между управляващия и подчинения компютър чрез преминаване към\ - TLS по гнездото. diff --git a/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol4/description_fr.properties b/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol4/description_fr.properties deleted file mode 100644 index 26c2dd5b35c8..000000000000 --- a/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol4/description_fr.properties +++ /dev/null @@ -1 +0,0 @@ -summary=Une connexion TLS sécurisée entre le contrôleur et l''agent a été effectuée via une mise à jour du TLS du socket. diff --git a/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol4/description_it.properties b/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol4/description_it.properties deleted file mode 100644 index 2d9452d7f068..000000000000 --- a/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol4/description_it.properties +++ /dev/null @@ -1,25 +0,0 @@ -# The MIT License -# -# Italian localization plugin for Jenkins -# Copyright © 2020 Alessandro Menti -# -# 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. - -summary=Una connessione fra il master e l''agente protetta da TLS ed \ - eseguita aggiornando il socket a TLS. diff --git a/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol4/description_pt_BR.properties b/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol4/description_pt_BR.properties deleted file mode 100644 index 74e67bc910b6..000000000000 --- a/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol4/description_pt_BR.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-, Kohsuke Kawaguchi, Sun Microsystems, Inc., and a number of other of contributors -# -# 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. - -summary=Uma conexão segura via TLS entre o controlador e o agente realizada por uma atualização de TLS no soquete. diff --git a/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol4/description_sv_SE.properties b/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol4/description_sv_SE.properties deleted file mode 100644 index 35b1deaba4fd..000000000000 --- a/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol4/description_sv_SE.properties +++ /dev/null @@ -1,23 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-, Kohsuke Kawaguchi, Sun Microsystems, Inc., and a number of other of contributors -# -# 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. - -summary=En TLS-säker anslutning mellan styrenheten och agenten utfördes av sockelns TLS-uppgradering. diff --git a/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol4/description_zh_TW.properties b/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol4/description_zh_TW.properties deleted file mode 100644 index 949804a0f146..000000000000 --- a/core/src/main/resources/jenkins/slaves/JnlpSlaveAgentProtocol4/description_zh_TW.properties +++ /dev/null @@ -1 +0,0 @@ -summary=透過 TLS 通訊端升級 Controller 和 Agent 間的 TLS 安全連線。 diff --git a/core/src/main/resources/jenkins/slaves/Messages.properties b/core/src/main/resources/jenkins/slaves/Messages.properties deleted file mode 100644 index 41abfc7a302c..000000000000 --- a/core/src/main/resources/jenkins/slaves/Messages.properties +++ /dev/null @@ -1,27 +0,0 @@ -# The MIT License -# -# Copyright 2016 Stephen Connolly -# -# 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. - -JnlpSlaveAgentProtocol.displayName=Inbound TCP Agent Protocol/1 (deprecated, unencrypted) -JnlpSlaveAgentProtocol2.displayName=Inbound TCP Agent Protocol/2 (deprecated, unencrypted) -JnlpSlaveAgentProtocol3.displayName=Inbound TCP Agent Protocol/3 (deprecated, basic encryption) -JnlpSlaveAgentProtocol4.displayName=Inbound TCP Agent Protocol/4 (TLS encryption) -DeprecatedAgentProtocolMonitor.displayName=Deprecated Agent Protocol Monitor diff --git a/core/src/main/resources/jenkins/slaves/Messages_bg.properties b/core/src/main/resources/jenkins/slaves/Messages_bg.properties deleted file mode 100644 index 459672fa3c3d..000000000000 --- a/core/src/main/resources/jenkins/slaves/Messages_bg.properties +++ /dev/null @@ -1,37 +0,0 @@ -# The MIT License -# -# Bulgarian translation: Copyright (c) 2016, 2017, Alexander Shopov -# -# 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. - -# Java Web Start Agent Protocol/3 -JnlpSlaveAgentProtocol3.displayName=\ - Протокол за стартиране на агента за Java през уeб, версия 3 -# Java Web Start Agent Protocol/1 -JnlpSlaveAgentProtocol.displayName=\ - Протокол за стартиране на агента за Java през уeб, версия 1 -# Java Web Start Agent Protocol/2 -JnlpSlaveAgentProtocol2.displayName=\ - Протокол за стартиране на агента за Java през уeб, версия 2 -# Java Web Start Agent Protocol/4 (TLS encryption) -JnlpSlaveAgentProtocol4.displayName=\ - Протокол за стартиране на агента за Java през уeб, версия 4 (шифриране с TLS) -# Deprecated Agent Protocol Monitor -DeprecatedAgentProtocolMonitor.displayName=\ - Датчик за остарели протоколи за стартиране на агента за Java през уeб diff --git a/core/src/main/resources/jenkins/slaves/Messages_de.properties b/core/src/main/resources/jenkins/slaves/Messages_de.properties deleted file mode 100644 index cceceebe685f..000000000000 --- a/core/src/main/resources/jenkins/slaves/Messages_de.properties +++ /dev/null @@ -1,27 +0,0 @@ -# The MIT License -# -# Copyright (c) 2017 Daniel Beck and a number of other of contributors -# -# 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. - -# Java Web Start Agent Protocol/4 (TLS encryption) -JnlpSlaveAgentProtocol4.displayName=Java-Web-Start-Agentenprotokoll Version 4 (TLS-Verschlüsselung) -JnlpSlaveAgentProtocol2.displayName=Java-Web-Start-Agentenprotokoll Version 2 (unverschlüsselt) -JnlpSlaveAgentProtocol.displayName=Java-Web-Start-Agentenprotokoll Version 1 (unverschlüsselt) -JnlpSlaveAgentProtocol3.displayName=Java-Web-Start-Agentenprotokoll Version 3 (einfache Verschlüsselung) diff --git a/core/src/main/resources/jenkins/slaves/Messages_fr.properties b/core/src/main/resources/jenkins/slaves/Messages_fr.properties deleted file mode 100644 index babe83c53fa8..000000000000 --- a/core/src/main/resources/jenkins/slaves/Messages_fr.properties +++ /dev/null @@ -1,27 +0,0 @@ -# The MIT License -# -# Copyright 2016 Stephen Connolly -# -# 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. - -JnlpSlaveAgentProtocol.displayName=Inbound TCP Agent Protocol/1 (obsolète, non chiffré) -JnlpSlaveAgentProtocol2.displayName=Inbound TCP Agent Protocol/2 (obsolète, non chiffré) -JnlpSlaveAgentProtocol3.displayName=Inbound TCP Agent Protocol/3 (obsolète, chiffrement basique) -JnlpSlaveAgentProtocol4.displayName=Inbound TCP Agent Protocol/4 (chiffrement TLS) -DeprecatedAgentProtocolMonitor.displayName=Moniteur de protocole obsolète d''agent diff --git a/core/src/main/resources/jenkins/slaves/Messages_it.properties b/core/src/main/resources/jenkins/slaves/Messages_it.properties deleted file mode 100644 index 15a4fd1854fa..000000000000 --- a/core/src/main/resources/jenkins/slaves/Messages_it.properties +++ /dev/null @@ -1,33 +0,0 @@ -# The MIT License -# -# Italian localization plugin for Jenkins -# Copyright © 2020 Alessandro Menti -# -# 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. - -DeprecatedAgentProtocolMonitor.displayName=Componente di monitoraggio \ - protocolli deprecati agenti -JnlpSlaveAgentProtocol.displayName=Protocollo in ingresso TCP per agenti, \ - versione 1 (deprecato, senza crittografia) -JnlpSlaveAgentProtocol2.displayName=Protocollo in ingresso TCP per agenti, \ - versione 2 (deprecato, senza crittografia) -JnlpSlaveAgentProtocol3.displayName=Protocollo in ingresso TCP per agenti, \ - versione 3 (deprecato, con crittografia di base) -JnlpSlaveAgentProtocol4.displayName=Protocollo in ingresso TCP per agenti, \ - versione 4 (crittografia TLS) diff --git a/core/src/main/resources/jenkins/slaves/Messages_pt_BR.properties b/core/src/main/resources/jenkins/slaves/Messages_pt_BR.properties deleted file mode 100644 index c185388bf156..000000000000 --- a/core/src/main/resources/jenkins/slaves/Messages_pt_BR.properties +++ /dev/null @@ -1,27 +0,0 @@ -# The MIT License -# -# Copyright (c) 2004-, Kohsuke Kawaguchi, Sun Microsystems, Inc., and a number of other of contributors -# -# 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. - -DeprecatedAgentProtocolMonitor.displayName=Monitor de protocolo de agente descontinuado -JnlpSlaveAgentProtocol3.displayName=Protocolo de agente TCP de entrada/3 (descontinuado, criptografia básica) -JnlpSlaveAgentProtocol2.displayName=Protocolo de agente TCP de entrada/2 (descontinuado, sem criptografia) -JnlpSlaveAgentProtocol.displayName=Protocolo de agente TCP de entrada/1 (descontinuado, sem criptografia) -JnlpSlaveAgentProtocol4.displayName=Protocolo de agente TCP de entrada/4 (criptografia via TLS) diff --git a/core/src/main/resources/jenkins/slaves/Messages_sv_SE.properties b/core/src/main/resources/jenkins/slaves/Messages_sv_SE.properties deleted file mode 100644 index 4274099d00a1..000000000000 --- a/core/src/main/resources/jenkins/slaves/Messages_sv_SE.properties +++ /dev/null @@ -1,27 +0,0 @@ -# The MIT License -# -# Copyright 2016 Stephen Connolly -# -# 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. - -JnlpSlaveAgentProtocol.displayName=Ingående TCP-agentsprotokoll/1 (inaktuell, ej krypterat) -JnlpSlaveAgentProtocol2.displayName=Ingående TCP-agentsprotokoll/2 (inaktuell, ej krypterat) -JnlpSlaveAgentProtocol3.displayName=Ingående TCP-agentsprotokoll/3 (inaktuell, enkel kryptering) -JnlpSlaveAgentProtocol4.displayName=Ingående TCP-agentsprotokoll/4 (TLS-kryptering) -DeprecatedAgentProtocolMonitor.displayName=Inaktuell övervakning av agentsprotokoll diff --git a/core/src/main/resources/jenkins/slaves/Messages_zh_TW.properties b/core/src/main/resources/jenkins/slaves/Messages_zh_TW.properties deleted file mode 100644 index 830e59610c15..000000000000 --- a/core/src/main/resources/jenkins/slaves/Messages_zh_TW.properties +++ /dev/null @@ -1,27 +0,0 @@ -# The MIT License -# -# Copyright 2016 Stephen Connolly -# -# 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. - -JnlpSlaveAgentProtocol.displayName=連入 TCP Agent 通訊協定/1 (棄用、未加密) -JnlpSlaveAgentProtocol2.displayName=連入 TCP Agent 通訊協定/2 (棄用、未加密) -JnlpSlaveAgentProtocol3.displayName=連入 TCP Agent 通訊協定/3 (棄用、基本加密) -JnlpSlaveAgentProtocol4.displayName=連入 TCP Agent 通訊協定/4 (TLS 加密) -DeprecatedAgentProtocolMonitor.displayName=已棄用的 Agent 通訊協定監視器 diff --git a/core/src/main/resources/jenkins/views/JenkinsHeader/search-box.js b/core/src/main/resources/jenkins/views/JenkinsHeader/search-box.js deleted file mode 100644 index 91e805c4ceaf..000000000000 --- a/core/src/main/resources/jenkins/views/JenkinsHeader/search-box.js +++ /dev/null @@ -1,6 +0,0 @@ -(function () { - var element = document.getElementById("search-box-completion"); - if (element) { - createSearchBox(element.getAttribute("data-search-url")); - } -})(); diff --git a/core/src/main/resources/lib/form/combobox.jelly b/core/src/main/resources/lib/form/combobox.jelly index 51fe7ce0d30f..517e12cd875e 100644 --- a/core/src/main/resources/lib/form/combobox.jelly +++ b/core/src/main/resources/lib/form/combobox.jelly @@ -70,11 +70,6 @@ THE SOFTWARE. ${descriptor.calcFillSettings(field,attrs)} - - "); - assertEquals(HttpURLConnection.HTTP_NOT_FOUND, resultPage.getWebResponse().getStatusCode()); + FreeStyleProject freeStyleProject = j.createFreeStyleProject("Project"); + freeStyleProject.setDisplayName(""); + + Page result = wc.search(""); + + assertNotNull(result); + assertEquals(j.getInstance().getRootUrl() + freeStyleProject.getUrl(), result.getUrl().toString()); } @Test @@ -127,7 +140,7 @@ public void testSearchByProjectNameInAFolder() throws Exception { MockFolder myMockFolder = j.createFolder("my-folder-1"); FreeStyleProject myFreeStyleProject = myMockFolder.createProject(FreeStyleProject.class, "my-job-1"); - Page result = j.createWebClient().goTo(myMockFolder.getUrl() + "search?q=" + myFreeStyleProject.getFullName()); + Page result = j.search(myFreeStyleProject.getName()); assertNotNull(result); j.assertGoodStatus(result); @@ -181,41 +194,6 @@ public void testSearch2ProjectsWithSameDisplayName() throws Exception { assertFalse(contents.contains(otherDisplayName)); } - @Test - public void testProjectNamePrecedesDisplayName() throws Exception { - final String project1Name = "foo"; - final String project1DisplayName = "project1DisplayName"; - final String project2Name = "project2Name"; - final String project2DisplayName = project1Name; - final String project3Name = "project3Name"; - final String project3DisplayName = "project3DisplayName"; - - // create 1 freestyle project with the name foo - FreeStyleProject project1 = j.createFreeStyleProject(project1Name); - project1.setDisplayName(project1DisplayName); - - // create another with the display name foo - FreeStyleProject project2 = j.createFreeStyleProject(project2Name); - project2.setDisplayName(project2DisplayName); - - // create a third project and make sure it's not picked up by search - FreeStyleProject project3 = j.createFreeStyleProject(project3Name); - project3.setDisplayName(project3DisplayName); - - // search for foo - Page result = j.search(project1Name); - assertNotNull(result); - j.assertGoodStatus(result); - - // make sure we get the project with the name foo - String contents = result.getWebResponse().getContentAsString(); - assertTrue(contents.contains(String.format("%s [Jenkins]", project1DisplayName))); - // make sure projects 2 and 3 were not picked up - assertFalse(contents.contains(project2Name)); - assertFalse(contents.contains(project3Name)); - assertFalse(contents.contains(project3DisplayName)); - } - @Test public void testGetSuggestionsHasBothNamesAndDisplayNames() throws Exception { final String projectName = "project name"; @@ -335,7 +313,7 @@ public void testProjectNameInAFolderDisplayName() throws Exception { String name = (String) jsonSuggestion.get("name"); - if (displayName2.equals(name)) { + if ("my-folder-1 » job-2".equals(name)) { foundDisplayName = true; } } diff --git a/test/src/test/java/hudson/security/HudsonPrivateSecurityRealmFIPSTest.java b/test/src/test/java/hudson/security/HudsonPrivateSecurityRealmFIPSTest.java index ab1352807117..b9f0449b725e 100644 --- a/test/src/test/java/hudson/security/HudsonPrivateSecurityRealmFIPSTest.java +++ b/test/src/test/java/hudson/security/HudsonPrivateSecurityRealmFIPSTest.java @@ -43,6 +43,9 @@ import org.hamcrest.Matcher; import org.hamcrest.Matchers; import org.htmlunit.FailingHttpStatusCodeException; +import org.htmlunit.html.HtmlForm; +import org.htmlunit.html.HtmlPage; +import org.htmlunit.html.HtmlPasswordInput; import org.junit.Rule; import org.junit.Test; import org.jvnet.hudson.test.For; @@ -149,6 +152,87 @@ private static void userCreationWithJBCryptPasswordsStep(JenkinsRule j) throws E is("The hashed password was hashed with an incorrect algorithm. Jenkins is expecting $PBKDF2")); } + @Test + public void validatePasswordLengthForFIPS() throws Throwable { + rjr.then(HudsonPrivateSecurityRealmFIPSTest::validatePasswordLengthForFIPSStep); + } + + private static void validatePasswordLengthForFIPSStep(JenkinsRule j) throws Exception { + HudsonPrivateSecurityRealm securityRealm = new HudsonPrivateSecurityRealm(false, false, null); + j.jenkins.setSecurityRealm(securityRealm); + + User u1 = securityRealm.createAccount("test", "aValidFipsPass"); + + WebClient wc = j.createWebClient(); + wc.login("test","aValidFipsPass"); + + HtmlPage configurePage = wc.goTo(u1.getUrl() + "/security/"); + HtmlPasswordInput password1 = configurePage.getElementByName("user.password"); + HtmlPasswordInput password2 = configurePage.getElementByName("user.password2"); + //Should fail as the password length is <14 (In FIPS mode) + password1.setText("mockPassword"); + password2.setText("mockPassword"); + + HtmlForm form = configurePage.getFormByName("config"); + assertThrows(FailingHttpStatusCodeException.class, () -> { + j.submit(form); + }); + } + + @Test + public void validatePasswordMismatchForFIPS() throws Throwable { + rjr.then(HudsonPrivateSecurityRealmFIPSTest::validatePasswordMismatchForFIPSStep); + } + + private static void validatePasswordMismatchForFIPSStep(JenkinsRule j) throws Exception { + HudsonPrivateSecurityRealm securityRealm = new HudsonPrivateSecurityRealm(false, false, null); + j.jenkins.setSecurityRealm(securityRealm); + + User u1 = securityRealm.createAccount("test", "aValidFipsPass"); + + + WebClient wc = j.createWebClient(); + wc.login("test","aValidFipsPass"); + + HtmlPage configurePage = wc.goTo(u1.getUrl() + "/security/"); + HtmlPasswordInput password1 = configurePage.getElementByName("user.password"); + HtmlPasswordInput password2 = configurePage.getElementByName("user.password2"); + //should fail as the passwords are different (even though the password length >=14) In FIPS mode + password1.setText("14charPassword"); + password2.setText("14charPa$$word"); + + HtmlForm form = configurePage.getFormByName("config"); + assertThrows(FailingHttpStatusCodeException.class, () -> { + j.submit(form); + }); + } + + @Test + public void validatePasswordSuccessForFIPS() throws Throwable { + rjr.then(HudsonPrivateSecurityRealmFIPSTest::validatePasswordSuccessForFIPSStep); + } + + private static void validatePasswordSuccessForFIPSStep(JenkinsRule j) throws Exception { + HudsonPrivateSecurityRealm securityRealm = new HudsonPrivateSecurityRealm(false, false, null); + j.jenkins.setSecurityRealm(securityRealm); + + User u1 = securityRealm.createAccount("test", "aValidFipsPass"); + + WebClient wc = j.createWebClient(); + wc.login("test","aValidFipsPass"); + + HtmlPage configurePage = wc.goTo(u1.getUrl() + "/security/"); + HtmlPasswordInput password1 = configurePage.getElementByName("user.password"); + HtmlPasswordInput password2 = configurePage.getElementByName("user.password2"); + //should pass as the passwords are same and length >=14 In FIPS mode. + password1.setText("14charPassword"); + password2.setText("14charPassword"); + + HtmlForm form = configurePage.getFormByName("config"); + HtmlPage success = j.submit(form); + assertThat(success.getWebResponse().getStatusCode(), is(200)); + } + private static Matcher incorrectHashingLogEntry() { return Matchers.hasProperty("message", is("A password appears to be stored (or is attempting to be stored) that was created with a different hashing/encryption algorithm, check the FIPS-140 state of the system has not changed inadvertently")); diff --git a/test/src/test/java/hudson/tasks/LogRotatorTest.java b/test/src/test/java/hudson/tasks/LogRotatorTest.java index 118a860147a1..d30d4d8115bc 100644 --- a/test/src/test/java/hudson/tasks/LogRotatorTest.java +++ b/test/src/test/java/hudson/tasks/LogRotatorTest.java @@ -31,7 +31,9 @@ import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; +import static org.junit.Assume.assumeFalse; +import hudson.Functions; import hudson.Launcher; import hudson.model.AbstractBuild; import hudson.model.BuildListener; @@ -50,10 +52,8 @@ import java.util.concurrent.TimeoutException; import java.util.logging.Level; import java.util.logging.Logger; -import org.junit.ClassRule; import org.junit.Rule; import org.junit.Test; -import org.jvnet.hudson.test.BuildWatcher; import org.jvnet.hudson.test.FailureBuilder; import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.JenkinsRule; @@ -64,9 +64,6 @@ */ public class LogRotatorTest { - @ClassRule - public static BuildWatcher watcher = new BuildWatcher(); - @Rule public JenkinsRule j = new JenkinsRule(); @@ -103,6 +100,8 @@ public void successVsFailureWithRemoveLastBuild() throws Exception { @Test public void ableToDeleteCurrentBuild() throws Exception { + assumeFalse("Deleting the current build while is is completing does not work consistently on Windows", + Functions.isWindows()); var p = j.createFreeStyleProject(); // Keep 0 builds, i.e. immediately delete builds as they complete. LogRotator logRotator = new LogRotator(-1, 0, -1, -1); diff --git a/test/src/test/java/hudson/widgets/HistoryWidgetTest.java b/test/src/test/java/hudson/widgets/HistoryWidgetTest.java index 45af1b28b806..bc1229d3b658 100644 --- a/test/src/test/java/hudson/widgets/HistoryWidgetTest.java +++ b/test/src/test/java/hudson/widgets/HistoryWidgetTest.java @@ -36,7 +36,7 @@ public void displayFilterInput() throws Exception { { // Filter input shouldn't display when there's no build HtmlPage page = wc.goTo("job/" + p.getName()); - DomNode searchInputContainer = page.querySelector(".jenkins-search"); + DomNode searchInputContainer = page.querySelector("#jenkins-builds .jenkins-search"); assertTrue(searchInputContainer.getAttributes().getNamedItem("class").getNodeValue().contains("jenkins-hidden")); } @@ -44,7 +44,7 @@ public void displayFilterInput() throws Exception { { // Filter input should display when there's a build HtmlPage page = wc.goTo("job/" + p.getName()); - DomNode searchInputContainer = page.querySelector(".jenkins-search"); + DomNode searchInputContainer = page.querySelector("#jenkins-builds .jenkins-search"); assertFalse(searchInputContainer.getAttributes().getNamedItem("class").getNodeValue().contains("jenkins-hidden")); } } diff --git a/test/src/test/java/jenkins/AgentProtocolTest.java b/test/src/test/java/jenkins/AgentProtocolTest.java deleted file mode 100644 index 531281109db7..000000000000 --- a/test/src/test/java/jenkins/AgentProtocolTest.java +++ /dev/null @@ -1,93 +0,0 @@ -/* - * The MIT License - * - * Copyright (c) 2017 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; - -import static org.junit.Assert.fail; - -import edu.umd.cs.findbugs.annotations.CheckForNull; -import java.util.ArrayList; -import java.util.List; -import java.util.Set; -import jenkins.model.Jenkins; -import org.junit.Rule; -import org.junit.Test; -import org.jvnet.hudson.test.Issue; -import org.jvnet.hudson.test.JenkinsRule; -import org.jvnet.hudson.test.recipes.LocalData; - -/** - * Tests for {@link AgentProtocol}. - * - * @author Oleg Nenashev - */ -public class AgentProtocolTest { - - @Rule - public JenkinsRule j = new JenkinsRule(); - - @Test - @LocalData - @Issue("JENKINS-45841") - public void testShouldNotOverrideUserConfiguration() throws Exception { - assertEnabled("JNLP4-connect"); - assertDisabled("JNLP2-connect", "JNLP3-connect"); - assertProtocols(true, "System protocols should be always enabled", "Ping"); - } - - private void assertEnabled(String ... protocolNames) { - assertProtocols(true, null, protocolNames); - } - - private void assertDisabled(String ... protocolNames) { - assertProtocols(false, null, protocolNames); - } - - private void assertProtocols(boolean shouldBeEnabled, @CheckForNull String why, String ... protocolNames) { - assertProtocols(j.jenkins, shouldBeEnabled, why, protocolNames); - } - - public static void assertProtocols(Jenkins jenkins, boolean shouldBeEnabled, @CheckForNull String why, String ... protocolNames) { - Set agentProtocols = jenkins.getAgentProtocols(); - List failedChecks = new ArrayList<>(); - for (String protocol : protocolNames) { - if (shouldBeEnabled && !agentProtocols.contains(protocol)) { - failedChecks.add(protocol); - } - if (!shouldBeEnabled && agentProtocols.contains(protocol)) { - failedChecks.add(protocol); - } - } - - if (!failedChecks.isEmpty()) { - String message = String.format("Protocol(s) are not %s: %s. %sEnabled protocols: %s", - shouldBeEnabled ? "enabled" : "disabled", - String.join(",", failedChecks), - why != null ? "Reason: " + why + ". " : "", - String.join(",", agentProtocols)); - fail(message); - } - } - -} diff --git a/test/src/test/java/jenkins/agents/JnlpProtocol4ProxyHandlerTest.java b/test/src/test/java/jenkins/agents/JnlpProtocol4ProxyHandlerTest.java index 76f11d455f4d..e4c031922ded 100644 --- a/test/src/test/java/jenkins/agents/JnlpProtocol4ProxyHandlerTest.java +++ b/test/src/test/java/jenkins/agents/JnlpProtocol4ProxyHandlerTest.java @@ -94,11 +94,6 @@ public String getName() { return JnlpProtocol4ProxyHandler.NAME; } - @Override - public String getDisplayName() { - return "Test handler"; - } - @Override public void handle(Socket socket) throws IOException, InterruptedException { var agentIO = socket.getChannel(); diff --git a/test/src/test/java/jenkins/model/JenkinsTest.java b/test/src/test/java/jenkins/model/JenkinsTest.java index c44b12cdd037..acef3b6a2300 100644 --- a/test/src/test/java/jenkins/model/JenkinsTest.java +++ b/test/src/test/java/jenkins/model/JenkinsTest.java @@ -30,26 +30,25 @@ import static org.awaitility.Awaitility.await; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.arrayContaining; +import static org.hamcrest.Matchers.contains; import static org.hamcrest.Matchers.containsString; import static org.hamcrest.Matchers.equalTo; -import static org.hamcrest.Matchers.hasItem; import static org.hamcrest.Matchers.is; -import static org.hamcrest.Matchers.not; +import static org.hamcrest.Matchers.isA; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertThrows; import static org.junit.Assert.assertTrue; import static org.junit.Assume.assumeFalse; -import edu.umd.cs.findbugs.annotations.CheckForNull; import hudson.ExtensionList; import hudson.Functions; import hudson.XmlFile; import hudson.init.InitMilestone; import hudson.init.Initializer; +import hudson.model.AllView; import hudson.model.Computer; import hudson.model.Failure; import hudson.model.FreeStyleProject; @@ -76,7 +75,6 @@ import java.io.File; import java.io.IOException; import java.net.HttpURLConnection; -import java.net.Socket; import java.net.URI; import java.net.URL; import java.nio.file.Files; @@ -84,7 +82,6 @@ import java.nio.file.attribute.PosixFilePermission; import java.util.ArrayList; import java.util.Arrays; -import java.util.Collections; import java.util.Comparator; import java.util.EnumSet; import java.util.HashSet; @@ -94,7 +91,6 @@ import java.util.Set; import java.util.concurrent.TimeUnit; import java.util.stream.Stream; -import jenkins.AgentProtocol; import org.apache.commons.io.FileUtils; import org.htmlunit.FailingHttpStatusCodeException; import org.htmlunit.HttpMethod; @@ -108,6 +104,7 @@ import org.junit.Test; import org.junit.experimental.categories.Category; import org.junit.rules.TemporaryFolder; +import org.jvnet.hudson.reactor.ReactorException; import org.jvnet.hudson.test.Issue; import org.jvnet.hudson.test.JenkinsRule; import org.jvnet.hudson.test.JenkinsRule.WebClient; @@ -139,8 +136,7 @@ public void verifyUploadedFingerprintFilePermission() throws Exception { assumeFalse(Functions.isWindows()); HtmlPage page = j.createWebClient().goTo("fingerprintCheck"); - // The form doesn't have a name, the page contain the search form and the one we're interested in - HtmlForm form = page.getForms().get(1); + HtmlForm form = page.getForms().get(0); File dir = tmp.newFolder(); File plugin = new File(dir, "htmlpublisher.jpi"); // We're using a plugin to have a file above DiskFileItemFactory.DEFAULT_SIZE_THRESHOLD @@ -600,174 +596,6 @@ public void onOnline(Computer c, TaskListener listener) throws IOException, Inte } } - @Test - @Issue("JENKINS-39465") - public void agentProtocols_singleEnable_roundtrip() throws Exception { - final Set defaultProtocols = Collections.unmodifiableSet(j.jenkins.getAgentProtocols()); - - final Set newProtocols = new HashSet<>(defaultProtocols); - newProtocols.add(MockOptInProtocol1.NAME); - j.jenkins.setAgentProtocols(newProtocols); - j.jenkins.save(); - final Set agentProtocolsBeforeReload = j.jenkins.getAgentProtocols(); - assertProtocolEnabled(MockOptInProtocol1.NAME, "before the roundtrip"); - - j.jenkins.reload(); - - final Set reloadedProtocols = j.jenkins.getAgentProtocols(); - assertNotSame("The protocol list must have been really reloaded", agentProtocolsBeforeReload, reloadedProtocols); - assertThat("We should have additional enabled protocol", - reloadedProtocols.size(), equalTo(defaultProtocols.size() + 1)); - assertProtocolEnabled(MockOptInProtocol1.NAME, "after the roundtrip"); - } - - @Test - @Issue("JENKINS-39465") - public void agentProtocols_multipleDisable_roundtrip() throws Exception { - final Set defaultProtocols = Collections.unmodifiableSet(j.jenkins.getAgentProtocols()); - assertProtocolEnabled(MockOptOutProtocol1.NAME, "after startup"); - - final Set newProtocols = new HashSet<>(defaultProtocols); - newProtocols.remove(MockOptOutProtocol1.NAME); - j.jenkins.setAgentProtocols(newProtocols); - j.jenkins.save(); - assertProtocolDisabled(MockOptOutProtocol1.NAME, "before the roundtrip"); - final Set agentProtocolsBeforeReload = j.jenkins.getAgentProtocols(); - j.jenkins.reload(); - - assertNotSame("The protocol list must have been really refreshed", agentProtocolsBeforeReload, j.jenkins.getAgentProtocols()); - assertThat("We should have disabled one protocol", - j.jenkins.getAgentProtocols().size(), equalTo(defaultProtocols.size() - 1)); - - assertProtocolDisabled(MockOptOutProtocol1.NAME, "after the roundtrip"); - } - - @Test - @Issue("JENKINS-39465") - public void agentProtocols_multipleEnable_roundtrip() throws Exception { - final Set defaultProtocols = Collections.unmodifiableSet(j.jenkins.getAgentProtocols()); - final Set newProtocols = new HashSet<>(defaultProtocols); - newProtocols.add(MockOptInProtocol1.NAME); - newProtocols.add(MockOptInProtocol2.NAME); - j.jenkins.setAgentProtocols(newProtocols); - j.jenkins.save(); - - final Set agentProtocolsBeforeReload = j.jenkins.getAgentProtocols(); - assertProtocolEnabled(MockOptInProtocol1.NAME, "before the roundtrip"); - assertProtocolEnabled(MockOptInProtocol2.NAME, "before the roundtrip"); - - j.jenkins.reload(); - - final Set reloadedProtocols = j.jenkins.getAgentProtocols(); - assertNotSame("The protocol list must have been really reloaded", agentProtocolsBeforeReload, reloadedProtocols); - assertThat("There should be two additional enabled protocols", - reloadedProtocols.size(), equalTo(defaultProtocols.size() + 2)); - assertProtocolEnabled(MockOptInProtocol1.NAME, "after the roundtrip"); - assertProtocolEnabled(MockOptInProtocol2.NAME, "after the roundtrip"); - } - - @Test - @Issue("JENKINS-39465") - public void agentProtocols_singleDisable_roundtrip() throws Exception { - final Set defaultProtocols = Collections.unmodifiableSet(j.jenkins.getAgentProtocols()); - final String protocolToDisable1 = MockOptOutProtocol1.NAME; - final String protocolToDisable2 = MockOptOutProtocol2.NAME; - - final Set newProtocols = new HashSet<>(defaultProtocols); - newProtocols.remove(protocolToDisable1); - newProtocols.remove(protocolToDisable2); - j.jenkins.setAgentProtocols(newProtocols); - j.jenkins.save(); - assertProtocolDisabled(protocolToDisable1, "before the roundtrip"); - assertProtocolDisabled(protocolToDisable2, "before the roundtrip"); - final Set agentProtocolsBeforeReload = j.jenkins.getAgentProtocols(); - j.jenkins.reload(); - - assertNotSame("The protocol list must have been really reloaded", agentProtocolsBeforeReload, j.jenkins.getAgentProtocols()); - assertThat("We should have disabled two protocols", - j.jenkins.getAgentProtocols().size(), equalTo(defaultProtocols.size() - 2)); - assertProtocolDisabled(protocolToDisable1, "after the roundtrip"); - assertProtocolDisabled(protocolToDisable2, "after the roundtrip"); - } - - private void assertProtocolDisabled(String protocolName, @CheckForNull String stage) { - assertThat(protocolName + " must be disabled. Stage=" + (stage != null ? stage : "undefined"), - j.jenkins.getAgentProtocols(), not(hasItem(protocolName))); - } - - private void assertProtocolEnabled(String protocolName, @CheckForNull String stage) { - assertThat(protocolName + " must be enabled. Stage=" + (stage != null ? stage : "undefined"), - j.jenkins.getAgentProtocols(), hasItem(protocolName)); - } - - @TestExtension - public static class MockOptInProtocol1 extends MockOptInProtocol { - - static final String NAME = "MOCK-OPTIN-1"; - - @Override - public String getName() { - return NAME; - } - } - - @TestExtension - public static class MockOptInProtocol2 extends MockOptInProtocol { - - static final String NAME = "MOCK-OPTIN-2"; - - @Override - public String getName() { - return NAME; - } - } - - private abstract static class MockOptInProtocol extends AgentProtocol { - @Override - public boolean isOptIn() { - return true; - } - - @Override - public void handle(Socket socket) throws IOException, InterruptedException { - throw new IOException("This is a mock agent protocol. It cannot be used for connection"); - } - } - - @TestExtension - public static class MockOptOutProtocol1 extends MockOptOutProtocol { - - static final String NAME = "MOCK-OPTOUT-1"; - - @Override - public String getName() { - return NAME; - } - } - - @TestExtension - public static class MockOptOutProtocol2 extends MockOptOutProtocol { - - static final String NAME = "MOCK-OPTOUT-2"; - - @Override - public String getName() { - return NAME; - } - } - - private abstract static class MockOptOutProtocol extends AgentProtocol { - @Override - public boolean isOptIn() { - return false; - } - - @Override - public void handle(Socket socket) throws IOException, InterruptedException { - throw new IOException("This is a mock agent protocol. It cannot be used for connection"); - } - } - @Test public void getComputers() throws Exception { List agents = new ArrayList<>(); @@ -931,4 +759,16 @@ public String getUrlName() { return null; } } + + @Test + public void reloadViews() throws Exception { + assertThat(j.jenkins.getPrimaryView(), isA(AllView.class)); + assertThat(j.jenkins.getViews(), contains(isA(AllView.class))); + Files.writeString(j.jenkins.getConfigFile().getFile().toPath(), " created = new ArrayList<>(); + private static final List updated = new ArrayList<>(); + private static final List deleted = new ArrayList<>(); + + @Override + protected void onCreated(@NonNull Node node) { + node.getRootDir(); + created.add(node.getNodeName()); + } + + @Override + protected void onUpdated(@NonNull Node oldOne, @NonNull Node newOne) { + if (oldOne instanceof DumbSlave oldDumbSlave && newOne instanceof DumbSlave newDumbSlave) { + updated.add(new DumbSlavePair(oldDumbSlave, newDumbSlave)); + } + } + + @Override + protected void onDeleted(@NonNull Node node) { + deleted.add(node.getNodeName()); + } + } + + private static class DumbSlaveNameAndRemoteFSMatcher extends TypeSafeMatcher { + private final DumbSlavePair expected; + + public DumbSlaveNameAndRemoteFSMatcher(DumbSlavePair expected) { + this.expected = expected; + } + + @Override + protected boolean matchesSafely(DumbSlavePair dumbSlavePair) { + return expected.oldNode.getNodeName().equals(dumbSlavePair.oldNode.getNodeName()) + && expected.oldNode.getRemoteFS().equals(dumbSlavePair.oldNode.getRemoteFS()) + && expected.newNode.getNodeName().equals(dumbSlavePair.newNode.getNodeName()) + && expected.newNode.getRemoteFS().equals(dumbSlavePair.newNode.getRemoteFS()); + } + + @Override + public void describeTo(Description description) { + description.appendText("NodePair(").appendValue(expected).appendText(")"); + } + } } diff --git a/test/src/test/java/jenkins/model/PeepholePermalinkTest.java b/test/src/test/java/jenkins/model/PeepholePermalinkTest.java index 8d06ab980173..2a4e05854962 100644 --- a/test/src/test/java/jenkins/model/PeepholePermalinkTest.java +++ b/test/src/test/java/jenkins/model/PeepholePermalinkTest.java @@ -82,7 +82,7 @@ public void basics() throws Exception { } private void assertStorage(String id, Job job, Run build) throws Exception { - assertThat(Files.readAllLines(PeepholePermalink.storageFor(job.getBuildDir()).toPath(), StandardCharsets.UTF_8), + assertThat(Files.readAllLines(PeepholePermalink.DefaultCache.storageFor(job.getBuildDir()).toPath(), StandardCharsets.UTF_8), hasItem(id + " " + (build == null ? -1 : build.getNumber()))); } diff --git a/test/src/test/java/jenkins/model/lazy/LazyBuildMixInTest.java b/test/src/test/java/jenkins/model/lazy/LazyBuildMixInTest.java index 6b2ef4e6cf41..53f0603dd0b1 100644 --- a/test/src/test/java/jenkins/model/lazy/LazyBuildMixInTest.java +++ b/test/src/test/java/jenkins/model/lazy/LazyBuildMixInTest.java @@ -24,6 +24,9 @@ package jenkins.model.lazy; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.nullValue; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotSame; import static org.junit.Assert.assertNull; @@ -87,6 +90,40 @@ public class LazyBuildMixInTest { assertEquals(b3, b1a.getNextBuild()); } + @Test public void numericLookups() throws Exception { + var p = r.createFreeStyleProject(); + var b1 = r.buildAndAssertSuccess(p); + var b2 = r.buildAndAssertSuccess(p); + var b3 = r.buildAndAssertSuccess(p); + var b4 = r.buildAndAssertSuccess(p); + var b5 = r.buildAndAssertSuccess(p); + var b6 = r.buildAndAssertSuccess(p); + b1.delete(); + b3.delete(); + b6.delete(); + // leaving 2, 4, 5 + assertThat(p.getFirstBuild(), is(b2)); + assertThat(p.getLastBuild(), is(b5)); + assertThat(p.getNearestBuild(-1), is(b2)); + assertThat(p.getNearestBuild(0), is(b2)); + assertThat(p.getNearestBuild(1), is(b2)); + assertThat(p.getNearestBuild(2), is(b2)); + assertThat(p.getNearestBuild(3), is(b4)); + assertThat(p.getNearestBuild(4), is(b4)); + assertThat(p.getNearestBuild(5), is(b5)); + assertThat(p.getNearestBuild(6), nullValue()); + assertThat(p.getNearestBuild(7), nullValue()); + assertThat(p.getNearestOldBuild(-1), nullValue()); + assertThat(p.getNearestOldBuild(0), nullValue()); + assertThat(p.getNearestOldBuild(1), nullValue()); + assertThat(p.getNearestOldBuild(2), is(b2)); + assertThat(p.getNearestOldBuild(3), is(b2)); + assertThat(p.getNearestOldBuild(4), is(b4)); + assertThat(p.getNearestOldBuild(5), is(b5)); + assertThat(p.getNearestOldBuild(6), is(b5)); + assertThat(p.getNearestOldBuild(7), is(b5)); + } + @Issue("JENKINS-20662") @Test public void newRunningBuildRelationFromPrevious() throws Exception { FreeStyleProject p = r.createFreeStyleProject(); diff --git a/test/src/test/java/jenkins/security/ResourceDomainTest.java b/test/src/test/java/jenkins/security/ResourceDomainTest.java index b8f2d551b094..a2eb9de0b89a 100644 --- a/test/src/test/java/jenkins/security/ResourceDomainTest.java +++ b/test/src/test/java/jenkins/security/ResourceDomainTest.java @@ -55,6 +55,34 @@ public void prepare() throws Exception { configuration.setUrl(resourceRoot); } + @Test + public void groupPermissionsWork() throws Exception { + final JenkinsRule.DummySecurityRealm securityRealm = j.createDummySecurityRealm(); + securityRealm.addGroups("alice", "admins"); + j.jenkins.setSecurityRealm(securityRealm); + MockAuthorizationStrategy a = new MockAuthorizationStrategy().grant(Jenkins.READ).everywhere().to("admins"); + j.jenkins.setAuthorizationStrategy(a); + + JenkinsRule.WebClient webClient = j.createWebClient().login("alice"); + + { // DBS directory listing is shown as always + Page page = webClient.goTo("userContent"); + Assert.assertEquals("successful request", 200, page.getWebResponse().getStatusCode()); + Assert.assertTrue("still on the original URL", page.getUrl().toString().contains("/userContent")); + Assert.assertTrue("web page", page.isHtmlPage()); + Assert.assertTrue("complex web page", page.getWebResponse().getContentAsString().contains("javascript")); + } + { // DBS on primary domain forwards to second domain when trying to access a file URL + webClient.setRedirectEnabled(true); + Page page = webClient.goTo("userContent/readme.txt", "text/plain"); + final String resourceResponseUrl = page.getUrl().toString(); + Assert.assertEquals("resource response success", 200, page.getWebResponse().getStatusCode()); + Assert.assertNull("no CSP headers", page.getWebResponse().getResponseHeaderValue("Content-Security-Policy")); + Assert.assertTrue("Served from resource domain", resourceResponseUrl.contains(RESOURCE_DOMAIN)); + Assert.assertTrue("Served from resource action", resourceResponseUrl.contains("static-files")); + } + } + @Test public void secondDomainBasics() throws Exception { JenkinsRule.WebClient webClient = j.createWebClient(); diff --git a/test/src/test/java/jenkins/security/Security2779Test.java b/test/src/test/java/jenkins/security/Security2779Test.java index ef8ef8fef7d5..8e2460687aaf 100644 --- a/test/src/test/java/jenkins/security/Security2779Test.java +++ b/test/src/test/java/jenkins/security/Security2779Test.java @@ -49,7 +49,7 @@ private void noCrossSiteScriptingInHelp(String selector) throws Exception { // assert leading space to identify unintentional double-escaping (&lt;) as test failure assertThat("tooltip does not contain dangerous HTML", jsResultString, not(containsString(" ballIcons = StreamSupport.stream(ballColorAborted.getChildElements().spliterator(), false).collect(Collectors.toList()); - assertIconToSvgIconOkay(ballIcons.get(0).getFirstElementChild(), "icon-aborted icon-md"); + assertThat("Aborted", is(ballColorAborted.getTextContent())); + HtmlElement symbol = ballColorAborted.getElementsByTagName("svg").get(0); + assertThat("icon-md", is(symbol.getAttribute("class"))); + + assertIconToSymbolOkay(symbol); DomElement statusIcons = p.getElementById("statusIcons"); - List statusIconsList = StreamSupport.stream(statusIcons.getChildElements().spliterator(), false).collect(Collectors.toList()); + List statusIconsList = StreamSupport.stream(statusIcons.getChildElements().spliterator(), false).toList(); assertIconToSvgOkay(statusIconsList.get(0).getFirstElementChild().getNextElementSibling(), "icon-user icon-xlg"); @@ -182,13 +185,6 @@ private void assertIconToSvgOkay(DomElement icon, String classSpec) { } } - private void assertIconToSvgIconOkay(DomElement icon, String classSpec) { - assertThat(icon.getTagName(), is("span")); - if (classSpec != null) { - assertThat(icon.getAttribute("class"), endsWith(classSpec)); - } - } - private void assertIconToSymbolOkay(DomElement icon) { assertThat("svg", is(icon.getTagName())); } diff --git a/test/src/test/java/lib/layout/LayoutTest.java b/test/src/test/java/lib/layout/LayoutTest.java index 30d43cb545c2..0d1fe2f00bc8 100644 --- a/test/src/test/java/lib/layout/LayoutTest.java +++ b/test/src/test/java/lib/layout/LayoutTest.java @@ -53,4 +53,9 @@ public class LayoutTest { } } + @Test public void fullScreen() throws Exception { + // Example page using : + r.createWebClient().goTo("setupWizard/proxy-configuration"); + } + } diff --git a/test/src/test/resources/jenkins/AgentProtocolTest/config.xml b/test/src/test/resources/jenkins/AgentProtocolTest/config.xml deleted file mode 100644 index b072a16fa96e..000000000000 --- a/test/src/test/resources/jenkins/AgentProtocolTest/config.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - 1.0 - 2 - NORMAL - true - - - false - - ${ITEM_ROOTDIR}/workspace - ${ITEM_ROOTDIR}/builds - - - - - - 0 - - - - all - false - false - - - - all - 0 - - JNLP4-connect - - - JNLP2-connect - JNLP3-connect - - - - - \ No newline at end of file diff --git a/war/pom.xml b/war/pom.xml index b7b41897bc9e..a36affc90a15 100644 --- a/war/pom.xml +++ b/war/pom.xml @@ -46,7 +46,7 @@ THE SOFTWARE. localhost 8080 - 2.14.0-133.vcc091215a_358 + 2.14.0-138.v6341ee58e1df 3107.v665000b_51092 @@ -286,7 +286,7 @@ THE SOFTWARE. org.jenkins-ci.plugins matrix-auth - 3.2.2 + 3.2.3 hpi @@ -307,14 +307,14 @@ THE SOFTWARE. org.jenkins-ci.plugins script-security - 1365.v4778ca_84b_de5 + 1369.v9b_98a_4e95b_2d hpi org.jenkins-ci.plugins junit - 1304.vc85a_b_ca_96613 + 1312.v1a_235a_b_94a_31 hpi @@ -350,7 +350,7 @@ THE SOFTWARE. org.jenkins-ci.plugins.workflow workflow-support - 926.v9f4f9b_b_98c19 + 936.v9fa_77211ca_e1 hpi @@ -365,7 +365,7 @@ THE SOFTWARE. io.jenkins.plugins echarts-api - 5.5.1-2 + 5.5.1-5 hpi @@ -432,14 +432,14 @@ THE SOFTWARE. org.jenkins-ci.plugins bouncycastle-api - 2.30.1.78.1-248.ve27176eb_46cb_ + 2.30.1.79-254.vfdb_814e7791e hpi org.jenkins-ci.plugins command-launcher - 115.vd8b_301cc15d0 + 116.vd85919c54a_d6 hpi @@ -523,7 +523,7 @@ THE SOFTWARE. io.jenkins.plugins gson-api - 2.11.0-41.v019fcf6125dc + 2.11.0-85.v1f4e87273c33 hpi @@ -551,7 +551,7 @@ THE SOFTWARE. io.jenkins.plugins json-api - 20240303-101.v7a_8666713110 + 20241224-119.va_dca_a_b_ea_7da_5 hpi @@ -645,7 +645,7 @@ THE SOFTWARE. org.eclipse.jetty.ee9 jetty-ee9-maven-plugin - 12.0.14 + 12.0.16