Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
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
65 changes: 62 additions & 3 deletions core/src/main/resources/hudson/model/Run/index.jelly
Original file line number Diff line number Diff line change
Expand Up @@ -69,15 +69,74 @@ THE SOFTWARE.
<table>
<t:artifactList build="${it}" caption="${%Build Artifacts}"/>

<!-- give actions a chance to contribute summary item -->
<!-- 1. Test failures / failed tests -->
<j:forEach var="a" items="${it.allActions}">
<st:include page="summary.jelly" from="${a}" optional="true" it="${a}" />
<j:if test="${a.class.name.contains('TestResultAction') || a.urlName == 'testReport' || a.urlName == 'testngreports'}">
<st:include page="summary.jelly" from="${a}" optional="true" it="${a}" />
</j:if>
</j:forEach>
</table>

<!-- 2. Changelog -->
<st:include page="main.jelly" optional="true" />

<table>
<!-- 3. SVN revisions -->
<tbody id="svn-revisions-container">
<j:forEach var="a" items="${it.allActions}">
<j:if test="${a.class.name.contains('SubversionTagAction') || a.class.name.contains('MultipleScmRevisionState')}">
<st:include page="summary.jelly" from="${a}" optional="true" it="${a}" />
</j:if>
</j:forEach>
</tbody>

<!-- 4. Other less-critical build information -->
<j:forEach var="a" items="${it.allActions}">
<j:if test="${!a.class.name.contains('TestResultAction') and a.urlName != 'testReport' and a.urlName != 'testngreports' and !a.class.name.contains('SubversionTagAction') and !a.class.name.contains('MultipleScmRevisionState')}">
<st:include page="summary.jelly" from="${a}" optional="true" it="${a}" />
</j:if>
</j:forEach>

<st:include page="summary.jelly" optional="true" />
</table>

<st:include page="main.jelly" optional="true" />
<script>
(function() {
var svnContainer = document.getElementById('svn-revisions-container');
if (svnContainer) {
var lists = svnContainer.querySelectorAll('ul, ol');
for (var i = 0; i &lt; lists.length; i++) {
var list = lists[i];
var items = list.querySelectorAll('li');
if (items.length > 5) {
var toggle = document.createElement('a');
toggle.href = '#';
toggle.innerHTML = '&#x25B6; SVN Revisions (' + items.length + ')';
toggle.style.fontWeight = 'bold';
toggle.style.display = 'block';
toggle.style.marginBottom = '5px';

var wrapper = document.createElement('div');
wrapper.style.display = 'none';
list.parentNode.insertBefore(toggle, list);
list.parentNode.insertBefore(wrapper, list);
wrapper.appendChild(list);

toggle.onclick = function(e) {
e.preventDefault();
if (wrapper.style.display === 'none') {
wrapper.style.display = 'block';
this.innerHTML = '&#x25BC; SVN Revisions (' + items.length + ')';
} else {
wrapper.style.display = 'none';
this.innerHTML = '&#x25B6; SVN Revisions (' + items.length + ')';
}
};
}
}
}
})();
</script>
</l:main-panel>
</l:layout>
</j:otherwise>
Expand Down
46 changes: 46 additions & 0 deletions implementation_plan.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# SVN Revisions Collapse & Ordering Implementation Plan

## Goal Description
The goal is to improve the UX of the build/job details page by:
1. Collapsing the SVN revisions/locations list when there are more than 5 revisions, adding an expand/collapse toggle.
2. Reordering the build details page so the most useful debugging information is at the top:
- Test Failures
- Changelog
- SVN Revisions
- Other build info

## Investigation Findings
During my investigation of the `c:\jenkins\jenkins` workspace, I found that this workspace contains the **Jenkins Core** source code, not the Subversion plugin.
1. **Ordering**: The build details page is rendered by `core\src\main\resources\hudson\model\Run\index.jelly`. Currently, all Action summaries (including Test Failures and SVN Revisions) are rendered in a loop, followed by the changelog (included via `main.jelly`). Reordering these would require modifying this jelly file to explicitly filter and order actions, which might violate Jenkins' plugin-agnostic architecture if we hardcode SVN/Test plugins in core.
2. **SVN Revisions UI**: The UI component that renders "SVN revisions/locations" (typically `SubversionTagAction/summary.jelly`) is part of the `subversion-plugin`, which is **not present** in this workspace. Searches for SVN-related UI files yielded no results.

> [!WARNING]
> **Missing Subversion Plugin**
> The component responsible for displaying SVN revisions is not present in the provided `c:\jenkins\jenkins` workspace (which only contains Jenkins Core). Therefore, I cannot implement the collapse behavior without access to the Subversion plugin's source code.

