Skip to content

Instantly share code, notes, and snippets.

@Sharparam
Created October 14, 2012 14:20
Show Gist options
  • Save Sharparam/3888726 to your computer and use it in GitHub Desktop.
Save Sharparam/3888726 to your computer and use it in GitHub Desktop.
WoW AddOn packager (with Git support using NGit)
using System;
namespace F16Gaming.WoW.AddonPackager.Extensions
{
public static class DateTimeExtensions
{
public static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, 0);
public static long ToUnixTimestamp(this DateTime time)
{
return (long) Math.Floor((time - Epoch).TotalSeconds);
}
}
}
using System;
namespace F16Gaming.WoW.AddonPackager.Git
{
public class GitInfo
{
private static readonly DateTime Origin = new DateTime(1970, 1, 1, 0, 0, 0, 0);
public string Name;
public string Path;
public string Hash { get; internal set; }
public string ShortHash { get { return Hash.Substring(0, 6); } }
public string Message { get; internal set; }
public string FullMessage { get; internal set; }
public string Author { get; internal set; }
public string Committer { get; internal set; }
public DateTime Time { get; internal set; }
public string Tag { get; internal set; }
internal GitInfo()
{
}
internal static DateTime ParseTime(int time)
{
return Origin + TimeSpan.FromSeconds(time);
}
}
}
using System;
namespace F16Gaming.WoW.AddonPackager.Extensions
{
public static class Int64Extensions
{
public static DateTime ToDateTime(this Int64 timestamp)
{
return DateTimeExtensions.Epoch + TimeSpan.FromSeconds(timestamp);
}
}
}
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using ICSharpCode.SharpZipLib.Core;
using ICSharpCode.SharpZipLib.Zip;
using F16Gaming.WoW.AddonPackager.Extensions;
using F16Gaming.WoW.AddonPackager.Git;
namespace F16Gaming.WoW.AddonPackager
{
internal enum ExitCodes
{
Success,
GitRepoNotFound
}
public class Program
{
private static readonly Regex FileRegex =
new Regex(@"\.git|\.gitignore|\.pkgmeta|GitInfo\.lua|.*(?:\.png|\.jpg|\.exe|\.sh|\.bat|\.zip|\.template)|locales\\.*\.xml",
RegexOptions.Compiled);
private static readonly Regex DirectoryRegex = new Regex(@"\.git|\.svn|PACKAGE_.*", RegexOptions.Compiled);
private static readonly Regex TemplateSettingsRegex = new Regex(@"^--\$GITINFO_PREFIX\$(?<prefix>[\w.[\]()"":]+)$", RegexOptions.Compiled);
private GitInfo _info;
private string _packagePath;
private static void Main(string[] args)
{
new Program().Run(args);
}
private void Run(string[] args)
{
string directory;
if (args.Length > 0)
{
directory = args[0];
if (!Directory.Exists(directory) || !Directory.Exists(directory + @"\.git"))
{
Console.WriteLine("No git repository found in {0}, exiting...", directory);
Environment.Exit((int)ExitCodes.GitRepoNotFound);
}
directory = Path.GetFullPath(directory);
Environment.CurrentDirectory = directory;
}
else
{
directory = Environment.CurrentDirectory;
if (!Directory.Exists(directory + @"\.git"))
{
#if DEBUG
directory = @"F:\Dropbox\WoW_AddOns\Command";
#else
Console.WriteLine("No git repo found in the current directory, exiting...");
Environment.Exit((int)ExitCodes.GitRepoNotFound);
#endif
}
}
bool halt = args.Length >= 2 && args[1] == "halt";
#if DEBUG
halt = true;
#endif
var repo = new Repository(directory);
_info = repo.GetInfo();
var name = directory.TrimEnd('\\');
name = name.Substring(name.LastIndexOf('\\') + 1);
_info.Name = name;
_info.Path = directory;
_packagePath = PreparePackage();
var files = CopyFiles(_info.Path, _packagePath).ToList();
UpdateTOC(Path.Combine(_packagePath, _info.Name + ".toc"));
bool gitInfoWritten = false;
foreach (var file in files.Where(f => Path.GetExtension(f) == ".lua"))
{
var reader = new StreamReader(file);
var line = reader.ReadLine();
reader.Close();
reader.Dispose();
if (line != null && TemplateSettingsRegex.IsMatch(line))
{
WriteGitInfo(file, Path.Combine(_info.Path, file.Substring(_packagePath.Length + 1)));
gitInfoWritten = true;
}
}
if (!gitInfoWritten || File.Exists(Path.Combine(_info.Path, "GitInfo.template")))
WriteGitInfo(_packagePath);
CreatePackage(_packagePath);
try
{
Directory.Delete(_packagePath, true);
}
catch (IOException)
{
Console.WriteLine("Failed to delete temporary packager folder. Please delete leftover files manually.");
}
Console.WriteLine("All done! AddOn has been packaged and placed in the root addon folder.");
if (halt)
{
Console.Write("Press ENTER to exit...");
Console.ReadLine();
}
}
private string PreparePackage()
{
var timestamp = DateTime.Now.ToUnixTimestamp();
var dir = _info.Path + @"\PACKAGE_" + timestamp;
if (Directory.Exists(dir))
Directory.Delete(dir, true);
Directory.CreateDirectory(dir);
return dir;
}
private IEnumerable<string> CopyFiles(string source, string destination)
{
Console.WriteLine("Copying files from {0} to {1}", source, destination);
foreach (var file in Directory.GetFiles(source))
{
if (!FileRegex.IsMatch(file))
{
var f = Path.GetFileName(file);
if (string.IsNullOrEmpty(f))
throw new Exception("f is null!");
var dest = Path.Combine(destination, f);
File.Copy(file, dest);
yield return dest;
}
}
foreach (var directory in Directory.GetDirectories(source))
{
if (!DirectoryRegex.IsMatch(directory))
{
var dest = Path.Combine(_packagePath, directory.Substring(_info.Path.Length + 1));
Directory.CreateDirectory(dest);
foreach (var file in CopyFiles(directory, dest))
yield return file;
}
}
}
private void UpdateTOC(string file)
{
Console.WriteLine("Updating TOC file");
var lines = File.ReadAllLines(file);
for (int i = 0; i < lines.Length; i++)
{
if (lines[i].Contains("@project-version@"))
lines[i] = lines[i].Replace("@project-version@", _info.Tag ?? _info.ShortHash);
}
File.WriteAllLines(file, lines);
}
private void WriteGitInfo(string dir)
{
if (File.Exists(Path.Combine(_info.Path, "GitInfo.template")))
{
WriteGitInfo(Path.Combine(dir, "GitInfo.lua"), Path.Combine(_info.Path, "GitInfo.template"));
return;
}
Console.WriteLine("Writing git info to {0}", Path.Combine(dir, "GitInfo.lua"));
var writer = new StreamWriter(Path.Combine(dir, "GitInfo.lua"), false);
writer.WriteLine(string.Format("{0}_GitInfo = {{}}", _info.Name));
WriteGitInfo(writer, string.Format("{0}_GitInfo", _info.Name));
writer.Flush();
writer.Close();
writer.Dispose();
}
private void WriteGitInfo(string file, string templateFile)
{
Console.WriteLine("Writing git info to {0} using {1} as template", file, templateFile);
var reader = new StreamReader(templateFile);
var writer = new StreamWriter(file, false);
var line = reader.ReadLine();
if (string.IsNullOrEmpty(line))
throw new Exception("Unable to read template file or first line was empty");
if (!TemplateSettingsRegex.IsMatch(line))
throw new Exception("Invalid format of template file!");
var match = TemplateSettingsRegex.Match(line);
var prefix = match.Groups["prefix"].Value;
while (line != null && !line.Contains("$WRITE_GITINFO$"))
{
writer.WriteLine(line);
line = reader.ReadLine();
}
WriteGitInfo(writer, prefix);
line = reader.ReadLine();
while (line != null)
{
writer.WriteLine(line);
line = reader.ReadLine();
}
writer.Flush();
writer.Close();
writer.Dispose();
}
private void WriteGitInfo(StreamWriter writer, string prefix)
{
writer.WriteLine("{0}.Name = \"{1}\"", prefix, _info.Name);
writer.WriteLine("{0}.Path = \"{1}\"", prefix, _info.Path);
writer.WriteLine("{0}.Hash = \"{1}\"", prefix, _info.Hash);
writer.WriteLine("{0}.ShortHash = \"{1}\"", prefix, _info.ShortHash);
writer.WriteLine("{0}.Message = \"{1}\"", prefix, _info.Message);
writer.WriteLine("{0}.FullMessage = [==[{1}]==]", prefix, _info.FullMessage);
writer.WriteLine("{0}.Author = \"{1}\"", prefix, _info.Author);
writer.WriteLine("{0}.Committer = \"{1}\"", prefix, _info.Author);
writer.WriteLine("{0}.Time = \"{1}\"", prefix, _info.Time.ToString());
writer.WriteLine("{0}.Tag = {1}", prefix, string.IsNullOrEmpty(_info.Tag) ? "nil" : "\"" + _info.Tag + "\"");
}
private void CreatePackage(string directory)
{
string zipName = string.Format("{0}_{1}.zip", _info.Name, _info.Tag);
string zipFile = Path.Combine(_info.Path, zipName);
Console.WriteLine("Packaging into {0}...", zipName);
if (File.Exists(zipFile))
File.Delete(zipFile);
FileStream fsOut = File.Create(zipFile);
var zipStream = new ZipOutputStream(fsOut);
zipStream.SetLevel(9);
int offset = directory.Length + (directory.EndsWith("\\") ? 0 : 1);
Compress(directory, zipStream, offset);
zipStream.IsStreamOwner = true;
zipStream.Close();
zipStream.Dispose();
}
private void Compress(string path, ZipOutputStream stream, int offset)
{
foreach (var file in Directory.GetFiles(path))
{
var info = new FileInfo(file);
string entry = Path.Combine(_info.Name, file.Substring(offset));
entry = ZipEntry.CleanName(entry);
var newEntry = new ZipEntry(entry);
newEntry.DateTime = info.LastWriteTime;
newEntry.Size = info.Length;
stream.PutNextEntry(newEntry);
var buffer = new byte[4096];
using (FileStream streamReader = File.OpenRead(file))
StreamUtils.Copy(streamReader, stream, buffer);
stream.CloseEntry();
}
foreach (var directory in Directory.GetDirectories(path))
Compress(directory, stream, offset);
}
}
}
using System;
using System.Linq;
using NGit.Storage.File;
namespace F16Gaming.WoW.AddonPackager.Git
{
public class Repository
{
private readonly FileRepository _repo;
private readonly NGit.Api.Git _git;
public Repository(string dir)
{
var builder = new FileRepositoryBuilder();
Console.WriteLine("Opening git repository in {0}", dir);
_repo = builder.SetGitDir(dir + @"\.git").ReadEnvironment().FindGitDir().Build();
_git = new NGit.Api.Git(_repo);
}
public GitInfo GetInfo()
{
var lastCommitId = _repo.Resolve("HEAD");
var log = _git.Log().Add(lastCommitId).Call().First();
var tag = _git.TagList().Call().LastOrDefault();
var info = new GitInfo
{
Author = log.GetAuthorIdent().GetName(),
Committer = log.GetCommitterIdent().GetName(),
FullMessage = log.GetFullMessage().Trim(),
Message = log.GetShortMessage().Trim(),
Hash = lastCommitId.Name,
Time = GitInfo.ParseTime(log.CommitTime)
};
if (tag != null)
{
info.Tag = string.Format("{0}-{1}", tag.GetName().Substring(10), lastCommitId.Name.Substring(0, 6));
}
else
{
info.Tag = lastCommitId.Name.Substring(0, 6);
}
return info;
}
public void Test()
{
Console.WriteLine(_git.TagList().Call().Last().GetName().Substring(10).Trim());
Console.WriteLine(_git.Log().Add(_repo.Resolve("HEAD")).Call().First().GetFullMessage().Trim());
var time = GitInfo.ParseTime(_git.Log().Add(_repo.Resolve("HEAD")).Call().First().CommitTime);
Console.WriteLine(time.ToString());
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment