Search in sources :

Example 1 with Revision

use of com.google.copybara.revision.Revision in project copybara by google.

the class Core method workflow.

@SuppressWarnings({ "unused", "unchecked" })
@StarlarkMethod(name = "workflow", doc = "Defines a migration pipeline which can be invoked via the Copybara command.\n" + "\n" + "Implicit labels that can be used/exposed:\n" + "\n" + "  - " + TransformWork.COPYBARA_CONTEXT_REFERENCE_LABEL + ": Requested reference. For example if copybara is invoked as `copybara" + " copy.bara.sky workflow master`, the value would be `master`.\n" + "  - " + TransformWork.COPYBARA_LAST_REV + ": Last reference that was migrated\n" + "  - " + TransformWork.COPYBARA_CURRENT_REV + ": The current reference being migrated\n" + "  - " + TransformWork.COPYBARA_CURRENT_REV_DATE_TIME + ": Date & time for the current reference being migrated in ISO format" + " (Example: \"2011-12-03T10:15:30+01:00\")\n" + "  - " + TransformWork.COPYBARA_CURRENT_MESSAGE + ": The current message at this point of the transformations\n" + "  - " + TransformWork.COPYBARA_CURRENT_MESSAGE_TITLE + ": The current message title (first line) at this point of the transformations\n" + "  - " + TransformWork.COPYBARA_AUTHOR + ": The author of the change\n", parameters = { @Param(name = "name", named = true, doc = "The name of the workflow.", positional = false), @Param(name = "origin", named = true, doc = "Where to read from the code to be migrated, before applying the " + "transformations. This is usually a VCS like Git, but can also be a local " + "folder or even a pending change in a code review system like Gerrit.", positional = false), @Param(name = "destination", named = true, doc = "Where to write to the code being migrated, after applying the " + "transformations. This is usually a VCS like Git, but can also be a local " + "folder or even a pending change in a code review system like Gerrit.", positional = false), @Param(name = "authoring", named = true, doc = "The author mapping configuration from origin to destination.", positional = false), @Param(name = "transformations", named = true, doc = "The transformations to be run for this workflow. They will run in sequence.", positional = false, defaultValue = "[]"), @Param(name = "origin_files", named = true, allowedTypes = { @ParamType(type = Glob.class), @ParamType(type = NoneType.class) }, doc = "A glob relative to the workdir that will be read from the" + " origin during the import. For example glob([\"**.java\"]), all java files," + " recursively, which excludes all other file types.", defaultValue = "None", positional = false), @Param(name = "destination_files", named = true, allowedTypes = { @ParamType(type = Glob.class), @ParamType(type = NoneType.class) }, doc = "A glob relative to the root of the destination repository that matches files that" + " are part of the migration. Files NOT matching this glob will never be" + " removed, even if the file does not exist in the source. For example" + " glob(['**'], exclude = ['**/BUILD']) keeps all BUILD files in destination" + " when the origin does not have any BUILD files. You can also use this to" + " limit the migration to a subdirectory of the destination, e.g." + " glob(['java/src/**'], exclude = ['**/BUILD']) to only affect non-BUILD" + " files in java/src.", defaultValue = "None", positional = false), @Param(name = "mode", named = true, doc = "Workflow mode. Currently we support four modes:<br><ul><li><b>'SQUASH'</b>:" + " Create a single commit in the destination with new tree" + " state.</li><li><b>'ITERATIVE'</b>: Import each origin change" + " individually.</li><li><b>'CHANGE_REQUEST'</b>: Import a pending change to" + " the Source-of-Truth. This could be a GH Pull Request, a Gerrit Change," + " etc. The final intention should be to submit the change in the SoT" + " (destination in this case).</li><li><b>'CHANGE_REQUEST_FROM_SOT'</b>:" + " Import a pending change **from** the Source-of-Truth. This mode is useful" + " when, despite the pending change being already in the SoT, the users want" + " to review the code on a different system. The final intention should never" + " be to submit in the destination, but just review or test</li></ul>", defaultValue = "\"SQUASH\"", positional = false), @Param(name = "reversible_check", named = true, allowedTypes = { @ParamType(type = Boolean.class), @ParamType(type = NoneType.class) }, doc = "Indicates if the tool should try to to reverse all the transformations" + " at the end to check that they are reversible.<br/>The default value is" + " True for 'CHANGE_REQUEST' mode. False otherwise", defaultValue = "None", positional = false), @Param(name = CHECK_LAST_REV_STATE, named = true, doc = "If set to true, Copybara will validate that the destination didn't change" + " since last-rev import for destination_files. Note that this" + " flag doesn't work for CHANGE_REQUEST mode.", defaultValue = "False", positional = false), @Param(name = "ask_for_confirmation", named = true, doc = "Indicates that the tool should show the diff and require user's" + " confirmation before making a change in the destination.", defaultValue = "False", positional = false), @Param(name = "dry_run", named = true, doc = "Run the migration in dry-run mode. Some destination implementations might" + " have some side effects (like creating a code review), but never submit to a" + " main branch.", defaultValue = "False", positional = false), @Param(name = "after_migration", named = true, doc = "Run a feedback workflow after one migration happens. This runs once per" + " change in `ITERATIVE` mode and only once for `SQUASH`.", defaultValue = "[]", positional = false), @Param(name = "after_workflow", named = true, doc = "Run a feedback workflow after all the changes for this workflow run are migrated." + " Prefer `after_migration` as it is executed per change (in ITERATIVE mode)." + " Tasks in this hook shouldn't be critical to execute. These actions" + " shouldn't record effects (They'll be ignored).", defaultValue = "[]", positional = false), @Param(name = "change_identity", named = true, allowedTypes = { @ParamType(type = String.class), @ParamType(type = NoneType.class) }, doc = "By default, Copybara hashes several fields so that each change has an unique" + " identifier that at the same time reuses the generated destination change." + " This allows to customize the identity hash generation so that the same" + " identity is used in several workflows. At least ${copybara_config_path}" + " has to be present. Current user is added to the hash" + " automatically.<br><br>Available variables:<ul> " + " <li>${copybara_config_path}: Main config file path</li> " + " <li>${copybara_workflow_name}: The name of the workflow being run</li> " + " <li>${copybara_reference}: The requested reference. In general Copybara" + " tries its best to give a repetable reference. For example Gerrit change" + " number or change-id or GitHub Pull Request number. If it cannot find a" + " context reference it uses the resolved revision.</li> " + " <li>${label:label_name}: A label present for the current change. Exposed" + " in the message or not.</li></ul>If any of the labels cannot be found it" + " defaults to the default identity (The effect would be no reuse of" + " destination change between workflows)", defaultValue = "None", positional = false), @Param(name = "set_rev_id", named = true, doc = "Copybara adds labels like 'GitOrigin-RevId' in the destination in order to" + " track what was the latest change imported. For `CHANGE_REQUEST` " + "workflows it is not used and is purely informational. This field " + "allows to disable it for that mode. Destinations might ignore the flag.", defaultValue = "True", positional = false), @Param(name = "smart_prune", named = true, doc = "By default CHANGE_REQUEST workflows cannot restore scrubbed files. This flag does" + " a best-effort approach in restoring the non-affected snippets. For now we" + " only revert the non-affected files. This only works for CHANGE_REQUEST" + " mode.", defaultValue = "False", positional = false), @Param(name = "migrate_noop_changes", named = true, doc = "By default, Copybara tries to only migrate changes that affect origin_files or" + " config files. This flag allows to include all the changes. Note that it" + " might generate more empty changes errors. In `ITERATIVE` mode it might" + " fail if some transformation is validating the message (Like has to contain" + " 'PUBLIC' and the change doesn't contain it because it is internal).", defaultValue = "False", positional = false), @Param(name = "experimental_custom_rev_id", named = true, allowedTypes = { @ParamType(type = String.class), @ParamType(type = NoneType.class) }, doc = "Use this label name instead of the one provided by the origin. This is subject" + " to change and there is no guarantee.", defaultValue = "None", positional = false), @Param(name = "description", allowedTypes = { @ParamType(type = String.class), @ParamType(type = NoneType.class) }, named = true, positional = false, doc = "A description of what this workflow achieves", defaultValue = "None"), @Param(name = "checkout", named = true, positional = false, doc = "Allows disabling the checkout. The usage of this feature is rare. This could" + " be used to update a file of your own repo when a dependant repo version" + " changes and you are not interested on the files of the dependant repo, just" + " the new version.", defaultValue = "True"), @Param(name = "reversible_check_ignore_files", named = true, allowedTypes = { @ParamType(type = Glob.class), @ParamType(type = NoneType.class) }, doc = "Ignore the files matching the glob in the reversible check", defaultValue = "None", positional = false) }, useStarlarkThread = true)
@UsesFlags({ WorkflowOptions.class })
@DocDefault(field = "origin_files", value = "glob([\"**\"])")
@DocDefault(field = "destination_files", value = "glob([\"**\"])")
@DocDefault(field = "reversible_check", value = "True for 'CHANGE_REQUEST' mode. False otherwise")
@DocDefault(field = "reversible_check_ignore_files", value = "None")
public void workflow(String workflowName, // <Revision>, but skylark allows only ?
Origin<?> origin, Destination<?> destination, Authoring authoring, net.starlark.java.eval.Sequence<?> transformations, Object originFiles, Object destinationFiles, String modeStr, Object reversibleCheckObj, boolean checkLastRevState, Boolean askForConfirmation, Boolean dryRunMode, net.starlark.java.eval.Sequence<?> afterMigrations, net.starlark.java.eval.Sequence<?> afterAllMigrations, Object changeIdentityObj, Boolean setRevId, Boolean smartPrune, Boolean migrateNoopChanges, Object customRevIdField, Object description, Boolean checkout, Object reversibleCheckIgnoreFiles, StarlarkThread thread) throws EvalException {
    WorkflowMode mode = stringToEnum("mode", modeStr, WorkflowMode.class);
    // Overwrite destination for testing workflow locally
    if (workflowOptions.toFolder) {
        destination = folderModule.destination();
    }
    Sequence sequenceTransform = Sequence.fromConfig(generalOptions.profiler(), workflowOptions, transformations, "transformations", printHandler, debugOptions::transformWrapper, Sequence.NoopBehavior.NOOP_IF_ANY_NOOP);
    Transformation reverseTransform = null;
    if (!generalOptions.isDisableReversibleCheck() && convertFromNoneable(reversibleCheckObj, mode == WorkflowMode.CHANGE_REQUEST)) {
        try {
            reverseTransform = sequenceTransform.reverse();
        } catch (NonReversibleValidationException e) {
            throw Starlark.errorf("%s", e.getMessage());
        }
    }
    ImmutableList<Token> changeIdentity = getChangeIdentity(changeIdentityObj);
    String customRevId = convertFromNoneable(customRevIdField, null);
    check(customRevId == null || CUSTOM_REVID_FORMAT.matches(customRevId), "Invalid experimental_custom_rev_id format. Format: %s", CUSTOM_REVID_FORMAT.pattern());
    if (setRevId) {
        check(mode != WorkflowMode.CHANGE_REQUEST || customRevId == null, "experimental_custom_rev_id is not allowed to be used in CHANGE_REQUEST mode if" + " set_rev_id is set to true. experimental_custom_rev_id is used for looking" + " for the baseline in the origin. No revId is stored in the destination.");
    } else {
        check(mode == WorkflowMode.CHANGE_REQUEST || mode == WorkflowMode.CHANGE_REQUEST_FROM_SOT, "'set_rev_id = False' is only supported" + " for CHANGE_REQUEST and CHANGE_REQUEST_FROM_SOT mode.");
    }
    if (smartPrune) {
        check(mode == WorkflowMode.CHANGE_REQUEST, "'smart_prune = True' is only supported" + " for CHANGE_REQUEST mode.");
    }
    if (checkLastRevState) {
        check(mode != WorkflowMode.CHANGE_REQUEST, "%s is not compatible with %s", CHECK_LAST_REV_STATE, WorkflowMode.CHANGE_REQUEST);
    }
    Authoring resolvedAuthoring = authoring;
    Author defaultAuthorFlag = workflowOptions.getDefaultAuthorFlag();
    if (defaultAuthorFlag != null) {
        resolvedAuthoring = new Authoring(defaultAuthorFlag, authoring.getMode(), authoring.getAllowlist());
    }
    WorkflowMode effectiveMode = generalOptions.squash ? WorkflowMode.SQUASH : mode;
    Workflow<Revision, ?> workflow = new Workflow<>(workflowName, convertFromNoneable(description, null), (Origin<Revision>) origin, destination, resolvedAuthoring, sequenceTransform, workflowOptions.getLastRevision(), workflowOptions.isInitHistory(), generalOptions, convertFromNoneable(originFiles, Glob.ALL_FILES), convertFromNoneable(destinationFiles, Glob.ALL_FILES), effectiveMode, workflowOptions, reverseTransform, convertFromNoneable(reversibleCheckIgnoreFiles, null), askForConfirmation, mainConfigFile, allConfigFiles, dryRunMode, checkLastRevState || workflowOptions.checkLastRevState, convertFeedbackActions(afterMigrations, printHandler), convertFeedbackActions(afterAllMigrations, printHandler), changeIdentity, setRevId, smartPrune, workflowOptions.migrateNoopChanges || migrateNoopChanges, customRevId, checkout);
    Module module = Module.ofInnermostEnclosingStarlarkFunction(thread);
    registerGlobalMigration(workflowName, workflow, module);
}
Also used : NonReversibleValidationException(com.google.copybara.exception.NonReversibleValidationException) SkylarkTransformation(com.google.copybara.transform.SkylarkTransformation) Transformations.toTransformation(com.google.copybara.transform.Transformations.toTransformation) Token(com.google.copybara.templatetoken.Token) Sequence(com.google.copybara.transform.Sequence) Authoring(com.google.copybara.authoring.Authoring) Revision(com.google.copybara.revision.Revision) Author(com.google.copybara.authoring.Author) LabelsAwareModule(com.google.copybara.config.LabelsAwareModule) FolderModule(com.google.copybara.folder.FolderModule) Module(net.starlark.java.eval.Module) StarlarkMethod(net.starlark.java.annot.StarlarkMethod) UsesFlags(com.google.copybara.doc.annotations.UsesFlags) DocDefault(com.google.copybara.doc.annotations.DocDefault)

