Build scripts to create publish folders and archive packages

This commit is contained in:
Leila Lali
2016-09-07 12:16:00 -07:00
parent 8ca88992be
commit 9fc32fa74e
12 changed files with 1170 additions and 9 deletions

104
scripts/archiving.cake Normal file
View File

@@ -0,0 +1,104 @@
#load "runhelpers.cake"
using System.IO.Compression;
using System.Text.RegularExpressions;
/// <summary>
/// Generate the build identifier based on the RID and framework identifier.
/// Special rules when running on Travis (for publishing purposes).
/// </summary>
/// <param name="runtime">The RID</param>
/// <param name="framework">The framework identifier</param>
/// <returns>The designated build identifier</returns>
string GetBuildIdentifier(string runtime, string framework)
{
var runtimeShort = "";
// Default RID uses package name set in build script
if (runtime.Equals("default"))
{
runtimeShort = Environment.GetEnvironmentVariable("SQLTOOLSSERVICE_PACKAGE_OSNAME");
}
else
{
// Remove version number. Note: because there are separate versions for Ubuntu 14 and 16,
// we treat Ubuntu as a special case.
if (runtime.StartsWith("ubuntu.14"))
{
runtimeShort = "ubuntu14-x64";
}
else if (runtime.StartsWith("ubuntu.16"))
{
runtimeShort = "ubuntu16-x64";
}
else
{
runtimeShort = Regex.Replace(runtime, "(\\d|\\.)*-", "-");
}
}
return $"{runtimeShort}-{framework}";
}
/// <summary>
/// Generate an archive out of the given published folder.
/// Use ZIP for Windows runtimes.
/// Use TAR.GZ for non-Windows runtimes.
/// Use 7z to generate TAR.GZ on Windows if available.
/// </summary>
/// <param name="runtime">The RID</param>
/// <param name="contentFolder">The folder containing the files to package</param>
/// <param name="archiveName">The target archive name (without extension)</param>
void DoArchive(string runtime, string contentFolder, string archiveName)
{
// On all platforms use ZIP for Windows runtimes
if (runtime.Contains("win") || (runtime.Equals("default") && IsRunningOnWindows()))
{
var zipFile = $"{archiveName}.zip";
Zip(contentFolder, zipFile);
}
// On all platforms use TAR.GZ for Unix runtimes
else
{
var tarFile = $"{archiveName}.tar.gz";
// Use 7z to create TAR.GZ on Windows
if (IsRunningOnWindows())
{
var tempFile = $"{archiveName}.tar";
try
{
Run("7z", $"a \"{tempFile}\"", contentFolder)
.ExceptionOnError($"Tar-ing failed for {contentFolder} {archiveName}");
Run("7z", $"a \"{tarFile}\" \"{tempFile}\"", contentFolder)
.ExceptionOnError($"Compression failed for {contentFolder} {archiveName}");
System.IO.File.Delete(tempFile);
}
catch(Win32Exception)
{
Information("Warning: 7z not available on PATH to pack tar.gz results");
}
}
// Use tar to create TAR.GZ on Unix
else
{
Run("tar", $"czf \"{tarFile}\" .", contentFolder)
.ExceptionOnError($"Compression failed for {contentFolder} {archiveName}");
}
}
}
/// <summary>
/// Package a given output folder using a build identifier generated from the RID and framework identifier.
/// </summary>
/// <param name="runtime">The RID</param>
/// <param name="framework">The framework identifier</param>
/// <param name="contentFolder">The folder containing the files to package</param>
/// <param name="packageFolder">The destination folder for the archive</param>
/// <param name="projectName">The project name</param>
void Package(string runtime, string framework, string contentFolder, string packageFolder, string projectName)
{
var buildIdentifier = GetBuildIdentifier(runtime, framework);
if (buildIdentifier != null)
{
DoArchive(runtime, contentFolder, $"{packageFolder}/{projectName}-{buildIdentifier}");
}
}

43
scripts/artifacts.cake Normal file
View File

