At work i wanted to automate the build releasing process (of mostly c# desktop/mobile applications which are developed using VS and distributed as installers). This is basically giving the QA a fresh versioned build of whatever the software that is being developed. Although it sound pretty easy and straightforward. It is not. And i have come to hate doing this manually, because it takes a lot of time and its repetitive work, so its very… boring!. The following CLI program that i wrote just automates:
- Update the working folder from the SVN repo
- Clean
- Build (using Release config).
- Zip (only the Installer(s) resulting from the final build).
- Extract the version of the resulting executables by reading the AssemblyInfo.cs of the main project.
- Name the zip file according to all the conventions (boring…).
- Copy it over to the QA’s SMB share, and put it in a folder structure (again some more naming conventions come in to play).
I know this is not the nicest way of doing this. I should probably be using MSBuild/nAnt/a continues integration server…etc to do it. But, it felt a bit more complicated, and it required me to spend a little bit of time learning stuff. So… i ended up writing this.
#region
using System;
using System.Diagnostics;
using System.IO;
#endregion
namespace auto_build
{
internal class Program
{
private const string Title = "auto_build v0.01";
private static void Main(string[] args)
{
Console.Title = Title;
var settings = Settings.Default;
PSeperator();
P(Title);
PSeperator();
P("Settings");
PSeperator();
P("Updating Source from SVN...");
Run(settings.SvnPath, string.Format(settings.SvnUpdateCommand, settings.SolutionRoot));
PBlankLine();
P("Cleaning Solution...");
Run(settings.DevenvPath, string.Format(settings.DevenvClean, settings.SolutionPath, settings.SolutionName));
PBlankLine();
P("Building (Release Config) Solution...");
Run(settings.DevenvPath,
string.Format(settings.DevenvBuildRelease, settings.SolutionPath, settings.SolutionName));
PBlankLine();
P("Zipping the Setup...");
//ex: "NAME-SG v1.0.0.3106 Setup 06-Jul-2010 10-40"
var zipFilePath = string.Format("{0}\\Release", settings.SetupProjectPath);
var zipFileName = string.Format(settings.ZipFileNameFormat, settings.ZipFileApplicationName,
GetProjectVersion(),
DateTime.Now.ToString(settings.ZipFileNameDateTimeFormat));
Run(settings.WinrarPath,
string.Format(settings.WinrarPathArgsForCreatingAnArchive, zipFileName, zipFilePath));
PBlankLine();
P("Coping the zipped archive to the destanation...");
var directory =
Directory.CreateDirectory(string.Format("{0}//v{1}", settings.DestPath, GetProjectVersion()));
File.Move(string.Format("{0}//{1}", zipFilePath, zipFileName),
string.Format("{0}//{1}", directory.FullName, zipFileName));
PBlankLine();
P("Done!");
P("Press any key to exit...");
Console.ReadLine();
}
private static string GetProjectVersion()
{
using (
var reader =
new StreamReader(
new FileStream(string.Format(Settings.Default.AssemblyInfoPath, Settings.Default.SolutionPath),
FileMode.Open, FileAccess.Read)))
{
while (!reader.EndOfStream)
{
var line = reader.ReadLine();
if (!line.Contains("// [assembly: AssemblyVersion(\"") &&
line.Contains("[assembly: AssemblyVersion(\""))
{
var start = line.IndexOf("[assembly: AssemblyVersion(\"") +
"[assembly: AssemblyVersion(\"".Length;
var end = line.IndexOf("\")]");
var length = end - start;
return line.Substring(start, length);
}
}
}
return "x.x.x.x"; //couldn't find the assebmly version
}
private static string Run(string command, string paras)
{
var pProcess = new Process
{
StartInfo =
{
FileName = command,
Arguments = paras,
UseShellExecute = false,
RedirectStandardOutput = true
}
};
pProcess.Start();
//Get program output
var strOutput = pProcess.StandardOutput.ReadToEnd();
pProcess.WaitForExit();
return strOutput;
}
#region Console Text Output Helpers
private static void P(string text)
{
Console.WriteLine(text);
}
private static void PBlankLine()
{
P(string.Empty);
}
private static void PSeperator()
{
PBlankLine();
P("------------------------------------------------------");
PBlankLine();
}
#endregion
}
}
The program above uses a settings file to get most of the information it requires. you can get the settings file and the above posted file here.