use of com.google.copybara.exception.EmptyChangeException in project copybara by google.
the class GitHubPrOrigin method newReader.
@Override
public Reader<GitRevision> newReader(Glob originFiles, Authoring authoring) throws ValidationException {
return new ReaderImpl(url, originFiles, authoring, gitOptions, gitOriginOptions, generalOptions, /*includeBranchCommitLogs=*/
false, submoduleStrategy, firstParent, partialFetch, patchTransformation, describeVersion, /*configPath=*/
null, /*workflowName=*/
null) {
/**
* Disable rebase since this is controlled by useMerge field.
*/
@Override
protected void maybeRebase(GitRepository repo, GitRevision ref, Path workdir) throws RepoException, CannotResolveRevisionException {
}
@Override
public Optional<Baseline<GitRevision>> findBaseline(GitRevision startRevision, String label) throws RepoException, ValidationException {
if (!baselineFromBranch) {
return super.findBaseline(startRevision, label);
}
return findBaselinesWithoutLabel(startRevision, /*limit=*/
1).stream().map(e -> new Baseline<>(e.getSha1(), e)).findFirst();
}
@Override
public ImmutableList<GitRevision> findBaselinesWithoutLabel(GitRevision startRevision, int limit) throws RepoException, ValidationException {
String baseline = Iterables.getLast(startRevision.associatedLabels().get(GITHUB_BASE_BRANCH_SHA1), null);
checkNotNull(baseline, "%s label should be present in %s", GITHUB_BASE_BRANCH_SHA1, startRevision);
GitRevision baselineRev = getRepository().resolveReference(baseline);
// Don't skip the first change as it is already the baseline
BaselinesWithoutLabelVisitor<GitRevision> visitor = new BaselinesWithoutLabelVisitor<>(originFiles, limit, /*skipFirst=*/
false);
visitChanges(baselineRev, visitor);
return visitor.getResult();
}
@Override
public Endpoint getFeedbackEndPoint(Console console) throws ValidationException {
gitHubOptions.validateEndpointChecker(endpointChecker);
return new GitHubEndPoint(gitHubOptions.newGitHubApiSupplier(url, endpointChecker, ghHost), url, console, ghHost);
}
/**
* Deal with the case of useMerge. We have a new commit (the merge) and first-parent from that
* commit doesn't work for this case.
*/
@Override
public ChangesResponse<GitRevision> changes(@Nullable GitRevision fromRef, GitRevision toRef) throws RepoException, ValidationException {
checkCondition(toRef.associatedLabels().containsKey(GITHUB_PR_USE_MERGE), "Cannot determine whether 'use_merge' was set.");
if (toRef.associatedLabel(GITHUB_PR_USE_MERGE).contains("false")) {
return super.changes(fromRef, toRef);
}
GitLogEntry merge = Iterables.getOnlyElement(getRepository().log(toRef.getSha1()).withLimit(1).run());
// Fast-forward merge
if (merge.getParents().size() == 1) {
return super.changes(fromRef, toRef);
}
// HEAD of the Pull Request
GitRevision gitRevision = merge.getParents().get(1);
ChangesResponse<GitRevision> prChanges = super.changes(fromRef, gitRevision);
// origin_files
if (prChanges.isEmpty()) {
return prChanges;
}
try {
return ChangesResponse.forChanges(ImmutableList.<Change<GitRevision>>builder().addAll(prChanges.getChanges()).add(change(merge.getCommit())).build());
} catch (EmptyChangeException e) {
throw new RepoException("Error getting the merge commit information: " + merge, e);
}
}
};
}
use of com.google.copybara.exception.EmptyChangeException in project copybara by google.
the class GitRepository method commit.
// TODO(malcon): Create a CommitCmd object builder
public void commit(@Nullable String author, boolean amend, @Nullable ZonedDateTime timestamp, String message) throws RepoException, ValidationException {
if (isEmptyStaging() && !amend) {
String baseline = "unknown";
try {
baseline = parseRef("HEAD");
} catch (CannotResolveRevisionException | RepoException e) {
logger.atWarning().withCause(e).log("Cannot find baseline.");
}
throw new EmptyChangeException(String.format("Migration of the revision resulted in an empty change from baseline '%s'.\n" + "Is the change already migrated?", baseline));
}
ImmutableList.Builder<String> params = ImmutableList.<String>builder().add("commit");
if (author != null) {
params.add("--author", author);
}
if (timestamp != null) {
params.add("--date", timestamp.format(ISO_OFFSET_DATE_TIME_NO_SUBSECONDS));
}
if (amend) {
params.add("--amend");
}
if (noVerify) {
params.add("--no-verify");
}
Path descriptionFile = null;
try {
if (message.getBytes(StandardCharsets.UTF_8).length > ARBITRARY_MAX_ARG_SIZE) {
descriptionFile = getCwd().resolve(UUID.randomUUID().toString() + ".desc");
Files.write(descriptionFile, message.getBytes(StandardCharsets.UTF_8));
params.add("-F", descriptionFile.toAbsolutePath().toString());
} else {
params.add("-m", message);
}
git(getCwd(), addGitDirAndWorkTreeParams(params.build()));
} catch (IOException e) {
throw new RepoException("Could not commit change: Failed to write file " + descriptionFile, e);
} finally {
try {
if (descriptionFile != null) {
Files.deleteIfExists(descriptionFile);
}
} catch (IOException e) {
logger.atWarning().log("Could not delete description file: %s", descriptionFile);
}
}
}
use of com.google.copybara.exception.EmptyChangeException in project copybara by google.
the class Mirror method run.
@Override
public void run(Path workdir, ImmutableList<String> sourceRefs) throws RepoException, IOException, ValidationException {
try (ProfilerTask ignore = generalOptions.profiler().start("run/" + name)) {
GitRepository repo = gitOptions.cachedBareRepoForUrl(origin);
if (Iterables.isEmpty(actions)) {
defaultMirror(repo);
} else {
ImmutableList.Builder<ActionResult> allResultsBuilder = ImmutableList.builder();
for (Action action : actions) {
GitMirrorContext context = new GitMirrorContext(action, new SkylarkConsole(generalOptions.console()), sourceRefs, refspec, origin, destination, generalOptions.isForced(), repo, generalOptions.getDirFactory(), Dict.empty());
try {
action.run(context);
ActionResult actionResult = context.getActionResult();
allResultsBuilder.add(actionResult);
// First error aborts the execution of the other actions unless --force is used
ValidationException.checkCondition(generalOptions.isForced() || actionResult.getResult() != Result.ERROR, "Feedback migration '%s' action '%s' returned error: %s. Aborting execution.", name, action.getName(), actionResult.getMsg());
} catch (NonFastForwardRepositoryException e) {
allResultsBuilder.add(ActionResult.error(action.getName() + ": " + e.getMessage()));
if (!generalOptions.isForced()) {
throw e;
}
logger.atWarning().withCause(e).log();
} finally {
generalOptions.eventMonitors().dispatchEvent(m -> m.onChangeMigrationFinished(new ChangeMigrationFinishedEvent(ImmutableList.copyOf(context.getNewDestinationEffects()), getOriginDescription(), getDestinationDescription())));
}
}
ImmutableList<ActionResult> allResults = allResultsBuilder.build();
if (allResults.stream().anyMatch(a -> a.getResult() == Result.ERROR)) {
String errors = allResults.stream().filter(a -> a.getResult() == Result.ERROR).map(ActionResult::getMsg).collect(Collectors.joining("\n - "));
throw new ValidationException("One or more errors happened during the migration:\n" + " - " + errors);
}
// This check also returns true if there are no actions
if (allResults.stream().allMatch(a -> a.getResult() == Result.NO_OP)) {
String detailedMessage = allResults.isEmpty() ? "actions field is empty" : allResults.stream().map(ActionResult::getMsg).collect(ImmutableList.toImmutableList()).toString();
throw new EmptyChangeException(String.format("git.mirror migration '%s' was noop. Detailed messages: %s", name, detailedMessage));
}
}
}
// More fine grain events based on the references created/updated/deleted:
ChangeMigrationFinishedEvent event = new ChangeMigrationFinishedEvent(ImmutableList.of(new DestinationEffect(generalOptions.dryRunMode ? DestinationEffect.Type.NOOP : DestinationEffect.Type.UPDATED, generalOptions.dryRunMode ? "Refspecs " + refspec + " can be mirrored" : "Refspecs " + refspec + " mirrored successfully", // TODO(danielromero): Populate OriginRef here
ImmutableList.of(), new DestinationRef(getOriginDestinationRef(destination), "mirror", /*url=*/
null))), getOriginDescription(), getDestinationDescription());
generalOptions.eventMonitors().dispatchEvent(m -> m.onChangeMigrationFinished(event));
}
use of com.google.copybara.exception.EmptyChangeException in project copybara by google.
the class SkylarkTransformation method transform.
@Override
public TransformationStatus transform(TransformWork work) throws IOException, ValidationException, RepoException {
SkylarkConsole skylarkConsole = new SkylarkConsole(work.getConsole());
TransformWork skylarkWork = work.withConsole(skylarkConsole).withParams(params);
TransformationStatus status = TransformationStatus.success();
try (Mutability mu = Mutability.create("dynamic_transform")) {
StarlarkThread thread = new StarlarkThread(mu, StarlarkSemantics.DEFAULT);
thread.setPrintHandler(printHandler);
Object result = Starlark.call(thread, function, ImmutableList.of(skylarkWork), /*kwargs=*/
ImmutableMap.of());
result = result == Starlark.NONE ? TransformationStatus.success() : result;
checkCondition(result instanceof TransformationStatus, "Dynamic transforms functions should return nothing or objects of type %s, but '%s'" + " returned: %s", TransformationStatus.STARLARK_TYPE_NAME, describe(), result);
status = (TransformationStatus) result;
} catch (EvalException e) {
if (e.getCause() instanceof EmptyChangeException) {
throw ((EmptyChangeException) e.getCause());
}
if (e.getCause() instanceof RepoException) {
throw new RepoException(String.format("Error while executing the skylark transformation %s: %s", describe(), e.getMessageWithStack()), e);
}
throw new ValidationException(String.format("Error while executing the skylark transformation %s: %s", describe(), e.getMessageWithStack()), e);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
throw new RuntimeException("This should not happen.", e);
} finally {
work.updateFrom(skylarkWork);
}
checkCondition(skylarkConsole.getErrorCount() == 0, "%d error(s) while executing %s", skylarkConsole.getErrorCount(), describe());
return status;
}
use of com.google.copybara.exception.EmptyChangeException in project copybara by google.
the class WorkflowTest method testDryRunWithLocalGitPath.
/**
* Regression test that checks that we reuse the same writer in dry-run mode for multiple
* invocations inside the same migration so that state is kept.
*/
@Test
public void testDryRunWithLocalGitPath() throws Exception {
Path originPath = Files.createTempDirectory("origin");
Path destinationPath = Files.createTempDirectory("destination");
GitRepository origin = GitRepository.newRepo(/*verbose*/
true, originPath, getGitEnv()).init();
GitRepository destination = GitRepository.newBareRepo(destinationPath, getGitEnv(), /*verbose=*/
true, DEFAULT_TIMEOUT, /*noVerify=*/
false).init();
String primaryBranch = origin.getPrimaryBranch();
String config = "core.workflow(" + " name = 'default',\n" + " origin = git.origin(\n" + " url = 'file://" + origin.getWorkTree() + "',\n" + " ref = '" + primaryBranch + "'\n" + " ),\n" + " destination = git.destination(" + " url = 'file://" + destination.getGitDir() + "',\n" + " push = '" + primaryBranch + "',\n" + " fetch = '" + primaryBranch + "'\n" + " ),\n" + " authoring = " + authoring + ",\n" + " mode = 'SQUASH',\n" + ")\n";
addGitFile(originPath, origin, "foo.txt", "not important");
commit(origin, "baseline\n\nOrigin-Label: 1234567");
options.setWorkdirToRealTempDir();
// Pass custom HOME directory so that we run an hermetic test and we
// can add custom configuration to $HOME/.gitconfig.
options.setEnvironment(GitTestUtil.getGitEnv().getEnvironment());
options.setHomeDir(Files.createTempDirectory("home").toString());
options.gitDestination.committerName = "Foo";
options.gitDestination.committerEmail = "foo@foo.com";
options.workflowOptions.initHistory = true;
loadConfig(config).getMigration("default").run(Files.createTempDirectory("workdir"), ImmutableList.of());
// Now run again with force and no changes so that it uses the default migrator (The affected
// path
options.gitDestination.localRepoPath = Files.createTempDirectory("temp").toString();
options.workflowOptions.initHistory = false;
options.general.dryRunMode = true;
options.setForce(true);
EmptyChangeException e = assertThrows(EmptyChangeException.class, () -> loadConfig(config).getMigration("default").run(Files.createTempDirectory("workdir"), ImmutableList.of()));
assertThat(e).hasMessageThat().contains("Migration of the revision resulted in an empty change");
assertThat(e).hasMessageThat().contains(destination.parseRef("HEAD"));
}
Aggregations