view mercurial-common/src/jetbrains/buildServer/buildTriggers/vcs/mercurial/command/CommitsAndMountPointsCommand.java @ 917:e467e67d5892 Hajipur-9.0.x

TW-39663 don't escape command output dedicated for parsing
author Dmitry Neverov <dmitry.neverov@jetbrains.com>
date Tue, 13 Jan 2015 20:52:21 +0100
parents 194c8f8d6e4c
children 7bf4d943d5bb
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.command;

import jetbrains.buildServer.buildTriggers.vcs.mercurial.*;
import jetbrains.buildServer.util.FileUtil;
import jetbrains.buildServer.vcs.VcsException;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.File;
import java.io.IOException;
import java.util.Date;

import static jetbrains.buildServer.buildTriggers.vcs.mercurial.command.CommitsAndMountPointsParser.parseCommits;
import static jetbrains.buildServer.buildTriggers.vcs.mercurial.command.CommitsAndMountPointsParser.parseFileLog;

/**
 * Created 03.01.14 14:53
 *
 * @author Eugene Petrenko (eugene.petrenko@jetbrains.com)
 */
public class CommitsAndMountPointsCommand extends VcsRootCommand {
  private static final HgVersion REQUIRED_VERSION = new HgVersion(2, 0, 0);
  private final HgRepo myRepo;

  public CommitsAndMountPointsCommand(@NotNull final HgRepo hgRepo,
                                      @NotNull final CommandSettings commandSettings,
                                      @NotNull final String hgPath,
                                      @NotNull final File workingDir,
                                      @NotNull final AuthSettings authSettings) {
    super(commandSettings.addHgEnv("HGENCODING", "UTF-8"), hgPath, workingDir, authSettings);
    myRepo = hgRepo;
  }

  @NotNull
  private File createTmpDir() throws VcsException {
    try {
      return HgFileUtil.createTempDir();
    } catch (IOException e) {
      throw new VcsException("Unable to create temporary directory", e);
    }
  }

  public void call(@NotNull final Callback consumer) throws VcsException {
    final HgVersion hgVersion = myRepo.version().call();
    if (!hgVersion.isEqualsOrGreaterThan(REQUIRED_VERSION)) {
      throw new VcsException("Current mercurial version is " + hgVersion + ", please use " + REQUIRED_VERSION + " or newer");
    }

    final File root = createTmpDir();

    try {
      callImpl(root, consumer);
    } finally {
      FileUtil.delete(root);
    }
  }
  
  public interface Callback {
    void processHGSubFile(@NotNull final String fileId, @NotNull final String file);
    void processHGSubStateFile(@NotNull final String fileId, @NotNull final String file);
    void onCommit(
            @NotNull String commitNum,
            @NotNull String commitId,
            @NotNull String[] parents,
            @NotNull String branch,
            @NotNull String[] tags,
            @NotNull String author,
            @NotNull String message,
            @NotNull final Date timestamp,
            @Nullable String hgsubNodeId,
            @Nullable String hgsubstateNodeId);
  }

  private void callImpl(@NotNull final File root,
                        @NotNull final Callback consumer) throws VcsException {
    final MercurialCommandLine cli = createCommandLine();
    cli.addParameter("--debug");

    setupExtensionsFromResource(cli, root, "load-substates-command.py");

    cli.addParameter("load-substates");
    cli.addParameter(new File(root, "result").getPath());

    final CommandResult res = runCommand(cli);
    final String rawOutput = res.getRawStdout();

    if (!rawOutput.contains("##Completed##")) throw new VcsException("Command failed: " + res.getSecureStdout());

    try {
      parseFileLog(new File(root, "result.hgsub"), new CommitsAndMountPointsParser.ContentsConsumer() {
        public void onCommit(@NotNull final String fileNodeId, @NotNull final String content) {
          consumer.processHGSubFile(fileNodeId, content);
        }
      });
      parseFileLog(new File(root, "result.hgsubstate"), new CommitsAndMountPointsParser.ContentsConsumer() {
        public void onCommit(@NotNull final String fileNodeId, @NotNull final String content) {
          consumer.processHGSubStateFile(fileNodeId, content);
        }
      });

      parseCommits(new File(root, "result.commits"), new CommitsAndMountPointsParser.CommitsConsumer() {
        public void onCommit(@NotNull String commitNum,
                             @NotNull String commitId,
                             @NotNull String[] parents,
                             @NotNull String branch,
                             @NotNull String[] tags,
                             @NotNull String author,
                             @NotNull String message,
                             @NotNull Date timestamp,
                             @Nullable String hgsubNodeId,
                             @Nullable String hgsubstateNodeId) {
          consumer.onCommit(commitNum, commitId, parents, branch, tags, author, message, timestamp, hgsubNodeId, hgsubstateNodeId);
        }
      });
    } catch (IOException e) {
      throw new VcsException("Failed to parse response files for 'load-substates' command. " + e.getMessage(), e);
    }
  }



}