Example 2 with Revision

use of com.google.copybara.revision.Revision in project copybara by google.

the class GitOriginTest method checkChangesMergeNoop.

@SuppressWarnings("unchecked")
private ImmutableList<? extends Change<?>> checkChangesMergeNoop(boolean importNoopChanges) throws Exception {
    RecordsProcessCallDestination destination = new RecordsProcessCallDestination();
    options.testingOptions.destination = destination;
    String author = "John Name <john@name.com>";
    singleFileCommit(author, "base", "base.txt", "");
    options.setLastRevision(repo.parseRef(defaultBranch));
    // Don't remove or add an include change before this commit. This allow us to test that we
    // traverse parents and not children:
    singleFileCommit(author, "exclude1", "exclude1", "");
    repo.branch("feature1").run();
    repo.branch("feature2").run();
    singleFileCommit(author, "main_branch_change", "one.txt", "");
    // Make sure one_change is shown before in git log.
    Thread.sleep(1100);
    git("checkout", "feature1");
    singleFileCommit(author, "feature1", "feature1.txt", "");
    git("checkout", defaultBranch);
    git("merge", defaultBranch, "feature1");
    String feature1Merge = repo.parseRef(defaultBranch);
    // Make sure one_change is shown before in git log.
    Thread.sleep(1100);
    git("checkout", "feature2");
    singleFileCommit(author, "change1", "base.txt", "base");
    // Revert
    singleFileCommit(author, "change2", "base.txt", "");
    singleFileCommit(author, "change3", "exclude.txt", "I should be excluded");
    git("checkout", defaultBranch);
    git("merge", defaultBranch, "feature2");
    String headSha1 = repo.parseRef(defaultBranch);
    Workflow<GitRevision, Revision> wf = (Workflow<GitRevision, Revision>) skylark.loadConfig("" + "core.workflow(\n" + "    name = 'default',\n" + "    origin = git.origin(\n" + "         url = '" + url + "',\n" + "         first_parent = False,\n" + "    ),\n" + "    origin_files = glob(['**'], exclude = ['exclude**']),\n" + "    destination = testing.destination(),\n" + "    migrate_noop_changes = " + (importNoopChanges ? "True" : "False") + ",\n" + "    authoring = authoring.pass_thru('example <example@example.com>'),\n" + ")\n").getMigration("default");
    wf.run(checkoutDir, ImmutableList.of(defaultBranch));
    String expected = importNoopChanges ? headSha1 : feature1Merge;
    String actual = Iterables.getLast(destination.processed).getOriginRef().asString();
    assertWithMessage(String.format("Expected:\n%s\nBut found:\n%s", Iterables.getOnlyElement(repo.log(expected).withLimit(1).run()), Iterables.getOnlyElement(repo.log(actual).withLimit(1).run()))).that(actual).isEqualTo(expected);
    return Iterables.getLast(destination.processed).getOriginChanges();
}
Also used : RecordsProcessCallDestination(com.google.copybara.testing.RecordsProcessCallDestination) Revision(com.google.copybara.revision.Revision) Workflow(com.google.copybara.Workflow)

