view mercurial-server/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/MercurialCommitSupport.java @ 869:d89e84bd4b60

Clean temp dirs after commit
author Dmitry Neverov <dmitry.neverov@jetbrains.com>
date Mon, 22 Sep 2014 10:11:51 +0200
parents 80d695d15cc0
children 207c4ec46d54
line wrap: on
line source
/*
 * Copyright 2000-2014 JetBrains s.r.o.
 *
 * 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 jetbrains.buildServer.buildTriggers.vcs.mercurial;

import com.intellij.openapi.diagnostic.Logger;
import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.HgVcsRoot;
import jetbrains.buildServer.buildTriggers.vcs.mercurial.command.PushCommand;
import jetbrains.buildServer.util.FileUtil;
import jetbrains.buildServer.vcs.*;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.*;
import java.util.HashSet;
import java.util.Set;

public class MercurialCommitSupport implements CommitSupport, MercurialServerExtension {

  private final static Logger LOG = Logger.getInstance(MercurialCommitSupport.class.getName());

  private final MercurialVcsSupport myVcs;
  private final MirrorManager myMirrorManager;
  private final ServerPluginConfig myConfig;
  private final HgVcsRootFactory myHgVcsRootFactory;
  private final HgRepoFactory myHgRepoFactory;


  public MercurialCommitSupport(@NotNull MercurialVcsSupport vcs,
                                @NotNull MirrorManager mirrorManager,
                                @NotNull ServerPluginConfig config,
                                @NotNull HgVcsRootFactory vcsRootFactory,
                                @NotNull HgRepoFactory hgRepoFactory) {
    myVcs = vcs;
    myMirrorManager = mirrorManager;
    myConfig = config;
    myHgVcsRootFactory = vcsRootFactory;
    myHgRepoFactory = hgRepoFactory;
    myVcs.addExtension(this);
  }

  @NotNull
  public CommitPatchBuilder getCommitPatchBuilder(@NotNull VcsRoot root) {
    File patchDir;
    try {
      patchDir = HgFileUtil.createTempDir();
    } catch (IOException e) {
      return new ErrorCommitPatchBuilder("Error while creating patch dir", e);
    }
    File checkoutDir;
    try {
      checkoutDir = HgFileUtil.createTempDir();
    } catch (IOException e) {
      return new ErrorCommitPatchBuilder("Error while creating checkout dir", e);
    }

    HgVcsRoot hgRoot;
    try {
      hgRoot = myHgVcsRootFactory.createHgRoot(root);
    } catch (VcsException e) {
      return new ErrorCommitPatchBuilder("Error while creating mercurial root", e);
    }
    return new MercurialCommitPatchBuilder(root, hgRoot, patchDir, checkoutDir);
  }


  private class MercurialCommitPatchBuilder implements CommitPatchBuilder {
    private final VcsRoot myRoot;
    private final HgVcsRoot myHgRoot;
    private final File myPatchDir;
    private final File myCheckoutDir;
    private final Set<String> myCreatedFiles = new HashSet<String>();
    private final Set<String> myDeletedFiles = new HashSet<String>();
    private final Set<String> myDeletedDirs = new HashSet<String>();
    private Exception myError;

    public MercurialCommitPatchBuilder(@NotNull VcsRoot root, @NotNull HgVcsRoot hgRoot, @NotNull File patchDir, @NotNull File checkoutDir) {
      myRoot = root;
      myHgRoot = hgRoot;
      myPatchDir = patchDir;
      myCheckoutDir = checkoutDir;
    }

    public void createFile(@NotNull String path, @NotNull InputStream content) {
      OutputStream out = null;
      try {
        File f = new File(myPatchDir, path);
        f.getParentFile().mkdirs();
        out = new BufferedOutputStream(new FileOutputStream(f));
        FileUtil.copy(content, out);
        myCreatedFiles.add(path);
      } catch (IOException e) {
        LOG.error("Error while creating file " + path, e);
        myError = e;
      } finally {
        if (out != null) {
          try {
            out.close();
          } catch (IOException e) {
            LOG.error(e);
          }
        }
      }
    }

    public void createDirectory(@NotNull String path) {
      //ignore
    }

    public void deleteFile(@NotNull String path) {
      myDeletedFiles.add(path);
    }

    public void deleteDirectory(@NotNull String path) {
      myDeletedDirs.add(path);
    }

    public void renameFile(@NotNull String oldPath, @NotNull String newPath, @NotNull InputStream content) {
      myDeletedFiles.add(oldPath);
      createFile(newPath, content);
    }

    @Nullable
    public String commit(@NotNull String userName, @NotNull String description) throws VcsException {
      if (myError != null)
        throw new VcsException(myError);


      try {
        RepositoryStateData state = myVcs.getCollectChangesPolicy().getCurrentState(myRoot);
        String defaultBranch = state.getDefaultBranchName();
        String defaultBranchRevision = state.getBranchRevisions().get(defaultBranch);

        new CheckoutRepository(myMirrorManager, myHgRepoFactory, myConfig.getPullTimeout(), myConfig.useTagsAsBranches(),
                myHgRoot, myCheckoutDir).setRevision(defaultBranchRevision).checkout();

        HgRepo repo = myHgRepoFactory.createRepo(myHgRoot, myCheckoutDir);
        for (String dir : myDeletedDirs) {
          FileUtil.delete(new File(myCheckoutDir, dir));
        }
        for (String f : myDeletedFiles) {
          FileUtil.delete(new File(myCheckoutDir, f));
        }
        for (String f : myCreatedFiles) {
          FileUtil.copy(new File(myPatchDir, f), new File(myCheckoutDir, f));
        }

        repo.addRemove().call();
        int exitCode = repo.commit().by(userName).message(description).call();
        if (exitCode == 1)
          return null;

        boolean bookmark = repo.isBookmark(defaultBranch);
        if (bookmark)
          repo.updateBookmark().name(defaultBranch).call();

        PushCommand push = repo.push().toRepository(myHgRoot.getRepository());
        if (bookmark)
          push.bookmark(defaultBranch);
        push.call();

        return repo.getWorkingDirRevision();
      } catch (Exception e) {
        throw new VcsException(e);
      } finally {
        FileUtil.delete(myPatchDir);
        FileUtil.delete(myCheckoutDir);
      }
    }
  }


  private class ErrorCommitPatchBuilder implements CommitPatchBuilder {
    private final String myMessage;
    private final Exception myError;
    public ErrorCommitPatchBuilder(@NotNull String message, @NotNull Exception error) {
      myMessage = message;
      myError = error;
    }

    public void createFile(@NotNull String path, @NotNull InputStream content) {
    }
    public void createDirectory(@NotNull String path) {
    }
    public void deleteFile(@NotNull String path) {
    }
    public void deleteDirectory(@NotNull String path) {
    }
    public void renameFile(@NotNull String oldPath, @NotNull String newPath, @NotNull InputStream content) {
    }
    @Nullable
    public String commit(@NotNull String userName, @NotNull String description) throws VcsException {
      throw new VcsException(myMessage, myError);
    }
  }
}