content: package org.ajoberstar.gradle.git.publish;
  
  import org.ajoberstar.gradle.git.publish.tasks.GitPublishCommit;
  import org.ajoberstar.gradle.git.publish.tasks.GitPublishPush;
  import org.ajoberstar.gradle.git.publish.tasks.GitPublishReset;
  import org.gradle.api.Plugin;
  import org.gradle.api.Project;
  import org.gradle.api.provider.Provider;
  import org.gradle.api.provider.ProviderFactory;
  import org.gradle.api.tasks.Copy;
  import org.gradle.api.tasks.TaskProvider;
  import org.gradle.util.GradleVersion;
  
  public class GitPublishPlugin implements Plugin<Project> {
    @Override
    public void apply(Project project) {
      var extension = project.getExtensions().create("gitPublish", GitPublishExtension.class, project);
  
      // create the default
      extension.getPublications().create("main");
  
      // configure defaults and tasks for each publication
      extension.getPublications().configureEach(publication -> {
        configurePublicationDefaults(project, publication);
  
        var reset = createResetTask(project, publication);
        var copy = createCopyTask(project, publication);
        var commit = createCommitTask(project, publication);
        var push = createPushTask(project, publication);
  
        push.configure(t -> t.dependsOn(commit));
        commit.configure(t -> t.dependsOn(copy));
        copy.configure(t -> t.dependsOn(reset));
      });
  
      // add helper task to push all publications
      project.getTasks().register("gitPublishPushAll", task -> {
        task.setGroup("publishing");
        task.setDescription("Pushes all publications to git");
        task.dependsOn(project.getTasks().withType(GitPublishPush.class));
      });
    }
  
    private void configurePublicationDefaults(Project project, GitPublication publication) {
      publication.getCommitMessage().set("Generated by gradle-git-publish.");
      publication.getRepoUri().set(getOriginUriProvider(project.getProviders()));
      publication.getReferenceRepoUri().set(getGitDirProvider(project.getProviders()));
      publication.getRepoDir().set(project.getLayout().getBuildDirectory().dir("gitPublish/" + publication.getName()));
    }
  
    private TaskProvider<GitPublishReset> createResetTask(Project project, GitPublication publication) {
      return project.getTasks().register(getTaskName(publication, "Reset"), GitPublishReset.class, task -> {
        task.setGroup("publishing");
        task.setDescription("Prepares a git repo for " + publication.getName() + " publication content to be generated.");
        task.getRepoDir().set(publication.getRepoDir());
        task.getRepoUri().set(publication.getRepoUri());
        task.getReferenceRepoUri().set(publication.getReferenceRepoUri());
        task.getBranch().set(publication.getBranch());
        task.getFetchDepth().set(publication.getFetchDepth());
        task.setPreserve(publication.getPreserve());
        task.getUsername().set(publication.getUsername());
        task.getPassword().set(publication.getPassword());
      });
    }
  
    private TaskProvider<Copy> createCopyTask(Project project, GitPublication publication) {
      return project.getTasks().register(getTaskName(publication, "Copy"), Copy.class, task -> {
        task.setGroup("publishing");
        task.setDescription("Copy " + publication.getName() + " publication contents to be published to git.");
        task.with(publication.getContents());
        task.into(publication.getRepoDir());
  
        // do not track state added in Gradle 7.3
        if (GradleVersion.version("7.3").compareTo(GradleVersion.current()) <= 0) {
          task.doNotTrackState("Tracked by Git instead");
        }
      });
    }
  
    private TaskProvider<GitPublishCommit> createCommitTask(Project project, GitPublication publication) {
      return project.getTasks().register(getTaskName(publication, "Commit"), GitPublishCommit.class, task -> {
        task.setGroup("publishing");
        task.setDescription("Commits " + publication.getName() + " publication changes to be published to git.");
        task.getRepoDir().set(publication.getRepoDir());
        task.getMessage().set(publication.getCommitMessage());
        task.getSign().set(publication.getSign());
      });
    }
  
    private TaskProvider<GitPublishPush> createPushTask(Project project, GitPublication publication) {
      return project.getTasks().register(getTaskName(publication, "Push"), GitPublishPush.class, task -> {
        task.setGroup("publishing");
        task.setDescription("Pushes " + publication.getName() + " publication changes to git.");
        task.getRepoDir().set(publication.getRepoDir());
        task.getBranch().set(publication.getBranch());
        task.getUsername().set(publication.getUsername());
        task.getPassword().set(publication.getPassword());
      });
    }
  
    private Provider<String> getOriginUriProvider(ProviderFactory providers) {
      return providers.of(GitCliValueSource.class, spec -> {
        spec.getParameters().getGitArguments().addAll("remote", "get-url", "origin");
      });
    }
  
    private Provider<String> getGitDirProvider(ProviderFactory providers) {
      return providers.of(GitCliValueSource.class, spec -> {
        spec.getParameters().getGitArguments().addAll("rev-parse", "--absolute-git-dir");
      });
    }
  
    private String getTaskName(GitPublication publication, String task) {
      if ("main".equals(publication.getName())) {
        return "gitPublish" + task;
      } else {
        var capitalizedName = publication.getName().substring(0, 1).toUpperCase() + publication.getName().substring(1);
        return "gitPublish" + capitalizedName + task;
      }
    }
  }
  