Skip to content
Open
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
10 changes: 9 additions & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,15 @@
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>script-security</artifactId>
</dependency>

<dependency>
<groupId>io.jenkins.plugins</groupId>
<artifactId>markdown-formatter</artifactId>
<version>95.v17a_965e696ee</version>
Copy link
Member

Choose a reason for hiding this comment

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

Not in bom? I would omit this one and stick to the more standard antisamy-markup-formatter.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The antisamy-markup-formatter can only sanitize html and is not able to convert markdown to html. So without this dependency it is not possible to use markdown files for documentation (and I think we should avoid directly setting a dependency to the commonmark java library when we have a plugin that provides it). Maybe we can add it to the bom

Copy link
Member

Choose a reason for hiding this comment

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

without this dependency it is not possible to use markdown files for documentation

Right, I was proposing to just drop that feature.

Maybe we can add it to the bom

You could. I have no idea how well supported this plugin is.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Mark Waite is maintaining the plugin, so should be well supported

Copy link
Contributor

Choose a reason for hiding this comment

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

I really like the markdown formatter plugin and plan to continue maintaining it. I have it installed on my local Jenkins instances and use it regularly

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm happy to submit the plugin to the BOM. It has less than 1000 installations but it is very helpful and has worked well in all the cases where I've used it.

Copy link
Contributor

Choose a reason for hiding this comment

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

Submitted for possible inclusion in the plugin bill of materials:

Copy link
Contributor

Choose a reason for hiding this comment

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

The markdown formatter plugin is included in plugin bill of materials 2961.v1f472390972e (April 8, 2024).

</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>antisamy-markup-formatter</artifactId>
Copy link
Member

Choose a reason for hiding this comment

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

Needs to be optional, and then use @OptionalExtension.

</dependency>
<dependency>
<groupId>org.jenkins-ci.plugins</groupId>
<artifactId>variant</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
package org.jenkinsci.plugins.workflow.cps.global;

import hudson.Extension;
import hudson.ExtensionPoint;
import hudson.markup.MarkupFormatter;
import hudson.markup.RawHtmlMarkupFormatter;
import io.jenkins.plugins.MarkdownFormatter;
import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.List;
import jenkins.model.Jenkins;
import org.apache.commons.io.FileUtils;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;

