Skip to content

Commit

Permalink
Merge pull request #122 from philippart-s/add-current-version-step
Browse files Browse the repository at this point in the history
feat: Add currentVersion step support
  • Loading branch information
ADI10HERO authored Oct 18, 2021
2 parents 1f7f035 + 7c1cde2 commit e573748
Show file tree
Hide file tree
Showing 11 changed files with 388 additions and 93 deletions.
14 changes: 10 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@

## Introduction

This plugin can be used to determine the next release version based on previous tags and the commit messages used.
:warning: By default only [annotated tag](https://git-scm.com/book/en/v2/Git-Basics-Tagging) are supported, to support non annotated tag you must use an option to activate this feature (see below).:warning:
It calculates the version number based on the format of the commit message.
This plugin can be used to determine the next release version based on previous tags and the commit messages used.
It can also be used to get the current version.
:warning: By default only [annotated tag](https://git-scm.com/book/en/v2/Git-Basics-Tagging) are supported, to support non annotated tag you must use an option to activate this feature (see below).:warning:
It calculates the version number based on the format of the commit message.
The commit message format used is [conventional commits](https://www.conventionalcommits.org/en/v1.0.0/).

## Getting started
Expand All @@ -24,12 +25,14 @@ pipeline {
agent any
environment {
CURRENT_VERSION = currentVersion()
NEXT_VERSION = nextVersion()
}
stages {
stage('Hello') {
steps {
echo "current vesion = ${CURRENT_VERSION}"
echo "next version = ${NEXT_VERSION}"
}
}
Expand All @@ -42,6 +45,8 @@ pipeline {
def NEXT_VERSION
node {
stage('Get next version ...') {
CURRENT_VERSION=currentVersion()
echo "Current version: $CURRENT_VERSION"
NEXT_VERSION=nextVersion()
echo "Next version : $NEXT_VERSION"
}
Expand All @@ -54,7 +59,8 @@ node {

## Using Optional Parameters

The plugin provides provision to use optional parameters for support of build metadata, pre-release information, settnig the start tag, etc.
The plugin provides provision to use optional parameters for support of build metadata, pre-release information, settings the start tag, etc.
:warning: These parameters are only for the _nextVersion_ step ! :warning:

### Build Metadata

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package io.jenkins.plugins.conventionalcommits;

import com.github.zafarkhaja.semver.Version;
import com.google.common.collect.ImmutableSet;
import hudson.Extension;
import hudson.FilePath;
import hudson.model.TaskListener;
import io.jenkins.plugins.conventionalcommits.utils.CurrentVersion;
import io.jenkins.plugins.conventionalcommits.utils.TagsHelper;
import java.io.File;
import java.io.IOException;
import java.util.Set;
import javax.annotation.Nonnull;
import org.jenkinsci.plugins.workflow.steps.Step;
import org.jenkinsci.plugins.workflow.steps.StepContext;
import org.jenkinsci.plugins.workflow.steps.StepDescriptor;
import org.jenkinsci.plugins.workflow.steps.StepExecution;
import org.jenkinsci.plugins.workflow.steps.SynchronousStepExecution;
import org.kohsuke.stapler.DataBoundConstructor;

/**
* Step to get the current version of the project.
* Example :
* <code>def CURRENT_VERSION = currentVersion()</code>
*/
public class CurrentVersionStep extends Step {

@DataBoundConstructor
public CurrentVersionStep() {
// empty constructor, for now...
}

@Override
public StepExecution start(StepContext stepContext) throws Exception {
return new Execution(stepContext);
}

/**
* This class extends Step Execution class, contains the run method.
* This is the main entry point of the step.
*/
public static class Execution extends SynchronousStepExecution<String> {

private static final long serialVersionUID = 1L;

/**
* Constructor with fields initialisation.
*
* @param context Jenkins context
*/
protected Execution(@Nonnull StepContext context) {
super(context);
}

/**
* Entry point of the step.
*
* @return The current version of the project.
* @throws Exception If errors occurs ;).
*/
@Override
protected String run() throws Exception {
FilePath workspace = getContext().get(FilePath.class);
if (workspace == null) {
throw new IOException("no workspace");
}

// if the workspace is remote then lets make a local copy
if (workspace.isRemote()) {
throw new IOException("workspace.isRemote(), not entirely sure what to do here...");
} else {
File dir = new File(workspace.getRemote());
String latestTag = TagsHelper.getLatestTag(getContext(), dir, false);

Version currentVersion =
new CurrentVersion()
.getCurrentVersion(
dir, latestTag, getContext().get(TaskListener.class).getLogger());

return currentVersion.toString();
}
}
}

/**
* This Class implements the abstract class StepDescriptor.
*/
@Extension
public static class DescriptorImpl extends StepDescriptor {

@Override
public String getDisplayName() {
return "determine the current version from the conventional commit history";
}

@Override
public Set<? extends Class<?>> getRequiredContext() {
return ImmutableSet.of(TaskListener.class, FilePath.class);
}

@Override
public String getFunctionName() {
return "currentVersion";
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
package io.jenkins.plugins.conventionalcommits;

import static io.jenkins.plugins.conventionalcommits.process.ProcessUtil.execute;
import static io.jenkins.plugins.conventionalcommits.utils.TagsHelper.getLatestTag;

import com.github.zafarkhaja.semver.Version;
import com.google.common.collect.ImmutableSet;
import com.google.common.io.LineReader;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import hudson.Extension;
import hudson.FilePath;
Expand All @@ -11,12 +13,8 @@
import io.jenkins.plugins.conventionalcommits.utils.WriteVersion;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import javax.annotation.Nonnull;
import org.apache.commons.lang.StringUtils;
Expand All @@ -28,7 +26,9 @@
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.DataBoundSetter;

/** Base class of the plugin. */
/**
* Base class of the plugin.
*/
public class NextVersionStep extends Step {

private String outputFormat;
Expand All @@ -49,48 +49,6 @@ public NextVersionStep() {
// empty constructor, for now...
}

private static String execute(File dir, String... commandAndArgs)
throws IOException, InterruptedException {
ProcessBuilder builder = new ProcessBuilder().directory(dir).command(commandAndArgs);

Process process = builder.start();
int exitCode = process.waitFor();
if (exitCode != 0) {
String stderr = stdout(process.getErrorStream());
throw new IOException(
"executing '"
+ String.join(" ", commandAndArgs)
+ "' failed in '"
+ dir
+ "' with exit code"
+ exitCode
+ " and error "
+ stderr);
}
return stdout(process.getInputStream());
}

/**
* Reads data from stdout.
*
* @param in InputStream object.
* @return read data.
* @throws IOException If an error occur reading files.
*/
public static String stdout(InputStream in) throws IOException {
StringBuilder builder = new StringBuilder();
LineReader reader = new LineReader(new InputStreamReader(in, StandardCharsets.UTF_8));
while (true) {
String line = reader.readLine();
if (line == null) {
break;
}
builder.append(line);
builder.append(System.getProperty("line.separator"));
}
return builder.toString();
}

public String getOutputFormat() {
return outputFormat;
}
Expand Down Expand Up @@ -177,7 +135,9 @@ public StepExecution start(StepContext stepContext) throws Exception {
stepContext);
}

/** This class extends Step Execution class, contains the run method. */
/**
* This class extends Step Execution class, contains the run method.
*/
public static class Execution extends SynchronousStepExecution<String> {

private static final long serialVersionUID = 1L;
Expand Down Expand Up @@ -230,15 +190,15 @@ public static class Execution extends SynchronousStepExecution<String> {
/**
* Constructor with fields initialisation.
*
* @param outputFormat Output format for the next version
* @param startTag Git tag
* @param buildMetadata Add meta date to the version.
* @param writeVersion Should write the new version in the file.
* @param preRelease Pre release information to add
* @param preservePreRelease Keep existing prerelease information or not
* @param outputFormat Output format for the next version
* @param startTag Git tag
* @param buildMetadata Add meta date to the version.
* @param writeVersion Should write the new version in the file.
* @param preRelease Pre release information to add
* @param preservePreRelease Keep existing prerelease information or not
* @param incrementPreRelease Increment prerelease information or not
* @param nonAnnotatedTag Should use or non annotated tags
* @param context Jenkins context
* @param nonAnnotatedTag Should use or non annotated tags
* @param context Jenkins context
*/
protected Execution(
String outputFormat,
Expand All @@ -261,35 +221,6 @@ protected Execution(
this.nonAnnotatedTag = nonAnnotatedTag;
}

/**
* Return the last tag.
*
* @param dir The project's directory.
* @param includeNonAnnotatedTags If true include the non annotated tag.
*
* @return The last tag of the project.
*/
private String getLatestTag(File dir, boolean includeNonAnnotatedTags)
throws InterruptedException, IOException {
Objects.requireNonNull(dir, "Directory is mandatory");
String latestTag = "";
try {
if (includeNonAnnotatedTags) {
latestTag = execute(dir, "git", "tag", "-l").trim();
latestTag = latestTag.substring(latestTag.lastIndexOf("\n") + 1);
} else {
latestTag = execute(dir, "git", "describe", "--abbrev=0", "--tags").trim();
}
} catch (IOException exp) {
if (exp.getMessage().contains("No names found, cannot describe anything.")) {
getContext().get(TaskListener.class).getLogger().println("No tags found");
}
}

getContext().get(TaskListener.class).getLogger().println("Current Tag is: " + latestTag);
return latestTag;
}

@Override
protected String run() throws Exception {
FilePath workspace = getContext().get(FilePath.class);
Expand All @@ -302,7 +233,7 @@ protected String run() throws Exception {
throw new IOException("workspace.isRemote(), not entirely sure what to do here...");
} else {
File dir = new File(workspace.getRemote());
String latestTag = getLatestTag(dir, nonAnnotatedTag);
String latestTag = getLatestTag(getContext(), dir, nonAnnotatedTag);

Version currentVersion =
new CurrentVersion()
Expand Down Expand Up @@ -362,7 +293,9 @@ protected String run() throws Exception {
}
}

/** This Class implements the abstract class StepDescriptor. */
/**
* This Class implements the abstract class StepDescriptor.
*/
@Extension
public static class DescriptorImpl extends StepDescriptor {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package io.jenkins.plugins.conventionalcommits.process;

import com.google.common.io.LineReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;

/**
* Class to execute some CLI commands.
*/
public class ProcessUtil {

/**
* Reads data from stdout.
*
* @param in InputStream object.
* @return read data.
* @throws IOException If an error occur reading files.
*/
private static String stdout(InputStream in) throws IOException {
StringBuilder builder = new StringBuilder();
LineReader reader = new LineReader(new InputStreamReader(in, StandardCharsets.UTF_8));
while (true) {
String line = reader.readLine();
if (line == null) {
break;
}
builder.append(line);
builder.append(System.getProperty("line.separator"));
}
return builder.toString();
}

/**
* Execute a CLI command using ProcessBuilder.
*
* @param dir Directory where execute the command.
* @param commandAndArgs Command and parameters of the command.
* @return THe output of the command.
* @throws IOException If an error occur accessing files.
* @throws InterruptedException If the command is interrupted.
*/
public static String execute(File dir, String... commandAndArgs)
throws IOException, InterruptedException {
ProcessBuilder builder = new ProcessBuilder().directory(dir).command(commandAndArgs);

Process process = builder.start();
int exitCode = process.waitFor();
if (exitCode != 0) {
String stderr = stdout(process.getErrorStream());
throw new IOException(
"executing '"
+ String.join(" ", commandAndArgs)
+ "' failed in '"
+ dir
+ "' with exit code"
+ exitCode
+ " and error "
+ stderr);
}
return stdout(process.getInputStream());
}
}
Loading

0 comments on commit e573748

Please sign in to comment.