@@ -0,0 +1,43 @@
#load "runhelpers.cake"
/// <summary>
/// Generate the scripts which target the SQLTOOLSSERVICE binaries.
/// </summary>
/// <param name="outputRoot">The root folder where the publised (or installed) binaries are located</param>
void CreateRunScript(string outputRoot, string scriptFolder)
{
if (IsRunningOnWindows())
{
var coreScript = System.IO.Path.Combine(scriptFolder, "SQLTOOLSSERVICE.Core.cmd");
var sqlToolsServicePath = System.IO.Path.Combine(System.IO.Path.GetFullPath(outputRoot), "{0}", "SQLTOOLSSERVICE");
var content = new string[] {
"SETLOCAL",
"",
$"\"{sqlToolsServicePath}\" %*"
};
if (System.IO.File.Exists(coreScript))
{
System.IO.File.Delete(coreScript);
}
content[2] = String.Format(content[2], "netcoreapp1.0");
System.IO.File.WriteAllLines(coreScript, content);
}
else
{
var coreScript = System.IO.Path.Combine(scriptFolder, "SQLTOOLSSERVICE.Core");
var sqlToolsServicePath = System.IO.Path.Combine(System.IO.Path.GetFullPath(outputRoot), "{1}", "SQLTOOLSSERVICE");
var content = new string[] {
"#!/bin/bash",
"",
$"{{0}} \"{sqlToolsServicePath}{{2}}\" \"$@\""
};
if (System.IO.File.Exists(coreScript))
{
System.IO.File.Delete(coreScript);
}
content[2] = String.Format(content[2], "", "netcoreapp1.0", "");
System.IO.File.WriteAllLines(coreScript, content);
Run("chmod", $"+x \"{coreScript}\"");
}
}

110
scripts/cake-bootstrap.ps1 Normal file
View File

