Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
41 commits
Select commit Hold shift + click to select a range
db697ec
Init
janfaracik Oct 15, 2025
0bfbcfd
Refine JS
janfaracik Oct 16, 2025
6c58d4e
Refine
janfaracik Oct 16, 2025
6bed5ac
Update _buttons.scss
janfaracik Oct 16, 2025
06adfd2
Tidy
janfaracik Oct 16, 2025
7d2921f
Update templates.js
janfaracik Oct 16, 2025
96e2eb8
Update templates.js
janfaracik Oct 16, 2025
f93066b
Fix loading of breadcrumbs
janfaracik Oct 16, 2025
35a2f60
Create StopRunAction.java
janfaracik Oct 16, 2025
0c0571e
Merge branch 'master' into run-app-bar
janfaracik Oct 17, 2025
b544910
Tidy
janfaracik Oct 17, 2025
c07fbd2
Merge branch 'master' into run-app-bar
janfaracik Oct 17, 2025
c7af3be
Fix tests
janfaracik Oct 17, 2025
f64571c
Push
janfaracik Oct 18, 2025
7d1c235
Merge branch 'master' into run-app-bar
janfaracik Oct 18, 2025
3fea627
Merge branch 'master' into run-app-bar
janfaracik Oct 29, 2025
7b08893
Add Javadoc, tidy
janfaracik Oct 29, 2025
d09c8f0
Tidy
janfaracik Oct 29, 2025
9873a7b
Merge branch 'master' into run-app-bar
janfaracik Nov 11, 2025
7c59f80
Fix compilation
janfaracik Nov 11, 2025
efc1dd2
Fix javadoc build
timja Nov 11, 2025
7a675f8
Merge branch 'master' into run-app-bar
janfaracik Nov 17, 2025
efd2767
Merge branch 'master' into run-app-bar
janfaracik Jan 29, 2026
dd5a560
Merge branch 'master' into run-app-bar
timja Jan 29, 2026
e6150d5
Merge branch 'run-app-bar' of github.com:janfaracik/jenkins into run-…
timja Jan 29, 2026
1282382
Many fixes
timja Jan 29, 2026
399cbb3
WIP
timja Jan 31, 2026
6de8e03
Fix cancel
timja Feb 1, 2026
abc73c5
Fix javadoc
timja Feb 1, 2026
f8b82c2
Add more Javadoc
timja Feb 1, 2026
594d3f6
More javadoc
timja Feb 1, 2026
237c303
Fix TODOs
timja Feb 1, 2026
71ee89d
Fix wording
timja Feb 2, 2026
47834a9
Fix checkstyle
timja Feb 2, 2026
522381b
Fix spotbugs
timja Feb 2, 2026
5ffd7d6
Merge branch 'master' into run-app-bar
timja Feb 2, 2026
a42ed73
Spotbugs
timja Feb 2, 2026
a42f115
Mark API as beta
timja Feb 17, 2026
7d7240d
Merge branch 'master' into run-app-bar
timja Feb 17, 2026
e5b3082
Merge branch 'master' into run-app-bar
lemeurherve Mar 6, 2026
fccb53f
Merge branch 'master' into run-app-bar
timja Mar 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions core/src/main/java/hudson/Functions.java
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,7 @@
import jenkins.model.details.DetailGroup;
import jenkins.telemetry.impl.PasswordMasking;
import jenkins.util.SystemProperties;
import net.sf.json.JSONObject;
import org.apache.commons.jelly.JellyContext;
import org.apache.commons.jelly.JellyTagException;
import org.apache.commons.jelly.Script;
Expand Down Expand Up @@ -2660,6 +2661,21 @@
return String.valueOf(Math.floor(Math.random() * 3000));
}

/**
* Converts the given actions to a JSON object
*/
@Restricted(NoExternalUse.class)
public static String convertActionsToJson(String baseUrl, List<Action> actions) {
ModelObjectWithContextMenu.ContextMenu contextMenu = new ModelObjectWithContextMenu.ContextMenu();
contextMenu.addAll(actions
.stream()
.filter(action -> action.getIconFileName() != null)
.toList());
JSONObject jsonObject = JSONObject.fromObject(contextMenu);
jsonObject.put("url", Util.ensureEndsWith(baseUrl, "/"));
return jsonObject.toString();

Check warning on line 2676 in core/src/main/java/hudson/Functions.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 2669-2676 are not covered by tests
}

/**
* Returns a grouped list of Detail objects for the given Actionable object
*/
Expand Down
42 changes: 42 additions & 0 deletions core/src/main/java/hudson/model/Action.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@