## Open Questions

> [!IMPORTANT]
> **1. Workspace / Subversion Plugin Location**
> The `subversion-plugin` is not in this repository. Is it possible you intended for me to work on the `jenkinsci/subversion-plugin` repository instead? If so, please provide access to it or guide me to the correct directory if it's hidden.

> [!IMPORTANT]
> **2. Jenkins Core vs Plugin Architecture for Ordering**
> To reorder Test Failures, Changelog, and SVN Revisions, we would typically need to modify `Run/index.jelly` in Jenkins Core to hardcode the order of specific plugin actions. This is generally discouraged in Jenkins core. Do you still want me to implement hardcoded reordering in `Run/index.jelly`, or is there an existing ordering API / Extension Point you prefer me to use?

## Proposed Changes (Pending Answers)
If we proceed with modifying Jenkins core to hardcode the order, and assuming you can provide the subversion-plugin for the collapse feature:

### Jenkins Core (`hudson\model\Run\index.jelly`)
- Modify the `it.allActions` loop to separate actions into multiple blocks (Test Result, SVN, Other).
- Move the `<st:include page="main.jelly" />` (which contains Changelog) between Test Results and SVN Revisions.

### Subversion Plugin (Location TBA)
- Modify `summary.jelly` (e.g., in `hudson.scm.SubversionTagAction`) to count revisions.
- Add `<j:if>` statements to check if the count > 5.
- Add HTML/JS for an expand/collapse `<details>` or toggle button.

## Verification Plan
- Manual verification: Build a job with 0, 1-5, and >5 SVN revisions and verify the UI collapse behavior.
- Manual verification: Verify the ordering of Test Failures -> Changelog -> SVN Revisions.
- Automated Tests: Run existing UI/Jelly tests in both Jenkins core and the SVN plugin.
84 changes: 84 additions & 0 deletions test/src/test/java/hudson/model/RunIndexReorderingTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package hudson.model;

import com.gargoylesoftware.htmlunit.html.HtmlElement;
import com.gargoylesoftware.htmlunit.html.HtmlPage;
import org.junit.Rule;
import org.junit.Test;
import org.jvnet.hudson.test.JenkinsRule;

import static org.junit.Assert.*;

public class RunIndexReorderingTest {

@Rule
public JenkinsRule j = new JenkinsRule();

// Mock SVN Action that matches the class name check
public static class SubversionTagAction implements Action {
private final int numRevisions;

public SubversionTagAction(int numRevisions) {
this.numRevisions = numRevisions;
}

@Override
public String getIconFileName() { return "svn.png"; }
@Override
public String getDisplayName() { return "SVN Revisions"; }
@Override
public String getUrlName() { return "svn"; }

public int getNumRevisions() { return numRevisions; }
}

// A mock other action
public static class OtherAction implements Action {
@Override
public String getIconFileName() { return "other.png"; }
@Override
public String getDisplayName() { return "Other"; }
@Override
public String getUrlName() { return "other"; }
}

@Test
public void testZeroRevisions() throws Exception {
FreeStyleProject p = j.createFreeStyleProject();
FreeStyleBuild b = p.scheduleBuild2(0).get();
b.addAction(new SubversionTagAction(0));

HtmlPage page = j.createWebClient().getPage(b);
// Script should not crash. No collapse anchor should be present.
assertNull(page.getFirstByXPath("//a[contains(text(), 'SVN Revisions')]"));
}

@Test
public void testOneToFiveRevisions() throws Exception {
FreeStyleProject p = j.createFreeStyleProject();
FreeStyleBuild b = p.scheduleBuild2(0).get();
b.addAction(new SubversionTagAction(3));

HtmlPage page = j.createWebClient().getPage(b);
assertNull(page.getFirstByXPath("//a[contains(text(), 'SVN Revisions')]"));
}

@Test
public void testOrdering() throws Exception {
FreeStyleProject p = j.createFreeStyleProject();
FreeStyleBuild b = p.scheduleBuild2(0).get();

b.addAction(new OtherAction());
b.addAction(new SubversionTagAction(2));

HtmlPage page = j.createWebClient().getPage(b);
String text = page.asText();

// SVN Revisions (section 3) should appear before Other (section 4)
int svnIndex = text.indexOf("SVN Revisions");
int otherIndex = text.indexOf("Other");

if (svnIndex != -1 && otherIndex != -1) {
assertTrue(svnIndex < otherIndex);
}
}
}
Loading