@@ -0,0 +1,110 @@
<#
.SYNOPSIS
This is a Powershell script to bootstrap a Cake build.
.DESCRIPTION
This Powershell script will download NuGet if missing, restore NuGet tools (including Cake)
and execute your Cake build script with the parameters you provide.
.PARAMETER Script
The build script to execute.
.PARAMETER Target
The build script target to run.
.PARAMETER Configuration
The build configuration to use.
.PARAMETER Verbosity
Specifies the amount of information to be displayed.
Tells Cake to use the latest Roslyn release.
.PARAMETER WhatIf
Performs a dry run of the build script.
No tasks will be executed.
.PARAMETER Mono
Tells Cake to use the Mono scripting engine.
.LINK
http://cakebuild.net
#>
[CmdletBinding()]
Param(
[string]$Script = "build.cake",
[ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")]
[string]$Verbosity = "Verbose",
[Alias("DryRun","Noop")]
[switch]$WhatIf,
[switch]$Mono,
[switch]$SkipToolPackageRestore,
[Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)]
[string[]]$ScriptArgs
)
Write-Host "Preparing to run build script..."
$PS_SCRIPT_ROOT = split-path -parent $MyInvocation.MyCommand.Definition;
$TOOLS_DIR = Join-Path $PSScriptRoot "..\.tools"
$NUGET_EXE = Join-Path $TOOLS_DIR "nuget.exe"
$NUGET_URL = "https://dist.nuget.org/win-x86-commandline/v3.3.0/nuget.exe"
$CAKE_EXE = Join-Path $TOOLS_DIR "Cake/Cake.exe"
$PACKAGES_CONFIG = Join-Path $PS_SCRIPT_ROOT "packages.config"
# Should we use mono?
$UseMono = "";
if($Mono.IsPresent) {
Write-Verbose -Message "Using the Mono based scripting engine."
$UseMono = "-mono"
}
# Is this a dry run?
$UseDryRun = "";
if($WhatIf.IsPresent) {
$UseDryRun = "-dryrun"
}
# Make sure tools folder exists
if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) {
Write-Verbose -Message "Creating tools directory..."
New-Item -Path $TOOLS_DIR -Type directory | out-null
}
# Try download NuGet.exe if not exists
if (!(Test-Path $NUGET_EXE)) {
Write-Verbose -Message "Downloading NuGet.exe..."
try {
(New-Object System.Net.WebClient).DownloadFile($NUGET_URL, $NUGET_EXE)
} catch {
Throw "Could not download NuGet.exe."
}
}
# Save nuget.exe path to environment to be available to child processed
$ENV:NUGET_EXE = $NUGET_EXE
# Restore tools from NuGet?
if(-Not $SkipToolPackageRestore.IsPresent)
{
# Restore packages from NuGet.
Push-Location
Set-Location $TOOLS_DIR
Write-Verbose -Message "Restoring tools from NuGet..."
$NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install $PACKAGES_CONFIG -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`""
Write-Verbose -Message ($NuGetOutput | out-string)
Pop-Location
if ($LASTEXITCODE -ne 0)
{
exit $LASTEXITCODE
}
}
# Make sure that Cake has been installed.
if (!(Test-Path $CAKE_EXE)) {
Throw "Could not find Cake.exe at $CAKE_EXE"
}
# Start Cake
Write-Host "Running build script..."
Invoke-Expression "& `"$CAKE_EXE`" `"$Script`" -verbosity=`"$Verbosity`" $UseMono $UseDryRun $ScriptArgs"
exit $LASTEXITCODE

69
scripts/cake-bootstrap.sh Normal file
View File

@@ -0,0 +1,69 @@
#!/usr/bin/env bash
###############################################################
# This is the Cake bootstrapper script that is responsible for
# downloading Cake and all specified tools from NuGet.
###############################################################
# Define directories.
SCRIPT_DIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )
TOOLS_DIR=$SCRIPT_DIR/../.tools
export NUGET_EXE=$TOOLS_DIR/nuget.exe
CAKE_EXE=$TOOLS_DIR/Cake/Cake.exe
PACKAGES_CONFIG=$SCRIPT_DIR/packages.config
# Define default arguments.
SCRIPT="build.cake"
VERBOSITY="verbose"
DRYRUN=
SHOW_VERSION=false
SCRIPT_ARGUMENTS=()
# Parse arguments.
for i in "$@"; do
case $1 in
-s|--script) SCRIPT="$2"; shift ;;
-v|--verbosity) VERBOSITY="$2"; shift ;;
-d|--dryrun) DRYRUN="-dryrun" ;;
--version) SHOW_VERSION=true ;;
--) shift; SCRIPT_ARGUMENTS+=("$@"); break ;;
*) SCRIPT_ARGUMENTS+=("$1") ;;
esac
shift
done
# Make sure the tools folder exist.
if [ ! -d "$TOOLS_DIR" ]; then
mkdir "$TOOLS_DIR"
fi
# Download NuGet if it does not exist.
if [ ! -f "$NUGET_EXE" ]; then
echo "Downloading NuGet..."
curl -Lsfo "$NUGET_EXE" https://dist.nuget.org/win-x86-commandline/v3.3.0/nuget.exe
if [ $? -ne 0 ]; then
echo "An error occured while downloading nuget.exe."
exit 1
fi
fi
# Restore tools from NuGet.
pushd "$TOOLS_DIR" >/dev/null
mono "$NUGET_EXE" install "$PACKAGES_CONFIG" -ExcludeVersion -OutputDirectory "$TOOLS_DIR"
if [ $? -ne 0 ]; then
echo "Could not restore NuGet packages."
exit 1
fi
popd >/dev/null
# Make sure that Cake has been installed.
if [ ! -f "$CAKE_EXE" ]; then
echo "Could not find Cake.exe at '$CAKE_EXE'."
exit 1
fi
# Start Cake
if $SHOW_VERSION; then
exec mono "$CAKE_EXE" -version
else
exec mono "$CAKE_EXE" $SCRIPT -verbosity=$VERBOSITY $DRYRUN "${SCRIPT_ARGUMENTS[@]}"
fi

5
scripts/packages.config Normal file
View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Cake" version="0.10.1" />
<package id="Newtonsoft.Json" version="8.0.3" />
</packages>

204
scripts/runhelpers.cake Normal file
View File

@@ -0,0 +1,204 @@
using System.Collections.Generic;
using System.Diagnostics;
/// <summary>
/// Class encompassing the optional settings for running processes.
/// </summary>
public class RunOptions
{
/// <summary>
/// The working directory of the process.
/// </summary>
public string WorkingDirectory { get; set; }
/// <summary>
/// Container logging the StandardOutput content.
/// </summary>
public IList<string> StandardOutputListing { get; set; }
/// <summary>
/// Desired maximum time-out for the process
/// </summary>
public int TimeOut { get; set; }
}
/// <summary>
/// Wrapper for the exit code and state.
/// Used to query the result of an execution with method calls.
/// </summary>
public struct ExitStatus
{
private int _code;
private bool _timeOut;
/// <summary>
/// Default constructor when the execution finished.
/// </summary>
/// <param name="code">The exit code</param>
public ExitStatus(int code)
{
this._code = code;
this._timeOut = false;
}
/// <summary>
/// Default constructor when the execution potentially timed out.
/// </summary>
/// <param name="code">The exit code</param>
/// <param name="timeOut">True if the execution timed out</param>
public ExitStatus(int code, bool timeOut)
{
this._code = code;
this._timeOut = timeOut;
}
/// <summary>
/// Flag signalling that the execution timed out.
/// </summary>
public bool DidTimeOut { get { return _timeOut; } }
/// <summary>
/// Implicit conversion from ExitStatus to the exit code.
/// </summary>
/// <param name="exitStatus">The exit status</param>
/// <returns>The exit code</returns>
public static implicit operator int(ExitStatus exitStatus)
{
return exitStatus._code;
}
/// <summary>
/// Trigger Exception for non-zero exit code.
/// </summary>
/// <param name="errorMessage">The message to use in the Exception</param>
/// <returns>The exit status for further queries</returns>
public ExitStatus ExceptionOnError(string errorMessage)
{
if (this._code != 0)
{
throw new Exception(errorMessage);
}
return this;
}
}
/// <summary>
/// Run the given executable with the given arguments.
/// </summary>
/// <param name="exec">Executable to run</param>
/// <param name="args">Arguments</param>
/// <returns>The exit status for further queries</returns>
ExitStatus Run(string exec, string args)
{
return Run(exec, args, new RunOptions());
}
/// <summary>
/// Run the given executable with the given arguments.
/// </summary>
/// <param name="exec">Executable to run</param>
/// <param name="args">Arguments</param>
/// <param name="workingDirectory">Working directory</param>
/// <returns>The exit status for further queries</returns>
ExitStatus Run(string exec, string args, string workingDirectory)
{
return Run(exec, args,
new RunOptions()
{
WorkingDirectory = workingDirectory
});
}
/// <summary>
/// Run the given executable with the given arguments.
/// </summary>
/// <param name="exec">Executable to run</param>
/// <param name="args">Arguments</param>
/// <param name="runOptions">Optional settings</param>
/// <returns>The exit status for further queries</returns>
ExitStatus Run(string exec, string args, RunOptions runOptions)
{
var workingDirectory = runOptions.WorkingDirectory ?? System.IO.Directory.GetCurrentDirectory();
var process = System.Diagnostics.Process.Start(
new ProcessStartInfo(exec, args)
{
WorkingDirectory = workingDirectory,
UseShellExecute = false,
RedirectStandardOutput = runOptions.StandardOutputListing != null
});
if (runOptions.StandardOutputListing != null)
{
process.OutputDataReceived += (s, e) =>
{
if (e.Data != null)
{
runOptions.StandardOutputListing.Add(e.Data);
}
};
process.BeginOutputReadLine();
}
if (runOptions.TimeOut == 0)
{
process.WaitForExit();
return new ExitStatus(process.ExitCode);
}
else
{
bool finished = process.WaitForExit(runOptions.TimeOut);
if (finished)
{
return new ExitStatus(process.ExitCode);
}
else
{
KillProcessTree(process);
return new ExitStatus(0, true);
}
}
}
/// <summary>
/// Run restore with the given arguments
/// </summary>
/// <param name="exec">Executable to run</param>
/// <param name="args">Arguments</param>
/// <param name="runOptions">Optional settings</param>
/// <returns>The exit status for further queries</returns>
ExitStatus RunRestore(string exec, string args, string workingDirectory)
{
Information("Restoring packages....");
var p = StartAndReturnProcess(exec,
new ProcessSettings
{
Arguments = args,
RedirectStandardOutput = true,
WorkingDirectory = workingDirectory
});
p.WaitForExit();
var exitCode = p.GetExitCode();
if (exitCode == 0)
{
Information("Package restore successful!");
}
else
{
Error(string.Join("\n", p.GetStandardOutput()));
}
return new ExitStatus(exitCode);
}
/// <summary>
/// Kill the given process and all its child processes.
/// </summary>
/// <param name="process">Root process</param>
public void KillProcessTree(Process process)
{
// Child processes are not killed on Windows by default
// Use TASKKILL to kill the process hierarchy rooted in the process
if (IsRunningOnWindows())
{
StartProcess($"TASKKILL",
new ProcessSettings
{
Arguments = $"/PID {process.Id} /T /F",
});
}
else
{
process.Kill();
}
}