diff --git a/.gitignore b/.gitignore index 4d40434..0763c92 100644 --- a/.gitignore +++ b/.gitignore @@ -1,23 +1,117 @@ -# Object files -*.o -*.ko +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.sln.docstates + +# Build results +[Bb]in/ +[Cc]lientbin/ +[Dd]ebug/ +[Rr]elease/ +[Oo]utput*/ +[Pp]ackages*/ +bin +obj +[Ll]ib/ +*_i.c +*_p.c +*.ilk +*.meta *.obj -*.elf - -# Libraries -*.lib -*.a - -# Shared objects (inc. Windows DLLs) -*.dll -*.so -*.so.* -*.dylib - -# Executables -*.exe -*.out -*.app -*.i*86 -*.x86_64 -*.hex +*.orig +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.vspscc +*.xap +.builds + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf + +# Visual Studio profiler +*.psess +*.vsp + +# ReSharper is a .NET coding add-in +_ReSharper* +*.resharper.user + +# Catel +CatelLogging.txt + +# Dotcover +*.dotCover +*.dotsettings.user + +# Finalbuilder +*.fbl7 +*.fb7lck +*.fbpInf + +# Ghostdoc +*.GhostDoc.xml + +# Deployments +deployment/FinalBuilder/backup +deployment/InnoSetup/template/templates +deployment/InnoSetup/template/snippets +deployment/InnoSetup/template/libraries +deployment/InnoSetup/template/doc + +# Installshield output folder +[Ee]xpress + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish + +# Others +[Bb]in +[Oo]bj +sql +TestResults +*.Cache +ClientBin +stylecop.* +~$* +*.dbmdl +Generated_Code #added for RIA/Silverlight projects + +# Backup & report files from converting an old project file to a newer +# Visual Studio version. Backup files are not needed, because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML + +# Windows image file caches +Thumbs.db + +# Folder config file +Desktop.ini + +# mstest test results +TestResults \ No newline at end of file diff --git a/README.md b/README.md index 48ff07b..ab6e82e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,70 @@ GitHubLink ========== -Let users step through your code hosted on GitHub! +![GitHubLink](design/logo/logo_64.png) + +GitHubLink let's users step through your code hosted on GitHub! **This makes symbol servers obsolete** which saves you both time with uploading source files with symbols and the user no longer has to specify custom symbol servers (such as symbolsource.org). + +![Stepping through external source code](doc/images/GitHubLink_example.gif) + +The idea is based on the [SourceLink project](https://github.com/ctaggart/SourceLink "SourceLink project"). However it requires FAKE and no everyone likes to write code in F#. GitHubLink is available as console application and can be references as assembly as well to be used in other .NET assemblies. + +The advantage of GitHubLink is that it is fully customized for GitHub. It also works with GitHub urls so it **does not require a local git repository to work**. This makes it perfectly usable in continuous integration servers such as [Continua CI](http://www.finalbuilder.com/Continua-CI "Continua CI"). + +Updating all the pdb files is very fast. A solution with over 85 projects will be handled in less than 30 seconds. + +When using GitHubLink, the user no longer has to specify symbol servers. He/she only has to enable the support for source servers in Visual Studio as shown in the image below: + +![Enabling source server support](doc/images/visualstudio_enablesourceserversupport.png) + +# Using GitHubLink # + +Using GitHubLink is very simple: + +1. Build the software (in release mode with pdb files enabled) +2. Run the console application with the right command line parameters + +Below are a few examples. + +## Running for the default branch ## + + GitHubLink.exe c:\source\catel -u https://github.com/catel/catel + +This will use the default branch (which is in most cases **master**). You can find out the default branch by checking what branch is loaded by default on the GitHub page. + +## Running for a specific branch ## + + GitHubLink.exe c:\source\catel -u https://github.com/catel/catel -b develop + +This will use the develop branch. + +## Running for a specific branch and configuration ## + + GitHubLink.exe c:\source\catel -u https://github.com/catel/catel -b develop -c debug + +This will use the develop branch and the debug configuration. + +## Getting help ## + +When you need help about GitHubLink, use the following command line: + + GitHubLink.exe -help + +## Logging to a file ## + +When you need to log the information to a file, use the following command line: + + GitHubLink.exe c:\source\catel -u https://github.com/catel/catel -b develop -l GitHubLinkLog.log + + +# How does it work # + +The SrcSrv tool (Srcsrv.dll) enables a client to retrieve the exact version of the source files that were used to build an application. Because the source code for a module can change between versions and over the course of years, it is important to look at the source code as it existed when the version of the module in question was built. + +For more information, see the [official documentation of SrcSrv](http://msdn.microsoft.com/en-us/library/windows/hardware/ff558791(v=vs.85).aspx). + +GitHubLink creates a source index file and updates the PDB file so it will retrieve the files from the GitHub file handler. + +# Icon # + +Link by Dominic Whittle from The Noun Project \ No newline at end of file diff --git a/deployment/NuGet/template/GitHubLink.nuspec b/deployment/NuGet/template/GitHubLink.nuspec new file mode 100644 index 0000000..b715973 --- /dev/null +++ b/deployment/NuGet/template/GitHubLink.nuspec @@ -0,0 +1,24 @@ + + + + GitHubLink + [VERSION] + GitHubLink + GeertvanHorrik + + + GitHubLink let's users step through your code hosted on GitHub! This makes symbol servers obsolete which saves you both time + with uploading source files with symbols and the user no longer has to specify custom symbol servers (such as symbolsource.org). + + + + + + source symbol symbols server sourcelink github stepping debugging + + en-US + https://github.com/GeertvanHorrik/GitHubLink/ + https://github.com/GeertvanHorrik/GitHubLink/blob/develop/LICENSE + https://raw.githubusercontent.com/GeertvanHorrik/GitHubLink/develop/design/logo/logo_64.png + + \ No newline at end of file diff --git a/design/logo/Link/icon_15341.png b/design/logo/Link/icon_15341.png new file mode 100644 index 0000000..e6cf4a4 Binary files /dev/null and b/design/logo/Link/icon_15341.png differ diff --git a/design/logo/Link/icon_15341.svg b/design/logo/Link/icon_15341.svg new file mode 100644 index 0000000..8b5d0e2 --- /dev/null +++ b/design/logo/Link/icon_15341.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/design/logo/Link/license.txt b/design/logo/Link/license.txt new file mode 100644 index 0000000..ce933f9 --- /dev/null +++ b/design/logo/Link/license.txt @@ -0,0 +1,8 @@ +Thank you for using The Noun Project. This icon is licensed under Creative +Commons Attribution and must be attributed as: + + Link by Dominic Whittle from The Noun Project + +If you have a Premium Account or have purchased a license for this icon, you +don't need to worry about attribution! We will share the profits from your +purchase with this icon's designer. diff --git a/design/logo/logo.ico b/design/logo/logo.ico new file mode 100644 index 0000000..529b63c Binary files /dev/null and b/design/logo/logo.ico differ diff --git a/design/logo/logo_1024.png b/design/logo/logo_1024.png new file mode 100644 index 0000000..a460292 Binary files /dev/null and b/design/logo/logo_1024.png differ diff --git a/design/logo/logo_128.png b/design/logo/logo_128.png new file mode 100644 index 0000000..3a934be Binary files /dev/null and b/design/logo/logo_128.png differ diff --git a/design/logo/logo_16.png b/design/logo/logo_16.png new file mode 100644 index 0000000..c498f81 Binary files /dev/null and b/design/logo/logo_16.png differ diff --git a/design/logo/logo_256.png b/design/logo/logo_256.png new file mode 100644 index 0000000..98ea873 Binary files /dev/null and b/design/logo/logo_256.png differ diff --git a/design/logo/logo_32.png b/design/logo/logo_32.png new file mode 100644 index 0000000..8c77306 Binary files /dev/null and b/design/logo/logo_32.png differ diff --git a/design/logo/logo_512.png b/design/logo/logo_512.png new file mode 100644 index 0000000..690e1a5 Binary files /dev/null and b/design/logo/logo_512.png differ diff --git a/design/logo/logo_64.png b/design/logo/logo_64.png new file mode 100644 index 0000000..48818e3 Binary files /dev/null and b/design/logo/logo_64.png differ diff --git a/doc/history.txt b/doc/history.txt new file mode 100644 index 0000000..d14dc86 --- /dev/null +++ b/doc/history.txt @@ -0,0 +1,35 @@ +GitHubLink history +================== + +(+) Added +(*) Changed +(-) Removed +(x) Error / bug (fix) + +For more information about issues or new feature requests, please visit: + +Project website: https://github.com/GeertvanHorrik/GitHubLink + +********************************************************** + +================== +Version 1.0.0 +================== + +Release date: +============= +2014/xx/xx + +Added/fixed: +============ +xxx + +Roadmap: +======== +See https://github.com/GeertvanHorrik/GitHubLink + +Known issues: +============= +See https://github.com/GeertvanHorrik/GitHubLink + +********************************************************** diff --git a/doc/images/GitHubLink_example.gif b/doc/images/GitHubLink_example.gif new file mode 100644 index 0000000..fc0767a Binary files /dev/null and b/doc/images/GitHubLink_example.gif differ diff --git a/doc/images/visualstudio_enablesourceserversupport.png b/doc/images/visualstudio_enablesourceserversupport.png new file mode 100644 index 0000000..15eaca9 Binary files /dev/null and b/doc/images/visualstudio_enablesourceserversupport.png differ diff --git a/lib/RestorePackages.bat b/lib/RestorePackages.bat new file mode 100644 index 0000000..10cbafb --- /dev/null +++ b/lib/RestorePackages.bat @@ -0,0 +1,4 @@ +for /f %%a IN ('dir /b ..\src\*.sln') do call ..\tools\nuget\nuget.exe restore ..\src\%%a -PackagesDirectory .\ + + +pause \ No newline at end of file diff --git a/lib/repositories.config b/lib/repositories.config new file mode 100644 index 0000000..7a2ef9b --- /dev/null +++ b/lib/repositories.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/GitHubLink.DotSettings b/src/GitHubLink.DotSettings new file mode 100644 index 0000000..79c4384 --- /dev/null +++ b/src/GitHubLink.DotSettings @@ -0,0 +1,270 @@ + + SOLUTION + + <?xml version="1.0" encoding="utf-16"?><Profile name="Code cleanup"><CSArrangeThisQualifier>True</CSArrangeThisQualifier><CSMakeFieldReadonly>True</CSMakeFieldReadonly><CSUpdateFileHeader>True</CSUpdateFileHeader><CSOptimizeUsings><OptimizeUsings>True</OptimizeUsings><EmbraceInRegion>False</EmbraceInRegion><RegionName></RegionName></CSOptimizeUsings><CSReformatCode>True</CSReformatCode><CSReorderTypeMembers>True</CSReorderTypeMembers><AspOptimizeRegisterDirectives>True</AspOptimizeRegisterDirectives><HtmlReformatCode>True</HtmlReformatCode><CSharpFormatDocComments>False</CSharpFormatDocComments></Profile> + + Code cleanup + 2 + 1 + 0 + ALWAYS_ADD + ALWAYS_ADD + ALWAYS_ADD + ALWAYS_ADD + ALWAYS_ADD + ALWAYS_ADD + 1 + 1 + False + DoNotTouch + + 10000 + summary,remarks + False + False + False + + 20 + False + False + 1 + OnSingleLine + FirstAttributeOnSingleLine + <?xml version="1.0" encoding="utf-8" ?> + +<!-- +I. Overall + +I.1 Each pattern can have <Match>....</Match> element. For the given type declaration, the pattern with the match, evaluated to 'true' with the largest weight, will be used +I.2 Each pattern consists of the sequence of <Entry>...</Entry> elements. Type member declarations are distributed between entries +I.3 If pattern has RemoveAllRegions="true" attribute, then all regions will be cleared prior to reordering. Otherwise, only auto-generated regions will be cleared +I.4 The contents of each entry is sorted by given keys (First key is primary, next key is secondary, etc). Then the declarations are grouped and en-regioned by given property + +II. Available match operands + +Each operand may have Weight="..." attribute. This weight will be added to the match weight if the operand is evaluated to 'true'. +The default weight is 1 + +II.1 Boolean functions: +II.1.1 <And>....</And> +II.1.2 <Or>....</Or> +II.1.3 <Not>....</Not> + +II.2 Operands +II.2.1 <Kind Is="..."/>. Kinds are: class, struct, interface, enum, delegate, type, constructor, destructor, property, indexer, method, operator, field, constant, event, member +II.2.2 <Name Is="..." [IgnoreCase="true/false"] />. The 'Is' attribute contains regular expression +II.2.3 <HasAttribute CLRName="..." [Inherit="true/false"] />. The 'CLRName' attribute contains regular expression +II.2.4 <Access Is="..."/>. The 'Is' values are: public, protected, internal, protected internal, private +II.2.5 <Static/> +II.2.6 <Abstract/> +II.2.7 <Virtual/> +II.2.8 <Override/> +II.2.9 <Sealed/> +II.2.10 <Readonly/> +II.2.11 <ImplementsInterface CLRName="..."/>. The 'CLRName' attribute contains regular expression +II.2.12 <HandlesEvent /> +--> + +<Patterns xmlns="urn:shemas-jetbrains-com:member-reordering-patterns"> + + <!--Do not reorder COM interfaces and structs marked by StructLayout attribute--> + <Pattern> + <Match> + <Or Weight="100"> + <And> + <Kind Is="interface"/> + <Or> + <HasAttribute CLRName="System.Runtime.InteropServices.InterfaceTypeAttribute"/> + <HasAttribute CLRName="System.Runtime.InteropServices.ComImport"/> + </Or> + </And> + <HasAttribute CLRName="System.Runtime.InteropServices.StructLayoutAttribute"/> + </Or> + </Match> + </Pattern> + + <!--Special formatting of NUnit test fixture--> + <Pattern RemoveAllRegions="true"> + <Match> + <And Weight="100"> + <Kind Is="class"/> + <HasAttribute CLRName="NUnit.Framework.TestFixtureAttribute" Inherit="true"/> + </And> + </Match> + + <!--Setup/Teardow--> + <Entry> + <Match> + <And> + <Kind Is="method"/> + <Or> + <HasAttribute CLRName="NUnit.Framework.SetUpAttribute" Inherit="true"/> + <HasAttribute CLRName="NUnit.Framework.TearDownAttribute" Inherit="true"/> + <HasAttribute CLRName="NUnit.Framework.FixtureSetUpAttribute" Inherit="true"/> + <HasAttribute CLRName="NUnit.Framework.FixtureTearDownAttribute" Inherit="true"/> + </Or> + </And> + </Match> + <Group Region="Setup/Teardown"/> + </Entry> + + <!--All other members--> + <Entry/> + + <!--Test methods--> + <Entry> + <Match> + <And Weight="100"> + <Kind Is="method"/> + <HasAttribute CLRName="NUnit.Framework.TestAttribute" Inherit="false"/> + </And> + </Match> + <Sort> + <Name/> + </Sort> + </Entry> + </Pattern> + + <!--Default pattern--> + <Pattern> + + <!--public delegate--> + <Entry> + <Match> + <And Weight="100"> + <Access Is="public"/> + <Kind Is="delegate"/> + </And> + </Match> + <Sort> + <Name/> + </Sort> + <Group Region="Delegates"/> + </Entry> + + <!--public enum--> + <Entry> + <Match> + <And Weight="100"> + <Access Is="public"/> + <Kind Is="enum"/> + </And> + </Match> + <Sort> + <Name/> + </Sort> + <Group> + <Name Region="${Name} enum"/> + </Group> + </Entry> + + <!--static fields and constants--> + <Entry> + <Match> + <Or> + <Kind Is="constant"/> + <And> + <Kind Is="field"/> + <Static/> + </And> + </Or> + </Match> + <Sort> + <Kind Order="constant field"/> + </Sort> + <Group Region="Constants" /> + </Entry> + + <!--instance fields--> + <Entry> + <Match> + <And> + <Kind Is="field"/> + <Not> + <Static/> + </Not> + </And> + </Match> + <Sort> + <Readonly/> + <Name/> + </Sort> + <Group Region="Fields" /> + </Entry> + + <!--Constructors. Place static one first--> + <Entry> + <Match> + <Kind Is="constructor"/> + </Match> + <Sort> + <Static/> + </Sort> + <Group Region="Constructors" /> + </Entry> + + <!--properties, indexers--> + <Entry> + <Match> + <Or> + <Kind Is="property"/> + <Kind Is="indexer"/> + </Or> + </Match> + <Group Region="Properties" /> + </Entry> + + <!--interface implementations--> + <Entry> + <Match> + <And Weight="100"> + <Kind Is="member"/> + <ImplementsInterface/> + </And> + </Match> + <Sort> + <ImplementsInterface Immediate="true"/> + </Sort> + <Group> + <ImplementsInterface Immediate="true" Region="${ImplementsInterface} Members"/> + </Group> + </Entry> + + <!-- methods --> + <Entry> + <Match> + <Kind Is="method"/> + </Match> + <Group Region="Methods" /> + </Entry> + + <!--all other members--> + <Entry/> + + <!--nested types--> + <Entry> + <Match> + <Kind Is="type"/> + </Match> + <Sort> + <Name/> + </Sort> + <Group> + <Name Region="Nested type: ${Name}"/> + </Group> + </Entry> + </Pattern> + +</Patterns> + + CustomLayout + True + -------------------------------------------------------------------------------------------------------------------- +<copyright file="$FILENAME$" company="CatenaLogic"> + Copyright (c) 2008 - $CURRENT_YEAR$ CatenaLogic. All rights reserved. +</copyright> +-------------------------------------------------------------------------------------------------------------------- + True + None + <data /> + <data><IncludeFilters /><ExcludeFilters /></data> \ No newline at end of file diff --git a/src/GitHubLink.Test/ArgumentParserFacts.cs b/src/GitHubLink.Test/ArgumentParserFacts.cs new file mode 100644 index 0000000..9ee1367 --- /dev/null +++ b/src/GitHubLink.Test/ArgumentParserFacts.cs @@ -0,0 +1,83 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2012 - 2014 CatenaLogic. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace GitHubLink.Test +{ + using Catel.Test; + using GitHubLink; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + [TestClass] + public class ArgumentParserFacts + { + #region Methods + + [TestMethod] + public void ThrowsExceptionForEmptyParameters() + { + ExceptionTester.CallMethodAndExpectException(() => ArgumentParser.ParseArguments(string.Empty)); + } + + [TestMethod] + public void CorrectlyParsesSolutionDirectory() + { + var context = ArgumentParser.ParseArguments("solutionDirectory -u http://github.com/GeertvanHorrik/GitHubLink"); + + Assert.AreEqual("solutionDirectory", context.SolutionDirectory); + } + + [TestMethod] + public void CorrectlyParsesLogFilePath() + { + var context = ArgumentParser.ParseArguments("solutionDirectory -l logFilePath"); + + Assert.AreEqual("solutionDirectory", context.SolutionDirectory); + Assert.AreEqual("logFilePath", context.LogFile); + } + + [TestMethod] + public void CorrectlyParsesHelp() + { + var context = ArgumentParser.ParseArguments("-h"); + + Assert.IsTrue(context.IsHelp); + } + + [TestMethod] + public void CorrectlyParsesUrlAndBranchName() + { + var context = ArgumentParser.ParseArguments("solutionDirectory -u http://github.com/GeertvanHorrik/GitHubLink -b somebranch"); + + Assert.AreEqual("solutionDirectory", context.SolutionDirectory); + Assert.AreEqual("http://github.com/GeertvanHorrik/GitHubLink", context.TargetUrl); + Assert.AreEqual("somebranch", context.TargetBranch); + } + + [TestMethod] + public void CorrectlyParsesUrlAndConfiguration() + { + var context = ArgumentParser.ParseArguments("solutionDirectory -u http://github.com/GeertvanHorrik/GitHubLink -c someConfiguration"); + + Assert.AreEqual("solutionDirectory", context.SolutionDirectory); + Assert.AreEqual("http://github.com/GeertvanHorrik/GitHubLink", context.TargetUrl); + Assert.AreEqual("someConfiguration", context.ConfigurationName); + } + + [TestMethod] + public void ThrowsExceptionForInvalidNumberOfArguments() + { + ExceptionTester.CallMethodAndExpectException(() => ArgumentParser.ParseArguments("solutionDirectory -l logFilePath extraArg")); + } + + [TestMethod] + public void ThrowsExceptionForUnknownArgument() + { + ExceptionTester.CallMethodAndExpectException(() => ArgumentParser.ParseArguments("solutionDirectory -x logFilePath")); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/GitHubLink.Test/ContextFacts.cs b/src/GitHubLink.Test/ContextFacts.cs new file mode 100644 index 0000000..52efffe --- /dev/null +++ b/src/GitHubLink.Test/ContextFacts.cs @@ -0,0 +1,35 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2012 - 2014 CatenaLogic. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace GitHubLink.Test +{ + using Catel; + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public class ContextFacts + { + #region Nested type: TheDefaultValues + + [TestClass] + public class TheDefaultValues + { + #region Methods + + [TestMethod] + public void SetsRightDefaultValues() + { + var context = new Context(); + + Assert.AreEqual("Release", context.ConfigurationName); + Assert.IsFalse(context.IsHelp); + } + + #endregion + } + + #endregion + } +} \ No newline at end of file diff --git a/src/GitHubLink.Test/Extensions/StringExtensionsFacts.github.cs b/src/GitHubLink.Test/Extensions/StringExtensionsFacts.github.cs new file mode 100644 index 0000000..8266d06 --- /dev/null +++ b/src/GitHubLink.Test/Extensions/StringExtensionsFacts.github.cs @@ -0,0 +1,81 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2012 - 2014 CatenaLogic. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace GitHubLink.Test.Extensions +{ + using Microsoft.VisualStudio.TestTools.UnitTesting; + + public class StringExtensionsFacts + { + [TestClass] + public class TheGetGitHubCompanyNameMethod + { + [TestMethod] + public void ReturnsValidCompany() + { + var company = GitHubLink.StringExtensions.GetGitHubCompanyName("https://github.com/GeertvanHorrik/GitHubLink"); + + Assert.AreEqual("GeertvanHorrik", company); + } + } + + [TestClass] + public class TheGetGitHubProjectNameMethod + { + [TestMethod] + public void ReturnsValidProject() + { + var project = GitHubLink.StringExtensions.GetGitHubProjectName("https://github.com/GeertvanHorrik/GitHubLink"); + + Assert.AreEqual("GitHubLink", project); + } + } + + [TestClass] + public class TheGetGitHubProjectUrlMethod + { + [TestMethod] + public void ReturnsValidUrl() + { + var company = GitHubLink.StringExtensions.GetGitHubProjectUrl("https://github.com/GeertvanHorrik/GitHubLink"); + + Assert.AreEqual("https://github.com/GeertvanHorrik/GitHubLink", company); + } + + [TestMethod] + public void ReturnsValidUrlWhenGitIsAppended() + { + var company = GitHubLink.StringExtensions.GetGitHubProjectUrl("https://github.com/GeertvanHorrik/GitHubLink.git"); + + Assert.AreEqual("https://github.com/GeertvanHorrik/GitHubLink", company); + } + } + + [TestClass] + public class TheGetGitHubCompanyUrlMethod + { + [TestMethod] + public void ReturnsValidUrl() + { + var company = GitHubLink.StringExtensions.GetGitHubCompanyUrl("https://github.com/GeertvanHorrik/GitHubLink"); + + Assert.AreEqual("https://github.com/GeertvanHorrik", company); + } + } + + [TestClass] + public class TheGetGitHubRawUrlMethod + { + [TestMethod] + public void ReturnsValidUrl() + { + var company = GitHubLink.StringExtensions.GetGitHubRawUrl("https://github.com/GeertvanHorrik/GitHubLink"); + + Assert.AreEqual("https://raw.github.com/GeertvanHorrik/GitHubLink", company); + } + } + } +} \ No newline at end of file diff --git a/src/GitHubLink.Test/GitHubLink.Test.csproj b/src/GitHubLink.Test/GitHubLink.Test.csproj new file mode 100644 index 0000000..2f46601 --- /dev/null +++ b/src/GitHubLink.Test/GitHubLink.Test.csproj @@ -0,0 +1,98 @@ + + + + Debug + AnyCPU + {ED2B9579-59D2-40F2-BC5E-F4DC3DAB9A56} + Library + Properties + GitHubLink.Test + GitHubLink.Test + v4.5 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 10.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + true + full + false + ..\..\output\debug\Test\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\..\output\release\Test\ + TRACE + prompt + 4 + + + + False + ..\..\lib\Catel.Core.3.9.0.1404150943-beta\lib\net45\Catel.Core.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + {d68add77-913f-46d2-9a4f-5cc71c4718d8} + GitHubLink + + + + + + + False + + + False + + + False + + + False + + + + + + + + \ No newline at end of file diff --git a/src/GitHubLink.Test/Properties/AssemblyInfo.cs b/src/GitHubLink.Test/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..adf5e37 --- /dev/null +++ b/src/GitHubLink.Test/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("GitHubLink.Test")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("GitHubLink.Test")] +[assembly: AssemblyCopyright("Copyright © 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("d63e36fe-d056-46ad-af77-e8a14edc7a02")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/src/GitHubLink.Test/packages.config b/src/GitHubLink.Test/packages.config new file mode 100644 index 0000000..e4223ab --- /dev/null +++ b/src/GitHubLink.Test/packages.config @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/GitHubLink.sln b/src/GitHubLink.sln new file mode 100644 index 0000000..36b61ef --- /dev/null +++ b/src/GitHubLink.sln @@ -0,0 +1,61 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 2013 +VisualStudioVersion = 12.0.30324.0 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitHubLink", "GitHubLink\GitHubLink.csproj", "{D68ADD77-913F-46D2-9A4F-5CC71C4718D8}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A6694C2C-1A8A-49F0-B404-1BBB4D081808}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "lib", "lib", "{16B74389-D647-4CF8-90D3-38E5D5256BC5}" + ProjectSection(SolutionItems) = preProject + ..\lib\repositories.config = ..\lib\repositories.config + ..\lib\RestorePackages.bat = ..\lib\RestorePackages.bat + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "deployment", "deployment", "{99836492-466A-44E3-A92B-72CE3744F61C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{F3BABD63-7726-4219-BF31-13CF05EE3BD7}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "doc", "doc", "{88214CBE-4B92-4E22-8854-CC21543EE703}" + ProjectSection(SolutionItems) = preProject + ..\doc\history.txt = ..\doc\history.txt + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "NuGet", "NuGet", "{4FBE79F3-A1F1-4107-AA8C-DCB241880EDE}" + ProjectSection(SolutionItems) = preProject + ..\tools\NuGet\NuGet.exe = ..\tools\NuGet\NuGet.exe + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Core", "Core", "{A5FCE4FA-083A-4814-B38C-2AED9C37647F}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{D435DFD9-F741-4FCA-A65A-28B7D251983D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GitHubLink.Test", "GitHubLink.Test\GitHubLink.Test.csproj", "{ED2B9579-59D2-40F2-BC5E-F4DC3DAB9A56}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {D68ADD77-913F-46D2-9A4F-5CC71C4718D8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D68ADD77-913F-46D2-9A4F-5CC71C4718D8}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D68ADD77-913F-46D2-9A4F-5CC71C4718D8}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D68ADD77-913F-46D2-9A4F-5CC71C4718D8}.Release|Any CPU.Build.0 = Release|Any CPU + {ED2B9579-59D2-40F2-BC5E-F4DC3DAB9A56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ED2B9579-59D2-40F2-BC5E-F4DC3DAB9A56}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ED2B9579-59D2-40F2-BC5E-F4DC3DAB9A56}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ED2B9579-59D2-40F2-BC5E-F4DC3DAB9A56}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {D68ADD77-913F-46D2-9A4F-5CC71C4718D8} = {A5FCE4FA-083A-4814-B38C-2AED9C37647F} + {4FBE79F3-A1F1-4107-AA8C-DCB241880EDE} = {F3BABD63-7726-4219-BF31-13CF05EE3BD7} + {A5FCE4FA-083A-4814-B38C-2AED9C37647F} = {A6694C2C-1A8A-49F0-B404-1BBB4D081808} + {D435DFD9-F741-4FCA-A65A-28B7D251983D} = {A6694C2C-1A8A-49F0-B404-1BBB4D081808} + {ED2B9579-59D2-40F2-BC5E-F4DC3DAB9A56} = {D435DFD9-F741-4FCA-A65A-28B7D251983D} + EndGlobalSection +EndGlobal diff --git a/src/GitHubLink/ArgumentParser.cs b/src/GitHubLink/ArgumentParser.cs new file mode 100644 index 0000000..14bb491 --- /dev/null +++ b/src/GitHubLink/ArgumentParser.cs @@ -0,0 +1,129 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2012 - 2014 CatenaLogic. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace GitHubLink +{ + using System; + using System.Collections.Generic; + using System.Linq; + using Catel.Logging; + + public static class ArgumentParser + { + #region Constants + + private static readonly ILog Log = LogManager.GetCurrentClassLogger(); + + #endregion + + #region Methods + + public static Context ParseArguments(string commandLineArguments) + { + return ParseArguments(commandLineArguments.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries).ToList()); + } + + public static Context ParseArguments(params string[] commandLineArguments) + { + return ParseArguments(commandLineArguments.ToList()); + } + + public static Context ParseArguments(List commandLineArguments) + { + var context = new Context(); + + if (commandLineArguments.Count == 0) + { + Log.ErrorAndThrowException("Invalid number of arguments"); + } + + var firstArgument = commandLineArguments.First(); + if (IsHelp(firstArgument)) + { + context.IsHelp = true; + return context; + } + + if (commandLineArguments.Count < 3) + { + Log.ErrorAndThrowException("Invalid number of arguments"); + } + + context.SolutionDirectory = firstArgument; + + var namedArguments = commandLineArguments.Skip(1).ToList(); + + EnsureArgumentsEvenCount(commandLineArguments, namedArguments); + + for (var index = 0; index < namedArguments.Count; index = index + 2) + { + var name = namedArguments[index]; + var value = namedArguments[index + 1]; + + if (IsSwitch("l", name)) + { + context.LogFile = value; + continue; + } + + if (IsSwitch("c", name)) + { + context.ConfigurationName = value; + continue; + } + + if (IsSwitch("u", name)) + { + context.TargetUrl = value; + continue; + } + + if (IsSwitch("b", name)) + { + context.TargetBranch = value; + continue; + } + + Log.ErrorAndThrowException("Could not parse command line parameter '{0}'.", name); + } + + return context; + } + + private static bool IsSwitch(string switchName, string value) + { + if (value.StartsWith("-")) + { + value = value.Remove(0, 1); + } + + if (value.StartsWith("/")) + { + value = value.Remove(0, 1); + } + + return (string.Equals(switchName, value)); + } + + private static void EnsureArgumentsEvenCount(IEnumerable commandLineArguments, List namedArguments) + { + if (namedArguments.Count.IsOdd()) + { + Log.ErrorAndThrowException("Could not parse arguments: '{0}'.", string.Join(" ", commandLineArguments)); + } + } + + private static bool IsHelp(string singleArgument) + { + return (singleArgument == "?") || + IsSwitch("h", singleArgument) || + IsSwitch("help", singleArgument) || + IsSwitch("?", singleArgument); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/GitHubLink/Context.cs b/src/GitHubLink/Context.cs new file mode 100644 index 0000000..a951e8a --- /dev/null +++ b/src/GitHubLink/Context.cs @@ -0,0 +1,53 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2012 - 2014 CatenaLogic. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace GitHubLink +{ + using System; + using System.IO; + using Catel.Logging; + + public class Context + { + private static ILog Log = LogManager.GetCurrentClassLogger(); + + public Context() + { + TempDirectory = Path.Combine(Path.GetTempPath(), "SourceLink", Guid.NewGuid().ToString()); + Directory.CreateDirectory(TempDirectory); + + ConfigurationName = "Release"; + } + + public bool IsHelp { get; set; } + public string LogFile { get; set; } + + public string SolutionDirectory { get; set; } + public string ConfigurationName { get; set; } + public string TempDirectory { get; set; } + + public string TargetUrl { get; set; } + public string TargetBranch { get; set; } + + public void ValidateContext() + { + if (string.IsNullOrEmpty(SolutionDirectory)) + { + Log.ErrorAndThrowException("Solution directory is missing"); + } + + if (string.IsNullOrEmpty(ConfigurationName)) + { + Log.ErrorAndThrowException("Configuration name is missing"); + } + + if (string.IsNullOrEmpty(TargetUrl)) + { + Log.ErrorAndThrowException("Target url is missing"); + } + } + } +} \ No newline at end of file diff --git a/src/GitHubLink/Exceptions/GitHubLinkException.cs b/src/GitHubLink/Exceptions/GitHubLinkException.cs new file mode 100644 index 0000000..3fe895f --- /dev/null +++ b/src/GitHubLink/Exceptions/GitHubLinkException.cs @@ -0,0 +1,22 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2012 - 2014 CatenaLogic. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace GitHubLink +{ + using System; + + public class GitHubLinkException : Exception + { + #region Constructors + + public GitHubLinkException(string message) + : base(message) + { + } + + #endregion + } +} \ No newline at end of file diff --git a/src/GitHubLink/Extensions/IntExtensions.cs b/src/GitHubLink/Extensions/IntExtensions.cs new file mode 100644 index 0000000..b6e7d47 --- /dev/null +++ b/src/GitHubLink/Extensions/IntExtensions.cs @@ -0,0 +1,16 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2012 - 2014 CatenaLogic. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace GitHubLink +{ + public static class ExtensionMethods + { + public static bool IsOdd(this int number) + { + return number %2 != 0; + } + } +} \ No newline at end of file diff --git a/src/GitHubLink/Extensions/PdbExtensions.cs b/src/GitHubLink/Extensions/PdbExtensions.cs new file mode 100644 index 0000000..88be9a6 --- /dev/null +++ b/src/GitHubLink/Extensions/PdbExtensions.cs @@ -0,0 +1,92 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2012 - 2014 CatenaLogic. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace GitHubLink +{ + using System; + using System.Collections.Generic; + using System.Linq; + using Catel; + using SourceLink; + + public static class PdbExtensions + { + public static Dictionary VerifyPdbFiles(this PdbFile pdbFile, IEnumerable files) + { + Argument.IsNotNull(() => pdbFile); + + var missing = new Dictionary(StringComparer.OrdinalIgnoreCase); + var actualFileChecksums = (from x in files + select new KeyValuePair(Hex.encode(Crypto.hashesMD5(new[] { x }).First().Item1), x)).ToDictionary(x => x.Value, x => x.Key); + + foreach (var checksumInfo in pdbFile.GetChecksums()) + { + var file = checksumInfo.Key; + var checksum = checksumInfo.Value; + + if (!actualFileChecksums.ContainsKey(checksum)) + { + missing[file] = checksum; + } + } + + return missing; + } + + public static Dictionary GetChecksums(this PdbFile pdbFile) + { + Argument.IsNotNull(() => pdbFile); + + var checksums = new Dictionary(StringComparer.OrdinalIgnoreCase); + + foreach (var file in pdbFile.GetFiles()) + { + checksums.Add(file.Item1, Hex.encode(file.Item2)); + } + + return checksums; + } + + public static IEnumerable> GetFiles(this PdbFile pdbFile) + { + Argument.IsNotNull(() => pdbFile); + + //const int LastInterestingByte = 47; + const string FileIndicator = "/src/files/"; + + var values = pdbFile.Info.NameToPdbName.Values; + + var results = new List>(); + foreach (var value in values) + { + if (!value.Name.Contains(FileIndicator)) + { + continue; + } + + int num = value.Stream; + var name = value.Name.Substring(FileIndicator.Length); + + var bytes = pdbFile.ReadStreamBytes(num); + if (bytes.Length != 72) + { + continue; + } + + // Get last 16 bytes for checksum + byte[] buffer = new byte[16]; + for (int i = 0; i < 16; i++) + { + buffer[i] = bytes[i]; + } + + results.Add(new Tuple(name, buffer)); + } + + return results; + } + } +} \ No newline at end of file diff --git a/src/GitHubLink/Extensions/ProjectExtensions.cs b/src/GitHubLink/Extensions/ProjectExtensions.cs new file mode 100644 index 0000000..746f7e4 --- /dev/null +++ b/src/GitHubLink/Extensions/ProjectExtensions.cs @@ -0,0 +1,111 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2012 - 2014 CatenaLogic. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace GitHubLink +{ + using System.Collections.Generic; + using System.IO; + using System.Linq; + using Catel; + using Catel.Logging; + using Microsoft.Build.Evaluation; + using SourceLink; + using System; + + public static class ProjectExtensions + { + private static readonly ILog Log = LogManager.GetCurrentClassLogger(); + + public static string GetProjectName(this Project project) + { + Argument.IsNotNull(() => project); + + var projectName = GetProjectPropertyValue(project, "MSBuildProjectName"); + return projectName ?? Path.GetFileName(project.FullPath); + } + + public static string GetProjectPropertyValue(this Project project, string propertyName) + { + Argument.IsNotNull(() => project); + Argument.IsNotNullOrWhitespace(() => propertyName); + + var projectNameProperty = (from property in project.Properties + where string.Equals(property.Name, "MSBuildProjectName") + select property).FirstOrDefault(); + + if (projectNameProperty == null) + { + Log.Debug("Failed to get property 'MSBuildProjectName', returning null"); + return null; + } + + return projectNameProperty.EvaluatedValue; + } + + public static void CreateSrcSrv(this Project project, string rawUrl, string revision, Dictionary paths) + { + Argument.IsNotNull(() => project); + Argument.IsNotNullOrWhitespace(() => rawUrl); + Argument.IsNotNullOrWhitespace(() => revision); + + var srcsrvFile = GetOutputSrcSrvFile(project); + + File.WriteAllBytes(srcsrvFile, SrcSrv.create(rawUrl, revision, paths.Select(x => new Tuple(x.Key, x.Value)))); + } + + public static IEnumerable GetCompilableItems(this Project project) + { + Argument.IsNotNull(() => project); + + return project.Items.Where(x => string.Equals(x.ItemType, "Compile")); + } + + public static Dictionary VerifyPdbFiles(this Project project, IEnumerable files) + { + Argument.IsNotNull(() => project); + + using (var pdb = new PdbFile(Path.ChangeExtension(project.GetOutputFile(), ".pdb"))) + { + return pdb.VerifyPdbFiles(files); + } + } + + public static string GetOutputSrcSrvFile(this Project project) + { + Argument.IsNotNull(() => project); + + var pdbFile = GetOutputPdbFile(project); + return string.Format("{0}.srcsrv", pdbFile); + } + + public static string GetOutputPdbFile(this Project project) + { + Argument.IsNotNull(() => project); + + var outputFile = project.GetOutputFile(); + var pdbFile = Path.ChangeExtension(outputFile, ".pdb"); + + return pdbFile; + } + + public static string GetOutputFile(this Project project) + { + Argument.IsNotNull(() => project); + + string extension = ".dll"; + + string outputType = project.GetProperty("OutputType").EvaluatedValue; + if (outputType.Contains("Exe") || outputType.Contains("WinExe")) + { + extension = ".exe"; + } + + var projectOutputPath = project.GetPropertyValue("OutputPath"); + var outputPath = Path.Combine(project.DirectoryPath, projectOutputPath); + return Path.Combine(outputPath, string.Format("{0}{1}", project.GetProperty("AssemblyName").EvaluatedValue, extension)); + } + } +} \ No newline at end of file diff --git a/src/GitHubLink/Extensions/ProjectItemExtensions.cs b/src/GitHubLink/Extensions/ProjectItemExtensions.cs new file mode 100644 index 0000000..f752c0e --- /dev/null +++ b/src/GitHubLink/Extensions/ProjectItemExtensions.cs @@ -0,0 +1,31 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2012 - 2014 CatenaLogic. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace GitHubLink +{ + using Catel; + using Catel.IO; + using Microsoft.Build.Evaluation; + + public static class ProjectItemExtensions + { + public static string GetRelativeFileName(this ProjectItem projectItem) + { + Argument.IsNotNull(() => projectItem); + + return projectItem.EvaluatedInclude; + } + + public static string GetFullFileName(this ProjectItem projectItem) + { + Argument.IsNotNull(() => projectItem); + + var filePath = Path.Combine(projectItem.Project.DirectoryPath, projectItem.GetRelativeFileName()); + var fullFile = System.IO.Path.GetFullPath(filePath); + return fullFile; + } + } +} \ No newline at end of file diff --git a/src/GitHubLink/Extensions/StringExtensions.git.cs b/src/GitHubLink/Extensions/StringExtensions.git.cs new file mode 100644 index 0000000..8ecac4f --- /dev/null +++ b/src/GitHubLink/Extensions/StringExtensions.git.cs @@ -0,0 +1,26 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2012 - 2014 CatenaLogic. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace GitHubLink +{ + using System.Linq; + using Catel; + using LibGit2Sharp; + + public static partial class StringExtensions + { + public static string GetLatestCommitShaOfCurrentBranch(this string repositoryDirectory) + { + Argument.IsNotNull(() => repositoryDirectory); + + using (var repository = new Repository(repositoryDirectory)) + { + var lastCommit = repository.Commits.First(); + return lastCommit.Sha; + } + } + } +} \ No newline at end of file diff --git a/src/GitHubLink/Extensions/StringExtensions.github.cs b/src/GitHubLink/Extensions/StringExtensions.github.cs new file mode 100644 index 0000000..9908761 --- /dev/null +++ b/src/GitHubLink/Extensions/StringExtensions.github.cs @@ -0,0 +1,80 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2012 - 2014 CatenaLogic. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace GitHubLink +{ + using System; + using Catel; + + public static partial class StringExtensions + { + private const string GitHubUrl = "https://github.com"; + private const string GitHubRawUrl = "https://raw.github.com"; + + public static string GetGitHubCompanyName(this string url) + { + Argument.IsNotNullOrWhitespace(() => url); + + url = url.Replace(GitHubUrl, string.Empty); + var splittedUrl = url.Split(new[] { '/' }, StringSplitOptions.RemoveEmptyEntries); + if (splittedUrl.Length != 2) + { + return null; + } + + return splittedUrl[0]; + } + + public static string GetGitHubProjectName(this string url) + { + Argument.IsNotNullOrWhitespace(() => url); + + url = url.Replace(GitHubUrl, string.Empty); + var splittedUrl = url.Split(new[] {'/'}, StringSplitOptions.RemoveEmptyEntries); + if (splittedUrl.Length != 2) + { + return null; + } + + var projectName = splittedUrl[1]; + if (projectName.EndsWith(".git")) + { + projectName = projectName.Substring(0, projectName.Length - ".git".Length); + } + + return projectName; + } + + public static string GetGitHubProjectUrl(this string url) + { + Argument.IsNotNullOrWhitespace(() => url); + + var companyUrl = GetGitHubCompanyUrl(url); + var project = GetGitHubProjectName(url); + + return string.Format("{0}/{1}", companyUrl, project); + } + + public static string GetGitHubCompanyUrl(this string url) + { + Argument.IsNotNullOrWhitespace(() => url); + + var company = GetGitHubCompanyName(url); + + return string.Format("{0}/{1}", GitHubUrl, company); + } + + public static string GetGitHubRawUrl(this string url) + { + Argument.IsNotNullOrWhitespace(() => url); + + var company = GetGitHubCompanyName(url); + var project = GetGitHubProjectName(url); + + return string.Format("{0}/{1}/{2}", GitHubRawUrl, company, project); + } + } +} \ No newline at end of file diff --git a/src/GitHubLink/FodyWeavers.xml b/src/GitHubLink/FodyWeavers.xml new file mode 100644 index 0000000..c6e1b7c --- /dev/null +++ b/src/GitHubLink/FodyWeavers.xml @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/src/GitHubLink/Git/GitDirFinder.cs b/src/GitHubLink/Git/GitDirFinder.cs new file mode 100644 index 0000000..5882edf --- /dev/null +++ b/src/GitHubLink/Git/GitDirFinder.cs @@ -0,0 +1,30 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2012 - 2014 CatenaLogic. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace GitHubLink.Git +{ + using System.IO; + using LibGit2Sharp; + + public class GitDirFinder + { + #region Methods + + public static string TreeWalkForGitDir(string currentDirectory) + { + var gitDirectory = Repository.Discover(currentDirectory); + + if (gitDirectory != null) + { + return gitDirectory.TrimEnd(new[] {Path.DirectorySeparatorChar}); + } + + return null; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/GitHubLink/Git/GitPreparer.cs b/src/GitHubLink/Git/GitPreparer.cs new file mode 100644 index 0000000..f37654e --- /dev/null +++ b/src/GitHubLink/Git/GitPreparer.cs @@ -0,0 +1,91 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2012 - 2014 CatenaLogic. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace GitHubLink.Git +{ + using System.IO; + using Catel; + using Catel.Logging; + using LibGit2Sharp; + + public class GitPreparer + { + #region Constants + private static readonly ILog Log = LogManager.GetCurrentClassLogger(); + #endregion + + #region Fields + private readonly Context _context; + #endregion + + #region Constructors + + public GitPreparer(Context context) + { + Argument.IsNotNull(() => context); + + _context = context; + } + + #endregion + + #region Methods + + public string Prepare() + { + var gitPath = _context.TempDirectory; + + if (!string.IsNullOrWhiteSpace(_context.TargetUrl)) + { + gitPath = GetGitInfoFromUrl(); + } + + return GitDirFinder.TreeWalkForGitDir(gitPath); + } + + private string GetGitInfoFromUrl() + { + var gitDirectory = Path.Combine(_context.TempDirectory, ".git"); + if (Directory.Exists(gitDirectory)) + { + Log.Info("Deleting existing .git folder from '{0}' to force new checkout from url", gitDirectory); + + DeleteHelper.DeleteGitRepository(gitDirectory); + } + + Log.Info("Retrieving git info from url '{0}'", _context.TargetUrl); + + var cloneOptions = new CloneOptions + { + Checkout = false, + IsBare = true + }; + + Repository.Clone(_context.TargetUrl, gitDirectory, cloneOptions); + + if (!string.IsNullOrWhiteSpace(_context.TargetBranch)) + { + // Normalize (download branches) before using the branch + GitHelper.NormalizeGitDirectory(gitDirectory); + + using (var repository = new Repository(gitDirectory)) + { + var targetBranchName = string.Format("refs/heads/{0}", _context.TargetBranch); + if (!string.Equals(repository.Head.CanonicalName, targetBranchName)) + { + Log.Info("Switching to branch '{0}'", _context.TargetBranch); + + repository.Refs.UpdateTarget("HEAD", targetBranchName); + } + } + } + + return gitDirectory; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/GitHubLink/GitHubLink.csproj b/src/GitHubLink/GitHubLink.csproj new file mode 100644 index 0000000..28007d7 --- /dev/null +++ b/src/GitHubLink/GitHubLink.csproj @@ -0,0 +1,125 @@ + + + + + Debug + AnyCPU + {D68ADD77-913F-46D2-9A4F-5CC71C4718D8} + Exe + Properties + GitHubLink + GitHubLink + v4.5 + 512 + + 50d7f892 + + + true + full + false + ..\..\output\debug\GitHubLink\ + DEBUG;TRACE + prompt + 4 + false + true + + + + + pdbonly + true + ..\..\output\release\GitHubLink\ + TRACE + prompt + 4 + false + true + + + + + GitHubLink.Program + + + Resources\Icons\Logo.ico + + + + ..\..\lib\Catel.Core.3.9.0.1404150943-beta\lib\net45\Catel.Core.dll + + + + ..\..\lib\LibGit2Sharp.0.17.0.0\lib\net35\LibGit2Sharp.dll + True + + + + ..\..\lib\SourceLink.0.3.5-a1402171459-a1cba03d\lib\net45\SourceLink.dll + + + + + + + + + + + + + Properties\SolutionAssemblyInfo.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Designer + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Enable NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/src/GitHubLink/GitHubLinkEnvironment.cs b/src/GitHubLink/GitHubLinkEnvironment.cs new file mode 100644 index 0000000..fdd1bfd --- /dev/null +++ b/src/GitHubLink/GitHubLinkEnvironment.cs @@ -0,0 +1,13 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2012 - 2014 CatenaLogic. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace GitHubLink +{ + public static class GitHubLinkEnvironment + { + public const string GitHubBaseUrl = "https://raw.github.com/"; + } +} \ No newline at end of file diff --git a/src/GitHubLink/HelpWriter.cs b/src/GitHubLink/HelpWriter.cs new file mode 100644 index 0000000..e29aa2d --- /dev/null +++ b/src/GitHubLink/HelpWriter.cs @@ -0,0 +1,44 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2012 - 2014 CatenaLogic. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace GitHubLink +{ + using System; + using Catel.Reflection; + + public static class HelpWriter + { + #region Methods + + public static void WriteAppHeader(Action writer) + { + var assembly = typeof(HelpWriter).Assembly; + + writer(string.Format("{0} v{1}", assembly.Title(), assembly.Version())); + writer("==================="); + writer(string.Empty); + } + + public static void WriteHelp(Action writer) + { + var message = + @"Update pdb files to link all sources. This will allow anyone to step through the source code while debugging without a symbol source server. + +Note that the solution must be built because this application will update existing pdb files. + +GitHubLink [solutionPath] -url [urlToRepository] + + solutionPath The directory containing the solution with the pdb files. + -url [url] Url to remote git repository. + -b [branch] Name of the branch to use on the remote repository. + -l [file] +"; + writer(message); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/GitHubLink/Helpers/DeleteHelper.cs b/src/GitHubLink/Helpers/DeleteHelper.cs new file mode 100644 index 0000000..227b0dc --- /dev/null +++ b/src/GitHubLink/Helpers/DeleteHelper.cs @@ -0,0 +1,37 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2012 - 2014 CatenaLogic. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace GitHubLink +{ + using System.IO; + + public static class DeleteHelper + { + #region Methods + + public static void DeleteGitRepository(string directory) + { + if (string.IsNullOrEmpty(directory)) + { + return; + } + + foreach (var fileName in Directory.GetFiles(directory, "*.*", SearchOption.AllDirectories)) + { + var fileInfo = new FileInfo(fileName) + { + IsReadOnly = false + }; + + fileInfo.Delete(); + } + + Directory.Delete(directory, true); + } + + #endregion + } +} \ No newline at end of file diff --git a/src/GitHubLink/Helpers/GitHelper.cs b/src/GitHubLink/Helpers/GitHelper.cs new file mode 100644 index 0000000..86a9f93 --- /dev/null +++ b/src/GitHubLink/Helpers/GitHelper.cs @@ -0,0 +1,143 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2012 - 2014 CatenaLogic. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace GitHubLink +{ + using System; + using System.Linq; + using Catel.Logging; + using LibGit2Sharp; + + public static class GitHelper + { + #region Constants + + private static readonly ILog Log = LogManager.GetCurrentClassLogger(); + + #endregion + + #region Methods + + public static void NormalizeGitDirectory(string gitDirectory) + { + using (var repo = new Repository(gitDirectory)) + { + var remote = EnsureOnlyOneRemoteIsDefined(repo); + + Log.Info("Fetching from remote '{0}' using the following refspecs: {1}.", + remote.Name, string.Join(", ", remote.FetchRefSpecs.Select(r => r.Specification))); + + var fetchOptions = new FetchOptions(); + repo.Network.Fetch(remote, fetchOptions); + + CreateMissingLocalBranchesFromRemoteTrackingOnes(repo, remote.Name); + + if (!repo.Info.IsHeadDetached) + { + Log.Info("HEAD points at branch '{0}'.", repo.Refs.Head.TargetIdentifier); + return; + } + + Log.Info("HEAD is detached and points at commit '{0}'.", repo.Refs.Head.TargetIdentifier); + + CreateFakeBranchPointingAtThePullRequestTip(repo); + } + } + + private static void CreateFakeBranchPointingAtThePullRequestTip(Repository repo) + { + var remote = repo.Network.Remotes.Single(); + var remoteTips = repo.Network.ListReferences(remote); + + var headTipSha = repo.Head.Tip.Sha; + + var refs = remoteTips.Where(r => r.TargetIdentifier == headTipSha).ToList(); + + if (refs.Count == 0) + { + Log.ErrorAndThrowException( + "Couldn't find any remote tips from remote '{0}' pointing at the commit '{1}'.", remote.Url, + headTipSha); + } + + if (refs.Count > 1) + { + var names = string.Join(", ", refs.Select(r => r.CanonicalName)); + Log.ErrorAndThrowException( + "Found more than one remote tip from remote '{0}' pointing at the commit '{1}'. Unable to determine which one to use ({2}).", + remote.Url, headTipSha, names); + } + + var canonicalName = refs[0].CanonicalName; + Log.Info("Found remote tip '{0}' pointing at the commit '{1}'.", canonicalName, headTipSha); + + if (!canonicalName.StartsWith("refs/pull/")) + { + Log.ErrorAndThrowException( + "Remote tip '{0}' from remote '{1}' doesn't look like a valid pull request.", canonicalName, + remote.Url); + } + + var fakeBranchName = canonicalName.Replace("refs/pull/", "refs/heads/pull/"); + + Log.Info("Creating fake local branch '{0}'.", fakeBranchName); + repo.Refs.Add(fakeBranchName, new ObjectId(headTipSha)); + + Log.Info("Checking local branch '{0}' out.", fakeBranchName); + repo.Checkout(fakeBranchName); + } + + private static void CreateMissingLocalBranchesFromRemoteTrackingOnes(Repository repo, string remoteName) + { + var prefix = string.Format("refs/remotes/{0}/", remoteName); + + foreach (var remoteTrackingReference in repo.Refs.FromGlob(prefix + "*")) + { + var localCanonicalName = "refs/heads/" + remoteTrackingReference.CanonicalName.Substring(prefix.Length); + if (repo.Refs.Any(x => x.CanonicalName == localCanonicalName)) + { + Log.Info("Skipping local branch creation since it already exists '{0}'.", + remoteTrackingReference.CanonicalName); + continue; + } + + Log.Info("Creating local branch from remote tracking '{0}'.", remoteTrackingReference.CanonicalName); + + var symbolicReference = remoteTrackingReference as SymbolicReference; + if (symbolicReference == null) + { + repo.Refs.Add(localCanonicalName, new ObjectId(remoteTrackingReference.TargetIdentifier), true); + } + else + { + repo.Refs.Add(localCanonicalName, + new ObjectId(symbolicReference.ResolveToDirectReference().TargetIdentifier), true); + } + } + } + + private static Remote EnsureOnlyOneRemoteIsDefined(IRepository repo) + { + var remotes = repo.Network.Remotes; + var howMany = remotes.Count(); + + if (howMany == 1) + { + var remote = remotes.Single(); + Log.Info("One remote found ({0} -> '{1}').", remote.Name, remote.Url); + return remote; + } + + Log.ErrorAndThrowException( + "{0} remote(s) have been detected. When being run on a TeamCity agent, the Git repository is expected to bear one (and no more than one) remote.", + howMany); + + return null; + } + + #endregion + } +} \ No newline at end of file diff --git a/src/GitHubLink/Helpers/PdbStrHelper.cs b/src/GitHubLink/Helpers/PdbStrHelper.cs new file mode 100644 index 0000000..583cb31 --- /dev/null +++ b/src/GitHubLink/Helpers/PdbStrHelper.cs @@ -0,0 +1,92 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2012 - 2014 CatenaLogic. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace GitHubLink +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.IO; + using Catel; + using Catel.Logging; + + public static class PdbStrHelper + { + private static readonly ILog Log = LogManager.GetCurrentClassLogger(); + + #region Constants + + private static readonly List PossibleLocations; + + #endregion + + #region Constructors + + static PdbStrHelper() + { + // TODO: Read from registry in the future? + + PossibleLocations = new List(new [] + { + @"C:\Program Files (x86)\Windows Kits\8.1\Debuggers\x64\srcsrv\pdbstr.exe", // 6.3.9600.16384 + @"C:\Program Files\Microsoft Team Foundation Server 12.0\Tools\pdbstr.exe", // 6.3.9600.16384 + @"C:\Program Files (x86)\Windows Kits\8.0\Debuggers\x64\srcsrv\pdbstr.exe", // 6.2.9200.16384 + @"C:\Program Files\Microsoft Team Foundation Server 11.0\Tools\pdbstr.exe", // 6.2.9200.16384 + @"C:\Program Files\Debugging Tools for Windows (x64)\srcsrv\pdbstr.exe" + }); + } + + #endregion + + #region Methods + + public static bool IsPdbStrAvailable() + { + var pdbStrFileName = GetPdbStrFileName(); + return !string.IsNullOrEmpty(pdbStrFileName); + } + + public static string GetPdbStrFileName() + { + foreach (var possibleLocation in PossibleLocations) + { + if (File.Exists(possibleLocation)) + { + return possibleLocation; + } + } + + return null; + } + + public static void Execute(string projectPdbFile, string pdbStrFile) + { + Argument.IsNotNullOrWhitespace(() => projectPdbFile); + Argument.IsNotNullOrWhitespace(() => pdbStrFile); + + var pdbStrFileName = GetPdbStrFileName(); + var processStartInfo = new ProcessStartInfo(pdbStrFileName) + { + Arguments = string.Format("-w -s:srcsrv -p:\"{0}\" -i:\"{1}\"", projectPdbFile, pdbStrFile), + CreateNoWindow = true, + UseShellExecute = false + }; + + var process = new Process(); + process.StartInfo = processStartInfo; + process.Start(); + process.WaitForExit(); + + var processExitCode = process.ExitCode; + if (processExitCode != 0) + { + Log.ErrorAndThrowException("PdbStr exited with unexpected error code '{0}'", processExitCode); + } + } + + #endregion + } +} \ No newline at end of file diff --git a/src/GitHubLink/Helpers/ProjectHelper.cs b/src/GitHubLink/Helpers/ProjectHelper.cs new file mode 100644 index 0000000..af01616 --- /dev/null +++ b/src/GitHubLink/Helpers/ProjectHelper.cs @@ -0,0 +1,27 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2012 - 2014 CatenaLogic. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace GitHubLink +{ + using System.Collections.Generic; + using Catel; + using Microsoft.Build.Evaluation; + + public static class ProjectHelper + { + public static Project LoadProject(string projectName, string configurationName) + { + Argument.IsNotNullOrWhitespace(() => projectName); + Argument.IsNotNullOrWhitespace(() => configurationName); + + var collections = new Dictionary(); + collections["Configuration"] = configurationName; + + var project = new Project(projectName, collections, null); + return project; + } + } +} \ No newline at end of file diff --git a/src/GitHubLink/Linker.cs b/src/GitHubLink/Linker.cs new file mode 100644 index 0000000..5659a96 --- /dev/null +++ b/src/GitHubLink/Linker.cs @@ -0,0 +1,178 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2012 - 2014 CatenaLogic. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace GitHubLink +{ + using System; + using System.Collections.Generic; + using System.Diagnostics; + using System.IO; + using System.Linq; + using Catel; + using Catel.Logging; + using GitHubLink.Git; + + /// + /// Class Linker. + /// + public static class Linker + { + private static readonly ILog Log = LogManager.GetCurrentClassLogger(); + + public static int Link(Context context) + { + int? exitCode = null; + + var stopWatch = new Stopwatch(); + stopWatch.Start(); + + context.ValidateContext(); + + if (!string.IsNullOrEmpty(context.LogFile)) + { + var fileLogListener = new FileLogListener(context.LogFile, 25 * 1024); + fileLogListener.IgnoreCatelLogging = true; + LogManager.AddListener(fileLogListener); + } + + if (!PdbStrHelper.IsPdbStrAvailable()) + { + Log.Error("PdbStr is not found on the computer, please install 'Debugging Tools for Windows'"); + return -1; + } + + try + { + var gitPreparer = new GitPreparer(context); + gitPreparer.Prepare(); + + var projectFiles = Directory.GetFiles(context.SolutionDirectory, "*.csproj", SearchOption.AllDirectories); + + int projectCount = projectFiles.Count(); + var failedProjects = new List(); + Log.Info("Found '{0}' project(s)", projectCount); + + foreach (var projectFile in projectFiles) + { + try + { + if (!LinkProject(context, projectFile)) + { + failedProjects.Add(projectFile); + } + } + catch (Exception) + { + failedProjects.Add(projectFile); + } + } + + Log.Info("All projects are done. {0} of {1} succeeded", projectCount - failedProjects.Count, + projectCount); + + if (failedProjects.Count > 0) + { + Log.Info(string.Empty); + Log.Info("The following projects have failed:"); + Log.Indent(); + + foreach (var failedProject in failedProjects) + { + Log.Info("* {0}", Path.GetFileName(failedProject)); + } + + Log.Unindent(); + } + } + catch (GitHubLinkException ex) + { + Log.Error(ex, "An error occurred"); + } + catch (Exception ex) + { + Log.Error(ex, "An unexpected error occurred"); + } + finally + { + Log.Debug("Clearing temporary directory '{0}'", context.TempDirectory); + + DeleteHelper.DeleteGitRepository(context.TempDirectory); + } + + stopWatch.Stop(); + + Log.Info(string.Empty); + Log.Info("Completed in '{0}'", stopWatch.Elapsed); + + return exitCode ?? -1; + } + + public static bool LinkProject(Context context, string projectFile) + { + Argument.IsNotNull(() => context); + Argument.IsNotNullOrWhitespace(() => projectFile); + + Log.Info("Handling project '{0}'", projectFile); + + Log.Indent(); + + try + { + var project = ProjectHelper.LoadProject(projectFile, context.ConfigurationName); + string projectName = project.GetProjectName(); + + var compilables = project.GetCompilableItems().Select(x => x.GetFullFileName()); + + var projectPdbFile = Path.GetFullPath(project.GetOutputPdbFile()); + var projectStcSrvFile = Path.GetFullPath(project.GetOutputSrcSrvFile()); + if (!File.Exists(projectPdbFile)) + { + Log.Warning("No pdb file found for '{0}', is project built in release mode with pdb files enabled?", projectName); + Log.Unindent(); + return false; + } + + // Note: verify doesn't work yet, maybe implement later + Log.Warning("Pdb verification not yet implemented, cannot garantuee that pdb-files are up-to-date"); + //var missingFiles = project.VerifyPdbFiles(compilables); + //foreach (var missingFile in missingFiles) + //{ + // Log.Warning("Missing file '{0}' or checksum '{1}' did not match", missingFile.Key, missingFile.Value); + //} + + var rawUrl = string.Format("{0}/{{0}}/%var2%", context.TargetUrl.GetGitHubRawUrl()); + var revision = context.TempDirectory.GetLatestCommitShaOfCurrentBranch(); + + var paths = new Dictionary(); + foreach (var compilable in compilables) + { + var relativePathForUrl = compilable.Replace(context.SolutionDirectory, string.Empty).Replace("\\", "/"); + while (relativePathForUrl.StartsWith("/")) + { + relativePathForUrl = relativePathForUrl.Substring(1, relativePathForUrl.Length - 1); + } + + paths.Add(compilable, relativePathForUrl); + } + + project.CreateSrcSrv(rawUrl, revision, paths); + + Log.Debug("Created source server link file, updating pdb file '{0}'", projectPdbFile); + + PdbStrHelper.Execute(projectPdbFile, projectStcSrvFile); + } + catch (Exception ex) + { + Log.Warning(ex, "An error occurred while processing project '{0}'", projectFile); + throw; + } + + Log.Unindent(); + + return true; + } + } +} \ No newline at end of file diff --git a/src/GitHubLink/Logging/OutputLogListener.cs b/src/GitHubLink/Logging/OutputLogListener.cs new file mode 100644 index 0000000..c849c35 --- /dev/null +++ b/src/GitHubLink/Logging/OutputLogListener.cs @@ -0,0 +1,24 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2012 - 2014 CatenaLogic. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace GitHubLink.Logging +{ + using Catel.Logging; + + public class OutputLogListener : ConsoleLogListener + { + public OutputLogListener() + { + IgnoreCatelLogging = true; + IsDebugEnabled = true; + } + + protected override string FormatLogEvent(ILog log, string message, LogEvent logEvent, object extraData) + { + return message; + } + } +} \ No newline at end of file diff --git a/src/GitHubLink/Program.cs b/src/GitHubLink/Program.cs new file mode 100644 index 0000000..d95fddf --- /dev/null +++ b/src/GitHubLink/Program.cs @@ -0,0 +1,66 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2012 - 2014 CatenaLogic. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace GitHubLink +{ + using System; + using Catel.Logging; + using GitHubLink.Logging; + + internal class Program + { + private static readonly ILog Log = LogManager.GetCurrentClassLogger(); + + #region Methods + + private static int Main(string[] args) + { + var consoleLogListener = new OutputLogListener(); + + LogManager.AddListener(consoleLogListener); + + try + { + HelpWriter.WriteAppHeader(s => Log.Write(LogEvent.Info, s)); + + Log.Info("Arguments: {0}", string.Join(" ", args)); + Log.Info(string.Empty); + + var context = ArgumentParser.ParseArguments(args); + if (context.IsHelp) + { + HelpWriter.WriteHelp(s => Log.Write(LogEvent.Info, s)); + + WaitForKeyPress(); + + return 0; + } + + var result = Linker.Link(context); + +#if DEBUG + WaitForKeyPress(); +#endif + + return result; + } + catch (Exception) + { + WaitForKeyPress(); + return -1; + } + } + + #endregion + + private static void WaitForKeyPress() + { + Log.Info(string.Empty); + Log.Info("Press any key to continue"); + Console.ReadKey(); + } + } +} \ No newline at end of file diff --git a/src/GitHubLink/Properties/AssemblyInfo.cs b/src/GitHubLink/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..0bef22b --- /dev/null +++ b/src/GitHubLink/Properties/AssemblyInfo.cs @@ -0,0 +1,23 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2012 - 2014 CatenaLogic. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +using System.Reflection; +using System.Runtime.InteropServices; + +// All other assembly info is defined in SharedAssembly.cs + +[assembly: AssemblyTitle("GitHubLink")] +[assembly: AssemblyProduct("GitHubLink")] +[assembly: AssemblyDescription("GitHubLink library")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. + +#if !PCL + +[assembly: ComVisible(false)] +#endif \ No newline at end of file diff --git a/src/GitHubLink/Resources/Icons/Logo.ico b/src/GitHubLink/Resources/Icons/Logo.ico new file mode 100644 index 0000000..529b63c Binary files /dev/null and b/src/GitHubLink/Resources/Icons/Logo.ico differ diff --git a/src/GitHubLink/costura32/git2-06d772d.dll b/src/GitHubLink/costura32/git2-06d772d.dll new file mode 100644 index 0000000..1ceb3b2 Binary files /dev/null and b/src/GitHubLink/costura32/git2-06d772d.dll differ diff --git a/src/GitHubLink/costura64/git2-06d772d.dll b/src/GitHubLink/costura64/git2-06d772d.dll new file mode 100644 index 0000000..639b55e Binary files /dev/null and b/src/GitHubLink/costura64/git2-06d772d.dll differ diff --git a/src/GitHubLink/packages.config b/src/GitHubLink/packages.config new file mode 100644 index 0000000..0aaeea6 --- /dev/null +++ b/src/GitHubLink/packages.config @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/Settings.StyleCop b/src/Settings.StyleCop new file mode 100644 index 0000000..000eac0 --- /dev/null +++ b/src/Settings.StyleCop @@ -0,0 +1,359 @@ + + + + preprocessor, pre-processor + shortlived, short-lived + + NoMerge + + db + + + + + + + \.g\.cs$ + \.generated\.cs$ + \.g\.i\.cs$ + + + + + + + + + + False + + + + + False + + + + + False + + + + + + as + do + id + if + in + is + my + no + on + to + ui + + + + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + + + + + + False + + + + + False + + + + + False + + + + + False + + + + + + + + + + False + + + + + False + + + + + False + + + + + + + + + + False + + + + + + + \ No newline at end of file diff --git a/src/SolutionAssemblyInfo.cs b/src/SolutionAssemblyInfo.cs new file mode 100644 index 0000000..998372e --- /dev/null +++ b/src/SolutionAssemblyInfo.cs @@ -0,0 +1,38 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2012 - 2014 CatenaLogic. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +#pragma warning disable 1699 // 1699 = Use command line option '/keyfile' or appropriate project settings instead of 'AssemblyKeyFile' + +using System; +using System.Reflection; +using System.Resources; + +// Shared assembly info that is common for all assemblies of this project + +////[assembly: AssemblyTitle("DEFINED IN ACTUAL ASSEMBLYINFO")] +////[assembly: AssemblyProduct("DEFINED IN ACTUAL ASSEMBLYINFO")] +////[assembly: AssemblyDescription("DEFINED IN ACTUAL ASSEMBLYINFO")] + +[assembly: AssemblyCompany("CatenaLogic")] +[assembly: AssemblyCopyright("Copyright © CatenaLogic 2014")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: NeutralResourcesLanguage("en-US")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0")] +[assembly: AssemblyInformationalVersion("1.0, manual release in Visual Studio")] + +[assembly: CLSCompliant(false)] \ No newline at end of file diff --git a/src/nuget.config b/src/nuget.config new file mode 100644 index 0000000..29a9bd8 --- /dev/null +++ b/src/nuget.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/tools/GitHubLink/GitHubLink.exe b/tools/GitHubLink/GitHubLink.exe new file mode 100644 index 0000000..d5a3002 Binary files /dev/null and b/tools/GitHubLink/GitHubLink.exe differ diff --git a/tools/GitVersion/GitVersion.exe b/tools/GitVersion/GitVersion.exe new file mode 100644 index 0000000..3fb076f Binary files /dev/null and b/tools/GitVersion/GitVersion.exe differ diff --git a/tools/NuGet/NuGet.exe b/tools/NuGet/NuGet.exe new file mode 100644 index 0000000..3ffdd33 Binary files /dev/null and b/tools/NuGet/NuGet.exe differ