org.jboss.pnc.bacon.pig.Pig Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source.
* Copyright 2018 Red Hat, Inc., and individual contributors
* as indicated by the @author tags.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jboss.pnc.bacon.pig;
import lombok.extern.slf4j.Slf4j;
import org.jboss.pnc.bacon.common.ObjectHelper;
import org.jboss.pnc.bacon.common.cli.JSONCommandHandler;
import org.jboss.pnc.bacon.common.exception.FatalException;
import org.jboss.pnc.bacon.config.Config;
import org.jboss.pnc.bacon.config.PigConfig;
import org.jboss.pnc.bacon.config.Validate;
import org.jboss.pnc.bacon.pig.impl.PigContext;
import org.jboss.pnc.bacon.pig.impl.config.GroupBuildInfo;
import org.jboss.pnc.bacon.pig.impl.config.PigConfiguration;
import org.jboss.pnc.bacon.pig.impl.out.PigBuildOutput;
import org.jboss.pnc.bacon.pig.impl.out.PigReleaseOutput;
import org.jboss.pnc.bacon.pig.impl.out.PigRunOutput;
import org.jboss.pnc.bacon.pig.impl.pnc.ImportResult;
import org.jboss.pnc.bacon.pig.impl.repo.RepositoryData;
import org.jboss.pnc.bacon.pig.impl.utils.AlignmentType;
import org.jboss.pnc.bacon.pig.impl.utils.FileDownloadUtils;
import org.jboss.pnc.bacon.pnc.common.ParameterChecker;
import org.jboss.pnc.enums.RebuildMode;
import picocli.CommandLine.Command;
import picocli.CommandLine.Option;
import picocli.CommandLine.Parameters;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Collections;
import java.util.Map;
import java.util.Scanner;
import java.util.concurrent.Callable;
/**
* @author Michal Szynkiewicz, [email protected]
* Date: 12/13/18
*/
@Command(
name = "pig",
description = "PiG tool",
subcommands = {
Pig.Configure.class,
Pig.Cancel.class,
Pig.Build.class,
Pig.Run.class,
Pig.GenerateRepository.class,
Pig.GenerateLicenses.class,
Pig.GenerateJavadocs.class,
Pig.GenerateSources.class,
Pig.GenerateSharedContentAnalysis.class,
Pig.GenerateDocuments.class,
Pig.Release.class,
Pig.PreProcessYaml.class,
Pig.TriggerAddOns.class,
PigExport.class })
public class Pig {
private Pig() {
}
public static final String REBUILD_MODE_DESC = "The build mode EXPLICIT_DEPENDENCY_CHECK, IMPLICIT_DEPENDENCY_CHECK, FORCE. Defaults to EXPLICIT";
public static final String REBUILD_MODE_DEFAULT = "EXPLICIT_DEPENDENCY_CHECK";
public static final String REBUILD_MODE = "--mode";
public static final String TEMP_BUILD_TIME_STAMP = "--tempBuildTimeStamp";
public static final String TEMP_BUILD_TIME_STAMP_DEFAULT = "false";
public static final String TEMP_BUILD_TIME_STAMP_DESC = "NOT SUPPORTED - If specified, artifacts from temporary builds will have timestamp in versions";
public static final String REMOVE_M2_DUPLICATES_DESC = "If enabled, only the newest versions of each of the dependencies (groupId:artifactId) "
+ "are kept in the generated repository zip";
public static final String REMOVE_M2_DUPLICATES = "--removeGeneratedM2Dups";
public static final String SKIP_BRANCH_CHECK = "--skipBranchCheck";
public static final String SKIP_BRANCH_CHECK_DEFAULT = "false";
public static final String SKIP_BRANCH_CHECK_DESC = "If set to true, pig won't try to determine if the branch that is used to build from is modified. "
+ "Branch modification check takes a lot of time, if you use tag, this switch can speed up the build.";
public abstract static class PigCommand extends JSONCommandHandler implements Callable {
@Parameters(description = "Directory containing the Pig configuration file")
String configDir;
@Option(
names = { "-t", "--tempBuild" },
defaultValue = "false",
description = "If specified, PNC will perform temporary builds.")
boolean tempBuild;
@Option(
names = { "--tempBuildAlignment" },
description = "Alignment preference for temporary build. Options: (default)TEMPORARY, PERSISTENT")
AlignmentType tempAlign = AlignmentType.TEMPORARY;
@Option(
names = "--releaseStorageUrl",
description = "Location of the release storage, typically on rcm-guest staging. Required for auto-incremented milestones, e.g. DR*")
protected String releaseStorageUrl;
@Option(
names = "--clean",
defaultValue = "false",
description = "If enabled, the pig execution will not attempt to continue the previous execution")
private boolean clean;
@Option(
names = "--downloadAttempts",
defaultValue = "5",
description = "How many times should attempts to download files (e.g. from Indy to repo zip) be made")
private int downloadAttempts;
@Option(
names = "--targetPath",
defaultValue = "target",
description = "The directory where the deliverables will be put")
private String targetPath;
@Option(
names = { "-e", "--env" },
description = "Override the variables in the build-config.yaml. e.g -eVariable1=value1 -e Variable2=value2 --env=Variable3=value3")
Map overrides = Collections.emptyMap();
/**
* Computes a result, or throws an exception if unable to do so.
*
* @return computed result
* @throws Exception if unable to compute a result
*/
@Override
public Integer call() throws Exception {
if (configDir == null) {
throw new FatalException("You need to specify the configuration directory!");
}
// validate the PiG config
PigConfig pig = Config.instance().getActiveProfile().getPig();
if (pig == null) {
throw new Validate.ConfigMissingException("Pig configuration missing");
}
pig.validate();
FileDownloadUtils.setAttempts(downloadAttempts);
PigContext.init(clean || isStartingPoint(), Paths.get(configDir), targetPath, releaseStorageUrl, overrides);
PigContext.get().setTempBuild(tempBuild);
ObjectHelper.print(getJsonOutput(), doExecute());
return 0;
}
boolean isStartingPoint() {
return false;
}
abstract T doExecute();
}
/* System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism", "10"); */
@Command(name = "run", description = "Run all the steps, except the release & pre-process-yaml phases")
public static class Run extends PigCommand {
// TODO: it is doable to do this step with build group id only, add this functionality
// @Option(shortName = 'b',
// description = "id of the group to build. Exactly one of {config, build-group} has to be provided")
// private Integer buildGroupId;
@Option(
names = TEMP_BUILD_TIME_STAMP,
defaultValue = TEMP_BUILD_TIME_STAMP_DEFAULT,
description = TEMP_BUILD_TIME_STAMP_DESC)
private boolean tempBuildTS;
@Option(names = REBUILD_MODE, defaultValue = REBUILD_MODE_DEFAULT, description = REBUILD_MODE_DESC)
private String rebuildMode;
@Option(
names = "--skipPncUpdate",
defaultValue = "false",
description = "Skip updating PNC entities. Use only if you have all entities created properly.")
private boolean skipPncUpdate;
@Option(
names = "--skipBuilds",
defaultValue = "false",
description = "Skip PNC builds. Use when all your builds went fine, something failed later "
+ "and you want to retry generating deliverables without rebuilding.")
private boolean skipBuilds;
@Option(names = "--skipSources", defaultValue = "false", description = "Skip sources generation.")
private boolean skipSources;
@Option(names = "--skipJavadoc", defaultValue = "false", description = "Skip Javadoc generation.")
private boolean skipJavadoc;
@Option(names = "--skipLicenses", defaultValue = "false", description = "Skip Licenses generation.")
private boolean skipLicenses;
@Option(
names = "--skipSharedContent",
defaultValue = "false",
description = "Skip generating shared content request input.")
private boolean skipSharedContent;
@Option(names = REMOVE_M2_DUPLICATES, description = REMOVE_M2_DUPLICATES_DESC)
private boolean removeGeneratedM2Dups;
@Option(
names = SKIP_BRANCH_CHECK,
defaultValue = SKIP_BRANCH_CHECK_DEFAULT,
description = SKIP_BRANCH_CHECK_DESC)
private boolean skipBranchCheck;
@Option(
names = { "-r", "--repoZip" },
description = "Repository zip. "
+ "Might be used if you have already downloaded repository zip to speed up the process.")
private String repoZipPath;
@Option(
names = "--strictLicenseCheck",
defaultValue = "true",
description = "if set to true will fail on license zip with missing/invalid entries")
private boolean strictLicenseCheck;
@Option(
names = "--strictDownloadSource",
defaultValue = "false",
description = "If set, any missing source jars will throw an error")
private boolean strictDownloadSource;
@Option(
names = "--licenseExceptionsPath",
description = "Path to file with exceptions for license generation. Usually rh-license-exceptions.json.")
private String licenseExceptionsPath;
@Option(
names = "--licenseNamesPath",
description = "Path to file with license names for license generation. Usually rh-license-names.json.")
private String licenseNamesPath;
/**
* addOns to skip. Specified in CLI by using the flag multiple times: --skipAddon=a --skipAddon=b
*
* If the flag is not specified, the defaultValue causes 'skippedAddons' to be an empty array
*/
@Option(
names = "--skipAddon",
defaultValue = "",
description = "Specify which addon(s) to skip. Can be specified multiple times (e.g --skipAddon=a --skipAddon=b")
private String[] skippedAddons;
@Override
public PigRunOutput doExecute() {
PigFacade.exitIfSkippedAddonsInvalid(skippedAddons);
ParameterChecker.checkRebuildModeOption(rebuildMode);
Path configurationDirectory = Paths.get(configDir);
GroupBuildInfo groupBuildInfo = PigFacade.run(
skipPncUpdate,
skipBuilds,
skipSources,
skipJavadoc,
skipLicenses,
skipSharedContent,
removeGeneratedM2Dups,
repoZipPath,
tempBuild,
tempAlign,
tempBuildTS,
RebuildMode.valueOf(rebuildMode),
skipBranchCheck,
strictLicenseCheck,
strictDownloadSource,
skippedAddons,
configurationDirectory,
licenseExceptionsPath,
licenseNamesPath);
PigContext context = PigContext.get();
return new PigRunOutput(
context.getFullVersion(),
groupBuildInfo,
context.getReleaseDirName(),
context.getReleasePath());
}
@Override
boolean isStartingPoint() {
return true;
}
}
@Command(name = "cancel", description = "Cancel running group build")
public static class Cancel extends PigCommand {
@Override
public String doExecute() {
return PigFacade.cancel();
}
}
@Command(name = "configure", description = "Configure PNC entities")
public static class Configure extends PigCommand {
@Option(
names = SKIP_BRANCH_CHECK,
defaultValue = SKIP_BRANCH_CHECK_DEFAULT,
description = SKIP_BRANCH_CHECK_DESC)
private boolean skipBranchCheck;
@Override
public ImportResult doExecute() {
ImportResult importResult = PigFacade.configure(skipBranchCheck, tempBuild);
PigContext.get().setPncImportResult(importResult);
PigContext.get().storeContext();
return importResult;
}
@Override
boolean isStartingPoint() {
return true;
}
}
@Command(name = "build", description = "Build")
public static class Build extends PigCommand {
// TODO: it is doable to do this step with build group id only, add this functionality
// @Option(shortName = 'b',
// description = "id of the group to build. Exactly one of {config, build-group} has to be provided")
// private Integer buildGroupId;
@Option(
names = { TEMP_BUILD_TIME_STAMP },
defaultValue = TEMP_BUILD_TIME_STAMP_DEFAULT,
description = TEMP_BUILD_TIME_STAMP_DESC)
private boolean tempBuildTS;
@Option(names = REBUILD_MODE, defaultValue = REBUILD_MODE_DEFAULT, description = REBUILD_MODE_DESC)
private String rebuildMode;
@Option(names = "--noWait", defaultValue = "true", description = "Start build and don't wait for result.")
private boolean wait;
@Override
public PigBuildOutput doExecute() {
ParameterChecker.checkRebuildModeOption(rebuildMode);
GroupBuildInfo groupBuildInfo = PigFacade
.build(tempBuild, tempBuildTS, RebuildMode.valueOf(rebuildMode), wait, tempAlign);
if (wait) {
PigContext context = PigContext.get();
context.setBuilds(groupBuildInfo.getBuilds());
context.storeContext();
return new PigBuildOutput(
"Build started, waited for result.",
new PigRunOutput(
context.getFullVersion(),
groupBuildInfo,
context.getReleaseDirName(),
context.getReleasePath()));
}
return new PigBuildOutput("Builds started, not waiting for result.", null);
}
}
@Command(name = "repo", description = "GenerateRepository")
public static class GenerateRepository extends PigCommand {
@Option(names = REMOVE_M2_DUPLICATES, description = REMOVE_M2_DUPLICATES_DESC)
private boolean removeGeneratedM2Dups;
@Option(
names = "--strictLicenseCheck",
defaultValue = "true",
description = "if set to true will fail on license zip with missing/invalid entries")
private boolean strictLicenseCheck;
@Option(
names = "--strictDownloadSource",
defaultValue = "false",
description = "If set, any missing source jars will throw an error")
private boolean strictDownloadSource;
@Override
public RepositoryData doExecute() {
Path configurationDirectory = Paths.get(configDir);
RepositoryData result = PigFacade.generateRepo(
removeGeneratedM2Dups,
configurationDirectory,
strictLicenseCheck,
strictDownloadSource);
PigContext.get().setRepositoryData(result);
PigContext.get().storeContext();
return result;
}
}
@Command(name = "licenses", description = "GenerateLicenses")
public static class GenerateLicenses extends PigCommand {
@Option(
names = "--strictLicenseCheck",
defaultValue = "true",
description = "if set to true will fail on license zip with missing/invalid entries")
private boolean strictLicenseCheck;
@Option(
names = "--licenseExceptionsPath",
description = "Path to file with exceptions for license generation. Usually rh-license-exceptions.json.")
private String licenseExceptionsPath;
@Option(
names = "--licenseNamesPath",
description = "Path to file with license names for license generation. Usually rh-license-names.json.")
private String licenseNamesPath;
@Override
public String doExecute() {
PigFacade.generateLicenses(strictLicenseCheck, licenseExceptionsPath, licenseNamesPath);
return "Licenses generated successfully"; // TODO: better output
}
}
@Command(name = "javadocs", description = "GenerateJavadocs")
public static class GenerateJavadocs extends PigCommand {
@Override
public String doExecute() {
PigFacade.generateJavadoc();
return "Javadocs generated successfully"; // TODO: better output
}
}
@Command(name = "sources", description = "GenerateSources")
public static class GenerateSources extends PigCommand {
@Override
public String doExecute() {
PigFacade.generateSources();
return "Sources gathered successfully"; // TODO: better output
}
}
@Command(name = "shared-content", description = "GenerateSharedContentAnalysis")
public static class GenerateSharedContentAnalysis extends PigCommand {
@Override
public String doExecute() {
PigFacade.prepareSharedContentAnalysis();
return "Shared content analysis document prepared successfully"; // TODO: better output
}
}
@Command(name = "docs", description = "GenerateDocuments")
public static class GenerateDocuments extends PigCommand {
@Override
public String doExecute() {
PigFacade.generateDocuments();
return "Documents generated successfully"; // TODO: better output
}
}
@Command(name = "addons", description = "Addons")
public static class TriggerAddOns extends PigCommand {
/**
* addOns to skip. Specified in CLI by using the flag multiple times: --skipAddon=a --skipAddon=b
*
* If the flag is not specified, the defaultValue causes 'skippedAddons' to be an empty array
*/
@Option(
names = "--skipAddon",
defaultValue = "",
description = "Specify which addon(s) to skip. Can be specified multiple times (e.g --skipAddon=a --skipAddon=b")
private String[] skippedAddons;
@Override
public String doExecute() {
PigFacade.exitIfSkippedAddonsInvalid(skippedAddons);
PigFacade.triggerAddOns(skippedAddons);
return "Add-ons executed successfully";
}
}
@Command(
name = "release",
description = "Push builds to brew, generate the NVR list, "
+ "close the PNC milestone, generate the upload to candidates script if repository generated")
public static class Release extends PigCommand {
@Override
public PigReleaseOutput doExecute() {
return PigFacade.release();
}
}
@Command(name = "pre-process-yaml", description = "Show the final YAML content with variables injected")
@Slf4j
public static class PreProcessYaml extends PigCommand {
@Override
public String doExecute() {
try {
File configFile = Paths.get(configDir).resolve("build-config.yaml").toFile();
InputStream preProcessed = PigConfiguration.preProcess(new FileInputStream(configFile), overrides);
String contents = "";
Scanner s = new Scanner(preProcessed);
s.useDelimiter("\\A");
if (s.hasNext()) {
contents = s.next();
}
// Yeah we'll violate the normal rules and just print to stdout. The JSON/YAML output has no effect here
System.out.println(contents);
} catch (FileNotFoundException e) {
log.error("Config file {}{}build-config.yaml does not exist", configDir, File.separator);
}
return "";
}
}
}