Example 3 with Revision

use of com.google.copybara.revision.Revision in project copybara by google.

the class GitOriginTest method partialFetch_canFetchRootFile.

@Test
public void partialFetch_canFetchRootFile() throws Exception {
    RecordsProcessCallDestination destination = new RecordsProcessCallDestination();
    options.testingOptions.destination = destination;
    options.setLastRevision(firstCommitRef);
    Files.write(remote.resolve("file.txt"), new byte[0]);
    git("add", "file.txt");
    git("commit", "-m", "message");
    @SuppressWarnings("unchecked") Workflow<GitRevision, Revision> wf = (Workflow<GitRevision, Revision>) skylark.loadConfig("" + "core.workflow(\n" + "    name = 'default',\n" + "    origin = git.origin(\n" + "         url = '" + url + "',\n" + "         include_branch_commit_logs = True,\n" + "         partial_fetch = True,\n" + "    ),\n" + "    origin_files = glob(['directory/**', 'file.txt']),\n" + "    destination = testing.destination(),\n" + "    authoring = authoring.pass_thru('example <example@example.com>'),\n" + ")\n").getMigration("default");
    wf.run(Files.createTempDirectory("foo"), ImmutableList.of("HEAD"));
    List<ProcessedChange> changes = destination.processed;
    assertThat(changes).hasSize(1);
}
Also used : RecordsProcessCallDestination(com.google.copybara.testing.RecordsProcessCallDestination) ProcessedChange(com.google.copybara.testing.RecordsProcessCallDestination.ProcessedChange) Revision(com.google.copybara.revision.Revision) Workflow(com.google.copybara.Workflow) Test(org.junit.Test)

