/* * Copyright 2015-present Facebook, Inc. * * 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 com.facebook.buck.cli; import static com.facebook.buck.jvm.java.JavaLibrary.MAVEN_JAR; import static com.facebook.buck.jvm.java.JavaLibrary.SRC_JAR; import static com.facebook.buck.jvm.java.Javadoc.DOC_JAR; import com.facebook.buck.event.ConsoleEvent; import com.facebook.buck.jvm.java.MavenPublishable; import com.facebook.buck.maven.Publisher; import com.facebook.buck.model.BuildTarget; import com.facebook.buck.parser.BuildTargetSpec; import com.facebook.buck.parser.NoSuchBuildTargetException; import com.facebook.buck.parser.TargetNodeSpec; import com.facebook.buck.rules.BuildRule; import com.facebook.buck.rules.SourcePathResolver; import com.facebook.buck.rules.SourcePathRuleFinder; import com.google.common.base.Joiner; import com.google.common.base.Preconditions; import com.google.common.collect.FluentIterable; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import java.io.IOException; import java.net.URL; import java.util.HashMap; import java.util.Map; import java.util.Optional; import javax.annotation.Nullable; import org.eclipse.aether.artifact.Artifact; import org.eclipse.aether.deployment.DeployResult; import org.eclipse.aether.deployment.DeploymentException; import org.kohsuke.args4j.Option; public class PublishCommand extends BuildCommand { public static final String REMOTE_REPO_LONG_ARG = "--remote-repo"; public static final String REMOTE_REPO_SHORT_ARG = "-r"; public static final String INCLUDE_SOURCE_LONG_ARG = "--include-source"; public static final String INCLUDE_SOURCE_SHORT_ARG = "-s"; public static final String INCLUDE_DOCS_LONG_ARG = "--include-docs"; public static final String INCLUDE_DOCS_SHORT_ARG = "-w"; public static final String TO_MAVEN_CENTRAL_LONG_ARG = "--to-maven-central"; public static final String DRY_RUN_LONG_ARG = "--dry-run"; @Option( name = REMOTE_REPO_LONG_ARG, aliases = REMOTE_REPO_SHORT_ARG, usage = "A url of the remote repository to publish artifact(s) to" ) @Nullable private URL remoteRepo = null; @Option( name = TO_MAVEN_CENTRAL_LONG_ARG, usage = "Same as \"" + REMOTE_REPO_LONG_ARG + " " + Publisher.MAVEN_CENTRAL_URL + "\"" ) private boolean toMavenCentral = false; @Option( name = INCLUDE_SOURCE_LONG_ARG, aliases = INCLUDE_SOURCE_SHORT_ARG, usage = "Publish source code as well" ) private boolean includeSource = false; @Option( name = INCLUDE_DOCS_LONG_ARG, aliases = INCLUDE_DOCS_SHORT_ARG, usage = "Publish docs as well" ) private boolean includeDocs = false; @Option(name = DRY_RUN_LONG_ARG, usage = "Just print the artifacts to be published") private boolean dryRun = false; @Option( name = "--username", aliases = "-u", usage = "User name to use to authenticate with the server" ) @Nullable private String username = null; @Option( name = "--password", aliases = "-p", usage = "Password to use to authenticate with the server" ) @Nullable private String password = null; @Override public int runWithoutHelp(CommandRunnerParams params) throws IOException, InterruptedException { // Input validation if (remoteRepo == null && !toMavenCentral) { params .getBuckEventBus() .post( ConsoleEvent.severe( "Please specify a remote repository to publish to.\n" + "Use " + REMOTE_REPO_LONG_ARG + " <URL> or " + TO_MAVEN_CENTRAL_LONG_ARG)); return 1; } // Build the specified target(s). int exitCode = super.runWithoutHelp(params); if (exitCode != 0) { return exitCode; } // Publish starting with the given targets. return publishTargets(getBuildTargets(), params) ? 0 : 1; } private boolean publishTargets( ImmutableList<BuildTarget> buildTargets, CommandRunnerParams params) { ImmutableSet.Builder<MavenPublishable> publishables = ImmutableSet.builder(); boolean success = true; for (BuildTarget buildTarget : buildTargets) { BuildRule buildRule = null; try { buildRule = getBuild().getRuleResolver().requireRule(buildTarget); } catch (NoSuchBuildTargetException e) { // This doesn't seem physically possible! throw new RuntimeException(e); } Preconditions.checkNotNull(buildRule); if (!(buildRule instanceof MavenPublishable)) { params .getBuckEventBus() .post( ConsoleEvent.severe( "Cannot publish rule of type %s", buildRule.getClass().getName())); success &= false; continue; } MavenPublishable publishable = (MavenPublishable) buildRule; if (!publishable.getMavenCoords().isPresent()) { params .getBuckEventBus() .post( ConsoleEvent.severe( "No maven coordinates specified for %s", buildTarget.getUnflavoredBuildTarget().getFullyQualifiedName())); success &= false; continue; } publishables.add(publishable); } Publisher publisher = new Publisher( params.getCell().getFilesystem(), Optional.ofNullable(remoteRepo), Optional.ofNullable(username), Optional.ofNullable(password), dryRun); try { ImmutableSet<DeployResult> deployResults = publisher.publish( new SourcePathResolver(new SourcePathRuleFinder(getBuild().getRuleResolver())), publishables.build()); for (DeployResult deployResult : deployResults) { printArtifactsInformation(params, deployResult); } } catch (DeploymentException e) { params.getConsole().printBuildFailureWithoutStacktraceDontUnwrap(e); return false; } return success; } private static void printArtifactsInformation( CommandRunnerParams params, DeployResult deployResult) { params .getConsole() .getStdOut() .println( "\nPublished artifacts:\n" + Joiner.on('\n') .join( FluentIterable.from(deployResult.getArtifacts()) .transform(PublishCommand::artifactToString))); params.getConsole().getStdOut().println("\nDeployRequest:\n" + deployResult.getRequest()); } private static String artifactToString(Artifact artifact) { return artifact.toString() + " < " + artifact.getFile(); } @Override public ImmutableList<TargetNodeSpec> parseArgumentsAsTargetNodeSpecs( BuckConfig config, Iterable<String> targetsAsArgs) { ImmutableList<TargetNodeSpec> specs = super.parseArgumentsAsTargetNodeSpecs(config, targetsAsArgs); Map<BuildTarget, TargetNodeSpec> uniqueSpecs = new HashMap<>(); for (TargetNodeSpec spec : specs) { if (!(spec instanceof BuildTargetSpec)) { throw new IllegalArgumentException( "Need to specify build targets explicitly when publishing. " + "Cannot modify " + spec); } BuildTargetSpec targetSpec = (BuildTargetSpec) spec; Preconditions.checkNotNull(targetSpec.getBuildTarget()); BuildTarget mavenTarget = targetSpec.getBuildTarget().withFlavors(MAVEN_JAR); uniqueSpecs.put(mavenTarget, targetSpec.withBuildTarget(mavenTarget)); if (includeSource) { BuildTarget sourceTarget = targetSpec.getBuildTarget().withFlavors(MAVEN_JAR, SRC_JAR); uniqueSpecs.put(sourceTarget, targetSpec.withBuildTarget(sourceTarget)); } if (includeDocs) { BuildTarget docsTarget = targetSpec.getBuildTarget().withFlavors(MAVEN_JAR, DOC_JAR); uniqueSpecs.put(docsTarget, targetSpec.withBuildTarget(docsTarget)); } } return ImmutableList.copyOf(uniqueSpecs.values()); } @Override public String getShortDescription() { return "builds and publishes a library to a central repository"; } }