import edu.umd.cs.findbugs.annotations.CheckForNull;
import hudson.Functions;
import jenkins.model.menu.Group;
import jenkins.model.menu.Semantic;
import jenkins.model.menu.event.Event;
import jenkins.model.menu.event.LinkEvent;

/**
* Object that contributes additional information, behaviors, and UIs to {@link ModelObject}
Expand Down Expand Up @@ -139,4 +143,42 @@
* @see Functions#getActionUrl(String, Action)
*/
@CheckForNull String getUrlName();

/**
* Returns the group that this item belongs to.
* The default implementation places the item in the menu group.
*
* @return the group of this item
*/
default Group getGroup() {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be part of Action? Or into its own interface?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this the best way of grouping/ordering items?

return Group.IN_MENU;
}

/**
* Returns the event associated with this item.
* By default, this creates a link event pointing to the item's URL name.
*
* @return the event representing this item
*/
default Event getEvent() {
return LinkEvent.of(getUrlName());
}

/**
* Returns the semantic information for this item.
*
* @return the semantic associated with this item, or {@code null} if none
*/
default Semantic getSemantic() {
return null;
}

/**
* Indicates whether this item should be visible in a context menu for an {@link Actionable}.
*
* @return {@code true} if the item is shown in the context menu, {@code false} otherwise
*/
default boolean isVisibleInContextMenu() {
return true;

Check warning on line 182 in core/src/main/java/hudson/model/Action.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 154-182 are not covered by tests
}
}
42 changes: 42 additions & 0 deletions core/src/main/java/hudson/model/Actionable.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,14 +30,23 @@
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.stream.Collectors;
import jenkins.model.ModelObjectWithContextMenu;
import jenkins.model.Tab;
import jenkins.model.TransientActionFactory;
import jenkins.model.menu.Group;
import jenkins.security.stapler.StaplerNotDispatchable;
import org.apache.commons.lang.StringUtils;
import org.jenkins.ui.icon.IconSpec;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerRequest2;
import org.kohsuke.stapler.StaplerResponse;
Expand Down Expand Up @@ -113,6 +122,39 @@
return Collections.unmodifiableList(_actions);
}

/**
* Collects and returns the list of actions to display in the application bar.
* <p>
* This method filters out actions that:
* <ul>
* <li>are instances of {@link Tab}</li>
* <li>lack a display name or an icon (either from {@link Action#getIconFileName()}
* or, if available, {@link IconSpec#getIconClassName()})</li>
* </ul>
* <p>
* The resulting actions are sorted first by their {@link Group#getOrder()} value,
* then alphabetically by display name. The list is returned as an unmodifiable collection.
*
* @return an unmodifiable list of actions suitable for display in the app bar
*/
@Restricted(NoExternalUse.class)
public List<Action> getAppBarActions() {
return getAllActions().stream()
.filter(e -> !(e instanceof Tab))
.filter(e -> {
String icon = e.getIconFileName();

if (e instanceof IconSpec iconSpec && iconSpec.getIconClassName() != null) {
icon = iconSpec.getIconClassName();
}

return !StringUtils.isBlank(e.getDisplayName()) || !StringUtils.isBlank(icon);
})
.sorted(Comparator.comparingInt((Action e) -> e.getGroup().getOrder())
.thenComparing(e -> Objects.requireNonNullElse(e.getDisplayName(), "")))
.collect(Collectors.toUnmodifiableList());

Check warning on line 155 in core/src/main/java/hudson/model/Actionable.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered lines

Lines 142-155 are not covered by tests
}

private <T> Collection<? extends Action> createFor(TransientActionFactory<T> taf) {
try {
Collection<? extends Action> result = taf.createFor(taf.type().cast(this));
Expand Down
3 changes: 2 additions & 1 deletion core/src/main/java/hudson/model/Run.java
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@
import jenkins.model.details.Detail;
import jenkins.model.details.DetailFactory;
import jenkins.model.details.DurationDetail;
import jenkins.model.details.KeptForeverDetail;
import jenkins.model.details.TimestampDetail;
import jenkins.model.lazy.BuildReference;
import jenkins.model.lazy.LazyBuildMixIn;
Expand Down Expand Up @@ -2684,7 +2685,7 @@
}

@NonNull @Override public List<? extends Detail> createFor(@NonNull Run target) {
return List.of(new CauseDetail(target), new TimestampDetail(target), new DurationDetail(target));
return List.of(new CauseDetail(target), new TimestampDetail(target), new DurationDetail(target), new KeptForeverDetail(target));

Check warning on line 2688 in core/src/main/java/hudson/model/Run.java

View check run for this annotation

ci.jenkins.io / Code Coverage

Not covered line

Line 2688 is not covered by tests
}
}

Expand Down
Loading
Loading