in Tasks/SshV0/ssh.ts [16:189]
async function run() {
let sshClientConnection: any;
let cleanUpScriptCmd: string;
const remoteCmdOptions: sshHelper.RemoteCommandOptions = new sshHelper.RemoteCommandOptions();
try {
tl.setResourcePath(path.join(__dirname, 'task.json'));
//read SSH endpoint input
const sshEndpoint = tl.getInput('sshEndpoint', true);
const username: string = tl.getEndpointAuthorizationParameter(sshEndpoint, 'username', false);
const password: string = tl.getEndpointAuthorizationParameter(sshEndpoint, 'password', true); //passphrase is optional
const privateKey: string = process.env['ENDPOINT_DATA_' + sshEndpoint + '_PRIVATEKEY']; //private key is optional, password can be used for connecting
const hostname: string = tl.getEndpointDataParameter(sshEndpoint, 'host', false);
const port: number = getServerPort(sshEndpoint); //port is optional, will use 22 as default port if not specified
const interactiveSession: boolean = tl.getBoolInput('interactiveSession', false);
const readyTimeout = getReadyTimeoutVariable();
//setup the SSH connection configuration based on endpoint details
const sshConfig: ConnectConfig = {
host: hostname,
port: port,
username: username,
readyTimeout: readyTimeout
};
if (privateKey) {
tl.debug('Using private key for ssh connection.');
sshConfig.privateKey = privateKey;
sshConfig.passphrase = password;
} else {
//use password
tl.debug('Using username and password for ssh connection.');
sshConfig.password = password;
}
//read the run options
const runOptions: string = tl.getInput('runOptions', true);
let commands: string[];
let scriptFile: string;
let args: string;
if (runOptions === 'commands') {
// Split on '\n' and ';', flatten, and remove empty entries
commands = tl.getDelimitedInput('commands', '\n', true)
.map(s => s.split(';'))
.reduce((a, b) => a.concat(b))
.filter(s => s.length > 0);
} else if (runOptions === 'inline') {
let inlineScript: string = tl.getInput('inline', true);
const interpreterCommand: string = tl.getInput('interpreterCommand');
if (inlineScript && !inlineScript.startsWith('#!') && interpreterCommand) {
tl.debug('No script header detected. Adding: #!' + interpreterCommand);
inlineScript = `#!${interpreterCommand}${os.EOL}${inlineScript}`;
}
const tempDir: string = tl.getVariable('Agent.TempDirectory') || os.tmpdir();
const scriptName: string = `sshscript_${generateRandomUUID()}`; // default name
scriptFile = path.join(tempDir, scriptName);
try {
// Make sure the directory exists or else we will get ENOENT
if (!fs.existsSync(tempDir)) {
tl.mkdirP(tempDir);
}
fs.writeFileSync(scriptFile, inlineScript);
} catch (err) {
tl.error(tl.loc('FailedToWriteScript', err.message));
tryDeleteFile(scriptFile);
throw err;
}
} else {
scriptFile = tl.getPathInput('scriptPath', true, true);
args = tl.getInput('args');
}
const failOnStdErr: boolean = tl.getBoolInput('failOnStdErr');
remoteCmdOptions.failOnStdErr = failOnStdErr;
//setup the SSH connection
console.log(tl.loc('SettingUpSshConnection', sshConfig.username, sshConfig.host, sshConfig.port));
try {
sshClientConnection = await sshHelper.setupSshClientConnection(sshConfig);
} catch (err) {
tl.setResult(tl.TaskResult.Failed, tl.loc('ConnectionFailed', err));
}
if (sshClientConnection) {
//SSH connection successful
console.log(tl.loc('SshConnectionSuccessful'));
if (runOptions === 'commands') {
//run commands specified by the user
for (const command of commands) {
tl.debug(`Running command ${command} on remote machine.`);
console.log(command);
const returnCode: string = await sshHelper.runCommandOnRemoteMachine(
command, sshClientConnection, remoteCmdOptions, password, interactiveSession);
tl.debug(`Command ${command} completed with return code = ${returnCode}`);
}
} else {
// both other runOptions: inline and script
// setup script path on remote machine relative to user's $HOME directory
let remoteScriptPath: string = `./${path.basename(scriptFile)}`;
const isWin: boolean = (os.platform() === 'win32');
tl.debug(`remoteScriptPath = ${remoteScriptPath}`);
//setup the scp configuration based on endpoint details
const scpConfig: sshHelper.ScpConfig = {
host: hostname,
port: port,
username: username,
};
if (privateKey) {
scpConfig.privateKey = privateKey;
scpConfig.passphrase = password;
} else {
scpConfig.password = password;
}
//copy script file to remote machine
tl.debug('Copying script to remote machine.');
await sshHelper.copyScriptToRemoteMachine(scriptFile, remoteScriptPath, scpConfig);
//change the line encodings
let originalScriptPath: string = '';
if (isWin) {
tl.debug('Fixing the line endings in case the file was created in Windows');
originalScriptPath = remoteScriptPath;
remoteScriptPath = await sshHelper.clearFileFromWindowsCRLF(sshClientConnection, remoteCmdOptions, originalScriptPath);
}
//set execute permissions on the script
tl.debug('Setting execute permission on script copied to remote machine');
console.log(`chmod +x ${remoteScriptPath}`);
await sshHelper.runCommandOnRemoteMachine(`chmod +x ${remoteScriptPath}`, sshClientConnection, remoteCmdOptions);
//run remote script file with args on the remote machine
let runScriptCmd = remoteScriptPath;
if (args) {
runScriptCmd = runScriptCmd.concat(' ' + args);
}
//setup command to clean up script file
cleanUpScriptCmd = `rm -f ${remoteScriptPath}`;
if (isWin) {
cleanUpScriptCmd = `rm -f ${remoteScriptPath} ${originalScriptPath}`;
}
console.log(runScriptCmd);
await sshHelper.runCommandOnRemoteMachine(
runScriptCmd, sshClientConnection, remoteCmdOptions, password, interactiveSession);
}
}
} catch (err) {
tl.setResult(tl.TaskResult.Failed, err);
} finally {
//clean up script file if needed
if (cleanUpScriptCmd) {
try {
tl.debug('Deleting the script file copied to the remote machine.');
await sshHelper.runCommandOnRemoteMachine(
cleanUpScriptCmd, sshClientConnection, remoteCmdOptions);
} catch (err) {
tl.warning(tl.loc('RemoteScriptFileCleanUpFailed', err));
}
}
//close the client connection to halt build execution
if (sshClientConnection) {
tl.debug('Closing the SSH client connection.');
sshClientConnection.end();
}
}
}