Custom plugin with a Build Feature that executes a long running process

I am developing a custom TeamCity plugin that integrates with an enterprise deployment platform. It should deploy the binaries at the end of the build to some deployment platform by executing a command line command. Currently there is a class that extends the BuildFeature class on the server side to setup the details of artifacts to be deployed. On the agent side there is a class that extends AgentLifeCycleAdapter class, which overrides the beforeBuildFinish() method and executes a long running command line process. I am using SimpleCommandLineProcessRunner class to execute the external command line process:

 

final ExecResult result = SimpleCommandLineProcessRunner.runCommand(commandLine,
null,
new SimpleCommandLineProcessRunner.RunCommandEventsAdapter());


The process is being stopped after two minutes, which looks like some timeout:

[18:13:50]Running as atom_builder
[18:13:50]Executing C:\TeamCity\buildAgent\work\db4107aa7e390a67\adeploy\adeploy.exe artifactVersion push C:\TeamCity\buildAgent\work\db4107aa7e390a67\agent\atom-agent-artifact-version.xml 
[18:13:50]Running at C:\TeamCity\buildAgent\work\db4107aa7e390a67\agent
[18:15:22]2018-11-07 18:13:51 [Information] ["ArtifactPushService:PushArtifactAsync"] Calling method with parameters '"adeploy.exe"
[18:15:22]Exit code: -1
 
What is the proper way to execute long running processes as part of the build when the build configuration has a custom Build Feature? 
1 comment
Comment actions Permalink

I found out the answer, so I will post it here, in case somebody else encounter the same problem.

Since we are talking about custom plugin, it's completely in the code of the agent -side of the plugin that executes the external process. As stated in the question, the external process is executed using SimpleCommandLineProcessRunner class and it passes an instance of RunCommandEventsAdapter class. The last one returns null in getOutputIdleSecondsTimeout() method. It means that SimpleCommandLineProcessRunner will use default timeout of 90 seconds unless it's defined in teamcity.execution.timeout parameter.

So the solution is to define a LongRunningProcessRunCallback class that implements ProcessRunCallback interface. Put attention that getOutputIdleSecondsTimeout() returns a pretty long timeout.

public class LongRunningProcessRunCallback implements SimpleCommandLineProcessRunner.ProcessRunCallback {

    private static final int DEFAULT_TIMEOUT_SECONDS = 60 * 60 * 24;

    @Nullable
    @Override
    public Integer getMaxAcceptedOutputSize() {
        return null;
    }

    @Override
    public void onProcessStarted(@NotNull Process process) {

    }

    @Override
    public void onProcessFinished(@NotNull Process process) {

    }

    @Nullable
    @Override
    public Integer getOutputIdleSecondsTimeout() {
        return TeamCityProperties.getInteger("teamcity.execution.timeout", DEFAULT_TIMEOUT_SECONDS);
    }
}

And then pass it to SimpleCommandLieProcessRunner:

final ExecResult result = SimpleCommandLineProcessRunner.runCommand(commandLine,
                null,
                new LongRunningProcessRunCallback());
1

Please sign in to leave a comment.