/**
* Formatter for the help of a library step.
* Allows to use markdown and html for the help. Avoids using the globally configured formatter
* that might not fit.
*/
@Restricted(NoExternalUse.class)
public abstract class HelpFormatter implements ExtensionPoint {

public boolean isApplicable(String file) {
return getFile(file).exists();
}

private File getFile(String file) {
return new File(file + getExtension());
}
protected abstract String getExtension();

protected abstract MarkupFormatter getFormatter();

public final String translate(String file) throws IOException {
return "<div class=\"library-step-help\">\n" +
getFormatter().translate(readFile(file)) +
"</div>";
}

private String readFile(String file) throws IOException {
// Util.escape translates \n but not \r, and we do not know what platform the library will
// be checked out on:
return FileUtils.readFileToString(getFile(file), StandardCharsets.UTF_8).replace("\r\n", "\n");
}

public static List<HelpFormatter> all() {
return Jenkins.get().getExtensionList(HelpFormatter.class);
}

@Extension(ordinal = 99999)
public static class MarkDown extends HelpFormatter {

private static final String EXTENSION = ".md";

@Override
protected String getExtension() {
return EXTENSION;
}

@Override
protected MarkupFormatter getFormatter() {
return new MarkdownFormatter();
}
}

/**
* formatter for html files based on the OWASP formatter
*/
@Extension(ordinal = 50000)
public static class HtmlFormatter extends HelpFormatter {

private static final String EXTENSION = ".html";

@Override
protected String getExtension() {
return EXTENSION;
}

protected MarkupFormatter getFormatter() {
return new RawHtmlMarkupFormatter(true);
}
}

/**
* Formatter for txt files, uses the globally configured formatter
*/
@Extension(ordinal = -99999)
public static class Default extends HelpFormatter {

private static final String EXTENSION = ".txt";

@Override
protected String getExtension() {
return EXTENSION;
}

@Override
protected MarkupFormatter getFormatter() {
return Jenkins.get().getMarkupFormatter();
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package org.jenkinsci.plugins.workflow.cps.global;

import groovy.lang.Binding;
import java.nio.charset.StandardCharsets;
import org.apache.commons.io.FileUtils;
import org.codehaus.groovy.control.MultipleCompilationErrorsException;
import org.jenkinsci.plugins.workflow.cps.CpsCompilationErrorsException;
import org.jenkinsci.plugins.workflow.cps.CpsScript;
Expand All @@ -11,9 +9,7 @@

import edu.umd.cs.findbugs.annotations.CheckForNull;
import edu.umd.cs.findbugs.annotations.NonNull;
import java.io.File;
import java.io.IOException;
import jenkins.model.Jenkins;

/**
* Global variable backed by user-supplied script.
Expand All @@ -22,10 +18,10 @@
*/
// not @Extension because these are instantiated programmatically
public class UserDefinedGlobalVariable extends GlobalVariable {
private final File help;
private final String help;
private final String name;

public UserDefinedGlobalVariable(String name, File help) {
public UserDefinedGlobalVariable(String name, String help) {
this.name = name;
this.help = help;
}
Expand Down Expand Up @@ -69,12 +65,12 @@ public Object getValue(@NonNull CpsScript script) throws Exception {
* Loads help from user-defined file, if available.
*/
public @CheckForNull String getHelpHtml() throws IOException {
if (!help.exists()) return null;

return Jenkins.get().getMarkupFormatter().translate(
FileUtils.readFileToString(help, StandardCharsets.UTF_8).
// Util.escape translates \n but not \r, and we do not know what platform the library will be checked out on:
replace("\r\n", "\n"));
for (HelpFormatter formatter: HelpFormatter.all()) {
if (formatter.isApplicable(help)) {
return formatter.translate(help);
}
}
return null;
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -358,7 +358,7 @@ private static String readResource(FilePath file, @CheckForNull String encoding)
List<GlobalVariable> vars = new ArrayList<>();
for (LibraryRecord library : action.getLibraries()) {
for (String variable : library.variables) {
vars.add(new UserDefinedGlobalVariable(variable, new File(run.getRootDir(), "libs/" + library.getDirectoryName() + "/vars/" + variable + ".txt")));
vars.add(new UserDefinedGlobalVariable(variable, new File(run.getRootDir(), "libs/" + library.getDirectoryName() + "/vars/" + variable).getAbsolutePath()));
}
}
return vars;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ protected final void doRetrieve(String name, boolean changelog, @NonNull SCM scm
}
// Cannot add WorkspaceActionImpl to private CpsFlowExecution.flowStartNodeActions; do we care?
// Copy sources with relevant files from the checkout:
lease.path.child(libraryPath).copyRecursiveTo("src/**/*.groovy,vars/*.groovy,vars/*.txt,resources/", excludes, target);
lease.path.child(libraryPath).copyRecursiveTo("src/**/*.groovy,vars/*.groovy,vars/*.txt,vars/*.md,vars/*.html,resources/", excludes, target);
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -245,7 +245,25 @@ public class LibraryAdderTest {
r.assertLogContains("something special", b);
GlobalVariable var = GlobalVariable.byName("myecho", b);
assertNotNull(var);
assertEquals("Says something very special!", ((UserDefinedGlobalVariable) var).getHelpHtml());
assertEquals("<div class=\"library-step-help\">\nSays something very special!</div>", ((UserDefinedGlobalVariable) var).getHelpHtml());
}

@Test public void globalVariableWithMarkdown() throws Exception {
sampleRepo.init();
sampleRepo.write("vars/myecho.groovy", "def call() {echo 'something special'}");
sampleRepo.write("vars/myecho.md", "# Says something very special!");
sampleRepo.git("add", "vars");
sampleRepo.git("commit", "--message=init");
GlobalLibraries.get().setLibraries(Collections.singletonList(
new LibraryConfiguration("echo-utils",
new SCMSourceRetriever(new GitSCMSource(null, sampleRepo.toString(), "", "*", "", true)))));
WorkflowJob p = r.jenkins.createProject(WorkflowJob.class, "p");
p.setDefinition(new CpsFlowDefinition("@Library('echo-utils@master') import myecho; myecho()", true));
WorkflowRun b = r.buildAndAssertSuccess(p);
r.assertLogContains("something special", b);
GlobalVariable var = GlobalVariable.byName("myecho", b);
assertNotNull(var);
assertEquals("<div class=\"library-step-help\">\n<h1>Says something very special!</h1>\n</div>", ((UserDefinedGlobalVariable) var).getHelpHtml());
}

@Test public void dynamicLibraries() throws Exception {
Expand Down