Skip to content

Commit

Permalink
[cli][docker] Better expose version/sha information of builds (#5736)
Browse files Browse the repository at this point in the history
* [cli] Some CLI improvements…

* Introduce --version
* Introduce --help
* Add --sha to version command for short SHA display
* Output Version and SHA details
* In new --version output, display repo and doc site

Additional cleanup to suppress warnings and code quality.

* [docker] Adds labels for metadata

This adds image labels to store metadata on the online and cli docker
images, using standard labels:

* org.opencontainers.image.created
* org.opencontainers.image.revision
* org.opencontainers.image.title
* org.opencontainers.image.version

These can be inspected via 'docker inspect IMAGE_NAME' and may be useful
in tooling/automation or bug reports submitted by users.

For more details on these labels, see:
https://github.com/opencontainers/image-spec/blob/master/annotations.md

* Include version --full for equiv to --version
  • Loading branch information
jimschubert authored Apr 2, 2020
1 parent 4623ec8 commit e14e5fc
Show file tree
Hide file tree
Showing 18 changed files with 297 additions and 64 deletions.
28 changes: 26 additions & 2 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -178,9 +178,33 @@ after_success:
fi;
fi;
## docker: build and push openapi-generator-online to DockerHub
- if [ $DOCKER_HUB_USERNAME ]; then echo "$DOCKER_HUB_PASSWORD" | docker login --username=$DOCKER_HUB_USERNAME --password-stdin && docker build -t $DOCKER_GENERATOR_IMAGE_NAME ./modules/openapi-generator-online && if [ ! -z "$TRAVIS_TAG" ]; then docker tag $DOCKER_GENERATOR_IMAGE_NAME:latest $DOCKER_GENERATOR_IMAGE_NAME:$TRAVIS_TAG; fi && if [ ! -z "$TRAVIS_TAG" ] || [ "$TRAVIS_BRANCH" = "master" ]; then docker push $DOCKER_GENERATOR_IMAGE_NAME && echo "Pushed to $DOCKER_GENERATOR_IMAGE_NAME"; fi; fi
- if [ $DOCKER_HUB_USERNAME ]; then
echo "$DOCKER_HUB_PASSWORD" | docker login --username=$DOCKER_HUB_USERNAME --password-stdin;
export cli_version=$(\mvn -o org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version | grep -v '\[');
export build_date=$(date -u +"%Y-%m-%dT%H:%M:%SZ");
docker build --label=org.opencontainers.image.created=$build_date --label=org.opencontainers.image.title=openapi-generator-online --label=org.opencontainers.image.revision=$TRAVIS_COMMIT --label=org.opencontainers.image.version=$cli_version -t $DOCKER_GENERATOR_IMAGE_NAME ./modules/openapi-generator-online;
if [ ! -z "$TRAVIS_TAG" ]; then
docker tag $DOCKER_GENERATOR_IMAGE_NAME:latest $DOCKER_GENERATOR_IMAGE_NAME:$TRAVIS_TAG;
fi;
if [ ! -z "$TRAVIS_TAG" ] || [ "$TRAVIS_BRANCH" = "master" ]; then
docker push $DOCKER_GENERATOR_IMAGE_NAME && echo "Pushed to $DOCKER_GENERATOR_IMAGE_NAME";
fi;
fi;
## docker: build cli image and push to Docker Hub
- if [ $DOCKER_HUB_USERNAME ]; then echo "$DOCKER_HUB_PASSWORD" | docker login --username=$DOCKER_HUB_USERNAME --password-stdin && cp docker-entrypoint.sh ./modules/openapi-generator-cli && docker build -t $DOCKER_CODEGEN_CLI_IMAGE_NAME ./modules/openapi-generator-cli && if [ ! -z "$TRAVIS_TAG" ]; then docker tag $DOCKER_CODEGEN_CLI_IMAGE_NAME:latest $DOCKER_CODEGEN_CLI_IMAGE_NAME:$TRAVIS_TAG; fi && if [ ! -z "$TRAVIS_TAG" ] || [ "$TRAVIS_BRANCH" = "master" ]; then docker push $DOCKER_CODEGEN_CLI_IMAGE_NAME && echo "Pushed to $DOCKER_CODEGEN_CLI_IMAGE_NAME"; fi; fi
- if [ $DOCKER_HUB_USERNAME ]; then
echo "$DOCKER_HUB_PASSWORD" | docker login --username=$DOCKER_HUB_USERNAME --password-stdin;
cp docker-entrypoint.sh ./modules/openapi-generator-cli;
export cli_version=$(\mvn -o org.apache.maven.plugins:maven-help-plugin:2.1.1:evaluate -Dexpression=project.version | grep -v '\[');
export build_date=$(date -u +"%Y-%m-%dT%H:%M:%SZ");
docker build --label=org.opencontainers.image.created=$build_date --label=org.opencontainers.image.title=openapi-generator-cli --label=org.opencontainers.image.revision=$TRAVIS_COMMIT --label=org.opencontainers.image.version=$cli_version -t $DOCKER_CODEGEN_CLI_IMAGE_NAME ./modules/openapi-generator-cli;
if [ ! -z "$TRAVIS_TAG" ]; then
docker tag $DOCKER_CODEGEN_CLI_IMAGE_NAME:latest $DOCKER_CODEGEN_CLI_IMAGE_NAME:$TRAVIS_TAG;
fi;
if [ ! -z "$TRAVIS_TAG" ] || [ "$TRAVIS_BRANCH" = "master" ]; then
docker push $DOCKER_CODEGEN_CLI_IMAGE_NAME;
echo "Pushed to $DOCKER_CODEGEN_CLI_IMAGE_NAME";
fi;
fi;
## publish latest website, variables below are secure environment variables which are unavailable to PRs from forks.
- if [ "$TRAVIS_BRANCH" = "master" ] && [ -z $TRAVIS_TAG ] && [ "$TRAVIS_PULL_REQUEST" == "false" ]; then
cd website;
Expand Down
16 changes: 16 additions & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,22 @@ The most commonly used openapi-generator-cli commands are:

See 'openapi-generator-cli help <command>' for more information on a specific
command.
```

## version

The version command provides version information, returning either the semver version by default or the git sha when passed `--sha`.

```bash
NAME
openapi-generator-cli version - Show version information

SYNOPSIS
openapi-generator-cli version [--sha]

OPTIONS
--sha
Git commit SHA version

```

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.openapitools.codegen;

public class Constants {
private Constants(){ }

public static final String CLI_NAME = "openapi-generator-cli";
public static final String GIT_REPO = "https://github.com/openapitools/openapi-generator";
public static final String SITE = "https://openapi-generator.tech/";
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,38 +18,38 @@
package org.openapitools.codegen;

import io.airlift.airline.Cli;
import io.airlift.airline.Help;
import io.airlift.airline.ParseArgumentsUnexpectedException;
import io.airlift.airline.ParseOptionMissingException;
import io.airlift.airline.ParseOptionMissingValueException;
import org.openapitools.codegen.cmd.*;

import java.util.Locale;

import static org.openapitools.codegen.Constants.CLI_NAME;

/**
* User: lanwen Date: 24.03.15 Time: 17:56
* <p>
* Command line interface for OpenAPI Generator use `openapi-generator-cli.jar help` for more info
*
* @since 2.1.3-M1
*/
public class OpenAPIGenerator {

public static void main(String[] args) {
String version = Version.readVersionFromResources();
Cli.CliBuilder<Runnable> builder =
Cli.<Runnable>builder("openapi-generator-cli")
BuildInfo buildInfo = new BuildInfo();
Cli.CliBuilder<OpenApiGeneratorCommand> builder =
Cli.<OpenApiGeneratorCommand>builder(CLI_NAME)
.withDescription(
String.format(
Locale.ROOT,
"OpenAPI generator CLI (version %s).",
version))
.withDefaultCommand(ListGenerators.class)
"OpenAPI Generator CLI %s (%s).",
buildInfo.getVersion(),
buildInfo.getSha()))
.withDefaultCommand(HelpCommand.class)
.withCommands(
ListGenerators.class,
Generate.class,
Meta.class,
Help.class,
HelpCommand.class,
ConfigHelp.class,
Validate.class,
Version.class,
Expand All @@ -60,7 +60,7 @@ public static void main(String[] args) {
try {
builder.build().parse(args).run();

// If CLI is run without a command, consider this an error. This exists after initial parse/run
// If CLI runs without a command, consider this an error. This exists after initial parse/run
// so we can present the configured "default command".
// We can check against empty args because unrecognized arguments/commands result in an exception.
// This is useful to exit with status 1, for example, so that misconfigured scripts fail fast.
Expand All @@ -71,10 +71,10 @@ public static void main(String[] args) {
System.exit(1);
}
} catch (ParseArgumentsUnexpectedException e) {
System.err.printf(Locale.ROOT,"[error] %s%n%nSee 'openapi-generator help' for usage.%n", e.getMessage());
System.err.printf(Locale.ROOT, "[error] %s%n%nSee '%s help' for usage.%n", e.getMessage(), CLI_NAME);
System.exit(1);
} catch (ParseOptionMissingException | ParseOptionMissingValueException e) {
System.err.printf(Locale.ROOT,"[error] %s%n", e.getMessage());
System.err.printf(Locale.ROOT, "[error] %s%n", e.getMessage());
System.exit(1);
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
package org.openapitools.codegen.cmd;

import java.io.IOException;
import java.io.InputStream;
import java.time.OffsetDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.DateTimeParseException;
import java.util.Locale;
import java.util.Properties;

import static org.openapitools.codegen.Constants.*;

/**
* Presents build-time information
*/
@SuppressWarnings({"java:S108"})
public class BuildInfo {
private static final String VERSION_PLACEHOLDER = "${project.version}";
private static final String UNSET = "unset";
private static final String UNKNOWN = "unknown";

private static final Properties properties = new Properties();

static {
try (InputStream is = BuildInfo.class.getResourceAsStream("/version.properties")) {
Properties versionProps = new Properties();
versionProps.load(is);
properties.putAll(versionProps);
} catch (IOException ignored) {
}
try (InputStream is = BuildInfo.class.getResourceAsStream("/openapi-generator-git.properties")) {
Properties gitProps = new Properties();
gitProps.load(is);
properties.putAll(gitProps);
} catch (IOException ignored) {
}
}

/**
* Gets the version of the toolset.
*
* @return A semver string
*/
public String getVersion() {
String version = (String) properties.getOrDefault("version", UNKNOWN);
if (VERSION_PLACEHOLDER.equals(version)) {
return UNSET;
} else {
return version;
}
}

/**
* Gets the git commit SHA1 hash. Useful for differentiating between SNAPSHOT builds.
*
* @return A short git SHA
*/
public String getSha() {
return (String) properties.getOrDefault("git.commit.id.abbrev", UNKNOWN);
}

/**
* Gets the time when this tool was built.
*
* @return The time as {@link OffsetDateTime}, or {@link OffsetDateTime#MIN} if metadata cannot be parsed.
*/
public OffsetDateTime getBuildTime() {
try {
String time = (String) properties.getOrDefault("git.build.time", "");
return OffsetDateTime.parse(time, DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ssZ", Locale.ROOT));
} catch (DateTimeParseException e) {
return OffsetDateTime.MIN;
}
}

/**
* Gets the full version display text, as one would expect from a '--version' CLI option
*
* @return Human-readable version display information
*/
public String versionDisplayText() {
StringBuilder sb = new StringBuilder(CLI_NAME);
sb.append(" ").append(this.getVersion()).append(System.lineSeparator());
sb.append(" commit : ").append(this.getSha()).append(System.lineSeparator());
sb.append(" built : ").append(this.getBuildTime().format(DateTimeFormatter.ISO_OFFSET_DATE_TIME)).append(System.lineSeparator());
sb.append(" source : ").append(GIT_REPO).append(System.lineSeparator());
sb.append(" docs : ").append(SITE).append(System.lineSeparator());
return sb.toString();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,9 @@
import static com.google.common.collect.Lists.newArrayList;
import static io.airlift.airline.ParserUtil.createInstance;

@SuppressWarnings({"java:S106"})
@Command(name = "completion", description = "Complete commands (for using in tooling such as Bash Completions).", hidden = true)
public class CompletionCommand
public class CompletionCommand extends OpenApiGeneratorCommand
implements Runnable, Callable<Void> {
private static final Map<Context, Class<? extends Suggester>> BUILTIN_SUGGESTERS = ImmutableMap.<Context, Class<? extends Suggester>>builder()
.put(Context.GLOBAL, GlobalSuggester.class)
Expand Down Expand Up @@ -95,7 +96,7 @@ public Iterable<String> generateSuggestions() {
}

@Override
public void run() {
void execute() {
System.out.println(Joiner.on("\n").join(generateSuggestions()));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,9 @@
import static org.apache.commons.lang3.StringEscapeUtils.escapeHtml4;
import static org.apache.commons.lang3.StringUtils.isEmpty;

@SuppressWarnings("unused")
@SuppressWarnings({"unused","java:S106"})
@Command(name = "config-help", description = "Config help for chosen lang")
public class ConfigHelp implements Runnable {
public class ConfigHelp extends OpenApiGeneratorCommand {

private static final Logger LOGGER = LoggerFactory.getLogger(Generate.class);

Expand Down Expand Up @@ -91,7 +91,7 @@ public class ConfigHelp implements Runnable {
private String newline = System.lineSeparator();

@Override
public void run() {
public void execute() {
if (isEmpty(generatorName)) {
LOGGER.error("[error] A generator name (--generator-name / -g) is required.");
System.exit(1);
Expand Down Expand Up @@ -320,6 +320,7 @@ private void generateYamlSample(StringBuilder sb, CodegenConfig config) {
}
}

@SuppressWarnings({"java:S1117"})
private void generatePlainTextHelp(StringBuilder sb, CodegenConfig config) {
sb.append(newline).append("CONFIG OPTIONS");
if (Boolean.TRUE.equals(namedHeader)) {
Expand Down Expand Up @@ -418,6 +419,7 @@ private void generatePlainTextHelp(StringBuilder sb, CodegenConfig config) {
}
}

@SuppressWarnings({"java:S1117"})
private void writePlainTextFromMap(
StringBuilder sb,
Map<String, String> map,
Expand Down Expand Up @@ -449,6 +451,7 @@ private void writePlainTextFromMap(
}
}

@SuppressWarnings({"java:S1117"})
private void writePlainTextFromArray(StringBuilder sb, String[] arr, String optIndent) {
if (arr.length > 0) {
// target a width of 20, then take the max up to 40.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,9 @@
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@SuppressWarnings({"java:S106"})
@Command(name = "generate", description = "Generate code with the specified generator.")
public class Generate implements Runnable {
public class Generate extends OpenApiGeneratorCommand {

CodegenConfigurator configurator;
Generator generator;
Expand Down Expand Up @@ -244,7 +245,7 @@ public class Generate implements Runnable {
private Boolean minimalUpdate;

@Override
public void run() {
public void execute() {
if (logToStderr != null) {
LoggerContext lc = (LoggerContext) LoggerFactory.getILoggerFactory();
Stream.of(Logger.ROOT_LOGGER_NAME, "io.swagger", "org.openapitools")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;

@SuppressWarnings({"unused", "MismatchedQueryAndUpdateOfCollection"})
@SuppressWarnings({"unused", "MismatchedQueryAndUpdateOfCollection", "java:S106"})
@Command(name = "batch", description = "Generate code in batch via external configs.", hidden = true)
public class GenerateBatch implements Runnable {
public class GenerateBatch extends OpenApiGeneratorCommand {

private static final Logger LOGGER = LoggerFactory.getLogger(GenerateBatch.class);

Expand Down Expand Up @@ -89,7 +89,7 @@ public class GenerateBatch implements Runnable {
* @see Thread#run()
*/
@Override
public void run() {
public void execute() {
if (configs.size() < 1) {
LOGGER.error("No configuration file inputs specified");
System.exit(1);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package org.openapitools.codegen.cmd;

import io.airlift.airline.Option;

import static io.airlift.airline.OptionType.GLOBAL;

public class GlobalOptions {
@Option(type = GLOBAL, name = "--version", description = "Display full version output", hidden = true)
public boolean version;

@Option(type = GLOBAL, name = "--help", description = "Display help about the tool", hidden = true)
public boolean help;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package org.openapitools.codegen.cmd;

import io.airlift.airline.Command;
import io.airlift.airline.Help;

import javax.inject.Inject;

@Command(name = "help", description = "Display help information about openapi-generator")
public class HelpCommand extends OpenApiGeneratorCommand {

@Inject
public Help help;

@Override
public void execute() {
help.call();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,9 @@
import java.util.stream.Collectors;

// NOTE: List can later have subcommands such as list languages, list types, list frameworks, etc.
@SuppressWarnings({"java:S106"})
@Command(name = "list", description = "Lists the available generators")
public class ListGenerators implements Runnable {
public class ListGenerators extends OpenApiGeneratorCommand {

@Option(name = {"-s", "--short" }, description = "shortened output (suitable for scripting)")
private Boolean shortened = false;
Expand All @@ -34,7 +35,7 @@ public class ListGenerators implements Runnable {
private String include = "stable,beta,experimental";

@Override
public void run() {
public void execute() {
List<CodegenConfig> generators = new ArrayList<>();
List<Stability> stabilities = Arrays.asList(Stability.values());

Expand Down
Loading

0 comments on commit e14e5fc

Please sign in to comment.