Example 4 with Revision

use of com.google.copybara.revision.Revision in project copybara by google.

the class GitOriginTest method partialFetchFailsWithFetchingTheWholeRepo.

@Test
public void partialFetchFailsWithFetchingTheWholeRepo() throws Exception {
    RecordsProcessCallDestination destination = new RecordsProcessCallDestination();
    options.testingOptions.destination = destination;
    options.setLastRevision(firstCommitRef);
    @SuppressWarnings("unchecked") Workflow<GitRevision, Revision> wf = (Workflow<GitRevision, Revision>) skylark.loadConfig("" + "core.workflow(\n" + "    name = 'default',\n" + "    origin = git.origin(\n" + "         url = '" + url + "',\n" + "         include_branch_commit_logs = True,\n" + "         partial_fetch = True,\n" + "    ),\n" + "    origin_files = glob(['**']),\n" + "    destination = testing.destination(),\n" + "    authoring = authoring.pass_thru('example <example@example.com>'),\n" + ")\n").getMigration("default");
    ValidationException repoException = assertThrows(ValidationException.class, () -> wf.run(Files.createTempDirectory("foo"), ImmutableList.of("HEAD")));
    assertThat(repoException).hasMessageThat().contains("Config error: partial_fetch feature is not compatible with fetching the whole repo.");
}
Also used : RecordsProcessCallDestination(com.google.copybara.testing.RecordsProcessCallDestination) ValidationException(com.google.copybara.exception.ValidationException) Revision(com.google.copybara.revision.Revision) Workflow(com.google.copybara.Workflow) Test(org.junit.Test)

