diff --git a/README.md b/README.md index 327130d..6cc17c5 100644 --- a/README.md +++ b/README.md @@ -11,3 +11,27 @@ Make sure your Locale is set to en_US, otherwise the expect script wont work as ## Usage +### Configure RPM signing keys +* Go to Jenkins >> Manage Jenkins >> Configure System +* Go to "RPM Signing Keys" section +* Click on "Add GPG key" button to configure gpg key in jenkins master. + +## Option-1: Using Jenkinsfile (or pipeline) +* Use Jenkins "Pipeline Syntax" (Snippet Generator) to help generate pipeline step. +* Select "rpmSign: [RPMSign] - Sign RPMs" from "Sample Step" drop down. +* Set values for "Sign RPMs" + * "GPG Key" drop down to select value from the configured GPG keys (mentioned in above section). + * "includes" textbox to provide rpm paths (default value is \*\*/target/*.rpm). + * "Cmdline Options" for custom options to be passed to rpm command. + * "Resign?" checkbox (enable it if resigning of rpm is required). +* Click on "Generate Pipeline Script" +* genereated step example: + ``` + rpmSign(rpms: [[gpgKeyName: '121ADA11', includes: 'build/distributions/*.rpm']]]) + ``` + +## Option-2: USing Jenkins job configuration +* Click on "Configure" button of jenkins job. +* Select "[RPMSign] - Sign RPMs" from "Post-build Action" drop down. +* Click on "Add RPM" button. +* Set values for GPG Key, includes, Cmdline Options, and Resign. diff --git a/pom.xml b/pom.xml index 696c3f4..ea3ec42 100644 --- a/pom.xml +++ b/pom.xml @@ -3,7 +3,7 @@ org.jenkins-ci.plugins plugin - 3.4 + 3.26 rpmsign-plugin @@ -14,8 +14,8 @@ UTF-8 - 1.625.3 - 7 + 2.138.2 + 8 diff --git a/src/main/java/jenkins/plugins/rpmsign/GpgKey.java b/src/main/java/jenkins/plugins/rpmsign/GpgKey.java index 2ba0eba..76b9b09 100644 --- a/src/main/java/jenkins/plugins/rpmsign/GpgKey.java +++ b/src/main/java/jenkins/plugins/rpmsign/GpgKey.java @@ -35,8 +35,8 @@ public String getId() { public int getUniqueId() { int result = name != null ? name.hashCode() : 0; - result = 31 * result + (privateKey.getPlainText() != null ? privateKey.getPlainText().hashCode() : 0); - result = 31 * result + (passphrase.getPlainText() != null ? passphrase.getPlainText().hashCode() : 0); + result = 31 * result + privateKey.getPlainText().hashCode(); + result = 31 * result + passphrase.getPlainText().hashCode(); return result; } diff --git a/src/main/java/jenkins/plugins/rpmsign/Rpm.java b/src/main/java/jenkins/plugins/rpmsign/Rpm.java index 494211e..ef8e0b4 100644 --- a/src/main/java/jenkins/plugins/rpmsign/Rpm.java +++ b/src/main/java/jenkins/plugins/rpmsign/Rpm.java @@ -4,15 +4,24 @@ import hudson.model.AbstractDescribableImpl; import hudson.model.Descriptor; import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.DataBoundSetter; public class Rpm extends AbstractDescribableImpl { - private final String gpgKeyName; - private final String includes; - private final String cmdlineOpts; - private final boolean resign; + private String gpgKeyName; + private String includes; + private String cmdlineOpts; + private boolean resign; @DataBoundConstructor + public Rpm() { + this.gpgKeyName = ""; + this.includes = "**/**.rpm"; + this.resign = false; + this.cmdlineOpts = ""; + } + + @Deprecated public Rpm(String gpgKeyName, String includes, String cmdlineOpts, boolean resign) { this.gpgKeyName = gpgKeyName; this.includes = includes; @@ -36,6 +45,26 @@ public boolean isResign() { return resign; } + @DataBoundSetter + public void setGpgKeyName(final String gpgKeyName) { + this.gpgKeyName = gpgKeyName; + } + + @DataBoundSetter + public void setIncludes(final String includes) { + this.includes = includes; + } + + @DataBoundSetter + public void setCmdlineOpts(final String cmdlineOpts) { + this.cmdlineOpts = cmdlineOpts; + } + + @DataBoundSetter + public void setResign(final boolean resign) { + this.resign = resign; + } + @Extension public static class DescriptorImpl extends Descriptor { diff --git a/src/main/java/jenkins/plugins/rpmsign/RpmSignPlugin.java b/src/main/java/jenkins/plugins/rpmsign/RpmSignPlugin.java index c33f592..681fd86 100644 --- a/src/main/java/jenkins/plugins/rpmsign/RpmSignPlugin.java +++ b/src/main/java/jenkins/plugins/rpmsign/RpmSignPlugin.java @@ -1,5 +1,6 @@ package jenkins.plugins.rpmsign; +import hudson.AbortException; import hudson.Extension; import hudson.FilePath; import hudson.Launcher; @@ -8,6 +9,8 @@ import hudson.model.AbstractProject; import hudson.model.BuildListener; import hudson.model.Result; +import hudson.model.Run; +import hudson.model.TaskListener; import hudson.tasks.BuildStepDescriptor; import hudson.tasks.BuildStepMonitor; import hudson.tasks.Publisher; @@ -16,11 +19,14 @@ import hudson.util.FormValidation; import hudson.util.ListBoxModel; import jenkins.model.Jenkins; +import jenkins.tasks.SimpleBuildStep; import net.sf.json.JSONObject; import org.apache.commons.lang.ArrayUtils; import org.apache.commons.lang.StringUtils; +import org.jenkinsci.Symbol; import org.kohsuke.stapler.AncestorInPath; import org.kohsuke.stapler.DataBoundConstructor; +import org.kohsuke.stapler.DataBoundSetter; import org.kohsuke.stapler.QueryParameter; import org.kohsuke.stapler.StaplerRequest; @@ -30,17 +36,23 @@ import java.util.Collections; import java.util.List; import java.util.StringTokenizer; +import javax.annotation.Nonnull; -public class RpmSignPlugin extends Recorder { +public class RpmSignPlugin extends Recorder implements SimpleBuildStep { - private List entries = Collections.emptyList(); + private List rpms; @DataBoundConstructor @SuppressWarnings("unused") public RpmSignPlugin(List rpms) { - this.entries = rpms; - if (this.entries == null) { - this.entries = Collections.emptyList(); + setRpms(rpms); + } + + @DataBoundSetter + public void setRpms(final List rpms) { + this.rpms = rpms; + if (this.rpms == null) { + this.rpms = Collections.emptyList(); } } @@ -49,38 +61,38 @@ public BuildStepMonitor getRequiredMonitorService() { return BuildStepMonitor.NONE; } - private boolean isPerformDeployment(AbstractBuild build) { + private boolean isPerformDeployment(final Run build) { Result result = build.getResult(); return result == null || result.isBetterOrEqualTo(Result.UNSTABLE); } @SuppressWarnings("unused") - public List getEntries() { - return entries; + public List getRpms() { + return rpms; } @Override - public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { + public void perform(@Nonnull final Run build, @Nonnull final FilePath workspace, @Nonnull final Launcher launcher, @Nonnull final TaskListener listener) throws InterruptedException, IOException { if (isPerformDeployment(build)) { listener.getLogger().println("[RpmSignPlugin] - Starting signing RPMs ..."); - for (Rpm rpmEntry : entries) { + for (Rpm rpmEntry : rpms) { StringTokenizer rpmGlobTokenizer = new StringTokenizer(rpmEntry.getIncludes(), ","); GpgKey gpgKey = getGpgKey(rpmEntry.getGpgKeyName()); if (gpgKey == null) { - throw new InterruptedException("No GPG key is available."); + throw new AbortException("No GPG key is available."); } if (gpgKey.getPrivateKey().getPlainText().length() > 0) { listener.getLogger().println("[RpmSignPlugin] - Importing private key"); - importGpgKey(gpgKey.getPrivateKey().getPlainText(), build, launcher, listener); + importGpgKey(gpgKey.getPrivateKey().getPlainText(), build, workspace, launcher, listener); listener.getLogger().println("[RpmSignPlugin] - Imported private key"); } - if (!isGpgKeyAvailable(gpgKey, build, launcher, listener)) { + if (!isGpgKeyAvailable(gpgKey, build, workspace, launcher, listener)) { listener.getLogger().println("[RpmSignPlugin] - Can't find GPG key: " + rpmEntry.getGpgKeyName()); - return false; + throw new AbortException("Can't find GPG key: " + rpmEntry.getGpgKeyName()); } while (rpmGlobTokenizer.hasMoreTokens()) { @@ -88,10 +100,6 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListen listener.getLogger().println("[RpmSignPlugin] - Publishing " + rpmGlob); - FilePath workspace = build.getWorkspace(); - if (workspace == null) { - throw new IllegalStateException("Could not get a workspace."); - } FilePath[] matchedRpms = workspace.list(rpmGlob); if (ArrayUtils.isEmpty(matchedRpms)) { listener.getLogger().println("[RpmSignPlugin] - No RPMs matching " + rpmGlob); @@ -119,7 +127,7 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListen int returnCode = proc.join(); if (returnCode != 0) { listener.getLogger().println(logPrefix + "Failed signing RPM ..."); - return false; + throw new AbortException("Failed signing RPM. returnCode: " + returnCode); } i++; } @@ -131,6 +139,15 @@ public boolean perform(AbstractBuild build, Launcher launcher, BuildListen } else { listener.getLogger().println("[RpmSignPlugin] - Skipping signing RPMs ..."); } + } + + @Override + public boolean perform(AbstractBuild build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { + FilePath workspace = build.getWorkspace(); + if (workspace == null) { + throw new AbortException("Could not get a workspace."); + } + perform(build, workspace, launcher, listener); return true; } @@ -178,12 +195,12 @@ private byte[] createExpectScriptFile(String signCommand, String passphrase) return baos.toByteArray(); } - private void importGpgKey(String privateKey, AbstractBuild build, Launcher launcher, BuildListener listener) throws InterruptedException, IOException { + private void importGpgKey(String privateKey, Run build, final FilePath workspace, Launcher launcher, TaskListener listener) throws InterruptedException, IOException { ArgumentListBuilder command = new ArgumentListBuilder(); command.add("gpg", "--import", "-"); Launcher.ProcStarter ps = launcher.new ProcStarter(); ps = ps.cmds(command).stdout(listener); - ps = ps.pwd(build.getWorkspace()).envs(build.getEnvironment(listener)); + ps = ps.pwd(workspace).envs(build.getEnvironment(listener)); try (InputStream is = new ByteArrayInputStream(privateKey.getBytes(StandardCharsets.UTF_8))) { ps.stdin(is); @@ -192,19 +209,19 @@ private void importGpgKey(String privateKey, AbstractBuild build, Launcher } } - private boolean isGpgKeyAvailable(GpgKey gpgKey, AbstractBuild build, Launcher launcher, BuildListener listener) throws IOException, InterruptedException { + private boolean isGpgKeyAvailable(GpgKey gpgKey, Run build, final FilePath workspace, Launcher launcher, TaskListener listener) throws IOException, InterruptedException { ArgumentListBuilder command = new ArgumentListBuilder(); command.add("gpg", "--fingerprint", gpgKey.getName()); Launcher.ProcStarter ps = launcher.new ProcStarter(); ps = ps.cmds(command).stdout(listener); - ps = ps.pwd(build.getWorkspace()).envs(build.getEnvironment(listener)); + ps = ps.pwd(workspace).envs(build.getEnvironment(listener)); Proc proc = launcher.launch(ps); return proc.join() == 0; } private GpgKey getGpgKey(String gpgKeyName) { - Jenkins jenkins = Jenkins.getInstance(); + Jenkins jenkins = Jenkins.get(); if (jenkins == null) { throw new IllegalStateException("Could not get a Jenkins instance."); } @@ -220,6 +237,7 @@ private GpgKey getGpgKey(String gpgKeyName) { } @Extension + @Symbol("rpmSign") @SuppressWarnings("unused") public static final class GpgSignerDescriptor extends BuildStepDescriptor { @@ -273,16 +291,7 @@ public FormValidation doCheckPassphrase(@AncestorInPath AbstractProject project, } public FormValidation doCheckIncludes(@AncestorInPath AbstractProject project, @QueryParameter String value) throws IOException, InterruptedException { - FilePath workspace = project.getSomeWorkspace(); - if (workspace != null) { - String msg = workspace.validateAntFileMask(value); - if (msg != null) { - return FormValidation.error(msg); - } return FormValidation.ok(); - } else { - return FormValidation.warning(Messages.noworkspace()); - } } } diff --git a/src/main/resources/jenkins/plugins/rpmsign/RpmSignPlugin/config.jelly b/src/main/resources/jenkins/plugins/rpmsign/RpmSignPlugin/config.jelly index eda7365..41743ab 100644 --- a/src/main/resources/jenkins/plugins/rpmsign/RpmSignPlugin/config.jelly +++ b/src/main/resources/jenkins/plugins/rpmsign/RpmSignPlugin/config.jelly @@ -3,7 +3,7 @@ - +