Created
October 14, 2012 14:20
-
-
Save Sharparam/3888726 to your computer and use it in GitHub Desktop.
WoW AddOn packager (with Git support using NGit)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using System; | |
namespace F16Gaming.WoW.AddonPackager.Extensions | |
{ | |
public static class Int64Extensions | |
{ | |
public static DateTime ToDateTime(this Int64 timestamp) | |
{ | |
return DateTimeExtensions.Epoch + TimeSpan.FromSeconds(timestamp); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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); | |
} | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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