Example 5 with Revision

use of com.google.copybara.revision.Revision in project copybara by google.

the class GitOriginTest method partialFetchAtGitOrigin.

@Test
public void partialFetchAtGitOrigin() throws Exception {
    Files.createDirectories(remote.resolve("include"));
    Files.write(remote.resolve("include/fileA.txt"), new byte[0]);
    git("add", "include/fileA.txt");
    git("commit", "-m", "not include");
    git("checkout", defaultBranch);
    Files.createDirectories(remote.resolve("include"));
    Files.write(remote.resolve("include/mainline-file.txt"), new byte[0]);
    git("add", "include/mainline-file.txt");
    git("commit", "-m", "message_a!");
    options.setForce(true);
    RecordsProcessCallDestination destination = new RecordsProcessCallDestination();
    options.testingOptions.destination = destination;
    options.setLastRevision(firstCommitRef);
    @SuppressWarnings("unchecked") Workflow<GitRevision, Revision> wf = (Workflow<GitRevision, Revision>) skylark.loadConfig("" + "core.workflow(\n" + "    name = 'default',\n" + "    origin = git.origin(\n" + "         url = '" + url + "',\n" + "         include_branch_commit_logs = True,\n" + "         partial_fetch = True,\n" + "    ),\n" + "    origin_files = glob(['include/mainline-file.txt']),\n" + "    destination = testing.destination(),\n" + "    mode = 'ITERATIVE',\n" + "    authoring = authoring.pass_thru('example <example@example.com>'),\n" + ")\n").getMigration("default");
    wf.run(Files.createTempDirectory("foo"), ImmutableList.of("HEAD"));
    List<ProcessedChange> changes = destination.processed;
    assertThat(changes).hasSize(1);
    assertThat(changes.get(0).getChangesSummary()).contains("message_a!");
}
Also used : RecordsProcessCallDestination(com.google.copybara.testing.RecordsProcessCallDestination) ProcessedChange(com.google.copybara.testing.RecordsProcessCallDestination.ProcessedChange) Revision(com.google.copybara.revision.Revision) Workflow(com.google.copybara.Workflow) Test(org.junit.Test)

Aggregations

Revision (com.google.copybara.revision.Revision)14 Test (org.junit.Test)10 Workflow (com.google.copybara.Workflow)6 RecordsProcessCallDestination (com.google.copybara.testing.RecordsProcessCallDestination)6 Config (com.google.copybara.config.Config)5 Migration (com.google.copybara.config.Migration)5 ValidationException (com.google.copybara.exception.ValidationException)5 Console (com.google.copybara.util.console.Console)5 IOException (java.io.IOException)5 ImmutableList (com.google.common.collect.ImmutableList)4 ImmutableMap (com.google.common.collect.ImmutableMap)4 MigrationReference (com.google.copybara.Info.MigrationReference)4 Author (com.google.copybara.authoring.Author)4 Change (com.google.copybara.revision.Change)4 DummyRevision (com.google.copybara.testing.DummyRevision)4 ProcessedChange (com.google.copybara.testing.RecordsProcessCallDestination.ProcessedChange)4 Path (java.nio.file.Path)4 ImmutableListMultimap (com.google.common.collect.ImmutableListMultimap)3 ImmutableMultimap (com.google.common.collect.ImmutableMultimap)3 Truth.assertThat (com.google.common.truth.Truth.assertThat)3