package com.hubspot.blazar.util;
import static com.hubspot.blazar.base.BuildTrigger.Type.INTER_PROJECT;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Set;
import java.util.stream.Collectors;
import javax.inject.Inject;
import javax.inject.Singleton;
import org.kohsuke.github.GHRepository;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.annotations.VisibleForTesting;
import com.google.common.base.Optional;
import com.google.common.collect.ImmutableList;
import com.hubspot.blazar.base.CommitInfo;
import com.hubspot.blazar.base.GitInfo;
import com.hubspot.blazar.base.Module;
import com.hubspot.blazar.base.RepositoryBuild;
import com.hubspot.blazar.base.RepositoryBuild.State;
import com.hubspot.blazar.data.service.BranchService;
import com.hubspot.blazar.data.service.DependenciesService;
import com.hubspot.blazar.data.service.ModuleDiscoveryService;
import com.hubspot.blazar.data.service.ModuleService;
import com.hubspot.blazar.data.service.RepositoryBuildService;
import com.hubspot.blazar.discovery.ModuleDiscoveryHandler;
import com.hubspot.blazar.exception.NonRetryableBuildException;
import com.hubspot.blazar.github.GitHubProtos.Commit;
@Singleton
public class RepositoryBuildLauncher {
private static final Logger LOG = LoggerFactory.getLogger(RepositoryBuildLauncher.class);
private final RepositoryBuildService repositoryBuildService;
private final BranchService branchService;
private final ModuleService moduleService;
private final DependenciesService dependenciesService;
private final ModuleDiscoveryService moduleDiscoveryService;
private final ModuleDiscoveryHandler moduleDiscoveryHandler;
private final GitHubHelper gitHubHelper;
@Inject
public RepositoryBuildLauncher(RepositoryBuildService repositoryBuildService,
BranchService branchService,
ModuleService moduleService,
DependenciesService dependenciesService,
ModuleDiscoveryService moduleDiscoveryService,
ModuleDiscoveryHandler moduleDiscoveryHandler,
GitHubHelper gitHubHelper) {
this.repositoryBuildService = repositoryBuildService;
this.branchService = branchService;
this.moduleService = moduleService;
this.dependenciesService = dependenciesService;
this.moduleDiscoveryService = moduleDiscoveryService;
this.moduleDiscoveryHandler = moduleDiscoveryHandler;
this.gitHubHelper = gitHubHelper;
}
public void launch(RepositoryBuild queued, Optional<RepositoryBuild> previous) throws Exception {
GitInfo branch = branchService.get(queued.getBranchId()).get();
CommitInfo commitInfo = calculateCommitInfoForBuild(branch, queued, previous);
moduleDiscoveryHandler.updateModules(branch, commitInfo, true);
// We get the modules from db instead of using the ModuleDiscoveryResult returned by
// moduleDiscoveryHandler.updateModules() in order to retrieve the ids of newly discovered modules
Set<Module> activeModules = moduleService.getByBranch(branch.getId().get()).stream().filter(Module::isActive).collect(Collectors.toSet());
RepositoryBuild launching = queued.toBuilder()
.setStartTimestamp(Optional.of(System.currentTimeMillis()))
.setState(State.LAUNCHING)
.setCommitInfo(Optional.of(commitInfo))
.setSha(Optional.of(commitInfo.getCurrent().getId()))
.setDependencyGraph(Optional.of(dependenciesService.buildDependencyGraph(branch, activeModules)))
.build();
LOG.info("Updating status of build {} to {}", launching.getId().get(), launching.getState());
repositoryBuildService.begin(launching);
}
@VisibleForTesting
CommitInfo calculateCommitInfoForBuild(
GitInfo gitInfo,
RepositoryBuild queuedBuild,
Optional<RepositoryBuild> previousBuild) throws IOException, NonRetryableBuildException {
LOG.info("Trying to fetch current sha for branch {}/{}", gitInfo.getRepository(), gitInfo.getBranch());
final GHRepository repository;
try {
repository = gitHubHelper.repositoryFor(gitInfo);
} catch (FileNotFoundException e) {
throw new NonRetryableBuildException("Couldn't find repository " + gitInfo.getFullRepositoryName(), e);
}
Optional<Commit> lastCommitInPreviousBuild = getCommitFromRepositoryBuild(previousBuild);
// Inter project builds re-build the project using the commit of the last build if available
// otherwise we fall back to using the newest commit available
if (queuedBuild.getBuildTrigger().getType() == INTER_PROJECT && lastCommitInPreviousBuild.isPresent()) {
return new CommitInfo(lastCommitInPreviousBuild.get(), lastCommitInPreviousBuild, ImmutableList.of(), false);
}
// Resolve newest sha
Optional<String> sha = gitHubHelper.shaFor(repository, gitInfo);
if (!sha.isPresent()) {
String message = String.format("Couldn't find branch %s for repository %s", gitInfo.getBranch(), gitInfo.getFullRepositoryName());
throw new NonRetryableBuildException(message);
} else {
LOG.info("Found sha {} for branch {}/{}", sha.get(), gitInfo.getRepository(), gitInfo.getBranch());
Commit currentCommit = gitHubHelper.toCommit(repository.getCommit(sha.get()));
return gitHubHelper.commitInfoFor(repository, currentCommit, lastCommitInPreviousBuild);
}
}
private static Optional<Commit> getCommitFromRepositoryBuild(Optional<RepositoryBuild> build) {
if (build.isPresent() && build.get().getCommitInfo() != null && build.get().getCommitInfo().isPresent()) {
return Optional.of(build.get().getCommitInfo().get().getCurrent());
} else {
return Optional.absent();
}
}
}