From ca2ce21481dbdab5c7652e663ecadaafb352e524 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Fri, 7 Oct 2016 21:41:53 -0700 Subject: [PATCH 01/85] Retrofit build authoring Use project.json instead of packages.config Stop linking assemblies together and embedding executables. --- .gitignore | 6 +- CONTRIBUTORS | 3 +- GitVersionConfig.yaml | 3 - scripts - Build - Debug.bat | 10 - scripts - Build - Release.bat | 10 - scripts - Clean all.bat | 7 - scripts - Restore packages.bat | 4 - src/.RepoSrcRoot | 0 src/GitLink.Tests/GitLink.Tests.csproj | 46 +-- src/GitLink.Tests/Properties/AssemblyInfo.cs | 32 -- src/GitLink.Tests/packages.config | 9 - src/GitLink.Tests/project.json | 12 + src/GitLink.sln | 23 +- src/GitLink/FodyWeavers.xml | 16 - src/GitLink/GitLink.csproj | 75 +--- src/GitLink/{Resources/Icons => }/Logo.ico | Bin src/GitLink/packages.config | 11 - src/GitLink/{Resources/Files => }/pdbstr.exe | Bin src/GitLink/project.json | 22 ++ .../{Resources/Files => }/winsdk.redist.txt | 0 src/GitLinkTask/GitLinkTask.csproj | 4 +- src/GitLinkTask/project.json | 10 + src/Settings.StyleCop | 359 ------------------ src/SolutionAssemblyInfo.cs | 38 -- src/version.json | 11 + tools/NuGet/NuGet.exe | Bin 1659904 -> 0 bytes 26 files changed, 85 insertions(+), 626 deletions(-) delete mode 100644 GitVersionConfig.yaml delete mode 100644 scripts - Build - Debug.bat delete mode 100644 scripts - Build - Release.bat delete mode 100644 scripts - Clean all.bat delete mode 100644 scripts - Restore packages.bat create mode 100644 src/.RepoSrcRoot delete mode 100644 src/GitLink.Tests/packages.config create mode 100644 src/GitLink.Tests/project.json delete mode 100644 src/GitLink/FodyWeavers.xml rename src/GitLink/{Resources/Icons => }/Logo.ico (100%) delete mode 100644 src/GitLink/packages.config rename src/GitLink/{Resources/Files => }/pdbstr.exe (100%) create mode 100644 src/GitLink/project.json rename src/GitLink/{Resources/Files => }/winsdk.redist.txt (100%) create mode 100644 src/GitLinkTask/project.json delete mode 100644 src/Settings.StyleCop delete mode 100644 src/SolutionAssemblyInfo.cs create mode 100644 src/version.json delete mode 100644 tools/NuGet/NuGet.exe diff --git a/.gitignore b/.gitignore index c2c5710..9f4ec6f 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,9 @@ *.sln.docstates .nuget/ +project.lock.json +*.nuget.targets +*.nuget.props tools/FAKE/ build-log.xml Nuget.key @@ -42,6 +45,7 @@ obj *.vspscc *.xap .builds +*.log # Visual C++ cache files ipch/ @@ -122,4 +126,4 @@ Desktop.ini # mstest test results TestResults -/.vs +.vs/ diff --git a/CONTRIBUTORS b/CONTRIBUTORS index f5482c8..c29170f 100644 --- a/CONTRIBUTORS +++ b/CONTRIBUTORS @@ -10,5 +10,6 @@ # Please keep the list sorted. +Andrew Arnott Geert van Horrik -Wesley Eledui \ No newline at end of file +Wesley Eledui diff --git a/GitVersionConfig.yaml b/GitVersionConfig.yaml deleted file mode 100644 index 35575d5..0000000 --- a/GitVersionConfig.yaml +++ /dev/null @@ -1,3 +0,0 @@ -mode: ContinuousDeployment -assembly-versioning-scheme: MajorMinorPatch -#next-version: 1.0.0 \ No newline at end of file diff --git a/scripts - Build - Debug.bat b/scripts - Build - Debug.bat deleted file mode 100644 index b1ae2b9..0000000 --- a/scripts - Build - Debug.bat +++ /dev/null @@ -1,10 +0,0 @@ -@echo off - -IF NOT "%VS110COMNTOOLS%" == "" (call "%VS110COMNTOOLS%vsvars32.bat") -IF NOT "%VS120COMNTOOLS%" == "" (call "%VS120COMNTOOLS%vsvars32.bat") -IF NOT "%VS130COMNTOOLS%" == "" (call "%VS130COMNTOOLS%vsvars32.bat") -IF NOT "%VS140COMNTOOLS%" == "" (call "%VS140COMNTOOLS%vsvars32.bat") - - -for /F %%A in ('dir /b src\*.sln') do call devenv src\%%A /build "Debug" -pause \ No newline at end of file diff --git a/scripts - Build - Release.bat b/scripts - Build - Release.bat deleted file mode 100644 index d5d0265..0000000 --- a/scripts - Build - Release.bat +++ /dev/null @@ -1,10 +0,0 @@ -@echo off - -IF NOT "%VS110COMNTOOLS%" == "" (call "%VS110COMNTOOLS%vsvars32.bat") -IF NOT "%VS120COMNTOOLS%" == "" (call "%VS120COMNTOOLS%vsvars32.bat") -IF NOT "%VS130COMNTOOLS%" == "" (call "%VS130COMNTOOLS%vsvars32.bat") -IF NOT "%VS140COMNTOOLS%" == "" (call "%VS140COMNTOOLS%vsvars32.bat") - - -for /F %%A in ('dir /b src\*.sln') do call devenv src\%%A /build "Release" -pause \ No newline at end of file diff --git a/scripts - Clean all.bat b/scripts - Clean all.bat deleted file mode 100644 index effd5ea..0000000 --- a/scripts - Clean all.bat +++ /dev/null @@ -1,7 +0,0 @@ -REM Deleting packages -for /d %%p in (".\lib\*.*") do rmdir "%%p" /s /q - -REM Deleting output -rmdir .\output /s /q - -pause \ No newline at end of file diff --git a/scripts - Restore packages.bat b/scripts - Restore packages.bat deleted file mode 100644 index 529df1f..0000000 --- a/scripts - Restore packages.bat +++ /dev/null @@ -1,4 +0,0 @@ -for /f %%a in ('dir /b src\*.sln') do call tools\nuget\nuget.exe restore src\%%a -PackagesDirectory .\lib - - -pause \ No newline at end of file diff --git a/src/.RepoSrcRoot b/src/.RepoSrcRoot new file mode 100644 index 0000000..e69de29 diff --git a/src/GitLink.Tests/GitLink.Tests.csproj b/src/GitLink.Tests/GitLink.Tests.csproj index ec4da18..ae3c2e5 100644 --- a/src/GitLink.Tests/GitLink.Tests.csproj +++ b/src/GitLink.Tests/GitLink.Tests.csproj @@ -1,6 +1,5 @@  - Debug AnyCPU @@ -38,39 +37,7 @@ 4 - - False - ..\..\lib\ApprovalTests.3.0.8\lib\net40\ApprovalTests.dll - True - - - False - ..\..\lib\ApprovalUtilities.3.0.8\lib\net45\ApprovalUtilities.dll - True - - - ..\..\lib\ApprovalUtilities.3.0.8\lib\net45\ApprovalUtilities.Net45.dll - True - - - False - ..\..\lib\Catel.Core.4.3.0\lib\net45\Catel.Core.dll - True - - - False - ..\..\lib\GitTools.Core.1.0.0-unstable0021\lib\net45\GitTools.Core.dll - True - - - False - ..\..\lib\LibGit2Sharp.0.21.0.176\lib\net40\LibGit2Sharp.dll - True - - - ..\..\lib\NUnit.2.6.4\lib\nunit.framework.dll - @@ -106,9 +73,6 @@ GitLink - - - @@ -125,7 +89,9 @@ Always - + + + @@ -146,12 +112,6 @@ - - - This project references NuGet package(s) that are missing on this computer. Use 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/GitLink/Resources/Icons/Logo.ico b/src/GitLink/Logo.ico similarity index 100% rename from src/GitLink/Resources/Icons/Logo.ico rename to src/GitLink/Logo.ico diff --git a/src/GitLink/packages.config b/src/GitLink/packages.config deleted file mode 100644 index c80bbbc..0000000 --- a/src/GitLink/packages.config +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - - - \ No newline at end of file diff --git a/src/GitLink/Resources/Files/pdbstr.exe b/src/GitLink/pdbstr.exe similarity index 100% rename from src/GitLink/Resources/Files/pdbstr.exe rename to src/GitLink/pdbstr.exe diff --git a/src/GitLink/project.json b/src/GitLink/project.json new file mode 100644 index 0000000..8644bb0 --- /dev/null +++ b/src/GitLink/project.json @@ -0,0 +1,22 @@ +{ + "dependencies": { + "Catel.Core": "4.3.0", + "GitTools.Core": "1.0.0-unstable0021", + "ImpromptuInterface": "6.2.2", + "LibGit2Sharp": "0.21.0.176", + "ReadOnlySourceTree": { + "version": "0.1.37-beta", + "suppressParent": "none" + }, + "Nerdbank.GitVersioning": { + "version": "1.5.46", + "suppressParent": "none" + } + }, + "frameworks": { + "net45": {} + }, + "runtimes": { + "win": {} + } +} \ No newline at end of file diff --git a/src/GitLink/Resources/Files/winsdk.redist.txt b/src/GitLink/winsdk.redist.txt similarity index 100% rename from src/GitLink/Resources/Files/winsdk.redist.txt rename to src/GitLink/winsdk.redist.txt diff --git a/src/GitLinkTask/GitLinkTask.csproj b/src/GitLinkTask/GitLinkTask.csproj index f0a98d5..871be58 100644 --- a/src/GitLinkTask/GitLinkTask.csproj +++ b/src/GitLinkTask/GitLinkTask.csproj @@ -39,9 +39,6 @@ - - Properties\SolutionAssemblyInfo.cs - @@ -54,6 +51,7 @@ + cXAl(hKgdcVGV_xZnJD(df8GYUnRkKCT>1!C1{ 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

S>iHS|@46sMM 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%0Dbb%>LJ;tb|o`Kp8Evufeht>W*BXmS?4^&TB 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%0A{4Qko^d0b`ex!LBAWYhRF*1EWi7u)hrY#_Gf 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 z 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 From 53e93235ed36d387164a100014196a290bca6819 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Fri, 7 Oct 2016 21:57:13 -0700 Subject: [PATCH 02/85] Get NUnit tests running in Test Explorer --- src/GitLink.Tests/GlobalInitialization.cs | 2 +- src/GitLink.Tests/project.json | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/GitLink.Tests/GlobalInitialization.cs b/src/GitLink.Tests/GlobalInitialization.cs index c0bf1c4..96017af 100644 --- a/src/GitLink.Tests/GlobalInitialization.cs +++ b/src/GitLink.Tests/GlobalInitialization.cs @@ -11,7 +11,7 @@ [SetUpFixture] public class GlobalInitialization { - [SetUp] + //[SetUp] public static void SetUp() { #if DEBUG diff --git a/src/GitLink.Tests/project.json b/src/GitLink.Tests/project.json index 8d7a574..ba90913 100644 --- a/src/GitLink.Tests/project.json +++ b/src/GitLink.Tests/project.json @@ -1,7 +1,8 @@ { "dependencies": { "ApprovalTests": "3.0.8", - "NUnit": "2.6.4" + "NUnit": "3.5.0", + "NUnit3TestAdapter": "3.4.1" }, "frameworks": { "net45": {} From 9e5934401497c0b884d7cc16e38af8ac83761698 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 07:26:43 -0700 Subject: [PATCH 03/85] Build nupkg with NuProj --- .../template/GitHubLink/GitHubLink.nuspec | 27 ----------- .../template/GitHubLink/tools/LICENSE.txt | 21 --------- .../template/GitLink/GitLink.nuspec | 24 ---------- .../template/GitLink/tools/LICENSE.txt | 21 --------- .../FinalBuilder/GitLink.CreatePackages.fbp7 | Bin 198570 -> 0 bytes .../template/GitHubLink/GitHubLink.nuspec | 28 ----------- .../NuGet/template/GitLink/GitLink.nuspec | 25 ---------- .../GitLinkTask/Build/dotnet/GitLink.targets | 11 ----- .../template/GitLinkTask/GitLinkTask.nuspec | 25 ---------- init.cmd | 1 + init.ps1 | 33 +++++++++++++ lib/repositories.config | 5 -- src/GitLink.NuGet/GitLink.NuGet.nuproj | 44 ++++++++++++++++++ src/GitLink.NuGet/project.json | 19 ++++++++ src/GitLink.sln | 6 +++ tools/Get-NuGetTool.ps1 | 15 ++++++ tools/Install-NuGetPackage.ps1 | 41 ++++++++++++++++ tools/Restore-NuGetPackages.ps1 | 21 +++++++++ 18 files changed, 180 insertions(+), 187 deletions(-) delete mode 100644 deployment/Chocolatey/template/GitHubLink/GitHubLink.nuspec delete mode 100644 deployment/Chocolatey/template/GitHubLink/tools/LICENSE.txt delete mode 100644 deployment/Chocolatey/template/GitLink/GitLink.nuspec delete mode 100644 deployment/Chocolatey/template/GitLink/tools/LICENSE.txt delete mode 100644 deployment/FinalBuilder/GitLink.CreatePackages.fbp7 delete mode 100644 deployment/NuGet/template/GitHubLink/GitHubLink.nuspec delete mode 100644 deployment/NuGet/template/GitLink/GitLink.nuspec delete mode 100644 deployment/NuGet/template/GitLinkTask/Build/dotnet/GitLink.targets delete mode 100644 deployment/NuGet/template/GitLinkTask/GitLinkTask.nuspec create mode 100644 init.cmd create mode 100644 init.ps1 delete mode 100644 lib/repositories.config create mode 100644 src/GitLink.NuGet/GitLink.NuGet.nuproj create mode 100644 src/GitLink.NuGet/project.json create mode 100644 tools/Get-NuGetTool.ps1 create mode 100644 tools/Install-NuGetPackage.ps1 create mode 100644 tools/Restore-NuGetPackages.ps1 diff --git a/deployment/Chocolatey/template/GitHubLink/GitHubLink.nuspec b/deployment/Chocolatey/template/GitHubLink/GitHubLink.nuspec deleted file mode 100644 index 07d91ae..0000000 --- a/deployment/Chocolatey/template/GitHubLink/GitHubLink.nuspec +++ /dev/null @@ -1,27 +0,0 @@ - - - - githublink - [VERSION] - GitHubLink - GeertvanHorrik - - - ** Note: this is a maintenance package, GitHubLink is replaced by GitLink ** - - -

- - - source symbol symbols server sourcelink github bitbucket git stepping debugging - - en-US - https://github.com/GitTools/GitLink/ - https://github.com/GitTools/GitLink/blob/develop/LICENSE - https://raw.githubusercontent.com/GitTools/GitLink/develop/design/logo/logo_64.png - - - - - - \ No newline at end of file diff --git a/deployment/Chocolatey/template/GitHubLink/tools/LICENSE.txt b/deployment/Chocolatey/template/GitHubLink/tools/LICENSE.txt deleted file mode 100644 index a46c961..0000000 --- a/deployment/Chocolatey/template/GitHubLink/tools/LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 - 2016 CatenaLogic - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/deployment/Chocolatey/template/GitLink/GitLink.nuspec b/deployment/Chocolatey/template/GitLink/GitLink.nuspec deleted file mode 100644 index 2b6ec9f..0000000 --- a/deployment/Chocolatey/template/GitLink/GitLink.nuspec +++ /dev/null @@ -1,24 +0,0 @@ - - - - gitlink - [VERSION] - GitLink - GeertvanHorrik - - - GitLink let's users step through your code hosted on any Git hosting service! 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). - - - - - - gitlink githublinksource symbol symbols server sourcelink github bitbucket git stepping debugging - - en-US - https://github.com/GitTools/GitLink/ - https://github.com/GitTools/GitLink/blob/develop/LICENSE - https://raw.githubusercontent.com/GitTools/GitLink/develop/design/logo/logo_128.png - - \ No newline at end of file diff --git a/deployment/Chocolatey/template/GitLink/tools/LICENSE.txt b/deployment/Chocolatey/template/GitLink/tools/LICENSE.txt deleted file mode 100644 index a46c961..0000000 --- a/deployment/Chocolatey/template/GitLink/tools/LICENSE.txt +++ /dev/null @@ -1,21 +0,0 @@ -The MIT License (MIT) - -Copyright (c) 2014 - 2016 CatenaLogic - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/deployment/FinalBuilder/GitLink.CreatePackages.fbp7 b/deployment/FinalBuilder/GitLink.CreatePackages.fbp7 deleted file mode 100644 index 2fe26a3aa0f343b99426321151fae08fab1d80f4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 198570 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 - - - githublink - [VERSION] - GitHubLink - GeertvanHorrik - True - - - ** Note: this is a maintenance package, GitHubLink is replaced by GitLink ** - - - - - - source symbol symbols server sourcelink github bitbucket git stepping debugging - - en-US - https://github.com/GitTools/GitLink/ - https://github.com/GitTools/GitLink/blob/develop/LICENSE - https://raw.githubusercontent.com/GitTools/GitLink/develop/design/logo/logo_64.png - - - - - - \ No newline at end of file diff --git a/deployment/NuGet/template/GitLink/GitLink.nuspec b/deployment/NuGet/template/GitLink/GitLink.nuspec deleted file mode 100644 index 84d8638..0000000 --- a/deployment/NuGet/template/GitLink/GitLink.nuspec +++ /dev/null @@ -1,25 +0,0 @@ - - - - gitlink - [VERSION] - GitLink - GeertvanHorrik - True - - - GitLink let's users step through your code hosted on any Git hosting service! 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). - - - - - - gitlink githublink source symbol symbols server sourcelink github bitbucket git stepping debugging - - en-US - https://github.com/GitTools/GitLink/ - https://github.com/GitTools/GitLink/blob/develop/LICENSE - https://raw.githubusercontent.com/GitTools/GitLink/develop/design/logo/logo_64.png - - \ No newline at end of file diff --git a/deployment/NuGet/template/GitLinkTask/Build/dotnet/GitLink.targets b/deployment/NuGet/template/GitLinkTask/Build/dotnet/GitLink.targets deleted file mode 100644 index c4e1f49..0000000 --- a/deployment/NuGet/template/GitLinkTask/Build/dotnet/GitLink.targets +++ /dev/null @@ -1,11 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/deployment/NuGet/template/GitLinkTask/GitLinkTask.nuspec b/deployment/NuGet/template/GitLinkTask/GitLinkTask.nuspec deleted file mode 100644 index 0ea4420..0000000 --- a/deployment/NuGet/template/GitLinkTask/GitLinkTask.nuspec +++ /dev/null @@ -1,25 +0,0 @@ - - - - gitlinktask - [VERSION] - GitLinkTask - GeertvanHorrik - True - - - GitLink let's users step through your code hosted on any Git hosting service! 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). - - - - - - gitlink githublink source symbol symbols server sourcelink github bitbucket git stepping debugging - - en-US - https://github.com/GitTools/GitLink/ - https://github.com/GitTools/GitLink/blob/develop/LICENSE - https://raw.githubusercontent.com/GitTools/GitLink/develop/design/logo/logo_64.png - - \ No newline at end of file diff --git a/init.cmd b/init.cmd new file mode 100644 index 0000000..7cb727e --- /dev/null +++ b/init.cmd @@ -0,0 +1 @@ +powershell.exe -ExecutionPolicy bypass -Command "& '%~dpn0.ps1'" %* diff --git a/init.ps1 b/init.ps1 new file mode 100644 index 0000000..c1398c5 --- /dev/null +++ b/init.ps1 @@ -0,0 +1,33 @@ +<# +.SYNOPSIS + Restores NuGet packages. +#> +Param( +) + +Push-Location $PSScriptRoot +try { + $HeaderColor = 'Green' + $toolsPath = "$PSScriptRoot\tools" + $nugetVerbosity = 'quiet' + if ($Verbose) { $nugetVerbosity = 'normal' } + + # First restore NuProj packages since the solution restore depends on NuProj evaluation succeeding. + gci "$PSScriptRoot\src\project.json" -rec |? { $_.FullName -imatch 'nuget' } |% { + & "$toolsPath\Restore-NuGetPackages.ps1" -Path $_ -Verbosity $nugetVerbosity + } + + # Restore VS solution dependencies + gci "$PSScriptRoot\src" -rec |? { $_.FullName.EndsWith('.sln') } |% { + & "$toolsPath\Restore-NuGetPackages.ps1" -Path $_.FullName -Verbosity $nugetVerbosity + } + + Write-Host "Successfully restored all dependencies" -ForegroundColor Yellow +} +catch { + Write-Error "Aborting script due to error" + exit $lastexitcode +} +finally { + Pop-Location +} diff --git a/lib/repositories.config b/lib/repositories.config deleted file mode 100644 index 8114fe5..0000000 --- a/lib/repositories.config +++ /dev/null @@ -1,5 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/GitLink.NuGet/GitLink.NuGet.nuproj b/src/GitLink.NuGet/GitLink.NuGet.nuproj new file mode 100644 index 0000000..5072b07 --- /dev/null +++ b/src/GitLink.NuGet/GitLink.NuGet.nuproj @@ -0,0 +1,44 @@ + + + + + Debug + AnyCPU + + + Release + AnyCPU + + + + 29e78909-b7fb-472c-a9a0-1749a8be9a9a + + + $(UserProfile)\.nuget\packages\NuProj\0.11.14-beta\tools + + + + GitLink + GitLink + GeertvanHorrik + GeertvanHorrik + GitLink.NuGet + GitLink let's users step through your code hosted on any Git hosting service! This makes symbol servers obsolete which saves you both time + + + https://github.com/GitTools/GitLink + https://github.com/GitTools/GitLink/blob/develop/LICENSE + GitLink.NuGet + true + https://raw.githubusercontent.com/GitTools/GitLink/develop/design/logo/logo_64.png + + + + + + + Tools + + + + \ No newline at end of file diff --git a/src/GitLink.NuGet/project.json b/src/GitLink.NuGet/project.json new file mode 100644 index 0000000..7826b01 --- /dev/null +++ b/src/GitLink.NuGet/project.json @@ -0,0 +1,19 @@ +{ + "dependencies": { + "ReadOnlySourceTree": { + "version": "0.1.37-beta", + "suppressParent": "none" + }, + "Nerdbank.GitVersioning": { + "version": "1.5.46", + "suppressParent": "none" + }, + "NuProj": "0.11.14-beta" + }, + "frameworks": { + "net451": {} + }, + "runtimes": { + "win": {} + } +} \ No newline at end of file diff --git a/src/GitLink.sln b/src/GitLink.sln index 8723830..6276f64 100644 --- a/src/GitLink.sln +++ b/src/GitLink.sln @@ -16,6 +16,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution version.json = version.json EndProjectSection EndProject +Project("{FF286327-C783-4F7A-AB73-9BCBAD0D4460}") = "GitLink.NuGet", "GitLink.NuGet\GitLink.NuGet.nuproj", "{29E78909-B7FB-472C-A9A0-1749A8BE9A9A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -34,6 +36,10 @@ Global {38BFDD5A-8CC2-43B8-96CE-BC572E3DFEF5}.Debug|Any CPU.Build.0 = Debug|Any CPU {38BFDD5A-8CC2-43B8-96CE-BC572E3DFEF5}.Release|Any CPU.ActiveCfg = Release|Any CPU {38BFDD5A-8CC2-43B8-96CE-BC572E3DFEF5}.Release|Any CPU.Build.0 = Release|Any CPU + {29E78909-B7FB-472C-A9A0-1749A8BE9A9A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {29E78909-B7FB-472C-A9A0-1749A8BE9A9A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {29E78909-B7FB-472C-A9A0-1749A8BE9A9A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {29E78909-B7FB-472C-A9A0-1749A8BE9A9A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/tools/Get-NuGetTool.ps1 b/tools/Get-NuGetTool.ps1 new file mode 100644 index 0000000..5dae5aa --- /dev/null +++ b/tools/Get-NuGetTool.ps1 @@ -0,0 +1,15 @@ +<# +.SYNOPSIS + Downloads the NuGet.exe tool and returns the path to it. +#> + +$binaryToolsPath = "$PSScriptRoot\..\obj\tools" +if (!(Test-Path $binaryToolsPath)) { $null = mkdir $binaryToolsPath } +$nugetPath = "$binaryToolsPath\nuget.exe" +if (!(Test-Path $nugetPath)) { + $NuGetVersion = "3.4.4" + Write-Host "Downloading nuget.exe $NuGetVersion..." -ForegroundColor Yellow + Invoke-WebRequest -Uri "https://dist.nuget.org/win-x86-commandline/v$NuGetVersion/NuGet.exe" -OutFile $nugetPath +} + +$nugetPath diff --git a/tools/Install-NuGetPackage.ps1 b/tools/Install-NuGetPackage.ps1 new file mode 100644 index 0000000..4bca51c --- /dev/null +++ b/tools/Install-NuGetPackage.ps1 @@ -0,0 +1,41 @@ +<# +.SYNOPSIS + Installs a NuGet package. +.PARAMETER PackageID + The Package ID to install. +.PARAMETER Version + The version of the package to install. If unspecified, the latest stable release is installed. +.PARAMETER Source + The package source feed to find the package to install from. +.PARAMETER PackagesDir + The directory to install the package to. By default, it uses the Packages folder at the root of the repo. +#> +Param( + [Parameter(Position=1,Mandatory=$true)] + [string]$PackageId, + [Parameter()] + [string]$Version, + [Parameter()] + [string]$Source, + [Parameter()] + [switch]$Prerelease, + [Parameter()] + [ValidateSet('Quiet','Normal','Detailed')] + [string]$Verbosity='normal' +) + +$nugetPath = & "$PSScriptRoot\Get-NuGetTool.ps1" + +try { + Write-Verbose "Installing $PackageId..." + $args = "Install",$PackageId + if ($Version) { $args += "-Version",$Version } + if ($Source) { $args += "-Source",$Source } + if ($Prerelease) { $args += "-Prerelease" } + $args += '-Verbosity',$Verbosity + + $p = Start-Process $nugetPath $args -NoNewWindow -Wait -PassThru + if ($p.ExitCode -ne 0) { throw } +} finally { + Pop-Location +} diff --git a/tools/Restore-NuGetPackages.ps1 b/tools/Restore-NuGetPackages.ps1 new file mode 100644 index 0000000..3e345e1 --- /dev/null +++ b/tools/Restore-NuGetPackages.ps1 @@ -0,0 +1,21 @@ +<# +.SYNOPSIS + Restores NuGet packages. +.PARAMETER Path + The path of the solution, directory, packages.config or project.json file to restore packages from. + If not specified, the current directory is used. +.PARAMETER Verbosity +#> +Param( + [Parameter(Position=1)] + [string]$Path=(Get-Location), + [Parameter()] + [ValidateSet('Quiet','Normal','Detailed')] + [string]$Verbosity='normal' +) + +$nugetPath = & "$PSScriptRoot\Get-NuGetTool.ps1" + +Write-Verbose "Restoring NuGet packages for $Path" +& $nugetPath restore $Path -Verbosity $Verbosity +if ($lastexitcode -ne 0) { throw } From 01eb8c17e120aa31fad6838500491494c7780523 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 07:29:57 -0700 Subject: [PATCH 04/85] Add prereq docs --- CONTRIBUTING.md | 16 ++++++++++++++++ src/GitLink.sln | 1 + 2 files changed, 17 insertions(+) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f8c392a..889389a 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,6 +1,22 @@ Contributors ============ +## Prerequisites + +### Required + +* [Microsoft Build Tools 2015](https://www.microsoft.com/en-us/download/details.aspx?id=48159) (automatically installed with Visual Studio 2015) + +### Better with + +* [Visual Studio 2015](https://www.visualstudio.com/en-us) +* [NuProj](http://nuproj.net) extension for Visual Studio + +### How to Build + +To build this projeect, first run the init script in the root of the repo, +then simply run "msbuild" from the src directory. + ## Contributing Process ### Get Buyoff Or Find Open Community Issues/Features diff --git a/src/GitLink.sln b/src/GitLink.sln index 6276f64..5616db6 100644 --- a/src/GitLink.sln +++ b/src/GitLink.sln @@ -12,6 +12,7 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{DADE411A-78DD-4586-B06C-E3991077DF76}" ProjectSection(SolutionItems) = preProject ..\.gitignore = ..\.gitignore + ..\CONTRIBUTING.md = ..\CONTRIBUTING.md nuget.config = nuget.config version.json = version.json EndProjectSection From 19a06ad75b1fc0ed9e90ae9f0fca6f13763c0486 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 07:31:04 -0700 Subject: [PATCH 05/85] Add appveyor.yml --- appveyor.yml | 18 ++++++++++++++++++ src/GitLink.sln | 1 + 2 files changed, 19 insertions(+) create mode 100644 appveyor.yml diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..130321d --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,18 @@ +version: 1.0.{build} +skip_tags: true +image: Visual Studio 2015 +configuration: Release +environment: + VisualStudioVersion: 14.0 +cache: +- '%USERPROFILE%\.nuget\packages -> **\project.json' +- 'obj\tools -> tools\**' +before_build: +- ps: .\init.ps1 +build: + project: src\GitLink.sln + parallel: true + verbosity: minimal +artifacts: +- path: bin\**\*.nupkg + name: NuGet Package diff --git a/src/GitLink.sln b/src/GitLink.sln index 5616db6..2db8ed8 100644 --- a/src/GitLink.sln +++ b/src/GitLink.sln @@ -12,6 +12,7 @@ EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{DADE411A-78DD-4586-B06C-E3991077DF76}" ProjectSection(SolutionItems) = preProject ..\.gitignore = ..\.gitignore + ..\appveyor.yml = ..\appveyor.yml ..\CONTRIBUTING.md = ..\CONTRIBUTING.md nuget.config = nuget.config version.json = version.json From da71df5d117b25f12cc603782357ecc190bf5eb2 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 07:36:42 -0700 Subject: [PATCH 06/85] Fix appveyor build failure --- src/GitLink/project.json | 1 + src/GitLinkTask/project.json | 1 + 2 files changed, 2 insertions(+) diff --git a/src/GitLink/project.json b/src/GitLink/project.json index 8644bb0..65fda3d 100644 --- a/src/GitLink/project.json +++ b/src/GitLink/project.json @@ -4,6 +4,7 @@ "GitTools.Core": "1.0.0-unstable0021", "ImpromptuInterface": "6.2.2", "LibGit2Sharp": "0.21.0.176", + "NuProj.Common": "0.11.14-beta", "ReadOnlySourceTree": { "version": "0.1.37-beta", "suppressParent": "none" diff --git a/src/GitLinkTask/project.json b/src/GitLinkTask/project.json index 162da9e..25ed17a 100644 --- a/src/GitLinkTask/project.json +++ b/src/GitLinkTask/project.json @@ -1,5 +1,6 @@ { "dependencies": { + "NuProj.Common": "0.11.14-beta" }, "frameworks": { "net45": {} From 31880c16be2cc1f2af1477b7ebd81ffbfe7a46dc Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 07:39:16 -0700 Subject: [PATCH 07/85] Add appveyor badge to README --- README.md | 2 ++ src/GitLink.sln | 1 + 2 files changed, 3 insertions(+) diff --git a/README.md b/README.md index c3bd2e7..d7283c6 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ GitLink ========== +[![Build status](https://ci.appveyor.com/api/projects/status/g56b4xyl4q0ean8i/branch/develop?svg=true)](https://ci.appveyor.com/project/AArnott/gitlink/branch/develop) + [![Join the chat at https://gitter.im/GitTools/GitLink](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/GitTools/GitLink?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) ![License](https://img.shields.io/github/license/gittools/gitlink.svg) diff --git a/src/GitLink.sln b/src/GitLink.sln index 2db8ed8..6ae28e4 100644 --- a/src/GitLink.sln +++ b/src/GitLink.sln @@ -15,6 +15,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ..\appveyor.yml = ..\appveyor.yml ..\CONTRIBUTING.md = ..\CONTRIBUTING.md nuget.config = nuget.config + ..\README.md = ..\README.md version.json = version.json EndProjectSection EndProject From fddbf35174f265c34bff15c7e3052d4c9095125d Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 08:13:26 -0700 Subject: [PATCH 08/85] Built nupkg includes all dependencies --- src/EnlistmentInfo.targets | 30 ++++++++++++++++++++++++++ src/GitLink.NuGet/GitLink.NuGet.nuproj | 10 +++++---- src/GitLink.sln | 1 + src/GitLink/project.json | 4 ++++ src/GitLinkTask/GitLinkTask.csproj | 17 ++++++++------- 5 files changed, 50 insertions(+), 12 deletions(-) create mode 100644 src/EnlistmentInfo.targets diff --git a/src/EnlistmentInfo.targets b/src/EnlistmentInfo.targets new file mode 100644 index 0000000..731d7a7 --- /dev/null +++ b/src/EnlistmentInfo.targets @@ -0,0 +1,30 @@ + + + + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + + + + + $(MSBuildProjectFullPath) + GetDeployableOutputs + + + $(MSBuildProjectFullPath) + GetDeployableOutputs + + + + diff --git a/src/GitLink.NuGet/GitLink.NuGet.nuproj b/src/GitLink.NuGet/GitLink.NuGet.nuproj index 5072b07..1a411d4 100644 --- a/src/GitLink.NuGet/GitLink.NuGet.nuproj +++ b/src/GitLink.NuGet/GitLink.NuGet.nuproj @@ -22,22 +22,24 @@ GitLink GeertvanHorrik GeertvanHorrik - GitLink.NuGet GitLink let's users step through your code hosted on any Git hosting service! This makes symbol servers obsolete which saves you both time https://github.com/GitTools/GitLink https://github.com/GitTools/GitLink/blob/develop/LICENSE - GitLink.NuGet + git pdb true https://raw.githubusercontent.com/GitTools/GitLink/develop/design/logo/logo_64.png + GetDeployableOutputs + + true - - Tools + + Build diff --git a/src/GitLink.sln b/src/GitLink.sln index 6ae28e4..3e213c6 100644 --- a/src/GitLink.sln +++ b/src/GitLink.sln @@ -14,6 +14,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ..\.gitignore = ..\.gitignore ..\appveyor.yml = ..\appveyor.yml ..\CONTRIBUTING.md = ..\CONTRIBUTING.md + EnlistmentInfo.targets = EnlistmentInfo.targets nuget.config = nuget.config ..\README.md = ..\README.md version.json = version.json diff --git a/src/GitLink/project.json b/src/GitLink/project.json index 65fda3d..4f152e5 100644 --- a/src/GitLink/project.json +++ b/src/GitLink/project.json @@ -5,6 +5,10 @@ "ImpromptuInterface": "6.2.2", "LibGit2Sharp": "0.21.0.176", "NuProj.Common": "0.11.14-beta", + "EnlistmentInfo": { + "version": "1.0.4", + "suppressParent": "none" + }, "ReadOnlySourceTree": { "version": "0.1.37-beta", "suppressParent": "none" diff --git a/src/GitLinkTask/GitLinkTask.csproj b/src/GitLinkTask/GitLinkTask.csproj index 871be58..6c75ee6 100644 --- a/src/GitLinkTask/GitLinkTask.csproj +++ b/src/GitLinkTask/GitLinkTask.csproj @@ -50,15 +50,16 @@ - + + PreserveNewest + + + + {d68add77-913f-46d2-9a4f-5cc71c4718d8} + GitLink + + - \ No newline at end of file From 8899ccd1325b88b464afc1bc7cc47c2ebb9f0260 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 09:02:47 -0700 Subject: [PATCH 09/85] Fix cmdline after using pdbstr.exe as a separate file --- src/GitLink/GitLink.csproj | 1 - src/GitLink/Helpers/ResourceHelper.cs | 28 --------------------------- src/GitLink/Linker.cs | 6 ++---- 3 files changed, 2 insertions(+), 33 deletions(-) delete mode 100644 src/GitLink/Helpers/ResourceHelper.cs diff --git a/src/GitLink/GitLink.csproj b/src/GitLink/GitLink.csproj index 7c8fef6..8d481ef 100644 --- a/src/GitLink/GitLink.csproj +++ b/src/GitLink/GitLink.csproj @@ -72,7 +72,6 @@ - diff --git a/src/GitLink/Helpers/ResourceHelper.cs b/src/GitLink/Helpers/ResourceHelper.cs deleted file mode 100644 index e19cbe7..0000000 --- a/src/GitLink/Helpers/ResourceHelper.cs +++ /dev/null @@ -1,28 +0,0 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) 2014 - 2015 CatenaLogic. All rights reserved. -// -// -------------------------------------------------------------------------------------------------------------------- - - -namespace GitLink -{ - using System.IO; - using Catel.Reflection; - - public static class ResourceHelper - { - public static void ExtractEmbeddedResource(string resourceName, string destinationFileName) - { - var assembly = typeof(ResourceHelper).Assembly; - - using (var resource = assembly.GetManifestResourceStream(resourceName)) - { - using (var file = new FileStream(destinationFileName, FileMode.Create, FileAccess.Write)) - { - resource.CopyTo(file); - } - } - } - } -} \ No newline at end of file diff --git a/src/GitLink/Linker.cs b/src/GitLink/Linker.cs index 28e4ce0..3111b04 100644 --- a/src/GitLink/Linker.cs +++ b/src/GitLink/Linker.cs @@ -12,6 +12,7 @@ namespace GitLink using System.Diagnostics; using System.IO; using System.Linq; + using System.Reflection; using Catel; using Catel.Logging; using GitTools; @@ -45,10 +46,7 @@ public static int Link(Context context) using (var temporaryFilesContext = new TemporaryFilesContext()) { - Log.Info("Extracting embedded pdbstr.exe"); - - var pdbStrFile = temporaryFilesContext.GetFile("pdbstr.exe"); - ResourceHelper.ExtractEmbeddedResource("GitLink.Resources.Files.pdbstr.exe", pdbStrFile); + var pdbStrFile = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "pdbstr.exe"); try { From a32ec859093d2a1aeac339d9450d1ac1a3f60ad3 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Fri, 7 Oct 2016 20:03:03 -0700 Subject: [PATCH 10/85] Remove unnecessary coupling to msbuild --- src/GitLink/Extensions/ProjectExtensions.cs | 9 ++++----- src/GitLink/Linker.cs | 4 ++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/GitLink/Extensions/ProjectExtensions.cs b/src/GitLink/Extensions/ProjectExtensions.cs index 0da2c55..d4f0785 100644 --- a/src/GitLink/Extensions/ProjectExtensions.cs +++ b/src/GitLink/Extensions/ProjectExtensions.cs @@ -37,12 +37,11 @@ public static void CreateSrcSrv(this Project project, SrcSrvContext srcSrvContex var srcsrvFile = GetOutputSrcSrvFile(project); - CreateSrcSrv(project, srcsrvFile, srcSrvContext); + CreateSrcSrv(srcsrvFile, srcSrvContext); } - public static void CreateSrcSrv(this Project project, string srcsrvFile, SrcSrvContext srcSrvContext) + public static void CreateSrcSrv(string srcsrvFile, SrcSrvContext srcSrvContext) { - Argument.IsNotNull(() => project); Argument.IsNotNull(() => srcSrvContext); Argument.IsNotNullOrWhitespace(() => srcSrvContext.RawUrl); Argument.IsNotNullOrWhitespace(() => srcSrvContext.Revision); @@ -71,10 +70,10 @@ public static Dictionary VerifyPdbFiles(this Project project, IE var pdbFile = GetOutputPdbFile(project); - return VerifyPdbFiles(project, files, pdbFile); + return VerifyPdbFiles(files, pdbFile); } - public static Dictionary VerifyPdbFiles(this Project project, IEnumerable files, string pdbFileFullPath) + public static Dictionary VerifyPdbFiles(IEnumerable files, string pdbFileFullPath) { using(var pdb = new PdbFile(pdbFileFullPath)) { diff --git a/src/GitLink/Linker.cs b/src/GitLink/Linker.cs index 3111b04..d26a3ae 100644 --- a/src/GitLink/Linker.cs +++ b/src/GitLink/Linker.cs @@ -200,7 +200,7 @@ private static bool LinkProject(Context context, Project project, string pdbStrF { Log.Info("Verifying pdb file"); - var missingFiles = project.VerifyPdbFiles(compilables, projectPdbFile); + var missingFiles = ProjectExtensions.VerifyPdbFiles(compilables, projectPdbFile); foreach (var missingFile in missingFiles) { Log.Warning("Missing file '{0}' or checksum '{1}' did not match", missingFile.Key, missingFile.Value); @@ -231,7 +231,7 @@ private static bool LinkProject(Context context, Project project, string pdbStrF srcSrvContext.VstsData["TFS_REPO"] = context.Provider.ProjectName; } - project.CreateSrcSrv(projectSrcSrvFile, srcSrvContext); + ProjectExtensions.CreateSrcSrv(projectSrcSrvFile, srcSrvContext); Log.Debug("Created source server link file, updating pdb file '{0}'", context.GetRelativePath(projectPdbFile)); From 281acca07676a7a569b7026b4613066709980dc8 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 10:51:07 -0700 Subject: [PATCH 11/85] Make "gitlink.exe some.pdb" work Also replace cmdline parsing Rip out MSBuild-oriented Linker method --- src/GitLink/ArgumentParser.cs | 248 -------------------- src/GitLink/Extensions/PdbExtensions.cs | 42 +--- src/GitLink/Extensions/ProjectExtensions.cs | 22 +- src/GitLink/GitLink.csproj | 3 +- src/GitLink/HelpWriter.cs | 51 ---- src/GitLink/LinkOptions.cs | 21 ++ src/GitLink/Linker.cs | 242 ++++--------------- src/GitLink/Program.cs | 71 +++--- src/GitLink/project.json | 1 + src/GitLinkTask/GitLink.targets | 15 +- src/GitLinkTask/GitLinkTask.csproj | 10 +- src/GitLinkTask/LinkProject.cs | 53 ++++- src/GitLinkTask/TaskItemExtensions.cs | 30 --- src/nuget.config | 6 +- 14 files changed, 190 insertions(+), 625 deletions(-) delete mode 100644 src/GitLink/ArgumentParser.cs delete mode 100644 src/GitLink/HelpWriter.cs create mode 100644 src/GitLink/LinkOptions.cs delete mode 100644 src/GitLinkTask/TaskItemExtensions.cs diff --git a/src/GitLink/ArgumentParser.cs b/src/GitLink/ArgumentParser.cs deleted file mode 100644 index 492856b..0000000 --- a/src/GitLink/ArgumentParser.cs +++ /dev/null @@ -1,248 +0,0 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. -// -// -------------------------------------------------------------------------------------------------------------------- - - -namespace GitLink -{ - using System; - using System.Collections.Generic; - using System.Linq; - using Catel.Collections; - using Catel.Logging; - using GitLink.Providers; - using GitTools; - using GitTools.Git; - using LibGit2Sharp; - - public static class ArgumentParser - { - private static readonly ILog Log = LogManager.GetCurrentClassLogger(); - - public static Context ParseArguments(string commandLineArguments) - { - return ParseArguments(commandLineArguments.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).ToList(), - new ProviderManager()); - } - - public static Context ParseArguments(params string[] commandLineArguments) - { - return ParseArguments(commandLineArguments.ToList(), new ProviderManager()); - } - - public static Context ParseArguments(List commandLineArguments, IProviderManager providerManager) - { - var context = new Context(providerManager); - - if (commandLineArguments.Count == 0) - { - context.IsHelp = true; - return context; - } - - var firstArgument = commandLineArguments.First(); - if (IsHelp(firstArgument)) - { - context.IsHelp = true; - return context; - } - - if (commandLineArguments.Count < 3 && commandLineArguments.Count != 1) - { - throw Log.ErrorAndCreateException("Invalid number of arguments"); - } - - context.SolutionDirectory = firstArgument; - - var namedArguments = commandLineArguments.Skip(1).ToList(); - for (var index = 0; index < namedArguments.Count; index++) - { - var name = namedArguments[index]; - - // First check everything without values - if (IsSwitch("debug", name)) - { - context.IsDebug = true; - continue; - } - - if (IsSwitch("errorsaswarnings", name)) - { - context.ErrorsAsWarnings = true; - continue; - } - - if (IsSwitch("skipverify", name)) - { - context.SkipVerify = true; - continue; - } - - if (IsSwitch("powershell", name)) - { - context.DownloadWithPowershell = true; - continue; - } - - // After this point, all arguments should have a value - index++; - var valueInfo = GetValue(namedArguments, index); - var value = valueInfo.Key; - index = index + (valueInfo.Value - 1); - - if (IsSwitch("l", name)) - { - context.LogFile = value; - continue; - } - - if (IsSwitch("c", name)) - { - context.ConfigurationName = value; - continue; - } - - if (IsSwitch("p", name)) - { - context.PlatformName = value; - continue; - } - - if (IsSwitch("u", name)) - { - context.TargetUrl = value; - continue; - } - - if (IsSwitch("b", name)) - { - context.TargetBranch = value; - continue; - } - - if (IsSwitch("s", name)) - { - context.ShaHash = value; - continue; - } - - if (IsSwitch("f", name)) - { - context.SolutionFile = value; - continue; - } - - if (IsSwitch("d", name)) - { - context.PdbFilesDirectory = value; - continue; - } - - if (IsSwitch("include", name)) - { - context.IncludedProjects.AddRange(value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim())); - continue; - } - - if (IsSwitch("ignore", name)) - { - context.IgnoredProjects.AddRange(value.Split(new []{ ',' }, StringSplitOptions.RemoveEmptyEntries).Select(x => x.Trim())); - continue; - } - - throw Log.ErrorAndCreateException("Could not parse command line parameter '{0}'.", name); - } - - if (string.IsNullOrEmpty(context.TargetUrl)) - { - Log.Info("No target url was specified, trying to determine the target url automatically"); - - var gitDir = GitDirFinder.TreeWalkForGitDir(context.SolutionDirectory); - if (gitDir != null) - { - using (var repo = RepositoryLoader.GetRepo(gitDir)) - { - var currentBranch = repo.Head; - - if (string.IsNullOrEmpty(context.ShaHash)) - { - context.ShaHash = currentBranch.Tip.Sha; - } - - if (currentBranch.Remote == null || currentBranch.IsDetachedHead()) - { - currentBranch = repo.GetBranchesContainingCommit(context.ShaHash).FirstOrDefault(b => b.Remote != null); - } - - if (currentBranch != null && currentBranch.Remote != null) - { - var url = currentBranch.Remote.Url; - if (url.StartsWith("https://")) - { - context.TargetUrl = url.OptimizeUrl(); - - Log.Info("Automatically determine target url '{0}'", context.TargetUrl); - } - } - } - } - } - - if (!string.IsNullOrEmpty(context.TargetUrl)) - { - context.Provider = providerManager.GetProvider(context.TargetUrl); - } - - return context; - } - - private static KeyValuePair GetValue(List arguments, int index) - { - var totalCounter = 1; - - var value = arguments[index]; - - while (value.StartsWith("\"")) - { - if (value.EndsWith("\"")) - { - break; - } - - index++; - value += " " + arguments[index]; - - totalCounter++; - } - - value = value.Trim('\"'); - - return new KeyValuePair(value, totalCounter); - } - - 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 bool IsHelp(string singleArgument) - { - return (singleArgument == "?") || - IsSwitch("h", singleArgument) || - IsSwitch("help", singleArgument) || - IsSwitch("?", singleArgument); - } - } -} \ No newline at end of file diff --git a/src/GitLink/Extensions/PdbExtensions.cs b/src/GitLink/Extensions/PdbExtensions.cs index ddd5065..ce8e684 100644 --- a/src/GitLink/Extensions/PdbExtensions.cs +++ b/src/GitLink/Extensions/PdbExtensions.cs @@ -15,32 +15,21 @@ namespace GitLink public static class PdbExtensions { - public static Dictionary VerifyPdbFiles(this PdbFile pdbFile, IEnumerable files) + public static IEnumerable FindMissingOrChangedSourceFiles(this PdbFile pdbFile) { Argument.IsNotNull(() => pdbFile); - var missing = new Dictionary(StringComparer.OrdinalIgnoreCase); - var actualFileChecksums = (from x in files - select new KeyValuePair(Hex.Encode(Crypto.GetMd5HashForFiles(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; + string file = checksumInfo.Key; + string expectedChecksum = checksumInfo.Value; + string actualChecksum = Hex.Encode(Crypto.GetMd5HashForFile(file)); - if (!actualFileChecksums.ContainsValue(checksum)) + if (expectedChecksum != actualChecksum) { - if (file.EndsWith(".xaml")) - { - // #64 ignore xaml files, not important - continue; - } - - missing[file] = checksum; + yield return file; } } - - return missing; } public static Dictionary GetChecksums(this PdbFile pdbFile) @@ -69,7 +58,7 @@ public static IEnumerable> GetFiles(this PdbFile pdbFile) var results = new List>(); foreach (var value in values) { - if (!value.Name.Contains(FileIndicator)) + if (!value.Name.StartsWith(FileIndicator)) { continue; } @@ -77,20 +66,11 @@ public static IEnumerable> GetFiles(this PdbFile pdbFile) var num = value.Stream; var name = value.Name.Substring(FileIndicator.Length); - var bytes = pdbFile.ReadStreamBytes(num); - if (bytes.Length != 88) - { - continue; - } - // Get last 16 bytes for checksum - var buffer = new byte[16]; - for (int i = 72; i < 88; i++) - { - buffer[i-72] = bytes[i]; - } - - results.Add(new Tuple(name, buffer)); + var bytes = pdbFile.ReadStreamBytes(num); + var checksum = new byte[16]; + Array.Copy(bytes, bytes.Length - 16, checksum, 0, 16); + results.Add(Tuple.Create(name, checksum)); } return results; diff --git a/src/GitLink/Extensions/ProjectExtensions.cs b/src/GitLink/Extensions/ProjectExtensions.cs index d4f0785..de28997 100644 --- a/src/GitLink/Extensions/ProjectExtensions.cs +++ b/src/GitLink/Extensions/ProjectExtensions.cs @@ -64,23 +64,6 @@ public static IEnumerable GetCompilableItems(this Project project) return project.Items.Where(x => string.Equals(x.ItemType, "Compile") || string.Equals(x.ItemType, "ClCompile") || string.Equals(x.ItemType, "ClInclude")); } - public static Dictionary VerifyPdbFiles(this Project project, IEnumerable files) - { - Argument.IsNotNull(() => project); - - var pdbFile = GetOutputPdbFile(project); - - return VerifyPdbFiles(files, pdbFile); - } - - public static Dictionary VerifyPdbFiles(IEnumerable files, string pdbFileFullPath) - { - using(var pdb = new PdbFile(pdbFileFullPath)) - { - return pdb.VerifyPdbFiles(files); - } - } - public static string GetOutputSrcSrvFile(this Project project) { Argument.IsNotNull(() => project); @@ -93,10 +76,9 @@ public static string GetOutputPdbFile(this Project project) { Argument.IsNotNull(() => project); - var outputFile = project.GetOutputFile(); - var pdbFile = Path.ChangeExtension(outputFile, ".pdb"); + var outputFile = project.GetPropertyValue("PdbFile"); - return pdbFile; + return outputFile; } public static string GetOutputFile(this Project project) diff --git a/src/GitLink/GitLink.csproj b/src/GitLink/GitLink.csproj index 8d481ef..bba990f 100644 --- a/src/GitLink/GitLink.csproj +++ b/src/GitLink/GitLink.csproj @@ -57,7 +57,6 @@ - @@ -72,8 +71,8 @@ - + diff --git a/src/GitLink/HelpWriter.cs b/src/GitLink/HelpWriter.cs deleted file mode 100644 index f16a2f8..0000000 --- a/src/GitLink/HelpWriter.cs +++ /dev/null @@ -1,51 +0,0 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. -// -// -------------------------------------------------------------------------------------------------------------------- - - -namespace GitLink -{ - using System; - using Catel.Reflection; - - public static class HelpWriter - { - 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) - { - const string 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. - -GitLink [solutionPath] -u [urlToRepository] - - solutionPath The directory containing the solution with the pdb files. - -u [url] Url to remote git repository. - -f [file] Solution file name. - -c [config] Name of the configuration, default value is 'Release'. - -p [platform] Name of the platform, default value is 'AnyCPU'. - -b [branch] Name of the branch to use on the remote repository. - -l [file] The log file to write to. - -s [shaHash] The SHA-1 hash of the commit. - -d [pdbDirectory] The directory where pdb files exists, default value - is the normal project output directory. - -powershell Use an indexing strategy that won't rely on SRCSRV http support, - but use a powershell command for URL download instead. - -errorsaswarnings Don't fail on errors, but treat them as warnings instead. - -skipverify Skip pdb verification in case it causes issues (it's a formality anyway) - -debug Enables debug mode with special dumps of msbuild. -"; - writer(message); - } - } -} \ No newline at end of file diff --git a/src/GitLink/LinkOptions.cs b/src/GitLink/LinkOptions.cs new file mode 100644 index 0000000..0a19ab0 --- /dev/null +++ b/src/GitLink/LinkOptions.cs @@ -0,0 +1,21 @@ +namespace GitLink +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + + public struct LinkOptions + { + /// + /// Gets or sets a value indicating whether the source should be downloaded with + /// powershell instead of simple HTTP(S) URLs. + /// + public bool DownloadWithPowerShell { get; set; } + + public bool SkipVerify { get; set; } + + public Uri GitRemoteUrl { get; set; } + } +} diff --git a/src/GitLink/Linker.cs b/src/GitLink/Linker.cs index d26a3ae..52807a5 100644 --- a/src/GitLink/Linker.cs +++ b/src/GitLink/Linker.cs @@ -16,6 +16,8 @@ namespace GitLink using Catel; using Catel.Logging; using GitTools; + using GitTools.Git; + using LibGit2Sharp; using Microsoft.Build.Evaluation; using Pdb; @@ -24,231 +26,81 @@ namespace GitLink /// public static class Linker { + private static readonly string PdbStrExePath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "pdbstr.exe"); private static readonly ILog Log = LogManager.GetCurrentClassLogger(); - public static int Link(Context context) + public static void Link(string pdbPath, LinkOptions options = default(LinkOptions)) { - int? exitCode = null; + Argument.IsNotNullOrEmpty(() => pdbPath); - var stopWatch = new Stopwatch(); - stopWatch.Start(); + var pdb = new PdbFile(pdbPath); + var filesAndChecksums = pdb.GetFiles(); + var sourceFiles = filesAndChecksums.Select(f => f.Item1); - context.ValidateContext(); - - if (!string.IsNullOrEmpty(context.LogFile)) - { - var fileLogListener = new FileLogListener(context.LogFile, 25 * 1024); - fileLogListener.IsDebugEnabled = context.IsDebug; - - fileLogListener.IgnoreCatelLogging = true; - LogManager.AddListener(fileLogListener); - } - - using (var temporaryFilesContext = new TemporaryFilesContext()) + string repositoryDirectory = GitDirFinder.TreeWalkForGitDir(Path.GetDirectoryName(sourceFiles.First())); + using (var repository = new Repository(repositoryDirectory)) { - var pdbStrFile = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "pdbstr.exe"); - - try + var providerManager = new Providers.ProviderManager(); + Providers.IProvider provider; + if (options.GitRemoteUrl == null) { - var projects = new List(); - string[] solutionFiles; - if (string.IsNullOrEmpty(context.SolutionFile)) - { - solutionFiles = Directory.GetFiles(context.SolutionDirectory, "*.sln", SearchOption.AllDirectories); - } - else - { - var pathToSolutionFile = Path.Combine(context.SolutionDirectory, context.SolutionFile); - if (!File.Exists(pathToSolutionFile)) - { - Log.Error("Could not find solution file: {0}", pathToSolutionFile); - return -1; - } - - solutionFiles = new[] { pathToSolutionFile }; - } - - foreach (var solutionFile in solutionFiles) - { - var solutionProjects = ProjectHelper.GetProjects(solutionFile, context.ConfigurationName, context.PlatformName); - projects.AddRange(solutionProjects); - } - - var provider = context.Provider; - if (provider == null) - { - throw Log.ErrorAndCreateException("Cannot find a matching provider for '{0}'", context.TargetUrl); - } - - Log.Info("Using provider '{0}'", provider.GetType().Name); - - var shaHash = context.Provider.GetShaHashOfCurrentBranch(context, temporaryFilesContext); - - Log.Info("Using commit sha '{0}' as version stamp", shaHash); - - var projectCount = projects.Count(); - var failedProjects = new List(); - Log.Info("Found '{0}' project(s)", projectCount); - Log.Info(string.Empty); - - foreach (var project in projects) - { - try - { - var projectName = project.GetProjectName(); - if (ProjectHelper.ShouldBeIgnored(projectName, context.IncludedProjects, context.IgnoredProjects)) - { - Log.Info("Ignoring '{0}'", project.GetProjectName()); - Log.Info(string.Empty); - continue; - } - - if (context.IsDebug) - { - project.DumpProperties(); - } - - if (!LinkProject(context, project, pdbStrFile, shaHash, context.PdbFilesDirectory)) - { - failedProjects.Add(project); - } - } - catch (Exception) - { - failedProjects.Add(project); - } - } - - 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}", context.GetRelativePath(failedProject.GetProjectName())); - } - - Log.Unindent(); - } - - exitCode = (failedProjects.Count == 0) ? 0 : -1; + var candidateProviders = from remote in repository.Network.Remotes + let p = providerManager.GetProvider(remote.Url) + where p != null + select p; + provider = candidateProviders.First(); } - catch (GitLinkException ex) + else { - Log.Error(ex, "An error occurred"); + provider = providerManager.GetProvider(options.GitRemoteUrl.AbsoluteUri); } - catch (Exception ex) - { - Log.Error(ex, "An unexpected error occurred"); - } - - stopWatch.Stop(); - } - Log.Info(string.Empty); - Log.Info("Completed in '{0}'", stopWatch.Elapsed); + var projectSrcSrvFile = pdbPath + ".srcsrv"; - exitCode = exitCode ?? -1; - - if (context.ErrorsAsWarnings && exitCode != 0) - { - Log.Info("One or more errors occurred, but treating it as warning instead"); - - exitCode = 0; - } - - return exitCode.Value; - } - - private static bool LinkProject(Context context, Project project, string pdbStrFile, string shaHash, string pathPdbDirectory = null) - { - Argument.IsNotNull(() => context); - Argument.IsNotNull(() => project); - - try - { - var projectName = project.GetProjectName(); - - Log.Info("Handling project '{0}'", projectName); - - Log.Indent(); - - var compilables = project.GetCompilableItems().Select(x => x.GetFullFileName()).ToList(); - - var outputPdbFile = project.GetOutputPdbFile(); - var projectPdbFile = pathPdbDirectory != null ? Path.Combine(pathPdbDirectory, Path.GetFileName(outputPdbFile)) : Path.GetFullPath(outputPdbFile); - var projectSrcSrvFile = projectPdbFile + ".srcsrv"; - - var srcSrvContext = new SrcSrvContext - { - Revision = shaHash, - RawUrl = context.Provider.RawGitUrl, - DownloadWithPowershell = context.DownloadWithPowershell - }; - - if (!File.Exists(projectPdbFile)) - { - Log.Warning("No pdb file found for '{0}', is project built in '{1}' mode with pdb files enabled? Expected file is '{2}'", projectName, context.ConfigurationName, projectPdbFile); - return false; - } - - if (!context.SkipVerify) + if (!options.SkipVerify) { Log.Info("Verifying pdb file"); - var missingFiles = ProjectExtensions.VerifyPdbFiles(compilables, projectPdbFile); + var missingFiles = pdb.FindMissingOrChangedSourceFiles(); foreach (var missingFile in missingFiles) { - Log.Warning("Missing file '{0}' or checksum '{1}' did not match", missingFile.Key, missingFile.Value); + Log.Warning($"File \"{missingFile}\" missing or changed since the PDB was compiled."); } } - if(!srcSrvContext.RawUrl.Contains("%var2%") && !srcSrvContext.RawUrl.Contains("{0}")) + string commitId; + commitId = repository.Head.Commits.First().Sha; + + string rawUrl = provider.RawGitUrl; + if (!rawUrl.Contains("%var2%") && !rawUrl.Contains("{0}")) { - srcSrvContext.RawUrl = string.Format("{0}/{{0}}/%var2%", srcSrvContext.RawUrl); + rawUrl = string.Format("{0}/{{0}}/%var2%", rawUrl); } - - foreach (var compilable in compilables) - { - var relativePathForUrl = compilable.Replace(context.SolutionDirectory, string.Empty).Replace("\\", "/"); - while (relativePathForUrl.StartsWith("/")) - { - relativePathForUrl = relativePathForUrl.Substring(1, relativePathForUrl.Length - 1); - } - srcSrvContext.Paths.Add(new Tuple(compilable, relativePathForUrl)); + var srcSrvContext = new SrcSrvContext + { + RawUrl = rawUrl, + DownloadWithPowershell = options.DownloadWithPowerShell, + Revision = commitId, + }; + foreach (string sourceFile in sourceFiles) + { + string repoRelativePath = Catel.IO.Path.GetRelativePath(sourceFile, Path.GetDirectoryName(repositoryDirectory)) + .Replace('\\', '/'); + srcSrvContext.Paths.Add(Tuple.Create(sourceFile, repoRelativePath)); } - // When using the VisualStudioTeamServicesProvider, add extra infomration to dictionary with VSTS-specific data - if (context.Provider.GetType().Name.EqualsIgnoreCase("VisualStudioTeamServicesProvider")) + if (provider is Providers.VisualStudioTeamServicesProvider) { - srcSrvContext.VstsData["TFS_COLLECTION"] = context.Provider.CompanyUrl; - srcSrvContext.VstsData["TFS_TEAM_PROJECT"] = context.Provider.ProjectName; - srcSrvContext.VstsData["TFS_REPO"] = context.Provider.ProjectName; + srcSrvContext.VstsData["TFS_COLLECTION"] = provider.CompanyUrl; + srcSrvContext.VstsData["TFS_TEAM_PROJECT"] = provider.ProjectName; + srcSrvContext.VstsData["TFS_REPO"] = provider.ProjectName; } ProjectExtensions.CreateSrcSrv(projectSrcSrvFile, srcSrvContext); - - Log.Debug("Created source server link file, updating pdb file '{0}'", context.GetRelativePath(projectPdbFile)); - - PdbStrHelper.Execute(pdbStrFile, projectPdbFile, projectSrcSrvFile); + Log.Debug("Created source server link file, updating pdb file '{0}'", Catel.IO.Path.GetRelativePath(pdbPath, repositoryDirectory)); + PdbStrHelper.Execute(PdbStrExePath, pdbPath, projectSrcSrvFile); } - catch (Exception ex) - { - Log.Warning(ex, "An error occurred while processing project '{0}'", project.GetProjectName()); - throw; - } - finally - { - Log.Unindent(); - Log.Info(string.Empty); - } - - return true; } } } \ No newline at end of file diff --git a/src/GitLink/Program.cs b/src/GitLink/Program.cs index 92d81f2..3ddea50 100644 --- a/src/GitLink/Program.cs +++ b/src/GitLink/Program.cs @@ -8,6 +8,9 @@ namespace GitLink { using System; + using System.CommandLine; + using System.Diagnostics; + using System.IO; using Catel.Logging; using Logging; @@ -24,51 +27,51 @@ private static int Main(string[] args) var consoleLogListener = new OutputLogListener(); LogManager.AddListener(consoleLogListener); - try + Uri remoteGitUrl = null; + string pdbPath = null; + bool skipVerify = false; + bool downloadWithPowershell = false; + var arguments = ArgumentSyntax.Parse(args, syntax => { - HelpWriter.WriteAppHeader(s => Log.Write(LogEvent.Info, s)); + syntax.DefineOption("u|url", ref remoteGitUrl, s => new Uri(s, UriKind.Absolute), "Url to remote git repository."); + syntax.DefineOption("s|skipVerify", ref skipVerify, "Verify all source files are available in source control."); + syntax.DefineOption("p|powershell", ref downloadWithPowershell, "Use an indexing strategy that won't rely on SRCSRV http support, but use a powershell command for URL download instead."); + syntax.DefineParameter("pdb", ref pdbPath, "The PDB to add source indexing to."); - Log.Info("Arguments: {0}", string.Join(" ", args)); - Log.Info(string.Empty); - - var context = ArgumentParser.ParseArguments(args); - if (context.IsHelp) + if (!string.IsNullOrEmpty(pdbPath) && !File.Exists(pdbPath)) { - HelpWriter.WriteHelp(s => Log.Write(LogEvent.Info, s)); - - WaitForKeyPress(); - - return 0; + syntax.ReportError($"File not found: \"{pdbPath}\""); } + }); - consoleLogListener.IsDebugEnabled = context.IsDebug; - - var result = Linker.Link(context); - -#if DEBUG - WaitForKeyPress(); -#endif - - return result; - } - catch (Exception ex) + if (string.IsNullOrEmpty(pdbPath)) { - Log.Error(ex, "An unexpected error occurred"); - -#if DEBUG - WaitForKeyPress(); -#endif - - return -1; + Log.Error("pdb parameter is required."); + return 1; } + + var options = new LinkOptions + { + GitRemoteUrl = remoteGitUrl, + SkipVerify = skipVerify, + DownloadWithPowerShell = downloadWithPowershell, + }; + + Linker.Link(pdbPath, options); + WaitForKeyPressWhenDebugging(); + return 0; } - private static void WaitForKeyPress() + [Conditional("DEBUG")] + private static void WaitForKeyPressWhenDebugging() { - Log.Info(string.Empty); - Log.Info("Press any key to continue"); + if (Debugger.IsAttached) // VS only closes the window immediately when debugging + { + Log.Info(string.Empty); + Log.Info("Press any key to continue"); - Console.ReadKey(); + Console.ReadKey(); + } } } } \ No newline at end of file diff --git a/src/GitLink/project.json b/src/GitLink/project.json index 4f152e5..f4c2be8 100644 --- a/src/GitLink/project.json +++ b/src/GitLink/project.json @@ -4,6 +4,7 @@ "GitTools.Core": "1.0.0-unstable0021", "ImpromptuInterface": "6.2.2", "LibGit2Sharp": "0.21.0.176", + "System.CommandLine": "0.1.0-e161008-1", "NuProj.Common": "0.11.14-beta", "EnlistmentInfo": { "version": "1.0.4", diff --git a/src/GitLinkTask/GitLink.targets b/src/GitLinkTask/GitLink.targets index c4e1f49..b8a8860 100644 --- a/src/GitLinkTask/GitLink.targets +++ b/src/GitLinkTask/GitLink.targets @@ -1,11 +1,14 @@  - + - - - + + + \ No newline at end of file diff --git a/src/GitLinkTask/GitLinkTask.csproj b/src/GitLinkTask/GitLinkTask.csproj index 6c75ee6..b6de744 100644 --- a/src/GitLinkTask/GitLinkTask.csproj +++ b/src/GitLinkTask/GitLinkTask.csproj @@ -41,9 +41,11 @@ - + + ..\..\lib\Catel.Core.4.3.0\lib\net45\Catel.Core.dll + @@ -61,5 +63,11 @@ GitLink + + + {d68add77-913f-46d2-9a4f-5cc71c4718d8} + GitLink + + \ No newline at end of file diff --git a/src/GitLinkTask/LinkProject.cs b/src/GitLinkTask/LinkProject.cs index 79a6bdb..1faafb9 100644 --- a/src/GitLinkTask/LinkProject.cs +++ b/src/GitLinkTask/LinkProject.cs @@ -7,22 +7,63 @@ namespace GitLinkTask { + using System; using System.IO; + using System.Linq; + using GitLink; + using GitLink.Pdb; + using GitLink.Providers; + using Catel.Logging; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; - public class LinkProject : ToolTask + public class LinkProject : Task { [Required] - public ITaskItem SolutionDirectory { get; set; } + public ITaskItem PdbFile { get; set; } [Required] - public string ProjectName { get; set; } + public ITaskItem[] SourceFiles { get; set; } - protected override string ToolName => "GitLink.exe"; + public bool DownloadWithPowershell { get; set; } - protected override string GenerateFullPathToTool() => Path.Combine(Path.GetDirectoryName(GetType().Assembly.Location), "GitLink.exe"); + public bool SkipVerify { get; set; } - protected override string GenerateCommandLineCommands() => $"{SolutionDirectory.FullPath()} -include {ProjectName}"; + public string GitRemoteUrl { get; set; } + + public override bool Execute() + { + LogManager.GetCurrentClassLogger().LogMessage += this.LinkProject_LogMessage; + + Linker.Link( + this.PdbFile.GetMetadata("FullPath"), + this.SourceFiles.Select(i => i.GetMetadata("FullPath")), + this.DownloadWithPowershell, + this.SkipVerify, + this.GitRemoteUrl); + + return !this.Log.HasLoggedErrors; + } + + private void LinkProject_LogMessage(object sender, LogMessageEventArgs e) + { + switch (e.LogEvent) + { + case LogEvent.Error: + this.Log.LogError(e.Message); + break; + case LogEvent.Warning: + this.Log.LogWarning(e.Message); + break; + case LogEvent.Info: + this.Log.LogMessage(MessageImportance.Normal, e.Message); + break; + case LogEvent.Debug: + this.Log.LogMessage(MessageImportance.Low, e.Message); + break; + default: + break; + } + } } } \ No newline at end of file diff --git a/src/GitLinkTask/TaskItemExtensions.cs b/src/GitLinkTask/TaskItemExtensions.cs deleted file mode 100644 index d9e965f..0000000 --- a/src/GitLinkTask/TaskItemExtensions.cs +++ /dev/null @@ -1,30 +0,0 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. -// -// -------------------------------------------------------------------------------------------------------------------- - - -namespace GitLinkTask -{ - using System.Diagnostics; - using Microsoft.Build.Framework; - - public static class TaskItemExtensions - { - [DebuggerStepThrough] - public static string FullPath(this ITaskItem item) => item.GetMetadata("FullPath"); - - [DebuggerStepThrough] - public static string RootDir(this ITaskItem item) => item.GetMetadata("RootDir"); - - [DebuggerStepThrough] - public static string Filename(this ITaskItem item) => item.GetMetadata("Filename"); - - [DebuggerStepThrough] - public static string Extension(this ITaskItem item) => item.GetMetadata("Extension"); - - [DebuggerStepThrough] - public static string Directory(this ITaskItem item) => item.GetMetadata("Directory"); - } -} \ No newline at end of file diff --git a/src/nuget.config b/src/nuget.config index 29a9bd8..f1cb37a 100644 --- a/src/nuget.config +++ b/src/nuget.config @@ -1,5 +1,9 @@ - + + + + + \ No newline at end of file From 0b2120b0cef999d97fe9cdab795a70f8a9b62c2b Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 11:18:10 -0700 Subject: [PATCH 12/85] Forward errors from pdbstr.exe to logger --- src/GitLink/Helpers/PdbStrHelper.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/GitLink/Helpers/PdbStrHelper.cs b/src/GitLink/Helpers/PdbStrHelper.cs index 323b31b..51f3d73 100644 --- a/src/GitLink/Helpers/PdbStrHelper.cs +++ b/src/GitLink/Helpers/PdbStrHelper.cs @@ -24,12 +24,19 @@ public static void Execute(string pdbStrFileName, string projectPdbFile, string { Arguments = string.Format("-w -s:srcsrv -p:\"{0}\" -i:\"{1}\"", projectPdbFile, pdbStrFile), CreateNoWindow = true, - UseShellExecute = false + UseShellExecute = false, + RedirectStandardOutput = true, + RedirectStandardError = true, }; var process = new Process(); + process.OutputDataReceived += (s, e) => Log.Info(e.Data); + process.ErrorDataReceived += (s, e) => Log.Error(e.Data); + process.EnableRaisingEvents = true; process.StartInfo = processStartInfo; process.Start(); + process.BeginErrorReadLine(); + process.BeginOutputReadLine(); process.WaitForExit(); var processExitCode = process.ExitCode; From f47ccdda0a1fefd9f73df8175e5133d088f47bbb Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 11:18:25 -0700 Subject: [PATCH 13/85] Skip exceptions for missing files --- src/GitLink/Extensions/PdbExtensions.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/GitLink/Extensions/PdbExtensions.cs b/src/GitLink/Extensions/PdbExtensions.cs index ce8e684..358eaee 100644 --- a/src/GitLink/Extensions/PdbExtensions.cs +++ b/src/GitLink/Extensions/PdbExtensions.cs @@ -9,6 +9,7 @@ namespace GitLink { using System; using System.Collections.Generic; + using System.IO; using System.Linq; using Catel; using Pdb; @@ -23,7 +24,7 @@ public static IEnumerable FindMissingOrChangedSourceFiles(this PdbFile p { string file = checksumInfo.Key; string expectedChecksum = checksumInfo.Value; - string actualChecksum = Hex.Encode(Crypto.GetMd5HashForFile(file)); + string actualChecksum = File.Exists(file) ? Hex.Encode(Crypto.GetMd5HashForFile(file)) : string.Empty; if (expectedChecksum != actualChecksum) { From 866f38eedc56241a30e7ee4c12482e7b02c11ea9 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 11:38:34 -0700 Subject: [PATCH 14/85] Normalize path capitalization per git index Now the pdb.srcsrv file actually works. The debugger can download files. --- .../Extensions/RepositoryExtensions.cs | 24 +++++++++++++++++++ src/GitLink/GitLink.csproj | 1 + src/GitLink/Linker.cs | 15 +++++++----- 3 files changed, 34 insertions(+), 6 deletions(-) create mode 100644 src/GitLink/Extensions/RepositoryExtensions.cs diff --git a/src/GitLink/Extensions/RepositoryExtensions.cs b/src/GitLink/Extensions/RepositoryExtensions.cs new file mode 100644 index 0000000..3ac764f --- /dev/null +++ b/src/GitLink/Extensions/RepositoryExtensions.cs @@ -0,0 +1,24 @@ +namespace GitLink +{ + using System; + using System.Collections.Generic; + using System.Linq; + using System.Text; + using System.Threading.Tasks; + using Catel; + using Catel.IO; + using LibGit2Sharp; + + internal static class RepositoryExtensions + { + internal static string GetRepoNormalizedPath(this Repository repository, string path) + { + Argument.IsNotNull(nameof(repository), repository); + Argument.IsNotNullOrEmpty(nameof(path), path); + + string relativePath = Path.GetRelativePath(path, repository.Info.WorkingDirectory); + var repoFile = repository.Index.FirstOrDefault(e => string.Equals(e.Path, relativePath, StringComparison.OrdinalIgnoreCase)); + return repoFile?.Path; + } + } +} diff --git a/src/GitLink/GitLink.csproj b/src/GitLink/GitLink.csproj index bba990f..3699f72 100644 --- a/src/GitLink/GitLink.csproj +++ b/src/GitLink/GitLink.csproj @@ -65,6 +65,7 @@ + diff --git a/src/GitLink/Linker.cs b/src/GitLink/Linker.cs index 52807a5..c6748c0 100644 --- a/src/GitLink/Linker.cs +++ b/src/GitLink/Linker.cs @@ -34,12 +34,13 @@ public static class Linker Argument.IsNotNullOrEmpty(() => pdbPath); var pdb = new PdbFile(pdbPath); - var filesAndChecksums = pdb.GetFiles(); - var sourceFiles = filesAndChecksums.Select(f => f.Item1); + var sourceFiles = pdb.GetFiles().Select(f => f.Item1).ToList(); string repositoryDirectory = GitDirFinder.TreeWalkForGitDir(Path.GetDirectoryName(sourceFiles.First())); using (var repository = new Repository(repositoryDirectory)) { + var repoSourceFiles = sourceFiles.ToDictionary(e => e, repository.GetRepoNormalizedPath); + var providerManager = new Providers.ProviderManager(); Providers.IProvider provider; if (options.GitRemoteUrl == null) @@ -83,11 +84,13 @@ public static class Linker DownloadWithPowershell = options.DownloadWithPowerShell, Revision = commitId, }; - foreach (string sourceFile in sourceFiles) + foreach (var sourceFile in repoSourceFiles) { - string repoRelativePath = Catel.IO.Path.GetRelativePath(sourceFile, Path.GetDirectoryName(repositoryDirectory)) - .Replace('\\', '/'); - srcSrvContext.Paths.Add(Tuple.Create(sourceFile, repoRelativePath)); + // Skip files that aren't tracked by source control. + if (sourceFile.Value != null) + { + srcSrvContext.Paths.Add(Tuple.Create(sourceFile.Key, sourceFile.Value.Replace('\\', '/'))); + } } if (provider is Providers.VisualStudioTeamServicesProvider) From 39cd23c7a0b9e280d93ab15d861c9cd7a704aafa Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 12:17:07 -0700 Subject: [PATCH 15/85] Fix pdb locking issue --- src/GitLink/Linker.cs | 115 +++++++++++++++++++++-------------------- src/GitLink/Program.cs | 2 +- 2 files changed, 60 insertions(+), 57 deletions(-) diff --git a/src/GitLink/Linker.cs b/src/GitLink/Linker.cs index c6748c0..64ff53e 100644 --- a/src/GitLink/Linker.cs +++ b/src/GitLink/Linker.cs @@ -33,77 +33,80 @@ public static class Linker { Argument.IsNotNullOrEmpty(() => pdbPath); - var pdb = new PdbFile(pdbPath); - var sourceFiles = pdb.GetFiles().Select(f => f.Item1).ToList(); - - string repositoryDirectory = GitDirFinder.TreeWalkForGitDir(Path.GetDirectoryName(sourceFiles.First())); - using (var repository = new Repository(repositoryDirectory)) + var projectSrcSrvFile = pdbPath + ".srcsrv"; + string repositoryDirectory; + using (var pdb = new PdbFile(pdbPath)) { - var repoSourceFiles = sourceFiles.ToDictionary(e => e, repository.GetRepoNormalizedPath); + var sourceFiles = pdb.GetFiles().Select(f => f.Item1).ToList(); - var providerManager = new Providers.ProviderManager(); - Providers.IProvider provider; - if (options.GitRemoteUrl == null) - { - var candidateProviders = from remote in repository.Network.Remotes - let p = providerManager.GetProvider(remote.Url) - where p != null - select p; - provider = candidateProviders.First(); - } - else + repositoryDirectory = GitDirFinder.TreeWalkForGitDir(Path.GetDirectoryName(sourceFiles.First())); + using (var repository = new Repository(repositoryDirectory)) { - provider = providerManager.GetProvider(options.GitRemoteUrl.AbsoluteUri); - } + var repoSourceFiles = sourceFiles.ToDictionary(e => e, repository.GetRepoNormalizedPath); - var projectSrcSrvFile = pdbPath + ".srcsrv"; - - if (!options.SkipVerify) - { - Log.Info("Verifying pdb file"); + var providerManager = new Providers.ProviderManager(); + Providers.IProvider provider; + if (options.GitRemoteUrl == null) + { + var candidateProviders = from remote in repository.Network.Remotes + let p = providerManager.GetProvider(remote.Url) + where p != null + select p; + provider = candidateProviders.First(); + } + else + { + provider = providerManager.GetProvider(options.GitRemoteUrl.AbsoluteUri); + } - var missingFiles = pdb.FindMissingOrChangedSourceFiles(); - foreach (var missingFile in missingFiles) + if (!options.SkipVerify) { - Log.Warning($"File \"{missingFile}\" missing or changed since the PDB was compiled."); + Log.Info("Verifying pdb file"); + + var missingFiles = pdb.FindMissingOrChangedSourceFiles(); + foreach (var missingFile in missingFiles) + { + Log.Warning($"File \"{missingFile}\" missing or changed since the PDB was compiled."); + } } - } - string commitId; - commitId = repository.Head.Commits.First().Sha; + string commitId; + commitId = repository.Head.Commits.First().Sha; - string rawUrl = provider.RawGitUrl; - if (!rawUrl.Contains("%var2%") && !rawUrl.Contains("{0}")) - { - rawUrl = string.Format("{0}/{{0}}/%var2%", rawUrl); - } + string rawUrl = provider.RawGitUrl; + if (!rawUrl.Contains("%var2%") && !rawUrl.Contains("{0}")) + { + rawUrl = string.Format("{0}/{{0}}/%var2%", rawUrl); + } - var srcSrvContext = new SrcSrvContext - { - RawUrl = rawUrl, - DownloadWithPowershell = options.DownloadWithPowerShell, - Revision = commitId, - }; - foreach (var sourceFile in repoSourceFiles) - { - // Skip files that aren't tracked by source control. - if (sourceFile.Value != null) + var srcSrvContext = new SrcSrvContext + { + RawUrl = rawUrl, + DownloadWithPowershell = options.DownloadWithPowerShell, + Revision = commitId, + }; + foreach (var sourceFile in repoSourceFiles) { - srcSrvContext.Paths.Add(Tuple.Create(sourceFile.Key, sourceFile.Value.Replace('\\', '/'))); + // Skip files that aren't tracked by source control. + if (sourceFile.Value != null) + { + srcSrvContext.Paths.Add(Tuple.Create(sourceFile.Key, sourceFile.Value.Replace('\\', '/'))); + } } - } - if (provider is Providers.VisualStudioTeamServicesProvider) - { - srcSrvContext.VstsData["TFS_COLLECTION"] = provider.CompanyUrl; - srcSrvContext.VstsData["TFS_TEAM_PROJECT"] = provider.ProjectName; - srcSrvContext.VstsData["TFS_REPO"] = provider.ProjectName; - } + if (provider is Providers.VisualStudioTeamServicesProvider) + { + srcSrvContext.VstsData["TFS_COLLECTION"] = provider.CompanyUrl; + srcSrvContext.VstsData["TFS_TEAM_PROJECT"] = provider.ProjectName; + srcSrvContext.VstsData["TFS_REPO"] = provider.ProjectName; + } - ProjectExtensions.CreateSrcSrv(projectSrcSrvFile, srcSrvContext); - Log.Debug("Created source server link file, updating pdb file '{0}'", Catel.IO.Path.GetRelativePath(pdbPath, repositoryDirectory)); - PdbStrHelper.Execute(PdbStrExePath, pdbPath, projectSrcSrvFile); + ProjectExtensions.CreateSrcSrv(projectSrcSrvFile, srcSrvContext); + } } + + Log.Debug("Created source server link file, updating pdb file '{0}'", Catel.IO.Path.GetRelativePath(pdbPath, repositoryDirectory)); + PdbStrHelper.Execute(PdbStrExePath, pdbPath, projectSrcSrvFile); } } } \ No newline at end of file diff --git a/src/GitLink/Program.cs b/src/GitLink/Program.cs index 3ddea50..91acf4b 100644 --- a/src/GitLink/Program.cs +++ b/src/GitLink/Program.cs @@ -46,7 +46,7 @@ private static int Main(string[] args) if (string.IsNullOrEmpty(pdbPath)) { - Log.Error("pdb parameter is required."); + Log.Info(arguments.GetHelpText()); return 1; } From 6f361c6025a9892f8b2a3fe0967b320d11523f21 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 12:30:57 -0700 Subject: [PATCH 16/85] Fix build breaks --- src/GitLink.Tests/ArgumentParserFacts.cs | 166 ------------------ src/GitLink.Tests/GitLink.Tests.csproj | 1 - .../IntegrationTests/IntegrationTestBase.cs | 2 +- src/GitLinkTask/LinkProject.cs | 13 +- 4 files changed, 8 insertions(+), 174 deletions(-) delete mode 100644 src/GitLink.Tests/ArgumentParserFacts.cs diff --git a/src/GitLink.Tests/ArgumentParserFacts.cs b/src/GitLink.Tests/ArgumentParserFacts.cs deleted file mode 100644 index f93bd13..0000000 --- a/src/GitLink.Tests/ArgumentParserFacts.cs +++ /dev/null @@ -1,166 +0,0 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. -// -// -------------------------------------------------------------------------------------------------------------------- - - -namespace GitLink.Tests -{ - using Catel.Test; - using NUnit.Framework; - - [TestFixture] - public class ArgumentParserFacts - { - [TestCase] - public void ReturnsHelpForEmptyParameters() - { - var context = ArgumentParser.ParseArguments(string.Empty); - Assert.IsTrue(context.IsHelp); - } - - [TestCase] - public void CorrectlyParsesSolutionDirectory() - { - var context = ArgumentParser.ParseArguments("solutionDirectory -u http://github.com/CatenaLogic/GitLink"); - - Assert.AreEqual("solutionDirectory", context.SolutionDirectory); - } - - [TestCase] - public void CorrectlyParsesLogFilePath() - { - var context = ArgumentParser.ParseArguments("solutionDirectory -l logFilePath"); - - Assert.AreEqual("solutionDirectory", context.SolutionDirectory); - Assert.AreEqual("logFilePath", context.LogFile); - } - - [TestCase] - public void CorrectlyParsesPdbFilesDirectory() - { - var context = ArgumentParser.ParseArguments("solutionDirectory -d pdbFilesDirectory"); - - Assert.AreEqual("solutionDirectory", context.SolutionDirectory); - Assert.AreEqual("pdbFilesDirectory", context.PdbFilesDirectory); - } - - [TestCase] - public void CorrectlyParsesHelp() - { - var context = ArgumentParser.ParseArguments("-h"); - - Assert.IsTrue(context.IsHelp); - } - - [TestCase] - public void CorrectlyParsesSolutionFile() - { - var context = ArgumentParser.ParseArguments("solutionDirectory -u http://github.com/CatenaLogic/GitLink -f someSolution"); - - Assert.AreEqual("someSolution", context.SolutionFile); - } - - [TestCase] - public void CorrectlyParsesUrlAndBranchName() - { - var context = ArgumentParser.ParseArguments("solutionDirectory -u http://github.com/CatenaLogic/GitLink -b somebranch"); - - Assert.AreEqual("solutionDirectory", context.SolutionDirectory); - Assert.AreEqual("http://github.com/CatenaLogic/GitLink", context.TargetUrl); - Assert.AreEqual("somebranch", context.TargetBranch); - } - - [TestCase] - public void CorrectlyParsesUrlAndConfiguration() - { - var context = ArgumentParser.ParseArguments("solutionDirectory -u http://github.com/CatenaLogic/GitLink -c someConfiguration"); - - Assert.AreEqual("solutionDirectory", context.SolutionDirectory); - Assert.AreEqual("http://github.com/CatenaLogic/GitLink", context.TargetUrl); - Assert.AreEqual("someConfiguration", context.ConfigurationName); - } - - [TestCase] - public void CorrectlyParsesUrlAndConfigurationAndPlatform() - { - var context = ArgumentParser.ParseArguments("solutionDirectory -u http://github.com/CatenaLogic/GitLink -c someConfiguration -p \"Any CPU\""); - - Assert.AreEqual("solutionDirectory", context.SolutionDirectory); - Assert.AreEqual("http://github.com/CatenaLogic/GitLink", context.TargetUrl); - Assert.AreEqual("someConfiguration", context.ConfigurationName); - Assert.AreEqual("Any CPU", context.PlatformName); - } - - [TestCase] - public void CorrectlyParsesUrlAndConfigurationWithDebug() - { - var context = ArgumentParser.ParseArguments("solutionDirectory -u http://github.com/CatenaLogic/GitLink -debug -c someConfiguration"); - - Assert.AreEqual("solutionDirectory", context.SolutionDirectory); - Assert.AreEqual("http://github.com/CatenaLogic/GitLink", context.TargetUrl); - Assert.AreEqual("someConfiguration", context.ConfigurationName); - Assert.IsTrue(context.IsDebug); - } - - [TestCase] - public void CorrectlyParsesIgnoredProjects() - { - var context = ArgumentParser.ParseArguments("solutionDirectory -u http://github.com/CatenaLogic/GitLink -debug -c someConfiguration -ignore test1,test2"); - - Assert.AreEqual("solutionDirectory", context.SolutionDirectory); - Assert.AreEqual("http://github.com/CatenaLogic/GitLink", context.TargetUrl); - Assert.AreEqual("someConfiguration", context.ConfigurationName); - Assert.IsTrue(context.IsDebug); - - Assert.AreEqual(2, context.IgnoredProjects.Count); - Assert.AreEqual("test1", context.IgnoredProjects[0]); - Assert.AreEqual("test2", context.IgnoredProjects[1]); - } - - [TestCase] - public void CorrectlyParsesIncludedProjects() - { - var context = ArgumentParser.ParseArguments("solutionDirectory -u http://github.com/CatenaLogic/GitLink -debug -c someConfiguration -include test1,test2"); - - Assert.AreEqual("solutionDirectory", context.SolutionDirectory); - Assert.AreEqual("http://github.com/CatenaLogic/GitLink", context.TargetUrl); - Assert.AreEqual("someConfiguration", context.ConfigurationName); - Assert.IsTrue(context.IsDebug); - - Assert.AreEqual(2, context.IncludedProjects.Count); - Assert.AreEqual("test1", context.IncludedProjects[0]); - Assert.AreEqual("test2", context.IncludedProjects[1]); - } - - [TestCase] - public void CorrectlyParsesIncludedProjectsWithRegex() - { - var context = ArgumentParser.ParseArguments("solutionDirectory -u http://github.com/CatenaLogic/GitLink -debug -c someConfiguration -include test1,/*.test*2/"); - - Assert.AreEqual("solutionDirectory", context.SolutionDirectory); - Assert.AreEqual("http://github.com/CatenaLogic/GitLink", context.TargetUrl); - Assert.AreEqual("someConfiguration", context.ConfigurationName); - Assert.IsTrue(context.IsDebug); - - Assert.AreEqual(2, context.IncludedProjects.Count); - Assert.AreEqual("test1", context.IncludedProjects[0]); - Assert.AreEqual("/*.test*2/", context.IncludedProjects[1]); - } - - [TestCase] - public void ThrowsExceptionForUnknownArgument() - { - ExceptionTester.CallMethodAndExpectException(() => ArgumentParser.ParseArguments("solutionDirectory -x logFilePath")); - } - - [TestCase] - public void PowershellDownloadSetToTrue() - { - var context = ArgumentParser.ParseArguments("solutionDirectory -u http://github.com/CatenaLogic/GitLink -powershell"); - - Assert.IsTrue(context.DownloadWithPowershell); - } - } -} \ No newline at end of file diff --git a/src/GitLink.Tests/GitLink.Tests.csproj b/src/GitLink.Tests/GitLink.Tests.csproj index ae3c2e5..3de4029 100644 --- a/src/GitLink.Tests/GitLink.Tests.csproj +++ b/src/GitLink.Tests/GitLink.Tests.csproj @@ -51,7 +51,6 @@ - diff --git a/src/GitLink.Tests/IntegrationTests/IntegrationTestBase.cs b/src/GitLink.Tests/IntegrationTests/IntegrationTestBase.cs index 720ff6f..b2335df 100644 --- a/src/GitLink.Tests/IntegrationTests/IntegrationTestBase.cs +++ b/src/GitLink.Tests/IntegrationTests/IntegrationTestBase.cs @@ -62,7 +62,7 @@ protected int RunGitLink(string directory, string repositoryUrl, string branchNa TargetBranch = branchName }; - return Linker.Link(context); + throw new NotImplementedException(); } protected void VerifyUpdatedPdbs(string directory, string configurationName) diff --git a/src/GitLinkTask/LinkProject.cs b/src/GitLinkTask/LinkProject.cs index 1faafb9..f8c3ae9 100644 --- a/src/GitLinkTask/LinkProject.cs +++ b/src/GitLinkTask/LinkProject.cs @@ -35,12 +35,13 @@ public override bool Execute() { LogManager.GetCurrentClassLogger().LogMessage += this.LinkProject_LogMessage; - Linker.Link( - this.PdbFile.GetMetadata("FullPath"), - this.SourceFiles.Select(i => i.GetMetadata("FullPath")), - this.DownloadWithPowershell, - this.SkipVerify, - this.GitRemoteUrl); + var options = new LinkOptions + { + DownloadWithPowerShell = this.DownloadWithPowershell, + SkipVerify = this.SkipVerify, + GitRemoteUrl = new Uri(this.GitRemoteUrl, UriKind.Absolute), + }; + Linker.Link(this.PdbFile.GetMetadata("FullPath"), options); return !this.Log.HasLoggedErrors; } From de7b0453a8bc1d5fd68da3f3f3de65de176888ad Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 12:33:38 -0700 Subject: [PATCH 17/85] Remove unnecessary SourceFiles task parameter --- src/GitLinkTask/GitLink.targets | 1 - src/GitLinkTask/LinkProject.cs | 3 --- 2 files changed, 4 deletions(-) diff --git a/src/GitLinkTask/GitLink.targets b/src/GitLinkTask/GitLink.targets index b8a8860..42ecb8b 100644 --- a/src/GitLinkTask/GitLink.targets +++ b/src/GitLinkTask/GitLink.targets @@ -6,7 +6,6 @@ diff --git a/src/GitLinkTask/LinkProject.cs b/src/GitLinkTask/LinkProject.cs index f8c3ae9..f3d328c 100644 --- a/src/GitLinkTask/LinkProject.cs +++ b/src/GitLinkTask/LinkProject.cs @@ -22,9 +22,6 @@ public class LinkProject : Task [Required] public ITaskItem PdbFile { get; set; } - [Required] - public ITaskItem[] SourceFiles { get; set; } - public bool DownloadWithPowershell { get; set; } public bool SkipVerify { get; set; } From de71ad79ddc0978e0ea7cbb277755a6367b8f56e Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 12:36:16 -0700 Subject: [PATCH 18/85] Remove obsolete comment --- src/GitLink/Properties/AssemblyInfo.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/GitLink/Properties/AssemblyInfo.cs b/src/GitLink/Properties/AssemblyInfo.cs index 18211b0..0eadb6c 100644 --- a/src/GitLink/Properties/AssemblyInfo.cs +++ b/src/GitLink/Properties/AssemblyInfo.cs @@ -8,8 +8,6 @@ using System.Reflection; using System.Runtime.InteropServices; -// All other assembly info is defined in SharedAssembly.cs - [assembly: AssemblyTitle("GitLink")] [assembly: AssemblyProduct("GitLink")] [assembly: AssemblyDescription("GitLink library")] From 8eac82f304ac1145deb06dfeb4cb57ee561feed3 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 12:38:40 -0700 Subject: [PATCH 19/85] Rename GitLink task from LinkProject --- src/GitLinkTask/{LinkProject.cs => GitLink.cs} | 8 ++++---- src/GitLinkTask/GitLink.targets | 4 ++-- src/GitLinkTask/GitLinkTask.csproj | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) rename src/GitLinkTask/{LinkProject.cs => GitLink.cs} (94%) diff --git a/src/GitLinkTask/LinkProject.cs b/src/GitLinkTask/GitLink.cs similarity index 94% rename from src/GitLinkTask/LinkProject.cs rename to src/GitLinkTask/GitLink.cs index f3d328c..172db30 100644 --- a/src/GitLinkTask/LinkProject.cs +++ b/src/GitLinkTask/GitLink.cs @@ -10,14 +10,14 @@ namespace GitLinkTask using System; using System.IO; using System.Linq; - using GitLink; - using GitLink.Pdb; - using GitLink.Providers; + using global::GitLink; + using global::GitLink.Pdb; + using global::GitLink.Providers; using Catel.Logging; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; - public class LinkProject : Task + public class GitLink : Task { [Required] public ITaskItem PdbFile { get; set; } diff --git a/src/GitLinkTask/GitLink.targets b/src/GitLinkTask/GitLink.targets index 42ecb8b..87e3b2c 100644 --- a/src/GitLinkTask/GitLink.targets +++ b/src/GitLinkTask/GitLink.targets @@ -3,8 +3,8 @@ - - + - + From 72d6154a8df51dd6997d4f02c09ee683f8f7ed90 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 12:52:15 -0700 Subject: [PATCH 20/85] Fix build integration --- src/GitLinkTask/GitLink.cs | 2 +- src/GitLinkTask/GitLink.targets | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/GitLinkTask/GitLink.cs b/src/GitLinkTask/GitLink.cs index 172db30..fa09467 100644 --- a/src/GitLinkTask/GitLink.cs +++ b/src/GitLinkTask/GitLink.cs @@ -36,7 +36,7 @@ public override bool Execute() { DownloadWithPowerShell = this.DownloadWithPowershell, SkipVerify = this.SkipVerify, - GitRemoteUrl = new Uri(this.GitRemoteUrl, UriKind.Absolute), + GitRemoteUrl = this.GitRemoteUrl != null ? new Uri(this.GitRemoteUrl, UriKind.Absolute) : null, }; Linker.Link(this.PdbFile.GetMetadata("FullPath"), options); diff --git a/src/GitLinkTask/GitLink.targets b/src/GitLinkTask/GitLink.targets index 87e3b2c..ce6cbeb 100644 --- a/src/GitLinkTask/GitLink.targets +++ b/src/GitLinkTask/GitLink.targets @@ -1,11 +1,11 @@  - + - From 7b643080e2d3fd09a56124f2b3e2bded6a412631 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 13:10:39 -0700 Subject: [PATCH 21/85] Report error rather than crash when no git repo is found --- src/GitLink/Linker.cs | 9 ++++++++- src/GitLinkTask/GitLink.cs | 4 ++-- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/src/GitLink/Linker.cs b/src/GitLink/Linker.cs index 64ff53e..507a424 100644 --- a/src/GitLink/Linker.cs +++ b/src/GitLink/Linker.cs @@ -29,7 +29,7 @@ public static class Linker private static readonly string PdbStrExePath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "pdbstr.exe"); private static readonly ILog Log = LogManager.GetCurrentClassLogger(); - public static void Link(string pdbPath, LinkOptions options = default(LinkOptions)) + public static bool Link(string pdbPath, LinkOptions options = default(LinkOptions)) { Argument.IsNotNullOrEmpty(() => pdbPath); @@ -40,6 +40,12 @@ public static class Linker var sourceFiles = pdb.GetFiles().Select(f => f.Item1).ToList(); repositoryDirectory = GitDirFinder.TreeWalkForGitDir(Path.GetDirectoryName(sourceFiles.First())); + if (repositoryDirectory == null) + { + Log.Error("No source files found that are tracked in a git repo."); + return false; + } + using (var repository = new Repository(repositoryDirectory)) { var repoSourceFiles = sourceFiles.ToDictionary(e => e, repository.GetRepoNormalizedPath); @@ -107,6 +113,7 @@ public static class Linker Log.Debug("Created source server link file, updating pdb file '{0}'", Catel.IO.Path.GetRelativePath(pdbPath, repositoryDirectory)); PdbStrHelper.Execute(PdbStrExePath, pdbPath, projectSrcSrvFile); + return true; } } } \ No newline at end of file diff --git a/src/GitLinkTask/GitLink.cs b/src/GitLinkTask/GitLink.cs index fa09467..e326f4d 100644 --- a/src/GitLinkTask/GitLink.cs +++ b/src/GitLinkTask/GitLink.cs @@ -38,9 +38,9 @@ public override bool Execute() SkipVerify = this.SkipVerify, GitRemoteUrl = this.GitRemoteUrl != null ? new Uri(this.GitRemoteUrl, UriKind.Absolute) : null, }; - Linker.Link(this.PdbFile.GetMetadata("FullPath"), options); + bool success = Linker.Link(this.PdbFile.GetMetadata("FullPath"), options); - return !this.Log.HasLoggedErrors; + return success && !this.Log.HasLoggedErrors; } private void LinkProject_LogMessage(object sender, LogMessageEventArgs e) From 93c3f7238575b9fcabf26e5aba68038e10f27814 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 13:29:50 -0700 Subject: [PATCH 22/85] Handle more error cases gracefully --- src/GitLink/Linker.cs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/GitLink/Linker.cs b/src/GitLink/Linker.cs index 507a424..ea4b84a 100644 --- a/src/GitLink/Linker.cs +++ b/src/GitLink/Linker.cs @@ -58,13 +58,19 @@ public static class Linker let p = providerManager.GetProvider(remote.Url) where p != null select p; - provider = candidateProviders.First(); + provider = candidateProviders.FirstOrDefault(); } else { provider = providerManager.GetProvider(options.GitRemoteUrl.AbsoluteUri); } + if (provider == null) + { + Log.Error("Unable to detect the remote git service."); + return false; + } + if (!options.SkipVerify) { Log.Info("Verifying pdb file"); @@ -77,7 +83,12 @@ public static class Linker } string commitId; - commitId = repository.Head.Commits.First().Sha; + commitId = repository.Head.Commits.FirstOrDefault()?.Sha; + if (commitId == null) + { + Log.Error("No commit is checked out to HEAD. Have you committed yet?"); + return false; + } string rawUrl = provider.RawGitUrl; if (!rawUrl.Contains("%var2%") && !rawUrl.Contains("{0}")) From f3c495a3d03ced2170a1e2263e5d71530efd1063 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 14:05:16 -0700 Subject: [PATCH 23/85] Suppress blank lines in output --- src/GitLink/Helpers/PdbStrHelper.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/GitLink/Helpers/PdbStrHelper.cs b/src/GitLink/Helpers/PdbStrHelper.cs index 51f3d73..4d704cd 100644 --- a/src/GitLink/Helpers/PdbStrHelper.cs +++ b/src/GitLink/Helpers/PdbStrHelper.cs @@ -30,8 +30,8 @@ public static void Execute(string pdbStrFileName, string projectPdbFile, string }; var process = new Process(); - process.OutputDataReceived += (s, e) => Log.Info(e.Data); - process.ErrorDataReceived += (s, e) => Log.Error(e.Data); + process.OutputDataReceived += (s, e) => { if (e.Data != null) Log.Info(e.Data); }; + process.ErrorDataReceived += (s, e) => { if (e.Data != null) Log.Error(e.Data); }; process.EnableRaisingEvents = true; process.StartInfo = processStartInfo; process.Start(); From 3b769d00cccd5733f254999f5712aadd755063c1 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 14:05:26 -0700 Subject: [PATCH 24/85] Print # of files indexed in output --- src/GitLink/Linker.cs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/GitLink/Linker.cs b/src/GitLink/Linker.cs index ea4b84a..58ad3e0 100644 --- a/src/GitLink/Linker.cs +++ b/src/GitLink/Linker.cs @@ -35,9 +35,11 @@ public static class Linker var projectSrcSrvFile = pdbPath + ".srcsrv"; string repositoryDirectory; + IReadOnlyCollection sourceFiles; + IReadOnlyDictionary repoSourceFiles; using (var pdb = new PdbFile(pdbPath)) { - var sourceFiles = pdb.GetFiles().Select(f => f.Item1).ToList(); + sourceFiles = pdb.GetFiles().Select(f => f.Item1).ToList(); repositoryDirectory = GitDirFinder.TreeWalkForGitDir(Path.GetDirectoryName(sourceFiles.First())); if (repositoryDirectory == null) @@ -48,7 +50,7 @@ public static class Linker using (var repository = new Repository(repositoryDirectory)) { - var repoSourceFiles = sourceFiles.ToDictionary(e => e, repository.GetRepoNormalizedPath); + repoSourceFiles = sourceFiles.ToDictionary(e => e, repository.GetRepoNormalizedPath); var providerManager = new Providers.ProviderManager(); Providers.IProvider provider; @@ -73,7 +75,7 @@ public static class Linker if (!options.SkipVerify) { - Log.Info("Verifying pdb file"); + Log.Debug("Verifying pdb file"); var missingFiles = pdb.FindMissingOrChangedSourceFiles(); foreach (var missingFile in missingFiles) @@ -124,6 +126,9 @@ public static class Linker Log.Debug("Created source server link file, updating pdb file '{0}'", Catel.IO.Path.GetRelativePath(pdbPath, repositoryDirectory)); PdbStrHelper.Execute(PdbStrExePath, pdbPath, projectSrcSrvFile); + var indexedFilesCount = repoSourceFiles.Values.Count(v => v != null); + Log.Info($"Remote git source information for {indexedFilesCount}/{sourceFiles.Count} files written to pdb: \"{pdbPath}\""); + return true; } } From 812135935d0bb9355bd5149d5503b05e33702bba Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 14:07:07 -0700 Subject: [PATCH 25/85] Defend against uncaught errors in PdbStr --- src/GitLink/Helpers/PdbStrHelper.cs | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/GitLink/Helpers/PdbStrHelper.cs b/src/GitLink/Helpers/PdbStrHelper.cs index 4d704cd..5d7cc1a 100644 --- a/src/GitLink/Helpers/PdbStrHelper.cs +++ b/src/GitLink/Helpers/PdbStrHelper.cs @@ -30,8 +30,22 @@ public static void Execute(string pdbStrFileName, string projectPdbFile, string }; var process = new Process(); - process.OutputDataReceived += (s, e) => { if (e.Data != null) Log.Info(e.Data); }; - process.ErrorDataReceived += (s, e) => { if (e.Data != null) Log.Error(e.Data); }; + bool errorsPrinted = false; + process.OutputDataReceived += (s, e) => + { + if (e.Data != null) + { + Log.Info(e.Data); + } + }; + process.ErrorDataReceived += (s, e) => + { + if (e.Data != null) + { + Log.Error(e.Data); + errorsPrinted = true; + } + }; process.EnableRaisingEvents = true; process.StartInfo = processStartInfo; process.Start(); @@ -44,6 +58,12 @@ public static void Execute(string pdbStrFileName, string projectPdbFile, string { throw Log.ErrorAndCreateException("PdbStr exited with unexpected error code '{0}'", processExitCode); } + + // PdbStr can print errors and still return 0 for its exit code. + if (errorsPrinted) + { + throw Log.ErrorAndCreateException("PdbStr printed errors."); + } } } } \ No newline at end of file From 73cb03b584ac3e7918ad3760d006434ccb6bb9de Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 14:21:49 -0700 Subject: [PATCH 26/85] Support SHA-1 hashes in PDBs --- src/GitLink/Extensions/PdbExtensions.cs | 58 +++++++++++++++---------- src/GitLink/GitLink.csproj | 1 - src/GitLink/Linker.cs | 2 +- src/GitLink/Pdb/Crypto.cs | 22 +--------- src/GitLink/Pdb/Hex.cs | 36 --------------- 5 files changed, 37 insertions(+), 82 deletions(-) delete mode 100644 src/GitLink/Pdb/Hex.cs diff --git a/src/GitLink/Extensions/PdbExtensions.cs b/src/GitLink/Extensions/PdbExtensions.cs index 358eaee..5a2fd7b 100644 --- a/src/GitLink/Extensions/PdbExtensions.cs +++ b/src/GitLink/Extensions/PdbExtensions.cs @@ -11,6 +11,7 @@ namespace GitLink using System.Collections.Generic; using System.IO; using System.Linq; + using System.Security.Cryptography; using Catel; using Pdb; @@ -20,34 +21,21 @@ public static IEnumerable FindMissingOrChangedSourceFiles(this PdbFile p { Argument.IsNotNull(() => pdbFile); - foreach (var checksumInfo in pdbFile.GetChecksums()) + foreach (var checksumInfo in pdbFile.GetFilesAndChecksums()) { string file = checksumInfo.Key; - string expectedChecksum = checksumInfo.Value; - string actualChecksum = File.Exists(file) ? Hex.Encode(Crypto.GetMd5HashForFile(file)) : string.Empty; + byte[] expectedChecksum = checksumInfo.Value; + HashAlgorithm hasher = expectedChecksum.Length == 16 ? (HashAlgorithm)MD5.Create() : SHA1.Create(); + byte[] actualChecksum = File.Exists(file) ? Crypto.HashFile(hasher, file) : null; - if (expectedChecksum != actualChecksum) + if (!AreEqualBuffers(expectedChecksum, actualChecksum)) { yield return file; } } } - 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) + public static IReadOnlyDictionary GetFilesAndChecksums(this PdbFile pdbFile) { Argument.IsNotNull(() => pdbFile); @@ -56,7 +44,7 @@ public static IEnumerable> GetFiles(this PdbFile pdbFile) var values = pdbFile.Info.NameToPdbName.Values; - var results = new List>(); + var results = new Dictionary(StringComparer.OrdinalIgnoreCase); foreach (var value in values) { if (!value.Name.StartsWith(FileIndicator)) @@ -67,14 +55,36 @@ public static IEnumerable> GetFiles(this PdbFile pdbFile) var num = value.Stream; var name = value.Name.Substring(FileIndicator.Length); - // Get last 16 bytes for checksum + // Get last bytes for checksum (it may be MD5 or SHA1) var bytes = pdbFile.ReadStreamBytes(num); - var checksum = new byte[16]; - Array.Copy(bytes, bytes.Length - 16, checksum, 0, 16); - results.Add(Tuple.Create(name, checksum)); + int hashLength = bytes.Length - 72; + var checksum = new byte[hashLength]; + Array.Copy(bytes, bytes.Length - hashLength, checksum, 0, hashLength); + results.Add(name, checksum); } return results; } + + private static bool AreEqualBuffers(byte[] first, byte[] second) + { + Argument.IsNotNull(nameof(first), first); + Argument.IsNotNull(nameof(second), second); + + if (first.Length != second.Length) + { + return false; + } + + for (int i = 0; i < first.Length; i++) + { + if (first[i] != second[i]) + { + return false; + } + } + + return true; + } } } \ No newline at end of file diff --git a/src/GitLink/GitLink.csproj b/src/GitLink/GitLink.csproj index 3699f72..0cf7968 100644 --- a/src/GitLink/GitLink.csproj +++ b/src/GitLink/GitLink.csproj @@ -76,7 +76,6 @@ - diff --git a/src/GitLink/Linker.cs b/src/GitLink/Linker.cs index 58ad3e0..e9dd466 100644 --- a/src/GitLink/Linker.cs +++ b/src/GitLink/Linker.cs @@ -39,7 +39,7 @@ public static class Linker IReadOnlyDictionary repoSourceFiles; using (var pdb = new PdbFile(pdbPath)) { - sourceFiles = pdb.GetFiles().Select(f => f.Item1).ToList(); + sourceFiles = pdb.GetFilesAndChecksums().Keys.ToList(); repositoryDirectory = GitDirFinder.TreeWalkForGitDir(Path.GetDirectoryName(sourceFiles.First())); if (repositoryDirectory == null) diff --git a/src/GitLink/Pdb/Crypto.cs b/src/GitLink/Pdb/Crypto.cs index f09fff6..e23baf4 100644 --- a/src/GitLink/Pdb/Crypto.cs +++ b/src/GitLink/Pdb/Crypto.cs @@ -16,29 +16,11 @@ namespace GitLink.Pdb public static class Crypto { - public static Tuple HashFile(HashAlgorithm ha, string file) + public static byte[] HashFile(HashAlgorithm ha, string file) { using (var fs = File.OpenRead(file)) { - return new Tuple(ha.ComputeHash(fs), file); - } - } - - public static byte[] GetMd5HashForFile(string file) - { - Argument.IsNotNull(() => file); - - using (var ha = MD5.Create()) - { - return HashFile(ha, file).Item1; - } - } - - public static Tuple[] GetMd5HashForFiles(IEnumerable files) - { - using (var ha = MD5.Create()) - { - return files.Select(file => HashFile(ha, file)).ToArray(); + return ha.ComputeHash(fs); } } } diff --git a/src/GitLink/Pdb/Hex.cs b/src/GitLink/Pdb/Hex.cs deleted file mode 100644 index a6e1070..0000000 --- a/src/GitLink/Pdb/Hex.cs +++ /dev/null @@ -1,36 +0,0 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. -// -// -------------------------------------------------------------------------------------------------------------------- - - -namespace GitLink.Pdb -{ - using System; - using System.Linq; - using Catel; - - public static class Hex - { - public static string Encode(byte[] buffer) - { - Argument.IsNotNullOrEmptyArray(() => buffer); - - return BitConverter.ToString(buffer).Replace("-", string.Empty); - } - - public static byte[] Decode(string hex) - { - if (string.IsNullOrEmpty(hex)) - { - return new byte[0]; - } - - return Enumerable.Range(0, hex.Length) - .Where(x => x % 2 == 0) - .Select(x => Convert.ToByte(hex.Substring(x, 2), 16)) - .ToArray(); - } - } -} \ No newline at end of file From b5a1619d7bc9a14455ebf3d63cb439acd89a6c2b Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 14:29:39 -0700 Subject: [PATCH 27/85] Fix MSBuild task logging --- src/GitLinkTask/GitLink.cs | 52 +++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/src/GitLinkTask/GitLink.cs b/src/GitLinkTask/GitLink.cs index e326f4d..9a62f89 100644 --- a/src/GitLinkTask/GitLink.cs +++ b/src/GitLinkTask/GitLink.cs @@ -8,12 +8,8 @@ namespace GitLinkTask { using System; - using System.IO; - using System.Linq; - using global::GitLink; - using global::GitLink.Pdb; - using global::GitLink.Providers; using Catel.Logging; + using global::GitLink; using Microsoft.Build.Framework; using Microsoft.Build.Utilities; @@ -30,7 +26,7 @@ public class GitLink : Task public override bool Execute() { - LogManager.GetCurrentClassLogger().LogMessage += this.LinkProject_LogMessage; + LogManager.AddListener(new MSBuildListener(this.Log)); var options = new LinkOptions { @@ -43,25 +39,35 @@ public override bool Execute() return success && !this.Log.HasLoggedErrors; } - private void LinkProject_LogMessage(object sender, LogMessageEventArgs e) + private class MSBuildListener : LogListenerBase { - switch (e.LogEvent) + private readonly TaskLoggingHelper log; + + internal MSBuildListener(TaskLoggingHelper log) + { + this.log = log; + } + + protected override void Write(ILog log, string message, LogEvent logEvent, object extraData, LogData logData, DateTime time) { - case LogEvent.Error: - this.Log.LogError(e.Message); - break; - case LogEvent.Warning: - this.Log.LogWarning(e.Message); - break; - case LogEvent.Info: - this.Log.LogMessage(MessageImportance.Normal, e.Message); - break; - case LogEvent.Debug: - this.Log.LogMessage(MessageImportance.Low, e.Message); - break; - default: - break; + switch (logEvent) + { + case LogEvent.Error: + this.log.LogError(message); + break; + case LogEvent.Warning: + this.log.LogWarning(message); + break; + case LogEvent.Info: + this.log.LogMessage(MessageImportance.Normal, message); + break; + case LogEvent.Debug: + this.log.LogMessage(MessageImportance.Low, message); + break; + default: + break; + } } } } -} \ No newline at end of file +} From 4c58992f40ec7eff0c128f59adeb7a8a270c19a8 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 14:38:43 -0700 Subject: [PATCH 28/85] Update copyright and author information --- AUTHORS | 1 + src/GitLink/Linker.cs | 4 ++-- src/GitLinkTask/GitLink.cs | 4 ++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/AUTHORS b/AUTHORS index 9f4ce2d..9d39982 100644 --- a/AUTHORS +++ b/AUTHORS @@ -9,4 +9,5 @@ # Please keep the list sorted. # Please notify the first person on the list to be added here. +Andrew Arnott CatenaLogic diff --git a/src/GitLink/Linker.cs b/src/GitLink/Linker.cs index e9dd466..27c419b 100644 --- a/src/GitLink/Linker.cs +++ b/src/GitLink/Linker.cs @@ -1,6 +1,6 @@ // -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. +// +// Copyright (c) 2016 Andrew Arnott. All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- diff --git a/src/GitLinkTask/GitLink.cs b/src/GitLinkTask/GitLink.cs index 9a62f89..0329303 100644 --- a/src/GitLinkTask/GitLink.cs +++ b/src/GitLinkTask/GitLink.cs @@ -1,6 +1,6 @@ // -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. +// +// Copyright (c) 2016 Andrew Arnott. All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- From 1ca7c68949aa5960ce587a3036196bb5fe8df847 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 18:06:00 -0700 Subject: [PATCH 29/85] Add support for no git repo at all Command line switches can provide all the necessary information. --- src/GitLink/LinkOptions.cs | 4 ++ src/GitLink/Linker.cs | 80 +++++++++++++++++++++++++++++++------- src/GitLink/Program.cs | 11 ++++++ 3 files changed, 80 insertions(+), 15 deletions(-) diff --git a/src/GitLink/LinkOptions.cs b/src/GitLink/LinkOptions.cs index 0a19ab0..3be1788 100644 --- a/src/GitLink/LinkOptions.cs +++ b/src/GitLink/LinkOptions.cs @@ -17,5 +17,9 @@ public struct LinkOptions public bool SkipVerify { get; set; } public Uri GitRemoteUrl { get; set; } + + public string CommitId { get; set; } + + public string GitWorkingDirectory { get; set; } } } diff --git a/src/GitLink/Linker.cs b/src/GitLink/Linker.cs index 27c419b..e654e5a 100644 --- a/src/GitLink/Linker.cs +++ b/src/GitLink/Linker.cs @@ -41,22 +41,37 @@ public static class Linker { sourceFiles = pdb.GetFilesAndChecksums().Keys.ToList(); - repositoryDirectory = GitDirFinder.TreeWalkForGitDir(Path.GetDirectoryName(sourceFiles.First())); - if (repositoryDirectory == null) + if (options.GitWorkingDirectory != null) { - Log.Error("No source files found that are tracked in a git repo."); - return false; + repositoryDirectory = Path.Combine(options.GitWorkingDirectory, ".git"); + } + else + { + repositoryDirectory = GitDirFinder.TreeWalkForGitDir(Path.GetDirectoryName(sourceFiles.First())); + if (repositoryDirectory == null) + { + Log.Error("No source files found that are tracked in a git repo."); + return false; + } } - using (var repository = new Repository(repositoryDirectory)) + string workingDirectory = Path.GetDirectoryName(repositoryDirectory); + + var repository = new Lazy(() => new Repository(repositoryDirectory)); + try { - repoSourceFiles = sourceFiles.ToDictionary(e => e, repository.GetRepoNormalizedPath); + string commitId = options.CommitId ?? repository.Value.Head.Commits.FirstOrDefault()?.Sha; + if (commitId == null) + { + Log.Error("No commit is checked out to HEAD. Have you committed yet?"); + return false; + } var providerManager = new Providers.ProviderManager(); Providers.IProvider provider; if (options.GitRemoteUrl == null) { - var candidateProviders = from remote in repository.Network.Remotes + var candidateProviders = from remote in repository.Value.Network.Remotes let p = providerManager.GetProvider(remote.Url) where p != null select p; @@ -73,6 +88,18 @@ public static class Linker return false; } + try + { + Repository repo = repository.Value; + repoSourceFiles = sourceFiles.ToDictionary(e => e, repo.GetRepoNormalizedPath); + } + catch (RepositoryNotFoundException) + { + // Normalize using file system since we can't find the git repo. + Log.Warning($"Unable to find git repo at \"{options.GitWorkingDirectory}\". Using file system to find canonical capitalization of file paths."); + repoSourceFiles = sourceFiles.ToDictionary(e => e, e => GetNormalizedPath(e, workingDirectory)); + } + if (!options.SkipVerify) { Log.Debug("Verifying pdb file"); @@ -84,14 +111,6 @@ public static class Linker } } - string commitId; - commitId = repository.Head.Commits.FirstOrDefault()?.Sha; - if (commitId == null) - { - Log.Error("No commit is checked out to HEAD. Have you committed yet?"); - return false; - } - string rawUrl = provider.RawGitUrl; if (!rawUrl.Contains("%var2%") && !rawUrl.Contains("{0}")) { @@ -122,6 +141,18 @@ public static class Linker ProjectExtensions.CreateSrcSrv(projectSrcSrvFile, srcSrvContext); } + catch (RepositoryNotFoundException) + { + Log.Error($"Unable to find git repo at \"{options.GitWorkingDirectory}\"."); + return false; + } + finally + { + if (repository.IsValueCreated) + { + repository.Value.Dispose(); + } + } } Log.Debug("Created source server link file, updating pdb file '{0}'", Catel.IO.Path.GetRelativePath(pdbPath, repositoryDirectory)); @@ -131,5 +162,24 @@ public static class Linker return true; } + + private static string GetNormalizedPath(string path, string gitRepoRootDir) + { + Argument.IsNotNullOrEmpty(nameof(path), path); + Argument.IsNotNullOrEmpty(nameof(gitRepoRootDir), gitRepoRootDir); + + string relativePath = Catel.IO.Path.GetRelativePath(path, gitRepoRootDir); + string[] segments = relativePath.Split(new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }); + DirectoryInfo currentDir = new DirectoryInfo(gitRepoRootDir); + for (int i = 0; i < segments.Length; i++) + { + string segment = segments[i]; + var next = currentDir.GetFileSystemInfos(segment).FirstOrDefault(); + segments[i] = next.Name; // get canonical capitalization + currentDir = next as DirectoryInfo; + } + + return Path.Combine(segments); + } } } \ No newline at end of file diff --git a/src/GitLink/Program.cs b/src/GitLink/Program.cs index 91acf4b..be2eb2d 100644 --- a/src/GitLink/Program.cs +++ b/src/GitLink/Program.cs @@ -28,12 +28,16 @@ private static int Main(string[] args) LogManager.AddListener(consoleLogListener); Uri remoteGitUrl = null; + string commitId = null; + string baseDir = null; string pdbPath = null; bool skipVerify = false; bool downloadWithPowershell = false; var arguments = ArgumentSyntax.Parse(args, syntax => { syntax.DefineOption("u|url", ref remoteGitUrl, s => new Uri(s, UriKind.Absolute), "Url to remote git repository."); + syntax.DefineOption("commit", ref commitId, "The git ref to assume all the source code belongs to."); + syntax.DefineOption("baseDir", ref baseDir, "The path to the root of the git repo."); syntax.DefineOption("s|skipVerify", ref skipVerify, "Verify all source files are available in source control."); syntax.DefineOption("p|powershell", ref downloadWithPowershell, "Use an indexing strategy that won't rely on SRCSRV http support, but use a powershell command for URL download instead."); syntax.DefineParameter("pdb", ref pdbPath, "The PDB to add source indexing to."); @@ -42,6 +46,11 @@ private static int Main(string[] args) { syntax.ReportError($"File not found: \"{pdbPath}\""); } + + if (!string.IsNullOrEmpty(baseDir) && !Directory.Exists(baseDir)) + { + syntax.ReportError($"Directory not found: \"{baseDir}\""); + } }); if (string.IsNullOrEmpty(pdbPath)) @@ -53,6 +62,8 @@ private static int Main(string[] args) var options = new LinkOptions { GitRemoteUrl = remoteGitUrl, + GitWorkingDirectory = baseDir != null ? Catel.IO.Path.GetFullPath(baseDir, Environment.CurrentDirectory) : null, + CommitId = commitId, SkipVerify = skipVerify, DownloadWithPowerShell = downloadWithPowershell, }; From 305009eb2f0f3eea209ec667e2bbe13ce3119520 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 18:13:23 -0700 Subject: [PATCH 30/85] Remove unused Authentication class --- src/GitLink/Authentication.cs | 23 ----------------------- src/GitLink/Context.cs | 3 --- src/GitLink/GitLink.csproj | 1 - 3 files changed, 27 deletions(-) delete mode 100644 src/GitLink/Authentication.cs diff --git a/src/GitLink/Authentication.cs b/src/GitLink/Authentication.cs deleted file mode 100644 index 4cb9ce8..0000000 --- a/src/GitLink/Authentication.cs +++ /dev/null @@ -1,23 +0,0 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. -// -// -------------------------------------------------------------------------------------------------------------------- - - -namespace GitLink -{ - using System; - - public class Authentication - { - public Authentication() - { - Username = Environment.GetEnvironmentVariable("GITLINK_REMOTE_USERNAME"); - Password = Environment.GetEnvironmentVariable("GITLINK_REMOTE_PASSWORD"); - } - - public string Password { get; set; } - public string Username { get; set; } - } -} \ No newline at end of file diff --git a/src/GitLink/Context.cs b/src/GitLink/Context.cs index 5679d8a..06b74ef 100644 --- a/src/GitLink/Context.cs +++ b/src/GitLink/Context.cs @@ -28,7 +28,6 @@ public Context(IProviderManager providerManager) _providerManager = providerManager; - Authentication = new Authentication(); ConfigurationName = "Release"; PlatformName = "AnyCPU"; IncludedProjects = new List(); @@ -58,8 +57,6 @@ public string SolutionDirectory public string PlatformName { get; set; } - public Authentication Authentication { get; private set; } - public IProvider Provider { get diff --git a/src/GitLink/GitLink.csproj b/src/GitLink/GitLink.csproj index 0cf7968..3e70720 100644 --- a/src/GitLink/GitLink.csproj +++ b/src/GitLink/GitLink.csproj @@ -59,7 +59,6 @@ - From 107a1ccec77cdf625350ccb973a66c421012f992 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 18:15:23 -0700 Subject: [PATCH 31/85] Remove dead code --- src/GitLink.Tests/ContextFacts.cs | 95 ------------- .../Extensions/ContextExtensionsFacts.cs | 45 ------ src/GitLink.Tests/GitLink.Tests.csproj | 2 - .../IntegrationTests/IntegrationTestBase.cs | 7 - src/GitLink/Constants.cs | 10 -- src/GitLink/Context.cs | 134 ------------------ src/GitLink/Extensions/ContextExtensions.cs | 23 --- src/GitLink/GitLink.csproj | 3 - src/GitLink/Providers/Interfaces/IProvider.cs | 2 - src/GitLink/Providers/ProviderBase.cs | 45 ------ 10 files changed, 366 deletions(-) delete mode 100644 src/GitLink.Tests/ContextFacts.cs delete mode 100644 src/GitLink.Tests/Extensions/ContextExtensionsFacts.cs delete mode 100644 src/GitLink/Constants.cs delete mode 100644 src/GitLink/Context.cs delete mode 100644 src/GitLink/Extensions/ContextExtensions.cs diff --git a/src/GitLink.Tests/ContextFacts.cs b/src/GitLink.Tests/ContextFacts.cs deleted file mode 100644 index e5a771e..0000000 --- a/src/GitLink.Tests/ContextFacts.cs +++ /dev/null @@ -1,95 +0,0 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. -// -// -------------------------------------------------------------------------------------------------------------------- - - -namespace GitLink.Tests -{ - using System; - using Catel.Test; - using GitLink.Providers; - using NUnit.Framework; - - public class ContextFacts - { - [TestFixture] - public class TheDefaultValues - { - [TestCase] - public void SetsRightDefaultValues() - { - var context = new Context(new ProviderManager()); - - Assert.AreEqual("Release", context.ConfigurationName); - Assert.IsFalse(context.IsHelp); - } - } - - [TestFixture] - public class TheValidateContextMethod - { - [TestCase] - public void ThrowsExceptionForMissingSolutionDirectory() - { - var context = new Context(new ProviderManager()); - - ExceptionTester.CallMethodAndExpectException(() => context.ValidateContext()); - } - - [TestCase] - public void ThrowsExceptionForMissingConfigurationName() - { - var context = new Context(new ProviderManager()) - { - SolutionDirectory = @"c:\source\GitLink", - ConfigurationName = string.Empty - }; - - ExceptionTester.CallMethodAndExpectException(() => context.ValidateContext()); - } - - [TestCase] - public void ThrowsExceptionForMissingTargetUrl() - { - var context = new Context(new ProviderManager()) - { - SolutionDirectory = @"c:\source\GitLink", - }; - - ExceptionTester.CallMethodAndExpectException(() => context.ValidateContext()); - } - - [TestCase] - public void SucceedsForValidContext() - { - var context = new Context(new ProviderManager()) - { - SolutionDirectory = @"c:\source\GitLink", - TargetUrl = "https://github.com/CatenaLogic/GitLink", - }; - - // should not throw - context.ValidateContext(); - } - - [TestCase(@".", @"c:\")] - [TestCase(@"C:\MyDirectory\", @"C:\MyDirectory\")] - [TestCase(@"C:\MyDirectory\..", @"C:\")] - [TestCase(@"C:\MyDirectory\..\TestDirectory\", @"C:\TestDirectory\")] - public void ExpandsDirectoryIfRequired(string solutionDirectory, string expectedValue) - { - Environment.CurrentDirectory = @"c:\"; - - var context = new Context(new ProviderManager()); - context.TargetUrl = "https://github.com/CatenaLogic/GitLink.git"; - context.SolutionDirectory = solutionDirectory; - - context.ValidateContext(); - - Assert.AreEqual(expectedValue, context.SolutionDirectory); - } - } - } -} \ No newline at end of file diff --git a/src/GitLink.Tests/Extensions/ContextExtensionsFacts.cs b/src/GitLink.Tests/Extensions/ContextExtensionsFacts.cs deleted file mode 100644 index 2e6222c..0000000 --- a/src/GitLink.Tests/Extensions/ContextExtensionsFacts.cs +++ /dev/null @@ -1,45 +0,0 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. -// -// -------------------------------------------------------------------------------------------------------------------- - - -namespace GitLink.Tests.Extensions -{ - using GitLink.Providers; - using NUnit.Framework; - - public class ContextExtensionsFacts - { - [TestFixture] - public class TheGetRelativePathMethod - { - [TestCase] - public void ReturnsRelativePathWithDirectoryDownwards() - { - var context = new Context(new ProviderManager()) - { - SolutionDirectory = @"c:\source\GitLink" - }; - - var relativePath = context.GetRelativePath(@"c:\source\GitLink\src\subdir1\somefile.cs"); - - Assert.AreEqual(@"src\subdir1\somefile.cs", relativePath); - } - - [TestCase] - public void ReturnsRelativePathWithDirectoryUpwards() - { - var context = new Context(new ProviderManager()) - { - SolutionDirectory = @"c:\source\GitLink" - }; - - var relativePath = context.GetRelativePath(@"c:\source\catel\src\subdir1\somefile.cs"); - - Assert.AreEqual(@"..\catel\src\subdir1\somefile.cs", relativePath); - } - } - } -} \ No newline at end of file diff --git a/src/GitLink.Tests/GitLink.Tests.csproj b/src/GitLink.Tests/GitLink.Tests.csproj index 3de4029..4f8904d 100644 --- a/src/GitLink.Tests/GitLink.Tests.csproj +++ b/src/GitLink.Tests/GitLink.Tests.csproj @@ -51,8 +51,6 @@ - - diff --git a/src/GitLink.Tests/IntegrationTests/IntegrationTestBase.cs b/src/GitLink.Tests/IntegrationTests/IntegrationTestBase.cs index b2335df..de6b421 100644 --- a/src/GitLink.Tests/IntegrationTests/IntegrationTestBase.cs +++ b/src/GitLink.Tests/IntegrationTests/IntegrationTestBase.cs @@ -55,13 +55,6 @@ protected int RunGitLink(string directory, string repositoryUrl, string branchNa { PrepareTestSolution(directory, configurationName); - var context = new Context(new ProviderManager()) - { - SolutionDirectory = directory, - TargetUrl = repositoryUrl, - TargetBranch = branchName - }; - throw new NotImplementedException(); } diff --git a/src/GitLink/Constants.cs b/src/GitLink/Constants.cs deleted file mode 100644 index bf3a0a5..0000000 --- a/src/GitLink/Constants.cs +++ /dev/null @@ -1,10 +0,0 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) 2014 - 2015 CatenaLogic. All rights reserved. -// -// -------------------------------------------------------------------------------------------------------------------- - - -namespace GitLink -{ -} \ No newline at end of file diff --git a/src/GitLink/Context.cs b/src/GitLink/Context.cs deleted file mode 100644 index 06b74ef..0000000 --- a/src/GitLink/Context.cs +++ /dev/null @@ -1,134 +0,0 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. -// -// -------------------------------------------------------------------------------------------------------------------- - - -namespace GitLink -{ - using System; - using System.Collections.Generic; - using Catel; - using Catel.IO; - using Catel.Logging; - using GitTools; - using Providers; - - public class Context : RepositoryContext - { - private static readonly ILog Log = LogManager.GetCurrentClassLogger(); - - private readonly IProviderManager _providerManager; - private IProvider _provider; - - public Context(IProviderManager providerManager) - { - Argument.IsNotNull(() => providerManager); - - _providerManager = providerManager; - - ConfigurationName = "Release"; - PlatformName = "AnyCPU"; - IncludedProjects = new List(); - IgnoredProjects = new List(); - } - - public bool DownloadWithPowershell { get; set; } - - public bool IsHelp { get; set; } - - public bool IsDebug { get; set; } - - public bool ErrorsAsWarnings { get; set; } - - public bool SkipVerify { get; set; } - - public string LogFile { get; set; } - - //[Obsolete("Use 'Directory' instead")] - public string SolutionDirectory - { - get { return Directory; } - set { Directory = value; } - } - - public string ConfigurationName { get; set; } - - public string PlatformName { get; set; } - - public IProvider Provider - { - get - { - if (_provider == null) - { - _provider = _providerManager.GetProvider(TargetUrl); - } - - return _provider; - } - set - { - _provider = value; - } - } - - //[Obsolete("Use 'Url' instead")] - public string TargetUrl - { - get { return Url; } - set { Url = value; } - } - - //[Obsolete("Use 'Branch' instead")] - public string TargetBranch - { - get { return Branch; } - set { Branch = value; } - } - - public string ShaHash { get; set; } - - public string SolutionFile { get; set; } - - public List IncludedProjects { get; private set; } - - public List IgnoredProjects { get; private set; } - - public string PdbFilesDirectory { get; set; } - - public void ValidateContext() - { - if (!string.IsNullOrWhiteSpace(SolutionDirectory)) - { - SolutionDirectory = Path.GetFullPath(SolutionDirectory, Environment.CurrentDirectory); - } - - if (string.IsNullOrEmpty(SolutionDirectory)) - { - throw Log.ErrorAndCreateException("Solution directory is missing"); - } - - if (string.IsNullOrEmpty(ConfigurationName)) - { - throw Log.ErrorAndCreateException("Configuration name is missing"); - } - - if (string.IsNullOrEmpty(PlatformName)) - { - throw Log.ErrorAndCreateException("Platform name is missing"); - } - - if (string.IsNullOrEmpty(TargetUrl)) - { - throw Log.ErrorAndCreateException("Target url is missing"); - } - - if (Provider == null) - { - throw Log.ErrorAndCreateException("Cannot determine git provider"); - } - } - } -} \ No newline at end of file diff --git a/src/GitLink/Extensions/ContextExtensions.cs b/src/GitLink/Extensions/ContextExtensions.cs deleted file mode 100644 index 94a2d82..0000000 --- a/src/GitLink/Extensions/ContextExtensions.cs +++ /dev/null @@ -1,23 +0,0 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. -// -// -------------------------------------------------------------------------------------------------------------------- - - -namespace GitLink -{ - using Catel; - using Catel.IO; - - public static class ContextExtensions - { - public static string GetRelativePath(this Context context, string fullPath) - { - Argument.IsNotNull(() => context); - Argument.IsNotNull(() => fullPath); - - return Path.GetRelativePath(fullPath, context.SolutionDirectory); - } - } -} \ No newline at end of file diff --git a/src/GitLink/GitLink.csproj b/src/GitLink/GitLink.csproj index 3e70720..e0c75fe 100644 --- a/src/GitLink/GitLink.csproj +++ b/src/GitLink/GitLink.csproj @@ -57,9 +57,7 @@ - - @@ -84,7 +82,6 @@ - diff --git a/src/GitLink/Providers/Interfaces/IProvider.cs b/src/GitLink/Providers/Interfaces/IProvider.cs index 9ec44e2..63b2ffb 100644 --- a/src/GitLink/Providers/Interfaces/IProvider.cs +++ b/src/GitLink/Providers/Interfaces/IProvider.cs @@ -42,7 +42,5 @@ public interface IProvider string RawGitUrl { get; } bool Initialize(string url); - - string GetShaHashOfCurrentBranch(Context context, TemporaryFilesContext temporaryFilesContext); } } \ No newline at end of file diff --git a/src/GitLink/Providers/ProviderBase.cs b/src/GitLink/Providers/ProviderBase.cs index 32176ab..c0be2de 100644 --- a/src/GitLink/Providers/ProviderBase.cs +++ b/src/GitLink/Providers/ProviderBase.cs @@ -60,50 +60,5 @@ protected ProviderBase(IRepositoryPreparer repositoryPreparer) public abstract string RawGitUrl { get; } public abstract bool Initialize(string url); - - public string GetShaHashOfCurrentBranch(Context context, TemporaryFilesContext temporaryFilesContext) - { - Argument.IsNotNull(() => context); - - string commitSha = null; - var repositoryDirectory = context.SolutionDirectory; - - if (_repositoryPreparer.IsPreparationRequired(context)) - { - Log.Info("No local repository is found in '{0}', creating a temporary one", repositoryDirectory); - - repositoryDirectory = _repositoryPreparer.Prepare(context, temporaryFilesContext); - } - - repositoryDirectory = GitDirFinder.TreeWalkForGitDir(repositoryDirectory); - - using (var repository = new Repository(repositoryDirectory)) - { - if (string.IsNullOrEmpty(context.ShaHash)) - { - Log.Info("No sha hash is available on the context, retrieving latest commit of current branch"); - - var lastCommit = repository.Commits.First(); - commitSha = lastCommit.Sha; - } - else - { - Log.Info("Checking if commit with sha hash '{0}' exists on the repository", context.ShaHash); - - var commit = repository.Commits.FirstOrDefault(c => string.Equals(c.Sha, context.ShaHash, StringComparison.OrdinalIgnoreCase)); - if (commit != null) - { - commitSha = commit.Sha; - } - } - } - - if (commitSha == null) - { - throw Log.ErrorAndCreateException("Cannot find commit '{0}' in repo.", context.ShaHash); - } - - return commitSha; - } } } \ No newline at end of file From 970cf9a6dc28de19ed645349d3b7370537b66d1f Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 18:24:30 -0700 Subject: [PATCH 32/85] Delete dead code --- src/GitLink.Tests/GitLink.Tests.csproj | 2 - src/GitLink.Tests/ProjectExtensionsFacts.cs | 52 ------ src/GitLink.Tests/ProjectHelperFacts.cs | 64 ------- .../IProjectConfigurationInSolution.cs | 7 - .../Interfaces/IProjectInSolution.cs | 9 - .../Interfaces/ISolutionParser.cs | 11 -- src/GitLink/Extensions/ProjectExtensions.cs | 174 ------------------ .../Extensions/ProjectItemExtensions.cs | 32 ---- src/GitLink/GitLink.csproj | 7 - src/GitLink/Helpers/ProjectHelper.cs | 158 ---------------- src/GitLink/Linker.cs | 19 +- 11 files changed, 18 insertions(+), 517 deletions(-) delete mode 100644 src/GitLink.Tests/ProjectExtensionsFacts.cs delete mode 100644 src/GitLink.Tests/ProjectHelperFacts.cs delete mode 100644 src/GitLink/Build/Construction/Interfaces/IProjectConfigurationInSolution.cs delete mode 100644 src/GitLink/Build/Construction/Interfaces/IProjectInSolution.cs delete mode 100644 src/GitLink/Build/Construction/Interfaces/ISolutionParser.cs delete mode 100644 src/GitLink/Extensions/ProjectExtensions.cs delete mode 100644 src/GitLink/Extensions/ProjectItemExtensions.cs delete mode 100644 src/GitLink/Helpers/ProjectHelper.cs diff --git a/src/GitLink.Tests/GitLink.Tests.csproj b/src/GitLink.Tests/GitLink.Tests.csproj index 4f8904d..7f3e92d 100644 --- a/src/GitLink.Tests/GitLink.Tests.csproj +++ b/src/GitLink.Tests/GitLink.Tests.csproj @@ -54,8 +54,6 @@ - - diff --git a/src/GitLink.Tests/ProjectExtensionsFacts.cs b/src/GitLink.Tests/ProjectExtensionsFacts.cs deleted file mode 100644 index 5a68e51..0000000 --- a/src/GitLink.Tests/ProjectExtensionsFacts.cs +++ /dev/null @@ -1,52 +0,0 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. -// -// -------------------------------------------------------------------------------------------------------------------- -namespace GitLink.Tests -{ - using NUnit.Framework; - - [TestFixture] - public class ProjectExtensionsFacts - { - [Test] - public void NoIncludesExcludes_ProjectNotIgnored() - { - Assert.IsFalse(ProjectHelper.ShouldBeIgnored("project", new string[0], new string[0])); - } - - [TestCase("ignoredProject", "ignoredProject", true)] - [TestCase("ignoredProject", "ignoredproject", true)] - [TestCase("ignoredProject", "/ignoredProject/", true)] - [TestCase("ignoredProject", "/ignoredproject/", true)] - [TestCase("ignoredProject", "/^i\\w+t$/", true)] - [TestCase("nonIgnoredProject", "ignoredProject", false)] - public void ExcludedProject_IgnoredOnlySpecifiedOne(string projectName, string ignorePattern, bool expectedIgnore) - { - Assert.AreEqual(expectedIgnore, ProjectHelper.ShouldBeIgnored(projectName, new string[0], new[] { ignorePattern })); - } - - [TestCase("anotherProject", "includedProject", true)] - [TestCase("anotherProject", "includedproject", true)] - [TestCase("anotherProject", "/includedProject/", true)] - [TestCase("anotherProject", "/includedproject/", true)] - [TestCase("anotherProject", "/[a-z]+/", false)] - [TestCase("includedProject", "includedProject", false)] - public void ExplicitlyIncludedProject_OthersAreIgnored(string projectName, string includePattern, bool expectedIgnore) - { - Assert.AreEqual(expectedIgnore, ProjectHelper.ShouldBeIgnored(projectName, new[] { includePattern }, new string[0])); - } - - [TestCase("excludedProject", true)] - [TestCase("includedProject", false)] - [TestCase("includedAndExcludedProject", true)] - [TestCase("notIncludedNorExcludedProject", true)] - public void BothIncludedAndExcludedProjects(string projectName, bool expectedIgnore) - { - Assert.AreEqual(expectedIgnore, ProjectHelper.ShouldBeIgnored(projectName, - new[] { "includedProject", "includedAndExcludedProject" }, - new[] { "excludedProject", "includedAndExcludedProject" })); - } - } -} \ No newline at end of file diff --git a/src/GitLink.Tests/ProjectHelperFacts.cs b/src/GitLink.Tests/ProjectHelperFacts.cs deleted file mode 100644 index 80dd7bb..0000000 --- a/src/GitLink.Tests/ProjectHelperFacts.cs +++ /dev/null @@ -1,64 +0,0 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. -// -// -------------------------------------------------------------------------------------------------------------------- -namespace GitLink.Tests -{ - using System.IO; - using System.Linq; - using System.Reflection; - using NUnit.Framework; - - [TestFixture] - public class ProjectHelperFacts - { - private static readonly string SolutionFile = Path.Combine( - Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), - @"TestSolution\TestSolution.sln"); - - [Test] - public void GettingProjectsFromSolution() - { - Assert.AreEqual(3, ProjectHelper.GetProjects(SolutionFile, Configuration.Debug, Platform.AnyCpu).Count()); - } - - [Test] - public void GettingProjectsFromSolution_SkipsProjectNotSelectedForPlatform() - { - var projects = ProjectHelper.GetProjects(SolutionFile, Configuration.Debug, Platform.x64).ToList(); - - Assert.AreEqual(2, projects.Count); - Assert.False(projects.Any(c => c.GetProjectName() == "BuiltInAnyCpuOnly")); - } - - [Test] - public void GettingProjectsFromSolution_SkipsProjectNotSelectedForConfiguration() - { - var projects = ProjectHelper.GetProjects(SolutionFile, Configuration.Release, Platform.AnyCpu).ToList(); - - Assert.AreEqual(2, projects.Count); - Assert.False(projects.Any(c => c.GetProjectName() == "BuiltInDebugOnly")); - } - - [Test] - public void GettingProjectsFromSolution_SkipsProjectNotSelectedForEitherConfigurationOrPlatform() - { - var project = ProjectHelper.GetProjects(SolutionFile, Configuration.Release, Platform.x64).Single(); - - Assert.AreEqual("BuiltAlways", project.GetProjectName()); - } - - private static class Configuration - { - public const string Debug = "Debug"; - public const string Release = "Release"; - } - - private static class Platform - { - public const string AnyCpu = "Any CPU"; - public const string x64 = "x64"; - } - } -} \ No newline at end of file diff --git a/src/GitLink/Build/Construction/Interfaces/IProjectConfigurationInSolution.cs b/src/GitLink/Build/Construction/Interfaces/IProjectConfigurationInSolution.cs deleted file mode 100644 index 0422e46..0000000 --- a/src/GitLink/Build/Construction/Interfaces/IProjectConfigurationInSolution.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace GitLink.Build.Construction -{ - public interface IProjectConfigurationInSolution - { - bool IncludeInBuild { get; } - } -} \ No newline at end of file diff --git a/src/GitLink/Build/Construction/Interfaces/IProjectInSolution.cs b/src/GitLink/Build/Construction/Interfaces/IProjectInSolution.cs deleted file mode 100644 index 6dfb9ee..0000000 --- a/src/GitLink/Build/Construction/Interfaces/IProjectInSolution.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace GitLink.Build.Construction -{ - public interface IProjectInSolution - { - object ProjectType { get; } - string RelativePath { get; } - object ProjectConfigurations { get; } - } -} \ No newline at end of file diff --git a/src/GitLink/Build/Construction/Interfaces/ISolutionParser.cs b/src/GitLink/Build/Construction/Interfaces/ISolutionParser.cs deleted file mode 100644 index 371cbc4..0000000 --- a/src/GitLink/Build/Construction/Interfaces/ISolutionParser.cs +++ /dev/null @@ -1,11 +0,0 @@ -namespace GitLink.Build.Construction -{ - using System.IO; - - public interface ISolutionParser - { - StreamReader SolutionReader { get; set; } - object[] Projects { get; } - void ParseSolution(); - } -} \ No newline at end of file diff --git a/src/GitLink/Extensions/ProjectExtensions.cs b/src/GitLink/Extensions/ProjectExtensions.cs deleted file mode 100644 index de28997..0000000 --- a/src/GitLink/Extensions/ProjectExtensions.cs +++ /dev/null @@ -1,174 +0,0 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. -// -// -------------------------------------------------------------------------------------------------------------------- - - -namespace GitLink -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using Catel; - using Catel.Logging; - using Microsoft.Build.Evaluation; - using Pdb; - - public static class ProjectExtensions - { - private static readonly ILog Log = LogManager.GetCurrentClassLogger(); - - public static string GetProjectName(this Project project) - { - Argument.IsNotNull(() => project); - - var projectName = project.GetPropertyValue("MSBuildProjectName"); - return projectName ?? Path.GetFileName(project.FullPath); - } - - public static void CreateSrcSrv(this Project project, SrcSrvContext srcSrvContext) - { - Argument.IsNotNull(() => project); - Argument.IsNotNull(() => srcSrvContext); - Argument.IsNotNullOrWhitespace(() => srcSrvContext.RawUrl); - Argument.IsNotNullOrWhitespace(() => srcSrvContext.Revision); - - var srcsrvFile = GetOutputSrcSrvFile(project); - - CreateSrcSrv(srcsrvFile, srcSrvContext); - } - - public static void CreateSrcSrv(string srcsrvFile, SrcSrvContext srcSrvContext) - { - Argument.IsNotNull(() => srcSrvContext); - Argument.IsNotNullOrWhitespace(() => srcSrvContext.RawUrl); - Argument.IsNotNullOrWhitespace(() => srcSrvContext.Revision); - Argument.IsNotNullOrWhitespace(() => srcsrvFile); - - if (srcSrvContext.VstsData.Count != 0) - { - File.WriteAllBytes(srcsrvFile, SrcSrv.CreateVsts(srcSrvContext.Revision, srcSrvContext.Paths, srcSrvContext.VstsData)); - } - else - { - File.WriteAllBytes(srcsrvFile, SrcSrv.Create(srcSrvContext.RawUrl, srcSrvContext.Revision, srcSrvContext.Paths, srcSrvContext.DownloadWithPowershell)); - } - } - - public static IEnumerable GetCompilableItems(this Project project) - { - Argument.IsNotNull(() => project); - - return project.Items.Where(x => string.Equals(x.ItemType, "Compile") || string.Equals(x.ItemType, "ClCompile") || string.Equals(x.ItemType, "ClInclude")); - } - - 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.GetPropertyValue("PdbFile"); - - return outputFile; - } - - public static string GetOutputFile(this Project project) - { - Argument.IsNotNull(() => project); - - var targetDirectory = GetTargetDirectory(project); - var targetFileName = GetTargetFileName(project); - - var outputFile = Path.Combine(targetDirectory, targetFileName); - outputFile = Path.GetFullPath(outputFile); - - return outputFile; - } - - public static string GetTargetDirectory(this Project project) - { - Argument.IsNotNull(() => project); - - var targetDirectory = project.GetPropertyValue("TargetDir"); - if (string.IsNullOrWhiteSpace(targetDirectory)) - { - var relativeTargetDirectory = GetRelativeTargetDirectory(project); - targetDirectory = Path.Combine(project.DirectoryPath, relativeTargetDirectory); - } - - return targetDirectory; - } - - public static string GetRelativeTargetDirectory(this Project project) - { - Argument.IsNotNull(() => project); - - var projectOutputDirectory = project.GetPropertyValue("OutputPath"); - if (string.IsNullOrWhiteSpace(projectOutputDirectory)) - { - projectOutputDirectory = project.GetPropertyValue("OutDir"); - } - - return projectOutputDirectory; - } - - public static string GetTargetFileName(this Project project) - { - Argument.IsNotNull(() => project); - - var targetFileName = project.GetPropertyValue("TargetFileName"); - if (string.IsNullOrWhiteSpace(targetFileName)) - { - var assemblyName = project.GetPropertyValue("AssemblyName"); - var extension = GetTargetExtension(project); - - targetFileName = string.Format("{0}{1}", assemblyName, extension); - } - - return targetFileName; - } - - public static string GetTargetExtension(this Project project) - { - Argument.IsNotNull(() => project); - - var extension = project.GetPropertyValue("TargetExt"); - if (string.IsNullOrWhiteSpace(extension)) - { - extension = ".dll"; - - var outputType = project.GetPropertyValue("OutputType").ToLower(); - if (outputType.Contains("exe") || outputType.Contains("winexe")) - { - extension = ".exe"; - } - } - - return extension; - } - - public static void DumpProperties(this Project project) - { - Log.Debug(""); - Log.Debug("Properties for project '{0}'", project.FullPath); - Log.Debug("-----------------------------------------------------------"); - - foreach (var property in project.Properties) - { - Log.Debug(" {0} => {1} ({2})", property.Name, property.EvaluatedValue, property.UnevaluatedValue); - } - - Log.Debug(""); - Log.Debug(""); - } - } -} \ No newline at end of file diff --git a/src/GitLink/Extensions/ProjectItemExtensions.cs b/src/GitLink/Extensions/ProjectItemExtensions.cs deleted file mode 100644 index 38393e6..0000000 --- a/src/GitLink/Extensions/ProjectItemExtensions.cs +++ /dev/null @@ -1,32 +0,0 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. -// -// -------------------------------------------------------------------------------------------------------------------- - - -namespace GitLink -{ - 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/GitLink/GitLink.csproj b/src/GitLink/GitLink.csproj index e0c75fe..ca9e2d5 100644 --- a/src/GitLink/GitLink.csproj +++ b/src/GitLink/GitLink.csproj @@ -59,16 +59,10 @@ - - - - - - @@ -106,7 +100,6 @@ - \ No newline at end of file diff --git a/src/GitLink/Helpers/ProjectHelper.cs b/src/GitLink/Helpers/ProjectHelper.cs deleted file mode 100644 index 2afd991..0000000 --- a/src/GitLink/Helpers/ProjectHelper.cs +++ /dev/null @@ -1,158 +0,0 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. -// -// -------------------------------------------------------------------------------------------------------------------- - - -namespace GitLink -{ - using System; - using System.Collections; - using System.Collections.Generic; - using System.Linq; - using Catel; - using Catel.Logging; - using Catel.Reflection; - using Microsoft.Build.Evaluation; - using System.IO; - using System.Text.RegularExpressions; - using Build.Construction; - using ImpromptuInterface; - - public static class ProjectHelper - { - private static readonly ILog Log = LogManager.GetCurrentClassLogger(); - - private static readonly Type SolutionParserType; - private static readonly object KnownToBeMsBuildFormat; - - static ProjectHelper() - { - SolutionParserType = TypeCache.GetType("Microsoft.Build.Construction.SolutionParser, Microsoft.Build, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"); - - var solutionProjectTypeType = TypeCache.GetType("Microsoft.Build.Construction.SolutionProjectType"); - if (solutionProjectTypeType != null) - { - KnownToBeMsBuildFormat = Enum.Parse(solutionProjectTypeType, "KnownToBeMSBuildFormat"); - } - } - - public static IEnumerable GetProjects(string solutionFile, string configurationName, string platformName) - { - var projects = new List(); - var solutionParser = ((object)Impromptu.InvokeConstructor(SolutionParserType)).ActLike(); - - using (var streamReader = new StreamReader(solutionFile)) - { - solutionParser.SolutionReader = streamReader; - solutionParser.ParseSolution(); - var solutionDirectory = Path.GetDirectoryName(solutionFile); - var projectsInSolution = solutionParser.Projects.AllActLike(); - foreach (var projectInSolution in projectsInSolution) - { - var isKnownToBeMsBuildFormat = ObjectHelper.AreEqual(projectInSolution.ProjectType, KnownToBeMsBuildFormat); - var isSelectedForBuild = ProjectIsSelectedForBuild(projectInSolution, configurationName, platformName); - if (!isKnownToBeMsBuildFormat || !isSelectedForBuild) - { - continue; - } - - var relativePath = projectInSolution.RelativePath; - var projectFile = Path.Combine(solutionDirectory, relativePath); - - var project = LoadProject(projectFile, configurationName, platformName, solutionDirectory); - if (project != null) - { - projects.Add(project); - } - } - } - - return projects; - } - - public static Project LoadProject(string projectFile, string configurationName, string platformName, string solutionDirectory) - { - Argument.IsNotNullOrWhitespace(() => projectFile); - Argument.IsNotNullOrWhitespace(() => configurationName); - Argument.IsNotNullOrWhitespace(() => platformName); - Argument.IsNotNullOrWhitespace(() => solutionDirectory); - - if (!solutionDirectory.EndsWith(@"\")) - { - solutionDirectory += @"\"; - } - - try - { - var collections = new Dictionary(); - collections["Configuration"] = configurationName; - collections["Platform"] = platformName; - collections["SolutionDir"] = solutionDirectory; - - var project = new Project(projectFile, collections, null); - return project; - } - catch (Exception ex) - { - Log.Warning("Failed to load project '{0}': {1}", projectFile, ex.Message); - return null; - } - } - - public static bool ShouldBeIgnored(string projectName, ICollection projectsToInclude, ICollection projectsToIgnore) - { - Argument.IsNotNull(() => projectName); - - if (projectsToIgnore.Any(projectToIgnore => ProjectNameMatchesPattern(projectName, projectToIgnore))) - { - return true; - } - - if (projectsToInclude.Count == 0) - { - return false; - } - - if (projectsToInclude.All(projectToInclude => !ProjectNameMatchesPattern(projectName, projectToInclude))) - { - return true; - } - - return false; - } - - // pattern may be either a literal string, and then we'll be comparing literally ignoring case - // or it can be a regex enclosed in slashes like /this-is-my-regex/ - private static bool ProjectNameMatchesPattern(string projectName, string pattern) - { - Argument.IsNotNull(() => pattern); - - if (pattern.Length > 2 && pattern.StartsWith("/") && pattern.EndsWith("/")) - { - var ignoreRegex = new Regex(pattern.Substring(1, pattern.Length - 2), RegexOptions.IgnoreCase); - if (ignoreRegex.IsMatch(projectName)) - { - return true; - } - } - return string.Equals(projectName, pattern, StringComparison.InvariantCultureIgnoreCase); - } - - private static bool ProjectIsSelectedForBuild(IProjectInSolution project, string configurationName, string platformName) - { - var configurationPlatformKey = configurationName + "|" + platformName; - - var configurationsDictionary = (IDictionary)project.ProjectConfigurations; - if (configurationsDictionary.Contains(configurationPlatformKey)) - { - var cis = configurationsDictionary[configurationPlatformKey].ActLike(); - - return cis.IncludeInBuild; - } - - return true; - } - } -} \ No newline at end of file diff --git a/src/GitLink/Linker.cs b/src/GitLink/Linker.cs index e654e5a..98474ac 100644 --- a/src/GitLink/Linker.cs +++ b/src/GitLink/Linker.cs @@ -139,7 +139,7 @@ public static class Linker srcSrvContext.VstsData["TFS_REPO"] = provider.ProjectName; } - ProjectExtensions.CreateSrcSrv(projectSrcSrvFile, srcSrvContext); + CreateSrcSrv(projectSrcSrvFile, srcSrvContext); } catch (RepositoryNotFoundException) { @@ -163,6 +163,23 @@ public static class Linker return true; } + private static void CreateSrcSrv(string srcsrvFile, SrcSrvContext srcSrvContext) + { + Argument.IsNotNull(() => srcSrvContext); + Argument.IsNotNullOrWhitespace(() => srcSrvContext.RawUrl); + Argument.IsNotNullOrWhitespace(() => srcSrvContext.Revision); + Argument.IsNotNullOrWhitespace(() => srcsrvFile); + + if (srcSrvContext.VstsData.Count != 0) + { + File.WriteAllBytes(srcsrvFile, SrcSrv.CreateVsts(srcSrvContext.Revision, srcSrvContext.Paths, srcSrvContext.VstsData)); + } + else + { + File.WriteAllBytes(srcsrvFile, SrcSrv.Create(srcSrvContext.RawUrl, srcSrvContext.Revision, srcSrvContext.Paths, srcSrvContext.DownloadWithPowershell)); + } + } + private static string GetNormalizedPath(string path, string gitRepoRootDir) { Argument.IsNotNullOrEmpty(nameof(path), path); From df1372a04a45c4fb28162332775169ae9f037e2f Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 19:38:15 -0700 Subject: [PATCH 33/85] Express poweshell option as one in an enum --- src/GitLink/GitLink.csproj | 1 + src/GitLink/LinkMethod.cs | 24 ++++++++++++++++++++++++ src/GitLink/LinkOptions.cs | 6 +----- src/GitLink/Linker.cs | 2 +- src/GitLink/Program.cs | 6 +++--- src/GitLinkTask/GitLink.cs | 10 ++++++++-- src/GitLinkTask/GitLink.targets | 2 +- 7 files changed, 39 insertions(+), 12 deletions(-) create mode 100644 src/GitLink/LinkMethod.cs diff --git a/src/GitLink/GitLink.csproj b/src/GitLink/GitLink.csproj index ca9e2d5..a84853d 100644 --- a/src/GitLink/GitLink.csproj +++ b/src/GitLink/GitLink.csproj @@ -64,6 +64,7 @@ + diff --git a/src/GitLink/LinkMethod.cs b/src/GitLink/LinkMethod.cs new file mode 100644 index 0000000..1fc1cf5 --- /dev/null +++ b/src/GitLink/LinkMethod.cs @@ -0,0 +1,24 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2016 Andrew Arnott. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace GitLink +{ + /// + /// The styles of operations a SRCSRV can perform to retrieve the source code. + /// + public enum LinkMethod + { + /// + /// SRCSRV downloads from a web URL directly. + /// + Http, + + /// + /// Use an indexing strategy that won't rely on SRCSRV http support, but use a powershell command for URL download instead. + /// + Powershell, + } +} diff --git a/src/GitLink/LinkOptions.cs b/src/GitLink/LinkOptions.cs index 3be1788..92e5fe8 100644 --- a/src/GitLink/LinkOptions.cs +++ b/src/GitLink/LinkOptions.cs @@ -8,11 +8,7 @@ public struct LinkOptions { - /// - /// Gets or sets a value indicating whether the source should be downloaded with - /// powershell instead of simple HTTP(S) URLs. - /// - public bool DownloadWithPowerShell { get; set; } + public LinkMethod Method { get; set; } public bool SkipVerify { get; set; } diff --git a/src/GitLink/Linker.cs b/src/GitLink/Linker.cs index 98474ac..c64f208 100644 --- a/src/GitLink/Linker.cs +++ b/src/GitLink/Linker.cs @@ -120,7 +120,7 @@ public static class Linker var srcSrvContext = new SrcSrvContext { RawUrl = rawUrl, - DownloadWithPowershell = options.DownloadWithPowerShell, + DownloadWithPowershell = options.Method == LinkMethod.Powershell, Revision = commitId, }; foreach (var sourceFile in repoSourceFiles) diff --git a/src/GitLink/Program.cs b/src/GitLink/Program.cs index be2eb2d..b023f77 100644 --- a/src/GitLink/Program.cs +++ b/src/GitLink/Program.cs @@ -32,14 +32,14 @@ private static int Main(string[] args) string baseDir = null; string pdbPath = null; bool skipVerify = false; - bool downloadWithPowershell = false; + LinkMethod method = LinkMethod.Http; var arguments = ArgumentSyntax.Parse(args, syntax => { + syntax.DefineOption("m|method", ref method, v => (LinkMethod)Enum.Parse(typeof(LinkMethod), v, true), "The method for SRCSRV to retrieve source code. One of <" + string.Join("|", Enum.GetNames(typeof(LinkMethod))) + ">. Default is " + method + "."); syntax.DefineOption("u|url", ref remoteGitUrl, s => new Uri(s, UriKind.Absolute), "Url to remote git repository."); syntax.DefineOption("commit", ref commitId, "The git ref to assume all the source code belongs to."); syntax.DefineOption("baseDir", ref baseDir, "The path to the root of the git repo."); syntax.DefineOption("s|skipVerify", ref skipVerify, "Verify all source files are available in source control."); - syntax.DefineOption("p|powershell", ref downloadWithPowershell, "Use an indexing strategy that won't rely on SRCSRV http support, but use a powershell command for URL download instead."); syntax.DefineParameter("pdb", ref pdbPath, "The PDB to add source indexing to."); if (!string.IsNullOrEmpty(pdbPath) && !File.Exists(pdbPath)) @@ -65,7 +65,7 @@ private static int Main(string[] args) GitWorkingDirectory = baseDir != null ? Catel.IO.Path.GetFullPath(baseDir, Environment.CurrentDirectory) : null, CommitId = commitId, SkipVerify = skipVerify, - DownloadWithPowerShell = downloadWithPowershell, + Method = method, }; Linker.Link(pdbPath, options); diff --git a/src/GitLinkTask/GitLink.cs b/src/GitLinkTask/GitLink.cs index 0329303..0b5764e 100644 --- a/src/GitLinkTask/GitLink.cs +++ b/src/GitLinkTask/GitLink.cs @@ -18,19 +18,25 @@ public class GitLink : Task [Required] public ITaskItem PdbFile { get; set; } - public bool DownloadWithPowershell { get; set; } + public string Method + { + get { return this.MethodEnum.ToString(); } + set { this.MethodEnum = string.IsNullOrEmpty(value) ? LinkMethod.Http : (LinkMethod)Enum.Parse(typeof(LinkMethod), value); } + } public bool SkipVerify { get; set; } public string GitRemoteUrl { get; set; } + private LinkMethod MethodEnum { get; set; } + public override bool Execute() { LogManager.AddListener(new MSBuildListener(this.Log)); var options = new LinkOptions { - DownloadWithPowerShell = this.DownloadWithPowershell, + Method = this.MethodEnum, SkipVerify = this.SkipVerify, GitRemoteUrl = this.GitRemoteUrl != null ? new Uri(this.GitRemoteUrl, UriKind.Absolute) : null, }; diff --git a/src/GitLinkTask/GitLink.targets b/src/GitLinkTask/GitLink.targets index ce6cbeb..f8b4451 100644 --- a/src/GitLinkTask/GitLink.targets +++ b/src/GitLinkTask/GitLink.targets @@ -6,7 +6,7 @@ From 2e00a6b60a7ee5c7c2b684efc038f16da29c0c01 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 19:42:20 -0700 Subject: [PATCH 34/85] Errors to index PDBs should not be fatal --- src/GitLinkTask/GitLink.targets | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/GitLinkTask/GitLink.targets b/src/GitLinkTask/GitLink.targets index f8b4451..8adb64e 100644 --- a/src/GitLinkTask/GitLink.targets +++ b/src/GitLinkTask/GitLink.targets @@ -8,6 +8,7 @@ PdbFile="$(IntermediateOutputPath)$(TargetName).pdb" Method="$(GitLinkMethod)" SkipVerify="$(GitLinkSkipVerify)" - GitRemoteUrl="$(GitLinkGitRemoteUrl)" /> + GitRemoteUrl="$(GitLinkGitRemoteUrl)" + ContinueOnError="true" /> \ No newline at end of file From b890f37402af5abfec20b0546854624b880a1556 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 19:48:52 -0700 Subject: [PATCH 35/85] Bump version to 3.0-beta --- src/version.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/version.json b/src/version.json index 4f75b17..3be50ff 100644 --- a/src/version.json +++ b/src/version.json @@ -1,5 +1,5 @@ { - "version": "2.0", + "version": "3.0-beta", "publicReleaseRefSpec": [ "^refs/heads/master$" ], From 339c6163642ff0119b38659dfba4284efeced361 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 21:23:01 -0700 Subject: [PATCH 36/85] Add additional parameters to GitLink task --- src/GitLinkTask/GitLink.cs | 6 ++++++ src/GitLinkTask/GitLink.targets | 2 ++ 2 files changed, 8 insertions(+) diff --git a/src/GitLinkTask/GitLink.cs b/src/GitLinkTask/GitLink.cs index 0b5764e..037071a 100644 --- a/src/GitLinkTask/GitLink.cs +++ b/src/GitLinkTask/GitLink.cs @@ -28,6 +28,10 @@ public string Method public string GitRemoteUrl { get; set; } + public string GitCommitId { get; set; } + + public string GitWorkingDirectory { get; set; } + private LinkMethod MethodEnum { get; set; } public override bool Execute() @@ -39,6 +43,8 @@ public override bool Execute() Method = this.MethodEnum, SkipVerify = this.SkipVerify, GitRemoteUrl = this.GitRemoteUrl != null ? new Uri(this.GitRemoteUrl, UriKind.Absolute) : null, + CommitId = this.GitCommitId, + GitWorkingDirectory = this.GitWorkingDirectory, }; bool success = Linker.Link(this.PdbFile.GetMetadata("FullPath"), options); diff --git a/src/GitLinkTask/GitLink.targets b/src/GitLinkTask/GitLink.targets index 8adb64e..57f02f5 100644 --- a/src/GitLinkTask/GitLink.targets +++ b/src/GitLinkTask/GitLink.targets @@ -9,6 +9,8 @@ Method="$(GitLinkMethod)" SkipVerify="$(GitLinkSkipVerify)" GitRemoteUrl="$(GitLinkGitRemoteUrl)" + GitWorkingDirectory="$(GitWorkingDirectory)" + GitCommitId="$(GitCommitId)" ContinueOnError="true" /> \ No newline at end of file From 094db6996a6def6cf0037b81fd6176fd1f87ad98 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 21:23:51 -0700 Subject: [PATCH 37/85] Update README for new usage --- README.md | 201 +++++++++++++++++------------------------------------- 1 file changed, 63 insertions(+), 138 deletions(-) diff --git a/README.md b/README.md index d7283c6..83002b3 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,9 @@ GitLink [![Join the chat at https://gitter.im/GitTools/GitLink](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/GitTools/GitLink?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) ![License](https://img.shields.io/github/license/gittools/gitlink.svg) -![NuGet downloads](https://img.shields.io/nuget/dt/gitlink.svg) -![Version](https://img.shields.io/nuget/v/gitlink.svg) -![Pre-release version](https://img.shields.io/nuget/vpre/gitlink.svg) +[![NuGet downloads](https://img.shields.io/nuget/dt/gitlink.svg)][NuGetDownload] +[![Version](https://img.shields.io/nuget/v/gitlink.svg)][NuGetDownload] +[![Pre-release version](https://img.shields.io/nuget/vpre/gitlink.svg)][NuGetDownload] ![Chocolatey count](https://img.shields.io/chocolatey/dt/gitlink.svg) ![Chocolatey version](https://img.shields.io/chocolatey/v/gitlink.svg) @@ -40,118 +40,27 @@ When using GitLink, the user no longer has to specify symbol servers. The only r ![Enabling source server support](doc/images/visualstudio_enablesourceserversupport.png) -# Troubleshooting - -## Source Stepping isn't working - -* Visual Studio 2012 needs to run elevated in order to download the source server files - -* Specify a value for Visual Studio -> Options -> Debugging -> Symbols -> `Cache Symbols in this directory` - -![Enabling source server support](doc/images/visualstudio_symbolslocation.png) - -## Source Stepping returns HTML -If your repository is private, you are likely seeing the logon HTML from your git host. - -* Log onto your git host in Internet Explorer -* Purge your local symbol cache +# How to use GitLink to enable source stepping on your own projects -Note that this approach is not guaranteed to work. Visual Studio needs to authenticate to retrieve the source files -but does not ask the user for credentials to do so. There are ways to work around this, but no mechanism is currently -provided out-of-the-box in *GitLink*. +## NuGet/MSBuild integration -Possible workarounds -* Include a mechanism in the pdb to retrieve credentials (using PowerShell and Windows credentials store) (see [#37](https://github.com/GitTools/GitLink/issues/37)) -* Use a proxy service that does not require authentication (see [#66](https://github.com/GitTools/GitLink/issues/66) and [Source server with Git repository](https://shonnlyga.wordpress.com/2016/05/28/source-server-with-git-repository)) +The simplest way to use GitLink is to [install its NuGet package][NuGetDownload] into your project. + Install-Package GitLink -# Supported git providers +Once installed, it automatically integrates with MSBuild to add source download instructions to your PDB. -GitLink supports the following providers out of the box (will auto-detect based on the url): +## Command line tool -* BitBucket -* GitHub -* Custom Provider (custom urls) - -Providers currently being worked on: - -* Assembla -* Beanstalk -* CloudForge -* Codebase -* FogCreek -* Planio -* ProjectLocker -* RhodeCode -* Unfuddle - -It is also possible to specify a custom url provider. +If you want to install the tool on your (build) computer, the package is available via Chocolatey. To install, use the following command: -# Using GitLink as command line tool + choco install gitlink Using GitLink via the command line is very simple: -1. Build the solution - in release mode with pdb files enabled -2. Run the console application with the right command line parameters -3. Include the PDB in your nuget package - -See [Oren Novotony's blog post](https://oren.codes/2015/09/23/enabling-source-code-debugging-for-your-nuget-packages-with-gitlink/) for even more detail and examples on build integration. - -## Most simple usage - -This is the most simple usage available **starting from 2.2.0**. It will automatically determine the url and commit based on a local *.git* directory. - - GitLink.exe c:\source\catel - -## Running for the default branch - - GitLink.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 - - GitLink.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 - - GitLink.exe c:\source\catel -u https://github.com/catel/catel -b develop -c debug - -This will use the develop branch and the debug configuration. - -## Running for a specific solution only - -Sometimes a repository contains more than 1 solution file. By default, all solutions will be processed. To only process a single solution file, use the *-f* option: - - GitLink.exe c:\source\catel -u https://github.com/catel/catel -f Catel.sln - -## Ignoring projects and explicitly including them + gitlink.exe -When specific projects should be ignored, use the *-ignore* option. This option accepts a comma separated list of patterns to ignore. Each pattern is either a literal project name (case-insensitive) or a regex enclosed in slashes. For example: - - GitLink.exe c:\source\catel -u https://github.com/catel/catel -f Catel.sln -ignore Catel.Core.WP80,Catel.MVVM.WP80 - GitLink.exe c:\source\catel -u https://github.com/catel/catel -f Catel.sln -ignore /^.+\.WP80$/,Catel.Core - -In case you want to ignore most of your projects, you can explicitly *-include* only the projects you need - others will be ignored automatically. Same as *-ignore* it accepts list of patterns. For example: - - GitLink.exe c:\source\catel -u https://github.com/catel/catel -f Catel.sln -include Catel.Core - GitLink.exe c:\source\catel -u https://github.com/catel/catel -f Catel.sln -include /Catel\..*$/,SomeOtherProject - -Finally, you can set both *-ignore* and *-include* options. In this case only projects matching one of *-include* patterns will be taken, but if and only if they don't match one of *-ignore*s. For example, the following command line will include only Catel.* projects, except "Catel.Core": - - GitLink.exe c:\source\catel -u https://github.com/catel/catel -f Catel.sln -include /Catel\..*$/ -ignore Catel.Core - -## Running for an uncommon / customized URL - -When working with a repository using uncommon URL you can use placeholders to specifiy where the filename and revision hash should be, use `-u` parameter with the custom URL - - GitLink.exe c:\source\catel -u "https://host/projects/catel/repos/catel/browse/{filename}?at={revision}&raw" - -The custom url will be used to fill the placeholders with the relative file path and the revision hash. - -## Running for a custom raw content URL +### Running for a custom raw content URL When working with a content proxy or an alternative git VCS system that supports direct HTTP access to specific file revisions use the `-u` parameter with the custom raw content root URL @@ -159,70 +68,85 @@ When working with a content proxy or an alternative git VCS system that supports The custom url will be used to fill in the following pattern `{customUrl}/{revision}/{relativeFilePath}` when generating the source mapping. -## Getting help +When working with a repository using uncommon URL you can use placeholders to specify where the filename and revision hash should be, use `-u` parameter with the custom URL -When you need help about GitLink, use the following command line: + GitLink.exe c:\source\catel -u "https://host/projects/catel/repos/catel/browse/{filename}?at={revision}&raw" - GitLink.exe -help +The custom url will be used to fill the placeholders with the relative file path and the revision hash. -## Logging to a file +### Logging to a file When you need to log the information to a file, use the following command line: GitLink.exe c:\source\catel -u https://github.com/catel/catel -b develop -l GitLinkLog.log -# Using GitLink in code - -GitLink is built with 2 usages in mind: command line and code reference. Though most people will use the command line version, it is possible to reference the executable and use the logic in code. +### More options -The command line implementation uses the same available API. +There are many more parameters you can use. Display the usage doc with the following command line: -## Creating a context + GitLink.exe -h -To link files to a Git project, a context must be created. The command line version does this by using the *ArgumentParser* class. It is also possible to create a context from scratch as shown in the example below: +# How does it work - var context = new GitLink.Context(new ProviderManager()); - context.SolutionDirectory = @"c:\source\catel"; - context.TargetUrl = "https://github.com/catel/catel"; - context.TargetBranch = "develop"; +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. -It is possible to create a context based on command line arguments: +For more information, see the official documentation of SrcSrv. - var context = ArgumentParser.Parse(@"c:\source\catel -u https://github.com/catel/catel -b develop"); +GitLink creates a source index file and updates the PDB file so it will retrieve the files from the Git host file handler. +To do this, GitLink must be aware of the public URL from which the source files you compiled with can be retrieved. +GitLink.exe reads your compiler-generated PDB, which already contains full paths to your local source files. +It then searches for a git repo that contains those source files and looks up the commit that HEAD points to. +It also searches your remotes for a URL pattern that it recognizes (e.g. https://github.com/name/repo). +It combines the URL and the commit ID to create a unique URL for each source file of this exact version, and adds this information to your PDB. -## Linking a context +When you share your PDB alongside your DLL, your users who debug with Source Server support enabled will automatically be able to step into your source code. -Once a context is created, the *Linker* class can be used to actually link the files: +# Troubleshooting - Linker.Link(context); +## Source Stepping isn't working -# How to get +* Visual Studio 2012 needs to run elevated in order to download the source server files -There are three general ways to get GitLink: +* Specify a value for Visual Studio -> Options -> Debugging -> Symbols -> `Cache Symbols in this directory` -## Get it from GitHub +![Enabling source server support](doc/images/visualstudio_symbolslocation.png) -The releases will be available as separate executable download on the [releases tab](https://github.com/GitTools/GitLink/releases) of the project. +## Source Stepping returns HTML +If your repository is private, you are likely seeing the logon HTML from your git host. -## Get it via Chocolatey +* Log onto your git host in Internet Explorer +* Purge your local symbol cache -If you want to install the tool on your (build) computer, the package is available via Chocolatey. To install, use the following command: +Note that this approach is not guaranteed to work. Visual Studio needs to authenticate to retrieve the source files +but does not ask the user for credentials to do so. There are ways to work around this, but no mechanism is currently +provided out-of-the-box in *GitLink*. - choco install gitlink +Possible workarounds +* Include a mechanism in the pdb to retrieve credentials (using PowerShell and Windows credentials store) (see [#37](https://github.com/GitTools/GitLink/issues/37)) +* Use a proxy service that does not require authentication (see [#66](https://github.com/GitTools/GitLink/issues/66) and [Source server with Git repository](https://shonnlyga.wordpress.com/2016/05/28/source-server-with-git-repository)) -## Get it via NuGet -If you want to reference the assembly to use it in code, the recommended way to get it is via NuGet. +# Supported git providers -**Note that getting GitLink via NuGet will add it as a reference to the project** +GitLink supports the following providers out of the box (will auto-detect based on the url): -# How does it work +* BitBucket +* GitHub +* Custom Provider (custom urls) -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. +Providers currently being worked on: -For more information, see the official documentation of SrcSrv. +* Assembla +* Beanstalk +* CloudForge +* Codebase +* FogCreek +* Planio +* ProjectLocker +* RhodeCode +* Unfuddle -GitLink creates a source index file and updates the PDB file so it will retrieve the files from the Git host file handler. +It is also possible to specify a custom url provider. # Projects using GitLink @@ -271,7 +195,8 @@ Are you using GitLink in your projects? Let us know and we will add your project *Note that you can also create a pull request on this document and add it yourself.* - # Icon Link by Dominic Whittle from The Noun Project + +[NuGetDownload]: https://www.nuget.org/packages/gitlink From f51e9dc3e0d663907f8f60c640f36a24ea09709e Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 22:02:16 -0700 Subject: [PATCH 38/85] Fix handling of custom URLs --- src/GitLink/Linker.cs | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/GitLink/Linker.cs b/src/GitLink/Linker.cs index c64f208..edda053 100644 --- a/src/GitLink/Linker.cs +++ b/src/GitLink/Linker.cs @@ -26,6 +26,8 @@ namespace GitLink /// public static class Linker { + private static readonly string FilenamePlaceholder = Uri.EscapeUriString("{filename}"); + private static readonly string RevisionPlaceholder = Uri.EscapeUriString("{revision}"); private static readonly string PdbStrExePath = Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "pdbstr.exe"); private static readonly ILog Log = LogManager.GetCurrentClassLogger(); @@ -112,9 +114,21 @@ public static class Linker } string rawUrl = provider.RawGitUrl; - if (!rawUrl.Contains("%var2%") && !rawUrl.Contains("{0}")) + if (rawUrl.Contains(RevisionPlaceholder) || rawUrl.Contains(FilenamePlaceholder)) { - rawUrl = string.Format("{0}/{{0}}/%var2%", rawUrl); + if (!rawUrl.Contains(RevisionPlaceholder) || !rawUrl.Contains(FilenamePlaceholder)) + { + Log.Error("Supplied custom URL pattern must contain both a revision and a filename placeholder."); + return false; + } + + rawUrl = rawUrl + .Replace(RevisionPlaceholder, commitId) + .Replace(FilenamePlaceholder, "%var2%"); + } + else + { + rawUrl = $"{rawUrl}/{{0}}/%var2%"; } var srcSrvContext = new SrcSrvContext From 678646f9e63c8d72ba267d60bf925ece10410866 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 22:34:28 -0700 Subject: [PATCH 39/85] Add LICENSE for chocolatey package --- src/GitLink.NuGet/GitLink.NuGet.nuproj | 7 +++++-- src/GitLink.NuGet/tools/LICENSE.txt | 25 +++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 2 deletions(-) create mode 100644 src/GitLink.NuGet/tools/LICENSE.txt diff --git a/src/GitLink.NuGet/GitLink.NuGet.nuproj b/src/GitLink.NuGet/GitLink.NuGet.nuproj index 1a411d4..66d2d50 100644 --- a/src/GitLink.NuGet/GitLink.NuGet.nuproj +++ b/src/GitLink.NuGet/GitLink.NuGet.nuproj @@ -26,10 +26,10 @@ https://github.com/GitTools/GitLink - https://github.com/GitTools/GitLink/blob/develop/LICENSE + https://raw.githubusercontent.com/GitTools/GitLink/$GitCommitIdShort$/LICENSE git pdb true - https://raw.githubusercontent.com/GitTools/GitLink/develop/design/logo/logo_64.png + https://raw.githubusercontent.com/GitTools/GitLink/$GitCommitIdShort$/design/logo/logo_64.png GetDeployableOutputs true @@ -42,5 +42,8 @@ Build + + + \ No newline at end of file diff --git a/src/GitLink.NuGet/tools/LICENSE.txt b/src/GitLink.NuGet/tools/LICENSE.txt new file mode 100644 index 0000000..8da68fb --- /dev/null +++ b/src/GitLink.NuGet/tools/LICENSE.txt @@ -0,0 +1,25 @@ +From: https://github.com/GitTools/GitLink/blob/develop/LICENSE + +LICENSE + +The MIT License (MIT) + +Copyright (c) 2014 - 2016 CatenaLogic + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. \ No newline at end of file From 920a68f288a1cda7ad36635bdb1d4075fe591e88 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 22:36:54 -0700 Subject: [PATCH 40/85] Let the license point to the github-wrapped UI --- src/GitLink.NuGet/GitLink.NuGet.nuproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GitLink.NuGet/GitLink.NuGet.nuproj b/src/GitLink.NuGet/GitLink.NuGet.nuproj index 66d2d50..c618968 100644 --- a/src/GitLink.NuGet/GitLink.NuGet.nuproj +++ b/src/GitLink.NuGet/GitLink.NuGet.nuproj @@ -26,7 +26,7 @@ https://github.com/GitTools/GitLink - https://raw.githubusercontent.com/GitTools/GitLink/$GitCommitIdShort$/LICENSE + https://github.com/GitTools/GitLink/blob/$GitCommitIdShort$/LICENSE git pdb true https://raw.githubusercontent.com/GitTools/GitLink/$GitCommitIdShort$/design/logo/logo_64.png From eee594f62f63924c19c0bf5f00386c03df9f8fde Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 22:50:11 -0700 Subject: [PATCH 41/85] Log message about server used --- src/GitLink/Linker.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/GitLink/Linker.cs b/src/GitLink/Linker.cs index edda053..4aba755 100644 --- a/src/GitLink/Linker.cs +++ b/src/GitLink/Linker.cs @@ -123,7 +123,7 @@ public static class Linker } rawUrl = rawUrl - .Replace(RevisionPlaceholder, commitId) + .Replace(RevisionPlaceholder, "{0}") .Replace(FilenamePlaceholder, "%var2%"); } else @@ -131,6 +131,7 @@ public static class Linker rawUrl = $"{rawUrl}/{{0}}/%var2%"; } + Log.Info($"Using {string.Format(rawUrl, commitId)} for source server URLs."); var srcSrvContext = new SrcSrvContext { RawUrl = rawUrl, From c5ad16e6399be9339bbe4a82ddf21a34d0b72475 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 22:52:26 -0700 Subject: [PATCH 42/85] Fix verification when files are missing --- src/GitLink/Extensions/PdbExtensions.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/GitLink/Extensions/PdbExtensions.cs b/src/GitLink/Extensions/PdbExtensions.cs index 5a2fd7b..860ca4e 100644 --- a/src/GitLink/Extensions/PdbExtensions.cs +++ b/src/GitLink/Extensions/PdbExtensions.cs @@ -68,8 +68,15 @@ public static IReadOnlyDictionary GetFilesAndChecksums(this PdbF private static bool AreEqualBuffers(byte[] first, byte[] second) { - Argument.IsNotNull(nameof(first), first); - Argument.IsNotNull(nameof(second), second); + if (first == null && second == null) + { + return true; + } + + if (first == null || second == null) + { + return false; + } if (first.Length != second.Length) { From e8c31a901263c927dca06637ee34927f5cd38246 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 8 Oct 2016 22:55:19 -0700 Subject: [PATCH 43/85] Add self as author of package --- src/GitLink.NuGet/GitLink.NuGet.nuproj | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/GitLink.NuGet/GitLink.NuGet.nuproj b/src/GitLink.NuGet/GitLink.NuGet.nuproj index c618968..c320a43 100644 --- a/src/GitLink.NuGet/GitLink.NuGet.nuproj +++ b/src/GitLink.NuGet/GitLink.NuGet.nuproj @@ -20,8 +20,8 @@ GitLink GitLink - GeertvanHorrik - GeertvanHorrik + GeertvanHorrik,AArnott + GeertvanHorrik,AArnott GitLink let's users step through your code hosted on any Git hosting service! This makes symbol servers obsolete which saves you both time From 49d61caf6ae4ae72e4010bac3a844d249c0433b1 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sun, 9 Oct 2016 12:31:43 -0700 Subject: [PATCH 44/85] Apply StyleCop.Analyzers and fix issues Some issues are suppressed to fix later. --- .gitignore | 1 - src/GitLink/Exceptions/GitLinkException.cs | 3 +- .../Extensions/BinaryReaderExtensions.cs | 3 +- src/GitLink/Extensions/PdbExtensions.cs | 5 +- .../Extensions/RepositoryExtensions.cs | 6 +- src/GitLink/Extensions/StringExtensions.cs | 3 +- src/GitLink/GitLink.csproj | 18 ++-- src/GitLink/GitLink.ruleset | 83 +++++++++++++++++++ src/GitLink/Helpers/PdbStrHelper.cs | 3 +- src/GitLink/LinkMethod.cs | 4 +- src/GitLink/LinkOptions.cs | 6 +- src/GitLink/Linker.cs | 5 +- src/GitLink/Logging/OutputLogListener.cs | 3 +- src/GitLink/Pdb/Crypto.cs | 3 +- src/GitLink/Pdb/PdbFile.cs | 28 +++++-- src/GitLink/Pdb/PdbInfo.cs | 16 +++- src/GitLink/Pdb/PdbName.cs | 5 +- src/GitLink/Pdb/PdbRoot.cs | 4 +- src/GitLink/Pdb/PdbStream.cs | 4 +- src/GitLink/Pdb/SrcSrv.cs | 11 ++- src/GitLink/Pdb/SrcSrvContext.cs | 2 - src/GitLink/Program.cs | 6 +- src/GitLink/Properties/AssemblyInfo.cs | 10 +-- src/GitLink/Providers/BitBucketProvider.cs | 5 +- src/GitLink/Providers/CustomRawUrlProvider.cs | 6 +- src/GitLink/Providers/CustomUrlProvider.cs | 16 ++-- src/GitLink/Providers/GitHubProvider.cs | 5 +- src/GitLink/Providers/Interfaces/IProvider.cs | 3 +- .../Providers/Interfaces/IProviderManager.cs | 3 +- src/GitLink/Providers/ProviderBase.cs | 3 +- src/GitLink/Providers/ProviderManager.cs | 5 +- .../VisualStudioTeamServicesProvider.cs | 1 - src/GitLink/project.json | 1 + src/GitLink/stylecop.json | 9 ++ src/GitLinkTask/GitLink.cs | 7 +- src/GitLinkTask/GitLinkTask.csproj | 6 ++ src/GitLinkTask/GitLinkTask.ruleset | 74 +++++++++++++++++ src/GitLinkTask/Properties/AssemblyInfo.cs | 14 +--- src/GitLinkTask/stylecop.json | 9 ++ 39 files changed, 292 insertions(+), 107 deletions(-) create mode 100644 src/GitLink/GitLink.ruleset create mode 100644 src/GitLink/stylecop.json create mode 100644 src/GitLinkTask/GitLinkTask.ruleset create mode 100644 src/GitLinkTask/stylecop.json diff --git a/.gitignore b/.gitignore index 9f4ec6f..8e6778c 100644 --- a/.gitignore +++ b/.gitignore @@ -107,7 +107,6 @@ sql TestResults *.Cache ClientBin -stylecop.* ~$* *.dbmdl Generated_Code #added for RIA/Silverlight projects diff --git a/src/GitLink/Exceptions/GitLinkException.cs b/src/GitLink/Exceptions/GitLinkException.cs index de98ae2..62caf7d 100644 --- a/src/GitLink/Exceptions/GitLinkException.cs +++ b/src/GitLink/Exceptions/GitLinkException.cs @@ -1,10 +1,9 @@ // -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- - namespace GitLink { using System; diff --git a/src/GitLink/Extensions/BinaryReaderExtensions.cs b/src/GitLink/Extensions/BinaryReaderExtensions.cs index 43ba1ac..c445cd4 100644 --- a/src/GitLink/Extensions/BinaryReaderExtensions.cs +++ b/src/GitLink/Extensions/BinaryReaderExtensions.cs @@ -1,10 +1,9 @@ // -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- - namespace GitLink { using System; diff --git a/src/GitLink/Extensions/PdbExtensions.cs b/src/GitLink/Extensions/PdbExtensions.cs index 860ca4e..36c3171 100644 --- a/src/GitLink/Extensions/PdbExtensions.cs +++ b/src/GitLink/Extensions/PdbExtensions.cs @@ -1,10 +1,9 @@ // -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- - namespace GitLink { using System; @@ -39,7 +38,7 @@ public static IReadOnlyDictionary GetFilesAndChecksums(this PdbF { Argument.IsNotNull(() => pdbFile); - //const int LastInterestingByte = 47; + // const int LastInterestingByte = 47; const string FileIndicator = "/src/files/"; var values = pdbFile.Info.NameToPdbName.Values; diff --git a/src/GitLink/Extensions/RepositoryExtensions.cs b/src/GitLink/Extensions/RepositoryExtensions.cs index 3ac764f..26463b0 100644 --- a/src/GitLink/Extensions/RepositoryExtensions.cs +++ b/src/GitLink/Extensions/RepositoryExtensions.cs @@ -1,4 +1,8 @@ -namespace GitLink +// +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. +// + +namespace GitLink { using System; using System.Collections.Generic; diff --git a/src/GitLink/Extensions/StringExtensions.cs b/src/GitLink/Extensions/StringExtensions.cs index 47cbc7a..26b5f11 100644 --- a/src/GitLink/Extensions/StringExtensions.cs +++ b/src/GitLink/Extensions/StringExtensions.cs @@ -1,10 +1,9 @@ // -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- - namespace GitLink { using Catel; diff --git a/src/GitLink/GitLink.csproj b/src/GitLink/GitLink.csproj index a84853d..502017c 100644 --- a/src/GitLink/GitLink.csproj +++ b/src/GitLink/GitLink.csproj @@ -23,8 +23,9 @@ 4 false true - - + ..\..\bin\Debug\GitLink\GitLink.XML + GitLink.ruleset + CS1591 pdbonly @@ -35,8 +36,9 @@ 4 false true - - + ..\..\bin\Release\GitLink\GitLink.XML + GitLink.ruleset + CS1591 GitLink.Program @@ -88,19 +90,23 @@ + PreserveNewest PreserveNewest - + - + + + + \ No newline at end of file diff --git a/src/GitLink/GitLink.ruleset b/src/GitLink/GitLink.ruleset new file mode 100644 index 0000000..d038a3e --- /dev/null +++ b/src/GitLink/GitLink.ruleset @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/GitLink/Helpers/PdbStrHelper.cs b/src/GitLink/Helpers/PdbStrHelper.cs index 5d7cc1a..ed98896 100644 --- a/src/GitLink/Helpers/PdbStrHelper.cs +++ b/src/GitLink/Helpers/PdbStrHelper.cs @@ -1,10 +1,9 @@ // -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- - namespace GitLink { using System.Diagnostics; diff --git a/src/GitLink/LinkMethod.cs b/src/GitLink/LinkMethod.cs index 1fc1cf5..d1c2e43 100644 --- a/src/GitLink/LinkMethod.cs +++ b/src/GitLink/LinkMethod.cs @@ -1,6 +1,6 @@ // -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) 2016 Andrew Arnott. All rights reserved. +// +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- diff --git a/src/GitLink/LinkOptions.cs b/src/GitLink/LinkOptions.cs index 92e5fe8..0adf8d2 100644 --- a/src/GitLink/LinkOptions.cs +++ b/src/GitLink/LinkOptions.cs @@ -1,4 +1,8 @@ -namespace GitLink +// +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. +// + +namespace GitLink { using System; using System.Collections.Generic; diff --git a/src/GitLink/Linker.cs b/src/GitLink/Linker.cs index 4aba755..56fece8 100644 --- a/src/GitLink/Linker.cs +++ b/src/GitLink/Linker.cs @@ -1,10 +1,9 @@ // -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) 2016 Andrew Arnott. All rights reserved. +// +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- - namespace GitLink { using System; diff --git a/src/GitLink/Logging/OutputLogListener.cs b/src/GitLink/Logging/OutputLogListener.cs index c9a269b..258b393 100644 --- a/src/GitLink/Logging/OutputLogListener.cs +++ b/src/GitLink/Logging/OutputLogListener.cs @@ -1,10 +1,9 @@ // -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- - namespace GitLink.Logging { using System; diff --git a/src/GitLink/Pdb/Crypto.cs b/src/GitLink/Pdb/Crypto.cs index e23baf4..ddb054b 100644 --- a/src/GitLink/Pdb/Crypto.cs +++ b/src/GitLink/Pdb/Crypto.cs @@ -1,10 +1,9 @@ // -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- - namespace GitLink.Pdb { using System; diff --git a/src/GitLink/Pdb/PdbFile.cs b/src/GitLink/Pdb/PdbFile.cs index b3d4b23..ad06990 100644 --- a/src/GitLink/Pdb/PdbFile.cs +++ b/src/GitLink/Pdb/PdbFile.cs @@ -1,4 +1,8 @@ -namespace GitLink.Pdb +// +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. +// + +namespace GitLink.Pdb { using System; using System.Collections.Generic; @@ -31,7 +35,7 @@ public PdbFile(string path) Argument.IsNotNullOrWhitespace(() => path); Path = path; - + _fs = File.Open(path, FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite); _br = new BinaryReader(_fs, Encoding.UTF8, true); _bw = new BinaryWriter(_fs, Encoding.UTF8, true); @@ -88,6 +92,7 @@ private void CheckPdbHeader() private void ReadPdbHeader() { // TODO: Create PdbHeader struct + //// code here _pageByteCount = _br.ReadInt32(); // 0x20 PagesFree = _br.ReadInt32(); // 0x24 TODO not sure meaning @@ -102,14 +107,20 @@ private void CheckPdb() var length = _fs.Length; if (length % _pageByteCount != 0) { - throw Log.ErrorAndCreateException("pdb length {0} bytes per page <> 0, {1}, {2}", length, _pageByteCount, + throw Log.ErrorAndCreateException( + "pdb length {0} bytes per page <> 0, {1}, {2}", + length, + _pageByteCount, PageCount); } if (length / _pageByteCount != PageCount) { - throw Log.ErrorAndCreateException("pdb length does not match page count, length: {0}, bytes per page: {1}, page count: {2}", - length, _pageByteCount, PageCount); + throw Log.ErrorAndCreateException( + "pdb length does not match page count, length: {0}, bytes per page: {1}, page count: {2}", + length, + _pageByteCount, + PageCount); } } @@ -215,7 +226,7 @@ private byte[] ReadStreamBytes(PdbStream stream) } var j = pages.Length - 1; - ReadPage(bytes, pages[j], j * _pageByteCount, (stream.ByteCount - j * _pageByteCount)); + ReadPage(bytes, pages[j], j * _pageByteCount, stream.ByteCount - (j * _pageByteCount)); } return bytes; @@ -240,7 +251,8 @@ private PdbInfo InternalInfo() var root = GetRoot(); if (root.Streams.Count <= 1) { - throw Log.ErrorAndCreateException("Expected at least 2 streams inside the pdb root, but only found '{0}', cannot read pdb info", + throw Log.ErrorAndCreateException( + "Expected at least 2 streams inside the pdb root, but only found '{0}', cannot read pdb info", root.Streams.Count); } @@ -295,7 +307,7 @@ private PdbInfo InternalInfo() } var tailByteCount = GetRoot().Streams[1].ByteCount - br.BaseStream.Position; - info.Tail = br.ReadBytes((int) tailByteCount); + info.Tail = br.ReadBytes((int)tailByteCount); foreach (var tuple in positions) { diff --git a/src/GitLink/Pdb/PdbInfo.cs b/src/GitLink/Pdb/PdbInfo.cs index fa3ef13..30d3bc0 100644 --- a/src/GitLink/Pdb/PdbInfo.cs +++ b/src/GitLink/Pdb/PdbInfo.cs @@ -1,10 +1,9 @@ // -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- - namespace GitLink.Pdb { using System; @@ -15,7 +14,7 @@ public class PdbInfo { public PdbInfo() { - Guid = new Guid(); + Guid = default(Guid); StreamToPdbName = new SortedDictionary(); NameToPdbName = new SortedDictionary(); FlagIndexToPdbName = new SortedDictionary(); @@ -25,16 +24,27 @@ public PdbInfo() } public int Version { get; set; } + public int Signature { get; set; } + public Guid Guid { get; set; } + public int Age { get; set; } + public int FlagIndexMax { get; set; } + public int FlagCount { get; set; } + public IDictionary StreamToPdbName { get; private set; } + public IDictionary NameToPdbName { get; private set; } + public IDictionary FlagIndexToPdbName { get; private set; } + public SortedSet FlagIndexes { get; private set; } + public string[] SrcSrv { get; set; } + public byte[] Tail { get; set; } public void ClearFlags() diff --git a/src/GitLink/Pdb/PdbName.cs b/src/GitLink/Pdb/PdbName.cs index 1980dd0..ac69ce5 100644 --- a/src/GitLink/Pdb/PdbName.cs +++ b/src/GitLink/Pdb/PdbName.cs @@ -1,10 +1,9 @@ // -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- - namespace GitLink.Pdb { public class PdbName @@ -15,7 +14,9 @@ public PdbName(string name = "") } public int Stream { get; set; } + public string Name { get; set; } + public int FlagIndex { get; set; } } } \ No newline at end of file diff --git a/src/GitLink/Pdb/PdbRoot.cs b/src/GitLink/Pdb/PdbRoot.cs index 8050edf..a718bb6 100644 --- a/src/GitLink/Pdb/PdbRoot.cs +++ b/src/GitLink/Pdb/PdbRoot.cs @@ -1,10 +1,9 @@ // -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- - namespace GitLink.Pdb { using System.Collections.Generic; @@ -21,6 +20,7 @@ public PdbRoot(PdbStream stream) } public PdbStream Stream { get; set; } + public List Streams { get; private set; } public int AddStream(PdbStream stream) diff --git a/src/GitLink/Pdb/PdbStream.cs b/src/GitLink/Pdb/PdbStream.cs index 4468b07..fca03cb 100644 --- a/src/GitLink/Pdb/PdbStream.cs +++ b/src/GitLink/Pdb/PdbStream.cs @@ -1,15 +1,15 @@ // -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- - namespace GitLink.Pdb { public class PdbStream { public int ByteCount { get; set; } + public int[] Pages { get; set; } } } \ No newline at end of file diff --git a/src/GitLink/Pdb/SrcSrv.cs b/src/GitLink/Pdb/SrcSrv.cs index 6affdda..ed0fcc2 100644 --- a/src/GitLink/Pdb/SrcSrv.cs +++ b/src/GitLink/Pdb/SrcSrv.cs @@ -1,10 +1,9 @@ // -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- - namespace GitLink.Pdb { using System; @@ -29,12 +28,12 @@ public static byte[] Create(string rawUrl, string revision, IEnumerable // -------------------------------------------------------------------------------------------------------------------- - namespace GitLink.Pdb { using System; @@ -16,7 +15,6 @@ public SrcSrvContext() { Paths = new List>(); VstsData = new Dictionary(); - } public string RawUrl { get; set; } diff --git a/src/GitLink/Program.cs b/src/GitLink/Program.cs index b023f77..025ab5c 100644 --- a/src/GitLink/Program.cs +++ b/src/GitLink/Program.cs @@ -1,10 +1,9 @@ // -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- - namespace GitLink { using System; @@ -76,7 +75,8 @@ private static int Main(string[] args) [Conditional("DEBUG")] private static void WaitForKeyPressWhenDebugging() { - if (Debugger.IsAttached) // VS only closes the window immediately when debugging + // VS only closes the window immediately when debugging + if (Debugger.IsAttached) { Log.Info(string.Empty); Log.Info("Press any key to continue"); diff --git a/src/GitLink/Properties/AssemblyInfo.cs b/src/GitLink/Properties/AssemblyInfo.cs index 0eadb6c..f902610 100644 --- a/src/GitLink/Properties/AssemblyInfo.cs +++ b/src/GitLink/Properties/AssemblyInfo.cs @@ -1,10 +1,9 @@ // -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- - using System.Reflection; using System.Runtime.InteropServices; @@ -12,11 +11,6 @@ [assembly: AssemblyProduct("GitLink")] [assembly: AssemblyDescription("GitLink 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 +#endif diff --git a/src/GitLink/Providers/BitBucketProvider.cs b/src/GitLink/Providers/BitBucketProvider.cs index b124d98..abbb2e8 100644 --- a/src/GitLink/Providers/BitBucketProvider.cs +++ b/src/GitLink/Providers/BitBucketProvider.cs @@ -1,10 +1,9 @@ // -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- - namespace GitLink.Providers { using System; @@ -15,7 +14,7 @@ public class BitBucketProvider : ProviderBase { private readonly Regex _gitHubRegex = new Regex(@"(?(?(?:https://)?bitbucket\.org/(?[^/]+))/(?[^/]+))"); - public BitBucketProvider() + public BitBucketProvider() : base(new GitPreparer()) { } diff --git a/src/GitLink/Providers/CustomRawUrlProvider.cs b/src/GitLink/Providers/CustomRawUrlProvider.cs index dc7b5d6..b289a5f 100644 --- a/src/GitLink/Providers/CustomRawUrlProvider.cs +++ b/src/GitLink/Providers/CustomRawUrlProvider.cs @@ -1,4 +1,8 @@ -namespace GitLink.Providers +// +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. +// + +namespace GitLink.Providers { using System; using System.Text.RegularExpressions; diff --git a/src/GitLink/Providers/CustomUrlProvider.cs b/src/GitLink/Providers/CustomUrlProvider.cs index 8086b42..716922e 100644 --- a/src/GitLink/Providers/CustomUrlProvider.cs +++ b/src/GitLink/Providers/CustomUrlProvider.cs @@ -1,7 +1,11 @@ -namespace GitLink.Providers +// +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. +// + +namespace GitLink.Providers { - using GitTools.Git; using System.Text.RegularExpressions; + using GitTools.Git; public sealed class CustomUrlProvider : ProviderBase { @@ -26,19 +30,19 @@ public override string RawGitUrl public override bool Initialize(string url) { - if (string.IsNullOrEmpty(url) || !_regexUrl.IsMatch(url) || + if (string.IsNullOrEmpty(url) || !_regexUrl.IsMatch(url) || (!url.Contains(FileNamePlaceHolder) && !url.Contains(RevisionPlaceHolder))) { return false; } - if(url.Contains(FileNamePlaceHolder)) - { + if (url.Contains(FileNamePlaceHolder)) + { _rawUrl = url.Replace(FileNamePlaceHolder, "%var2%"); } if (url.Contains(RevisionPlaceHolder)) - { + { _rawUrl = _rawUrl.Replace(RevisionPlaceHolder, "{0}"); } diff --git a/src/GitLink/Providers/GitHubProvider.cs b/src/GitLink/Providers/GitHubProvider.cs index 37053f0..5475078 100644 --- a/src/GitLink/Providers/GitHubProvider.cs +++ b/src/GitLink/Providers/GitHubProvider.cs @@ -1,10 +1,9 @@ // -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- - namespace GitLink.Providers { using System; @@ -15,7 +14,7 @@ public class GitHubProvider : ProviderBase { private readonly Regex _gitHubRegex = new Regex(@"(?(?(?:https://)?github\.com/(?[^/]+))/(?[^/]+))"); - public GitHubProvider() + public GitHubProvider() : base(new GitPreparer()) { } diff --git a/src/GitLink/Providers/Interfaces/IProvider.cs b/src/GitLink/Providers/Interfaces/IProvider.cs index 63b2ffb..59f5513 100644 --- a/src/GitLink/Providers/Interfaces/IProvider.cs +++ b/src/GitLink/Providers/Interfaces/IProvider.cs @@ -1,10 +1,9 @@ // -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- - namespace GitLink.Providers { using GitTools; diff --git a/src/GitLink/Providers/Interfaces/IProviderManager.cs b/src/GitLink/Providers/Interfaces/IProviderManager.cs index 30845ef..555259d 100644 --- a/src/GitLink/Providers/Interfaces/IProviderManager.cs +++ b/src/GitLink/Providers/Interfaces/IProviderManager.cs @@ -1,10 +1,9 @@ // -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- - namespace GitLink.Providers { public interface IProviderManager diff --git a/src/GitLink/Providers/ProviderBase.cs b/src/GitLink/Providers/ProviderBase.cs index c0be2de..488ac8b 100644 --- a/src/GitLink/Providers/ProviderBase.cs +++ b/src/GitLink/Providers/ProviderBase.cs @@ -1,10 +1,9 @@ // -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- - namespace GitLink.Providers { using System; diff --git a/src/GitLink/Providers/ProviderManager.cs b/src/GitLink/Providers/ProviderManager.cs index 8fc4304..0dfe3e9 100644 --- a/src/GitLink/Providers/ProviderManager.cs +++ b/src/GitLink/Providers/ProviderManager.cs @@ -1,10 +1,9 @@ // -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- - namespace GitLink.Providers { using Catel.IoC; @@ -20,7 +19,7 @@ public ProviderBase GetProvider(string url) foreach (var providerType in providerTypes) { - var provider = (ProviderBase) typeFactory.CreateInstance(providerType); + var provider = (ProviderBase)typeFactory.CreateInstance(providerType); if (provider.Initialize(url)) { return provider; diff --git a/src/GitLink/Providers/VisualStudioTeamServicesProvider.cs b/src/GitLink/Providers/VisualStudioTeamServicesProvider.cs index db452dd..4b2811c 100644 --- a/src/GitLink/Providers/VisualStudioTeamServicesProvider.cs +++ b/src/GitLink/Providers/VisualStudioTeamServicesProvider.cs @@ -4,7 +4,6 @@ // // -------------------------------------------------------------------------------------------------------------------- - namespace GitLink.Providers { using System; diff --git a/src/GitLink/project.json b/src/GitLink/project.json index d5607a1..5e3e502 100644 --- a/src/GitLink/project.json +++ b/src/GitLink/project.json @@ -14,6 +14,7 @@ "version": "0.1.37-beta", "suppressParent": "none" }, + "StyleCop.Analyzers": "1.0.0", "Nerdbank.GitVersioning": { "version": "1.5.46", "suppressParent": "none" diff --git a/src/GitLink/stylecop.json b/src/GitLink/stylecop.json new file mode 100644 index 0000000..d600716 --- /dev/null +++ b/src/GitLink/stylecop.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", + "settings": { + "documentationRules": { + "companyName": "CatenaLogic", + "copyrightText": " Copyright (c) 2014 - 2016 {companyName}. All rights reserved." + } + } +} diff --git a/src/GitLinkTask/GitLink.cs b/src/GitLinkTask/GitLink.cs index 037071a..8225d0a 100644 --- a/src/GitLinkTask/GitLink.cs +++ b/src/GitLinkTask/GitLink.cs @@ -1,9 +1,4 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) 2016 Andrew Arnott. All rights reserved. -// -// -------------------------------------------------------------------------------------------------------------------- - +// Copyright (c) Andrew Arnott. All rights reserved. namespace GitLinkTask { diff --git a/src/GitLinkTask/GitLinkTask.csproj b/src/GitLinkTask/GitLinkTask.csproj index 53b303b..81b1292 100644 --- a/src/GitLinkTask/GitLinkTask.csproj +++ b/src/GitLinkTask/GitLinkTask.csproj @@ -23,6 +23,8 @@ 4 true 1591;1998 + ..\..\bin\Debug\GitLinkTask\GitLinkTask.XML + GitLinkTask.ruleset AnyCPU @@ -34,6 +36,8 @@ 4 1591;1998 true + ..\..\bin\Release\GitLinkTask\GitLinkTask.XML + GitLinkTask.ruleset @@ -55,7 +59,9 @@ PreserveNewest + + diff --git a/src/GitLinkTask/GitLinkTask.ruleset b/src/GitLinkTask/GitLinkTask.ruleset new file mode 100644 index 0000000..a7d6baf --- /dev/null +++ b/src/GitLinkTask/GitLinkTask.ruleset @@ -0,0 +1,74 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/GitLinkTask/Properties/AssemblyInfo.cs b/src/GitLinkTask/Properties/AssemblyInfo.cs index e67bdcc..e554b1d 100644 --- a/src/GitLinkTask/Properties/AssemblyInfo.cs +++ b/src/GitLinkTask/Properties/AssemblyInfo.cs @@ -1,24 +1,12 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. -// -// -------------------------------------------------------------------------------------------------------------------- - +// Copyright (c) Andrew Arnott. All rights reserved. using System.Reflection; using System.Runtime.InteropServices; -// All other assembly info is defined in SharedAssembly.cs - [assembly: AssemblyTitle("GitLinkTask")] [assembly: AssemblyProduct("GitLinkTask")] [assembly: AssemblyDescription("GitLinkTask 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/GitLinkTask/stylecop.json b/src/GitLinkTask/stylecop.json new file mode 100644 index 0000000..c48d1e4 --- /dev/null +++ b/src/GitLinkTask/stylecop.json @@ -0,0 +1,9 @@ +{ + "$schema": "https://raw.githubusercontent.com/DotNetAnalyzers/StyleCopAnalyzers/master/StyleCop.Analyzers/StyleCop.Analyzers/Settings/stylecop.schema.json", + "settings": { + "documentationRules": { + "companyName": "Andrew Arnott", + "xmlHeader": false + } + } +} From 7bc098c81a90265adaf185fc6755c37816617920 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sun, 9 Oct 2016 12:55:59 -0700 Subject: [PATCH 45/85] More StyleCop fixes As is customary, VS eventually decided to find a bunch more issues after it already reported a clean slate. This fixes the next batch. --- src/GitLink.Tests/ApprovalTestsConfig.cs | 3 +- src/GitLink.Tests/GitLink.Tests.csproj | 10 +++ src/GitLink.Tests/GitLink.Tests.ruleset | 78 +++++++++++++++++++ src/GitLink.Tests/GlobalInitialization.cs | 5 +- .../IntegrationTests/BitBucketIntegration.cs | 5 +- .../IntegrationTests/GitHubIntegration.cs | 3 +- .../IntegrationTests/IntegrationTestBase.cs | 3 +- src/GitLink.Tests/Properties/AssemblyInfo.cs | 2 +- .../Providers/BitBucketProviderFacts.cs | 3 +- .../Providers/CustomRawUrlProviderFacts.cs | 8 +- .../Providers/CustomUrlProviderFacts.cs | 27 ++++--- .../Providers/GitHubProviderFacts.cs | 3 +- .../Providers/ProviderManagerFacts.cs | 3 +- 13 files changed, 122 insertions(+), 31 deletions(-) create mode 100644 src/GitLink.Tests/GitLink.Tests.ruleset diff --git a/src/GitLink.Tests/ApprovalTestsConfig.cs b/src/GitLink.Tests/ApprovalTestsConfig.cs index 7967abb..f5d3ac4 100644 --- a/src/GitLink.Tests/ApprovalTestsConfig.cs +++ b/src/GitLink.Tests/ApprovalTestsConfig.cs @@ -1,10 +1,9 @@ // -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- - using ApprovalTests.Reporters; [assembly: UseReporter(typeof(DiffReporter))] \ No newline at end of file diff --git a/src/GitLink.Tests/GitLink.Tests.csproj b/src/GitLink.Tests/GitLink.Tests.csproj index 7f3e92d..002abcf 100644 --- a/src/GitLink.Tests/GitLink.Tests.csproj +++ b/src/GitLink.Tests/GitLink.Tests.csproj @@ -27,6 +27,9 @@ DEBUG;TRACE prompt 4 + ..\..\bin\Debug\GitLink.Tests\GitLink.Tests.XML + CS1591 + GitLink.Tests.ruleset pdbonly @@ -35,6 +38,9 @@ TRACE prompt 4 + ..\..\bin\Release\GitLink.Tests\GitLink.Tests.XML + CS1591 + GitLink.Tests.ruleset @@ -85,6 +91,10 @@ + + stylecop.json + + diff --git a/src/GitLink.Tests/GitLink.Tests.ruleset b/src/GitLink.Tests/GitLink.Tests.ruleset new file mode 100644 index 0000000..3094758 --- /dev/null +++ b/src/GitLink.Tests/GitLink.Tests.ruleset @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/GitLink.Tests/GlobalInitialization.cs b/src/GitLink.Tests/GlobalInitialization.cs index 96017af..cf0c7c8 100644 --- a/src/GitLink.Tests/GlobalInitialization.cs +++ b/src/GitLink.Tests/GlobalInitialization.cs @@ -1,17 +1,16 @@ // -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- - using Catel.Logging; using NUnit.Framework; [SetUpFixture] public class GlobalInitialization { - //[SetUp] + ////[SetUp] public static void SetUp() { #if DEBUG diff --git a/src/GitLink.Tests/IntegrationTests/BitBucketIntegration.cs b/src/GitLink.Tests/IntegrationTests/BitBucketIntegration.cs index 3e9cbd7..abc12a8 100644 --- a/src/GitLink.Tests/IntegrationTests/BitBucketIntegration.cs +++ b/src/GitLink.Tests/IntegrationTests/BitBucketIntegration.cs @@ -1,10 +1,9 @@ // -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. +// +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- - namespace GitLink.Tests.IntegrationTests { using NUnit.Framework; diff --git a/src/GitLink.Tests/IntegrationTests/GitHubIntegration.cs b/src/GitLink.Tests/IntegrationTests/GitHubIntegration.cs index 2b08cfb..86105c0 100644 --- a/src/GitLink.Tests/IntegrationTests/GitHubIntegration.cs +++ b/src/GitLink.Tests/IntegrationTests/GitHubIntegration.cs @@ -1,10 +1,9 @@ // -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- - namespace GitLink.Tests.IntegrationTests { using NUnit.Framework; diff --git a/src/GitLink.Tests/IntegrationTests/IntegrationTestBase.cs b/src/GitLink.Tests/IntegrationTests/IntegrationTestBase.cs index de6b421..dedf90c 100644 --- a/src/GitLink.Tests/IntegrationTests/IntegrationTestBase.cs +++ b/src/GitLink.Tests/IntegrationTests/IntegrationTestBase.cs @@ -1,10 +1,9 @@ // -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- - namespace GitLink.Tests.IntegrationTests { using System; diff --git a/src/GitLink.Tests/Properties/AssemblyInfo.cs b/src/GitLink.Tests/Properties/AssemblyInfo.cs index 2add1d8..800d40d 100644 --- a/src/GitLink.Tests/Properties/AssemblyInfo.cs +++ b/src/GitLink.Tests/Properties/AssemblyInfo.cs @@ -1,6 +1,6 @@ // -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- diff --git a/src/GitLink.Tests/Providers/BitBucketProviderFacts.cs b/src/GitLink.Tests/Providers/BitBucketProviderFacts.cs index 5623278..1f0b85c 100644 --- a/src/GitLink.Tests/Providers/BitBucketProviderFacts.cs +++ b/src/GitLink.Tests/Providers/BitBucketProviderFacts.cs @@ -1,10 +1,9 @@ // -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- - namespace GitLink.Tests.Providers { using GitLink.Providers; diff --git a/src/GitLink.Tests/Providers/CustomRawUrlProviderFacts.cs b/src/GitLink.Tests/Providers/CustomRawUrlProviderFacts.cs index 1065e26..d8a5472 100644 --- a/src/GitLink.Tests/Providers/CustomRawUrlProviderFacts.cs +++ b/src/GitLink.Tests/Providers/CustomRawUrlProviderFacts.cs @@ -1,4 +1,10 @@ -namespace GitLink.Tests.Providers +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace GitLink.Tests.Providers { using GitLink.Providers; using NUnit.Framework; diff --git a/src/GitLink.Tests/Providers/CustomUrlProviderFacts.cs b/src/GitLink.Tests/Providers/CustomUrlProviderFacts.cs index 1c26e06..27060fe 100644 --- a/src/GitLink.Tests/Providers/CustomUrlProviderFacts.cs +++ b/src/GitLink.Tests/Providers/CustomUrlProviderFacts.cs @@ -1,20 +1,25 @@ -namespace GitLink.Tests.Providers +// +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. +// + +namespace GitLink.Tests.Providers { - using GitLink.Providers; - using NUnit.Framework; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; + using GitLink.Providers; + using NUnit.Framework; public class CustomUrlProviderFacts { - private const string correctUrl = "https://bitbucket.intra.company.com/projects/aaa/repos/a/browse/{filename}?at={revision}&raw"; + private const string CorrectUrl = "https://bitbucket.intra.company.com/projects/aaa/repos/a/browse/{filename}?at={revision}&raw"; + [TestFixture] public class TheInitialization { - [TestCase(correctUrl, true)] + [TestCase(CorrectUrl, true)] [TestCase("https://example.com/repo", false)] [TestCase("https://bitbucket.intra.company.com/projects/aaa/repos/a/browse/{filename}?raw", true)] [TestCase("gopher://example.com/repo", false)] @@ -34,7 +39,7 @@ public class TheGitHubProviderProperties public void ReturnsNullCompany() { var provider = new CustomUrlProvider(); - provider.Initialize(correctUrl); + provider.Initialize(CorrectUrl); Assert.IsNull(provider.CompanyName); } @@ -43,7 +48,7 @@ public void ReturnsNullCompany() public void ReturnsNullCompanyUrl() { var provider = new CustomUrlProvider(); - provider.Initialize(correctUrl); + provider.Initialize(CorrectUrl); Assert.IsNull(provider.CompanyUrl); } @@ -52,7 +57,7 @@ public void ReturnsNullCompanyUrl() public void ReturnsNullProject() { var provider = new CustomUrlProvider(); - provider.Initialize(correctUrl); + provider.Initialize(CorrectUrl); Assert.IsNull(provider.ProjectName); } @@ -61,7 +66,7 @@ public void ReturnsNullProject() public void ReturnsNullProjectUrl() { var provider = new CustomUrlProvider(); - provider.Initialize(correctUrl); + provider.Initialize(CorrectUrl); Assert.IsNull(provider.ProjectUrl); } @@ -70,9 +75,9 @@ public void ReturnsNullProjectUrl() public void ReturnsValidRawGitUrl() { var provider = new CustomUrlProvider(); - provider.Initialize(correctUrl); + provider.Initialize(CorrectUrl); - string correctReturnedUrl = correctUrl.Replace("{filename}", "%var2%"); + string correctReturnedUrl = CorrectUrl.Replace("{filename}", "%var2%"); correctReturnedUrl = correctReturnedUrl.Replace("{revision}", "{0}"); Assert.AreEqual(correctReturnedUrl, provider.RawGitUrl); diff --git a/src/GitLink.Tests/Providers/GitHubProviderFacts.cs b/src/GitLink.Tests/Providers/GitHubProviderFacts.cs index 45a9731..14f0df4 100644 --- a/src/GitLink.Tests/Providers/GitHubProviderFacts.cs +++ b/src/GitLink.Tests/Providers/GitHubProviderFacts.cs @@ -1,10 +1,9 @@ // -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- - namespace GitLink.Tests.Providers { using GitLink.Providers; diff --git a/src/GitLink.Tests/Providers/ProviderManagerFacts.cs b/src/GitLink.Tests/Providers/ProviderManagerFacts.cs index 2670de8..3a20627 100644 --- a/src/GitLink.Tests/Providers/ProviderManagerFacts.cs +++ b/src/GitLink.Tests/Providers/ProviderManagerFacts.cs @@ -1,10 +1,9 @@ // -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) 2014 - 2014 CatenaLogic. All rights reserved. +// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- - namespace GitLink.Tests.Providers { using System; From 6f3d77f850e110487787908731ab6c98c2c7ec3a Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sun, 9 Oct 2016 12:59:09 -0700 Subject: [PATCH 46/85] Fix msbuild warning about dependent assembly version conflicts --- src/GitLink.Tests/project.json | 1 + 1 file changed, 1 insertion(+) diff --git a/src/GitLink.Tests/project.json b/src/GitLink.Tests/project.json index ba90913..556abd5 100644 --- a/src/GitLink.Tests/project.json +++ b/src/GitLink.Tests/project.json @@ -1,6 +1,7 @@ { "dependencies": { "ApprovalTests": "3.0.8", + "ApprovalUtilities": "3.0.8", "NUnit": "3.5.0", "NUnit3TestAdapter": "3.4.1" }, From 1b905b6ff374126ee1b0d8c71e67b095ea22316f Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sun, 9 Oct 2016 13:41:56 -0700 Subject: [PATCH 47/85] fix typo --- CONTRIBUTING.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 889389a..9babfd8 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -14,7 +14,7 @@ Contributors ### How to Build -To build this projeect, first run the init script in the root of the repo, +To build this project, first run the init script in the root of the repo, then simply run "msbuild" from the src directory. ## Contributing Process From 3f48cce455fea12f4c8a129fa50efd1150fefdc9 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sun, 9 Oct 2016 17:28:04 -0700 Subject: [PATCH 48/85] Remove dead code and mark public types internal --- .../Extensions/BinaryReaderExtensions.cs | 11 ++------ src/GitLink/Extensions/PdbExtensions.cs | 16 ++++++++--- .../Extensions/RepositoryExtensions.cs | 28 ------------------- src/GitLink/Extensions/StringExtensions.cs | 21 -------------- src/GitLink/GitLink.csproj | 7 +---- src/GitLink/Helpers/PdbStrHelper.cs | 4 +-- src/GitLink/Linker.cs | 12 +++++++- src/GitLink/Logging/OutputLogListener.cs | 4 +-- src/GitLink/Pdb/Crypto.cs | 26 ----------------- 9 files changed, 30 insertions(+), 99 deletions(-) delete mode 100644 src/GitLink/Extensions/RepositoryExtensions.cs delete mode 100644 src/GitLink/Extensions/StringExtensions.cs delete mode 100644 src/GitLink/Pdb/Crypto.cs diff --git a/src/GitLink/Extensions/BinaryReaderExtensions.cs b/src/GitLink/Extensions/BinaryReaderExtensions.cs index c445cd4..269f02e 100644 --- a/src/GitLink/Extensions/BinaryReaderExtensions.cs +++ b/src/GitLink/Extensions/BinaryReaderExtensions.cs @@ -12,16 +12,9 @@ namespace GitLink using System.Text; using Catel; - public static class BinaryReaderExtensions + internal static class BinaryReaderExtensions { - public static Guid ReadGuid(this BinaryReader binaryReader) - { - Argument.IsNotNull(() => binaryReader); - - return new Guid(binaryReader.ReadBytes(16)); - } - - public static string ReadCString(this BinaryReader binaryReader) + internal static string ReadCString(this BinaryReader binaryReader) { Argument.IsNotNull(() => binaryReader); diff --git a/src/GitLink/Extensions/PdbExtensions.cs b/src/GitLink/Extensions/PdbExtensions.cs index 36c3171..1af1929 100644 --- a/src/GitLink/Extensions/PdbExtensions.cs +++ b/src/GitLink/Extensions/PdbExtensions.cs @@ -14,9 +14,9 @@ namespace GitLink using Catel; using Pdb; - public static class PdbExtensions + internal static class PdbExtensions { - public static IEnumerable FindMissingOrChangedSourceFiles(this PdbFile pdbFile) + internal static IEnumerable FindMissingOrChangedSourceFiles(this PdbFile pdbFile) { Argument.IsNotNull(() => pdbFile); @@ -25,7 +25,7 @@ public static IEnumerable FindMissingOrChangedSourceFiles(this PdbFile p string file = checksumInfo.Key; byte[] expectedChecksum = checksumInfo.Value; HashAlgorithm hasher = expectedChecksum.Length == 16 ? (HashAlgorithm)MD5.Create() : SHA1.Create(); - byte[] actualChecksum = File.Exists(file) ? Crypto.HashFile(hasher, file) : null; + byte[] actualChecksum = File.Exists(file) ? HashFile(hasher, file) : null; if (!AreEqualBuffers(expectedChecksum, actualChecksum)) { @@ -34,7 +34,7 @@ public static IEnumerable FindMissingOrChangedSourceFiles(this PdbFile p } } - public static IReadOnlyDictionary GetFilesAndChecksums(this PdbFile pdbFile) + internal static IReadOnlyDictionary GetFilesAndChecksums(this PdbFile pdbFile) { Argument.IsNotNull(() => pdbFile); @@ -65,6 +65,14 @@ public static IReadOnlyDictionary GetFilesAndChecksums(this PdbF return results; } + private static byte[] HashFile(HashAlgorithm ha, string file) + { + using (var fs = File.OpenRead(file)) + { + return ha.ComputeHash(fs); + } + } + private static bool AreEqualBuffers(byte[] first, byte[] second) { if (first == null && second == null) diff --git a/src/GitLink/Extensions/RepositoryExtensions.cs b/src/GitLink/Extensions/RepositoryExtensions.cs deleted file mode 100644 index 26463b0..0000000 --- a/src/GitLink/Extensions/RepositoryExtensions.cs +++ /dev/null @@ -1,28 +0,0 @@ -// -// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. -// - -namespace GitLink -{ - using System; - using System.Collections.Generic; - using System.Linq; - using System.Text; - using System.Threading.Tasks; - using Catel; - using Catel.IO; - using LibGit2Sharp; - - internal static class RepositoryExtensions - { - internal static string GetRepoNormalizedPath(this Repository repository, string path) - { - Argument.IsNotNull(nameof(repository), repository); - Argument.IsNotNullOrEmpty(nameof(path), path); - - string relativePath = Path.GetRelativePath(path, repository.Info.WorkingDirectory); - var repoFile = repository.Index.FirstOrDefault(e => string.Equals(e.Path, relativePath, StringComparison.OrdinalIgnoreCase)); - return repoFile?.Path; - } - } -} diff --git a/src/GitLink/Extensions/StringExtensions.cs b/src/GitLink/Extensions/StringExtensions.cs deleted file mode 100644 index 26b5f11..0000000 --- a/src/GitLink/Extensions/StringExtensions.cs +++ /dev/null @@ -1,21 +0,0 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. -// -// -------------------------------------------------------------------------------------------------------------------- - -namespace GitLink -{ - using Catel; - - public static class StringExtensions - { - public static string OptimizeUrl(this string url) - { - Argument.IsNotNullOrWhitespace(() => url); - - url = url.EndsWith(".git") ? url.Substring(0, url.Length - 4) : url; - return url; - } - } -} \ No newline at end of file diff --git a/src/GitLink/GitLink.csproj b/src/GitLink/GitLink.csproj index 502017c..77ac5b2 100644 --- a/src/GitLink/GitLink.csproj +++ b/src/GitLink/GitLink.csproj @@ -62,14 +62,11 @@ - - - @@ -102,9 +99,7 @@ - - - + diff --git a/src/GitLink/Helpers/PdbStrHelper.cs b/src/GitLink/Helpers/PdbStrHelper.cs index ed98896..f1fe32d 100644 --- a/src/GitLink/Helpers/PdbStrHelper.cs +++ b/src/GitLink/Helpers/PdbStrHelper.cs @@ -10,11 +10,11 @@ namespace GitLink using Catel; using Catel.Logging; - public static class PdbStrHelper + internal static class PdbStrHelper { private static readonly ILog Log = LogManager.GetCurrentClassLogger(); - public static void Execute(string pdbStrFileName, string projectPdbFile, string pdbStrFile) + internal static void Execute(string pdbStrFileName, string projectPdbFile, string pdbStrFile) { Argument.IsNotNullOrWhitespace(() => projectPdbFile); Argument.IsNotNullOrWhitespace(() => pdbStrFile); diff --git a/src/GitLink/Linker.cs b/src/GitLink/Linker.cs index 56fece8..c6ce4b1 100644 --- a/src/GitLink/Linker.cs +++ b/src/GitLink/Linker.cs @@ -92,7 +92,7 @@ public static class Linker try { Repository repo = repository.Value; - repoSourceFiles = sourceFiles.ToDictionary(e => e, repo.GetRepoNormalizedPath); + repoSourceFiles = sourceFiles.ToDictionary(e => e, e => GetNormalizedPath(e, repo)); } catch (RepositoryNotFoundException) { @@ -194,6 +194,16 @@ private static void CreateSrcSrv(string srcsrvFile, SrcSrvContext srcSrvContext) } } + private static string GetNormalizedPath(string path, Repository repository) + { + Argument.IsNotNull(nameof(repository), repository); + Argument.IsNotNullOrEmpty(nameof(path), path); + + string relativePath = Catel.IO.Path.GetRelativePath(path, repository.Info.WorkingDirectory); + var repoFile = repository.Index.FirstOrDefault(e => string.Equals(e.Path, relativePath, StringComparison.OrdinalIgnoreCase)); + return repoFile?.Path; + } + private static string GetNormalizedPath(string path, string gitRepoRootDir) { Argument.IsNotNullOrEmpty(nameof(path), path); diff --git a/src/GitLink/Logging/OutputLogListener.cs b/src/GitLink/Logging/OutputLogListener.cs index 258b393..cc58a3d 100644 --- a/src/GitLink/Logging/OutputLogListener.cs +++ b/src/GitLink/Logging/OutputLogListener.cs @@ -9,9 +9,9 @@ namespace GitLink.Logging using System; using Catel.Logging; - public class OutputLogListener : ConsoleLogListener + internal class OutputLogListener : ConsoleLogListener { - public OutputLogListener() + internal OutputLogListener() { IgnoreCatelLogging = true; IsDebugEnabled = true; diff --git a/src/GitLink/Pdb/Crypto.cs b/src/GitLink/Pdb/Crypto.cs deleted file mode 100644 index ddb054b..0000000 --- a/src/GitLink/Pdb/Crypto.cs +++ /dev/null @@ -1,26 +0,0 @@ -// -------------------------------------------------------------------------------------------------------------------- -// -// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. -// -// -------------------------------------------------------------------------------------------------------------------- - -namespace GitLink.Pdb -{ - using System; - using System.Collections.Generic; - using System.IO; - using System.Linq; - using System.Security.Cryptography; - using Catel; - - public static class Crypto - { - public static byte[] HashFile(HashAlgorithm ha, string file) - { - using (var fs = File.OpenRead(file)) - { - return ha.ComputeHash(fs); - } - } - } -} \ No newline at end of file From 249228e1c170e4a2437d3c9bf262d278c68965bf Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sun, 9 Oct 2016 17:34:23 -0700 Subject: [PATCH 49/85] Make more types internal --- src/GitLink/Pdb/PdbFile.cs | 30 ++++++++++++++-------------- src/GitLink/Pdb/PdbInfo.cs | 34 ++++++++++++++++---------------- src/GitLink/Pdb/PdbName.cs | 10 +++++----- src/GitLink/Pdb/PdbRoot.cs | 10 +++++----- src/GitLink/Pdb/PdbStream.cs | 6 +++--- src/GitLink/Pdb/SrcSrv.cs | 6 +++--- src/GitLink/Pdb/SrcSrvContext.cs | 14 ++++++------- 7 files changed, 55 insertions(+), 55 deletions(-) diff --git a/src/GitLink/Pdb/PdbFile.cs b/src/GitLink/Pdb/PdbFile.cs index ad06990..d4e82d8 100644 --- a/src/GitLink/Pdb/PdbFile.cs +++ b/src/GitLink/Pdb/PdbFile.cs @@ -12,7 +12,7 @@ namespace GitLink.Pdb using Catel; using Catel.Logging; - public class PdbFile : IDisposable + internal class PdbFile : IDisposable { private static readonly ILog Log = LogManager.GetCurrentClassLogger(); @@ -30,7 +30,7 @@ public class PdbFile : IDisposable private readonly SortedSet _freePages; private readonly byte[] _zerosPage; - public PdbFile(string path) + internal PdbFile(string path) { Argument.IsNotNullOrWhitespace(() => path); @@ -49,35 +49,35 @@ public PdbFile(string path) _zerosPage = new byte[_pageByteCount]; } - public string Path { get; private set; } + internal string Path { get; private set; } - public string PathSrcSrv + internal string PathSrcSrv { get { return Path + ".srcsrv"; } } - public bool HasSrcSrv + internal bool HasSrcSrv { get { return Info.NameToPdbName.ContainsKey(SrcSrvContent); } } - public int SrcSrv + internal int SrcSrv { get { return Info.NameToPdbName[SrcSrvContent].Stream; } } - public int RootPage { get; private set; } + internal int RootPage { get; private set; } - public PdbRoot Root { get; private set; } + internal PdbRoot Root { get; private set; } - public PdbRoot Stream0 + internal PdbRoot Stream0 { get { return ReadRoot(Root.Streams[0]); } } - public int PagesFree { get; private set; } + internal int PagesFree { get; private set; } - public int PageCount { get; private set; } + internal int PageCount { get; private set; } private void CheckPdbHeader() { @@ -161,7 +161,7 @@ private PdbRoot ReadRoot(PdbStream streamRoot) return root; } - public PdbStream GetRootPdbStream() + internal PdbStream GetRootPdbStream() { var pdbStream = new PdbStream(); pdbStream.ByteCount = _rootByteCount; @@ -350,19 +350,19 @@ private List AllocPages(int n) return pages; } - public byte[] ReadPdbStreamBytes(PdbStream pdbStream) + internal byte[] ReadPdbStreamBytes(PdbStream pdbStream) { Argument.IsNotNull(() => pdbStream); return ReadStreamBytes(pdbStream); } - public byte[] ReadStreamBytes(int stream) + internal byte[] ReadStreamBytes(int stream) { return ReadStreamBytes(Root.Streams[stream]); } - public PdbInfo Info + internal PdbInfo Info { get { return _info ?? (_info = InternalInfo()); } } diff --git a/src/GitLink/Pdb/PdbInfo.cs b/src/GitLink/Pdb/PdbInfo.cs index 30d3bc0..c83d379 100644 --- a/src/GitLink/Pdb/PdbInfo.cs +++ b/src/GitLink/Pdb/PdbInfo.cs @@ -10,9 +10,9 @@ namespace GitLink.Pdb using System.Collections.Generic; using Catel; - public class PdbInfo + internal class PdbInfo { - public PdbInfo() + internal PdbInfo() { Guid = default(Guid); StreamToPdbName = new SortedDictionary(); @@ -23,37 +23,37 @@ public PdbInfo() Tail = new byte[0]; } - public int Version { get; set; } + internal int Version { get; set; } - public int Signature { get; set; } + internal int Signature { get; set; } - public Guid Guid { get; set; } + internal Guid Guid { get; set; } - public int Age { get; set; } + internal int Age { get; set; } - public int FlagIndexMax { get; set; } + internal int FlagIndexMax { get; set; } - public int FlagCount { get; set; } + internal int FlagCount { get; set; } - public IDictionary StreamToPdbName { get; private set; } + internal IDictionary StreamToPdbName { get; private set; } - public IDictionary NameToPdbName { get; private set; } + internal IDictionary NameToPdbName { get; private set; } - public IDictionary FlagIndexToPdbName { get; private set; } + internal IDictionary FlagIndexToPdbName { get; private set; } - public SortedSet FlagIndexes { get; private set; } + internal SortedSet FlagIndexes { get; private set; } - public string[] SrcSrv { get; set; } + internal string[] SrcSrv { get; set; } - public byte[] Tail { get; set; } + internal byte[] Tail { get; set; } - public void ClearFlags() + internal void ClearFlags() { FlagIndexes.Clear(); FlagIndexToPdbName.Clear(); } - public void AddFlag(PdbName name) + internal void AddFlag(PdbName name) { Argument.IsNotNull(() => name); @@ -61,7 +61,7 @@ public void AddFlag(PdbName name) FlagIndexToPdbName.Add(name.FlagIndex, name); } - public void AddName(PdbName name) + internal void AddName(PdbName name) { Argument.IsNotNull(() => name); diff --git a/src/GitLink/Pdb/PdbName.cs b/src/GitLink/Pdb/PdbName.cs index ac69ce5..f74ab66 100644 --- a/src/GitLink/Pdb/PdbName.cs +++ b/src/GitLink/Pdb/PdbName.cs @@ -6,17 +6,17 @@ namespace GitLink.Pdb { - public class PdbName + internal class PdbName { - public PdbName(string name = "") + internal PdbName(string name = "") { Name = name; } - public int Stream { get; set; } + internal int Stream { get; set; } - public string Name { get; set; } + internal string Name { get; set; } - public int FlagIndex { get; set; } + internal int FlagIndex { get; set; } } } \ No newline at end of file diff --git a/src/GitLink/Pdb/PdbRoot.cs b/src/GitLink/Pdb/PdbRoot.cs index a718bb6..e13ad5e 100644 --- a/src/GitLink/Pdb/PdbRoot.cs +++ b/src/GitLink/Pdb/PdbRoot.cs @@ -9,9 +9,9 @@ namespace GitLink.Pdb using System.Collections.Generic; using Catel; - public class PdbRoot + internal class PdbRoot { - public PdbRoot(PdbStream stream) + internal PdbRoot(PdbStream stream) { Argument.IsNotNull(() => stream); @@ -19,11 +19,11 @@ public PdbRoot(PdbStream stream) Streams = new List(); } - public PdbStream Stream { get; set; } + internal PdbStream Stream { get; set; } - public List Streams { get; private set; } + internal List Streams { get; private set; } - public int AddStream(PdbStream stream) + internal int AddStream(PdbStream stream) { Streams.Add(stream); diff --git a/src/GitLink/Pdb/PdbStream.cs b/src/GitLink/Pdb/PdbStream.cs index fca03cb..1928258 100644 --- a/src/GitLink/Pdb/PdbStream.cs +++ b/src/GitLink/Pdb/PdbStream.cs @@ -6,10 +6,10 @@ namespace GitLink.Pdb { - public class PdbStream + internal class PdbStream { - public int ByteCount { get; set; } + internal int ByteCount { get; set; } - public int[] Pages { get; set; } + internal int[] Pages { get; set; } } } \ No newline at end of file diff --git a/src/GitLink/Pdb/SrcSrv.cs b/src/GitLink/Pdb/SrcSrv.cs index ed0fcc2..019aee3 100644 --- a/src/GitLink/Pdb/SrcSrv.cs +++ b/src/GitLink/Pdb/SrcSrv.cs @@ -11,14 +11,14 @@ namespace GitLink.Pdb using System.IO; using Catel; - public static class SrcSrv + internal static class SrcSrv { private static string CreateTarget(string rawUrl, string revision) { return string.Format(rawUrl, revision); } - public static byte[] Create(string rawUrl, string revision, IEnumerable> paths, bool downloadWithPowershell) + internal static byte[] Create(string rawUrl, string revision, IEnumerable> paths, bool downloadWithPowershell) { Argument.IsNotNullOrWhitespace(() => rawUrl); Argument.IsNotNullOrWhitespace(() => revision); @@ -61,7 +61,7 @@ public static byte[] Create(string rawUrl, string revision, IEnumerable> paths, Dictionary vstsData = null) + internal static byte[] CreateVsts(string revision, IEnumerable> paths, Dictionary vstsData = null) { Argument.IsNotNullOrWhitespace(() => revision); diff --git a/src/GitLink/Pdb/SrcSrvContext.cs b/src/GitLink/Pdb/SrcSrvContext.cs index b957557..5821738 100644 --- a/src/GitLink/Pdb/SrcSrvContext.cs +++ b/src/GitLink/Pdb/SrcSrvContext.cs @@ -9,22 +9,22 @@ namespace GitLink.Pdb using System; using System.Collections.Generic; - public class SrcSrvContext + internal class SrcSrvContext { - public SrcSrvContext() + internal SrcSrvContext() { Paths = new List>(); VstsData = new Dictionary(); } - public string RawUrl { get; set; } + internal string RawUrl { get; set; } - public bool DownloadWithPowershell { get; set; } + internal bool DownloadWithPowershell { get; set; } - public string Revision { get; set; } + internal string Revision { get; set; } - public List> Paths { get; private set; } + internal List> Paths { get; private set; } - public Dictionary VstsData { get; private set; } + internal Dictionary VstsData { get; private set; } } } From 38bf52b4edf221b1b18f414ce7269cd0cb87a425 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sun, 9 Oct 2016 17:43:11 -0700 Subject: [PATCH 50/85] Fix name of fields that produced copy/paste errors --- src/GitLink/Providers/BitBucketProvider.cs | 4 ++-- src/GitLink/Providers/CustomRawUrlProvider.cs | 4 ++-- src/GitLink/Providers/CustomUrlProvider.cs | 4 ++-- src/GitLink/Providers/GitHubProvider.cs | 4 ++-- src/GitLink/Providers/VisualStudioTeamServicesProvider.cs | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/GitLink/Providers/BitBucketProvider.cs b/src/GitLink/Providers/BitBucketProvider.cs index abbb2e8..7e5c857 100644 --- a/src/GitLink/Providers/BitBucketProvider.cs +++ b/src/GitLink/Providers/BitBucketProvider.cs @@ -12,7 +12,7 @@ namespace GitLink.Providers public class BitBucketProvider : ProviderBase { - private readonly Regex _gitHubRegex = new Regex(@"(?(?(?:https://)?bitbucket\.org/(?[^/]+))/(?[^/]+))"); + private static readonly Regex HostingUrlPattern = new Regex(@"(?(?(?:https://)?bitbucket\.org/(?[^/]+))/(?[^/]+))"); public BitBucketProvider() : base(new GitPreparer()) @@ -26,7 +26,7 @@ public override string RawGitUrl public override bool Initialize(string url) { - var match = _gitHubRegex.Match(url); + var match = HostingUrlPattern.Match(url); if (!match.Success) { diff --git a/src/GitLink/Providers/CustomRawUrlProvider.cs b/src/GitLink/Providers/CustomRawUrlProvider.cs index b289a5f..d908373 100644 --- a/src/GitLink/Providers/CustomRawUrlProvider.cs +++ b/src/GitLink/Providers/CustomRawUrlProvider.cs @@ -10,7 +10,7 @@ namespace GitLink.Providers public sealed class CustomRawUrlProvider : ProviderBase { - private readonly Regex _regex = new Regex(@"https?://.+"); + private static readonly Regex HostingUrlPattern = new Regex(@"https?://.+"); private string _rawUrl; @@ -29,7 +29,7 @@ public override string RawGitUrl public override bool Initialize(string url) { - if (string.IsNullOrEmpty(url) || !_regex.IsMatch(url)) + if (string.IsNullOrEmpty(url) || !HostingUrlPattern.IsMatch(url)) { return false; } diff --git a/src/GitLink/Providers/CustomUrlProvider.cs b/src/GitLink/Providers/CustomUrlProvider.cs index 716922e..2d4c3a1 100644 --- a/src/GitLink/Providers/CustomUrlProvider.cs +++ b/src/GitLink/Providers/CustomUrlProvider.cs @@ -11,7 +11,7 @@ public sealed class CustomUrlProvider : ProviderBase { private const string FileNamePlaceHolder = "{filename}"; private const string RevisionPlaceHolder = "{revision}"; - private readonly Regex _regexUrl = new Regex(@"https?://.+"); + private static readonly Regex HostingUrlPattern = new Regex(@"https?://.+"); private string _rawUrl; @@ -30,7 +30,7 @@ public override string RawGitUrl public override bool Initialize(string url) { - if (string.IsNullOrEmpty(url) || !_regexUrl.IsMatch(url) || + if (string.IsNullOrEmpty(url) || !HostingUrlPattern.IsMatch(url) || (!url.Contains(FileNamePlaceHolder) && !url.Contains(RevisionPlaceHolder))) { return false; diff --git a/src/GitLink/Providers/GitHubProvider.cs b/src/GitLink/Providers/GitHubProvider.cs index 5475078..17570fe 100644 --- a/src/GitLink/Providers/GitHubProvider.cs +++ b/src/GitLink/Providers/GitHubProvider.cs @@ -12,7 +12,7 @@ namespace GitLink.Providers public class GitHubProvider : ProviderBase { - private readonly Regex _gitHubRegex = new Regex(@"(?(?(?:https://)?github\.com/(?[^/]+))/(?[^/]+))"); + private static readonly Regex HostingUrlPattern = new Regex(@"(?(?(?:https://)?github\.com/(?[^/]+))/(?[^/]+))"); public GitHubProvider() : base(new GitPreparer()) @@ -26,7 +26,7 @@ public override string RawGitUrl public override bool Initialize(string url) { - var match = _gitHubRegex.Match(url); + var match = HostingUrlPattern.Match(url); if (!match.Success) { diff --git a/src/GitLink/Providers/VisualStudioTeamServicesProvider.cs b/src/GitLink/Providers/VisualStudioTeamServicesProvider.cs index 4b2811c..c3ea846 100644 --- a/src/GitLink/Providers/VisualStudioTeamServicesProvider.cs +++ b/src/GitLink/Providers/VisualStudioTeamServicesProvider.cs @@ -13,7 +13,7 @@ namespace GitLink.Providers public class VisualStudioTeamServicesProvider : ProviderBase { - private readonly Regex _visualStudioTeamServicesRegex = new Regex(@"(?(?(?:https://)?(?([a-zA-Z0-9\-\.]*)?)\.visualstudio\.com/)(?[a-zA-Z0-9\-\.]*)/?_git/(?[^/]+))"); + private static readonly Regex HostingUrlPattern = new Regex(@"(?(?(?:https://)?(?([a-zA-Z0-9\-\.]*)?)\.visualstudio\.com/)(?[a-zA-Z0-9\-\.]*)/?_git/(?[^/]+))"); public VisualStudioTeamServicesProvider() : base(new GitPreparer()) @@ -27,7 +27,7 @@ public override string RawGitUrl public override bool Initialize(string url) { - var match = _visualStudioTeamServicesRegex.Match(url); + var match = HostingUrlPattern.Match(url); if (!match.Success) { From deaeeddfaa912af68f5a1fc079f7d803f6509077 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sun, 9 Oct 2016 17:47:38 -0700 Subject: [PATCH 51/85] Abbreviate syntax for RawGitUrl property --- src/GitLink/Providers/BitBucketProvider.cs | 5 +---- src/GitLink/Providers/CustomRawUrlProvider.cs | 8 +------- src/GitLink/Providers/CustomUrlProvider.cs | 8 +------- src/GitLink/Providers/GitHubProvider.cs | 5 +---- src/GitLink/Providers/VisualStudioTeamServicesProvider.cs | 5 +---- 5 files changed, 5 insertions(+), 26 deletions(-) diff --git a/src/GitLink/Providers/BitBucketProvider.cs b/src/GitLink/Providers/BitBucketProvider.cs index 7e5c857..eb303cd 100644 --- a/src/GitLink/Providers/BitBucketProvider.cs +++ b/src/GitLink/Providers/BitBucketProvider.cs @@ -19,10 +19,7 @@ public BitBucketProvider() { } - public override string RawGitUrl - { - get { return String.Format("https://bitbucket.org/{0}/{1}/raw", CompanyName, ProjectName); } - } + public override string RawGitUrl => $"https://bitbucket.org/{CompanyName}/{ProjectName}/raw"; public override bool Initialize(string url) { diff --git a/src/GitLink/Providers/CustomRawUrlProvider.cs b/src/GitLink/Providers/CustomRawUrlProvider.cs index d908373..c597fa2 100644 --- a/src/GitLink/Providers/CustomRawUrlProvider.cs +++ b/src/GitLink/Providers/CustomRawUrlProvider.cs @@ -19,13 +19,7 @@ public CustomRawUrlProvider() { } - public override string RawGitUrl - { - get - { - return _rawUrl; - } - } + public override string RawGitUrl => _rawUrl; public override bool Initialize(string url) { diff --git a/src/GitLink/Providers/CustomUrlProvider.cs b/src/GitLink/Providers/CustomUrlProvider.cs index 2d4c3a1..236d7a1 100644 --- a/src/GitLink/Providers/CustomUrlProvider.cs +++ b/src/GitLink/Providers/CustomUrlProvider.cs @@ -20,13 +20,7 @@ public CustomUrlProvider() { } - public override string RawGitUrl - { - get - { - return _rawUrl; - } - } + public override string RawGitUrl => _rawUrl; public override bool Initialize(string url) { diff --git a/src/GitLink/Providers/GitHubProvider.cs b/src/GitLink/Providers/GitHubProvider.cs index 17570fe..6177977 100644 --- a/src/GitLink/Providers/GitHubProvider.cs +++ b/src/GitLink/Providers/GitHubProvider.cs @@ -19,10 +19,7 @@ public GitHubProvider() { } - public override string RawGitUrl - { - get { return String.Format("https://raw.github.com/{0}/{1}", CompanyName, ProjectName); } - } + public override string RawGitUrl => $"https://raw.github.com/{CompanyName}/{ProjectName}"; public override bool Initialize(string url) { diff --git a/src/GitLink/Providers/VisualStudioTeamServicesProvider.cs b/src/GitLink/Providers/VisualStudioTeamServicesProvider.cs index c3ea846..56ecb28 100644 --- a/src/GitLink/Providers/VisualStudioTeamServicesProvider.cs +++ b/src/GitLink/Providers/VisualStudioTeamServicesProvider.cs @@ -20,10 +20,7 @@ public VisualStudioTeamServicesProvider() { } - public override string RawGitUrl - { - get { return string.Empty; } - } + public override string RawGitUrl => string.Empty; public override bool Initialize(string url) { From cb93cfef6b1ec7c72313959dccdb3a53b27fbe6c Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sun, 16 Oct 2016 16:54:12 -0700 Subject: [PATCH 52/85] Suppress GitLink in design-time builds Also make a couple settings more user-configurable. --- src/GitLinkTask/GitLink.props | 7 +++++++ src/GitLinkTask/GitLink.targets | 6 ++++-- src/GitLinkTask/GitLinkTask.csproj | 3 +++ 3 files changed, 14 insertions(+), 2 deletions(-) create mode 100644 src/GitLinkTask/GitLink.props diff --git a/src/GitLinkTask/GitLink.props b/src/GitLinkTask/GitLink.props new file mode 100644 index 0000000..40dad90 --- /dev/null +++ b/src/GitLinkTask/GitLink.props @@ -0,0 +1,7 @@ + + + + true + true + + diff --git a/src/GitLinkTask/GitLink.targets b/src/GitLinkTask/GitLink.targets index 57f02f5..0829efb 100644 --- a/src/GitLinkTask/GitLink.targets +++ b/src/GitLinkTask/GitLink.targets @@ -3,7 +3,9 @@ - + + ContinueOnError="$(GitLinkContinueOnError)" /> \ No newline at end of file diff --git a/src/GitLinkTask/GitLinkTask.csproj b/src/GitLinkTask/GitLinkTask.csproj index 81b1292..c60fda6 100644 --- a/src/GitLinkTask/GitLinkTask.csproj +++ b/src/GitLinkTask/GitLinkTask.csproj @@ -56,6 +56,9 @@ + + PreserveNewest + PreserveNewest From 634ce9340ed3cbbbfe0f57637342a745204abb34 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sat, 12 Nov 2016 11:14:46 -0800 Subject: [PATCH 53/85] Fix NullReferenceException thrown by NuGet in the build. --- src/GitLink/project.json | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/GitLink/project.json b/src/GitLink/project.json index 5e3e502..a793228 100644 --- a/src/GitLink/project.json +++ b/src/GitLink/project.json @@ -14,7 +14,10 @@ "version": "0.1.37-beta", "suppressParent": "none" }, - "StyleCop.Analyzers": "1.0.0", + "StyleCop.Analyzers": { + "version": "1.0.0", + "type": "build" + }, "Nerdbank.GitVersioning": { "version": "1.5.46", "suppressParent": "none" From 91ac83dd82372ae5f5ef983116d66ed7ddaed379 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Mon, 21 Nov 2016 22:12:51 -0800 Subject: [PATCH 54/85] Code review comment fixes --- src/GitLink/LinkOptions.cs | 6 ++++-- src/GitLink/Linker.cs | 3 ++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/GitLink/LinkOptions.cs b/src/GitLink/LinkOptions.cs index 0adf8d2..145ade8 100644 --- a/src/GitLink/LinkOptions.cs +++ b/src/GitLink/LinkOptions.cs @@ -1,6 +1,8 @@ -// -// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2016 Andrew Arnott. All rights reserved. // +// -------------------------------------------------------------------------------------------------------------------- namespace GitLink { diff --git a/src/GitLink/Linker.cs b/src/GitLink/Linker.cs index c6ce4b1..0931284 100644 --- a/src/GitLink/Linker.cs +++ b/src/GitLink/Linker.cs @@ -146,6 +146,7 @@ public static class Linker } } + // When using the VisualStudioTeamServicesProvider, add extra infomration to dictionary with VSTS-specific data if (provider is Providers.VisualStudioTeamServicesProvider) { srcSrvContext.VstsData["TFS_COLLECTION"] = provider.CompanyUrl; @@ -169,7 +170,7 @@ public static class Linker } } - Log.Debug("Created source server link file, updating pdb file '{0}'", Catel.IO.Path.GetRelativePath(pdbPath, repositoryDirectory)); + Log.Debug("Created source server link file, updating pdb file \"{0}\"", Catel.IO.Path.GetRelativePath(pdbPath, repositoryDirectory)); PdbStrHelper.Execute(PdbStrExePath, pdbPath, projectSrcSrvFile); var indexedFilesCount = repoSourceFiles.Values.Count(v => v != null); Log.Info($"Remote git source information for {indexedFilesCount}/{sourceFiles.Count} files written to pdb: \"{pdbPath}\""); From 3fe82a1145dd22764b86beb24c63a494a23c9413 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Tue, 22 Nov 2016 15:37:13 -0800 Subject: [PATCH 55/85] Rename GitLink task --- src/GitLink/GitLink.ruleset | 2 ++ src/GitLink/Linker.cs | 9 ++++++++- src/GitLinkTask/GitLinkTask.csproj | 2 +- src/GitLinkTask/{GitLink.cs => LinkPdbToGitRemote.cs} | 2 +- 4 files changed, 12 insertions(+), 3 deletions(-) rename src/GitLinkTask/{GitLink.cs => LinkPdbToGitRemote.cs} (98%) diff --git a/src/GitLink/GitLink.ruleset b/src/GitLink/GitLink.ruleset index d038a3e..7bbe483 100644 --- a/src/GitLink/GitLink.ruleset +++ b/src/GitLink/GitLink.ruleset @@ -79,5 +79,7 @@ + + \ No newline at end of file diff --git a/src/GitLink/Linker.cs b/src/GitLink/Linker.cs index 0931284..859a8ca 100644 --- a/src/GitLink/Linker.cs +++ b/src/GitLink/Linker.cs @@ -48,7 +48,14 @@ public static class Linker } else { - repositoryDirectory = GitDirFinder.TreeWalkForGitDir(Path.GetDirectoryName(sourceFiles.First())); + string someSourceFile = sourceFiles.FirstOrDefault(); + if (someSourceFile == null) + { + Log.Error("No source files were found in the PDB."); + return false; + } + + repositoryDirectory = GitDirFinder.TreeWalkForGitDir(Path.GetDirectoryName(someSourceFile)); if (repositoryDirectory == null) { Log.Error("No source files found that are tracked in a git repo."); diff --git a/src/GitLinkTask/GitLinkTask.csproj b/src/GitLinkTask/GitLinkTask.csproj index c60fda6..d427497 100644 --- a/src/GitLinkTask/GitLinkTask.csproj +++ b/src/GitLinkTask/GitLinkTask.csproj @@ -44,7 +44,7 @@ - + diff --git a/src/GitLinkTask/GitLink.cs b/src/GitLinkTask/LinkPdbToGitRemote.cs similarity index 98% rename from src/GitLinkTask/GitLink.cs rename to src/GitLinkTask/LinkPdbToGitRemote.cs index 8225d0a..236ae47 100644 --- a/src/GitLinkTask/GitLink.cs +++ b/src/GitLinkTask/LinkPdbToGitRemote.cs @@ -8,7 +8,7 @@ namespace GitLinkTask using Microsoft.Build.Framework; using Microsoft.Build.Utilities; - public class GitLink : Task + public class LinkPdbToGitRemote : Task { [Required] public ITaskItem PdbFile { get; set; } From 775f40b9cd4c6236dc233109d35430c7f270153f Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Wed, 23 Nov 2016 07:43:20 -0700 Subject: [PATCH 56/85] Removed this. qualifier per CR feedback --- src/GitLinkTask/LinkPdbToGitRemote.cs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/GitLinkTask/LinkPdbToGitRemote.cs b/src/GitLinkTask/LinkPdbToGitRemote.cs index 236ae47..328d83d 100644 --- a/src/GitLinkTask/LinkPdbToGitRemote.cs +++ b/src/GitLinkTask/LinkPdbToGitRemote.cs @@ -15,8 +15,8 @@ public class LinkPdbToGitRemote : Task public string Method { - get { return this.MethodEnum.ToString(); } - set { this.MethodEnum = string.IsNullOrEmpty(value) ? LinkMethod.Http : (LinkMethod)Enum.Parse(typeof(LinkMethod), value); } + get { return MethodEnum.ToString(); } + set { MethodEnum = string.IsNullOrEmpty(value) ? LinkMethod.Http : (LinkMethod)Enum.Parse(typeof(LinkMethod), value); } } public bool SkipVerify { get; set; } @@ -31,19 +31,19 @@ public string Method public override bool Execute() { - LogManager.AddListener(new MSBuildListener(this.Log)); + LogManager.AddListener(new MSBuildListener(Log)); var options = new LinkOptions { - Method = this.MethodEnum, - SkipVerify = this.SkipVerify, - GitRemoteUrl = this.GitRemoteUrl != null ? new Uri(this.GitRemoteUrl, UriKind.Absolute) : null, - CommitId = this.GitCommitId, - GitWorkingDirectory = this.GitWorkingDirectory, + Method = MethodEnum, + SkipVerify = SkipVerify, + GitRemoteUrl = GitRemoteUrl != null ? new Uri(GitRemoteUrl, UriKind.Absolute) : null, + CommitId = GitCommitId, + GitWorkingDirectory = GitWorkingDirectory, }; - bool success = Linker.Link(this.PdbFile.GetMetadata("FullPath"), options); + bool success = Linker.Link(PdbFile.GetMetadata("FullPath"), options); - return success && !this.Log.HasLoggedErrors; + return success && !Log.HasLoggedErrors; } private class MSBuildListener : LogListenerBase From 7b9435b5e3a73e0366b721a2eadad6bc00937714 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Wed, 23 Nov 2016 07:47:24 -0700 Subject: [PATCH 57/85] Rename field to use _ prefix --- src/GitLinkTask/LinkPdbToGitRemote.cs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/GitLinkTask/LinkPdbToGitRemote.cs b/src/GitLinkTask/LinkPdbToGitRemote.cs index 328d83d..2b4a06c 100644 --- a/src/GitLinkTask/LinkPdbToGitRemote.cs +++ b/src/GitLinkTask/LinkPdbToGitRemote.cs @@ -48,11 +48,11 @@ public override bool Execute() private class MSBuildListener : LogListenerBase { - private readonly TaskLoggingHelper log; + private readonly TaskLoggingHelper _log; internal MSBuildListener(TaskLoggingHelper log) { - this.log = log; + _log = log; } protected override void Write(ILog log, string message, LogEvent logEvent, object extraData, LogData logData, DateTime time) @@ -60,16 +60,16 @@ protected override void Write(ILog log, string message, LogEvent logEvent, objec switch (logEvent) { case LogEvent.Error: - this.log.LogError(message); + _log.LogError(message); break; case LogEvent.Warning: - this.log.LogWarning(message); + _log.LogWarning(message); break; case LogEvent.Info: - this.log.LogMessage(MessageImportance.Normal, message); + _log.LogMessage(MessageImportance.Normal, message); break; case LogEvent.Debug: - this.log.LogMessage(MessageImportance.Low, message); + _log.LogMessage(MessageImportance.Low, message); break; default: break; From 349ff27d68e49d1c8d46ba82c3d9a84583eef639 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Fri, 25 Nov 2016 08:15:17 -0700 Subject: [PATCH 58/85] Applied code review fixes --- README.md | 11 +++++------ src/GitLink/Linker.cs | 15 +++++++++++---- 2 files changed, 16 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 83002b3..5eaf98d 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,6 @@ GitLink [![Join the chat at https://gitter.im/GitTools/GitLink](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/GitTools/GitLink?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) ![License](https://img.shields.io/github/license/gittools/gitlink.svg) -[![NuGet downloads](https://img.shields.io/nuget/dt/gitlink.svg)][NuGetDownload] [![Version](https://img.shields.io/nuget/v/gitlink.svg)][NuGetDownload] [![Pre-release version](https://img.shields.io/nuget/vpre/gitlink.svg)][NuGetDownload] ![Chocolatey count](https://img.shields.io/chocolatey/dt/gitlink.svg) @@ -64,13 +63,13 @@ Using GitLink via the command line is very simple: When working with a content proxy or an alternative git VCS system that supports direct HTTP access to specific file revisions use the `-u` parameter with the custom raw content root URL - GitLink.exe c:\source\catel -u https://raw.githubusercontent.com/catel/catel + GitLink.exe -u https://raw.githubusercontent.com/catel/catel The custom url will be used to fill in the following pattern `{customUrl}/{revision}/{relativeFilePath}` when generating the source mapping. When working with a repository using uncommon URL you can use placeholders to specify where the filename and revision hash should be, use `-u` parameter with the custom URL - GitLink.exe c:\source\catel -u "https://host/projects/catel/repos/catel/browse/{filename}?at={revision}&raw" + GitLink.exe -u "https://host/projects/catel/repos/catel/browse/{filename}?at={revision}&raw" The custom url will be used to fill the placeholders with the relative file path and the revision hash. @@ -78,7 +77,7 @@ The custom url will be used to fill the placeholders with the relative file path When you need to log the information to a file, use the following command line: - GitLink.exe c:\source\catel -u https://github.com/catel/catel -b develop -l GitLinkLog.log + GitLink.exe -l GitLinkLog.log ### More options @@ -99,7 +98,7 @@ It then searches for a git repo that contains those source files and looks up th It also searches your remotes for a URL pattern that it recognizes (e.g. https://github.com/name/repo). It combines the URL and the commit ID to create a unique URL for each source file of this exact version, and adds this information to your PDB. -When you share your PDB alongside your DLL, your users who debug with Source Server support enabled will automatically be able to step into your source code. +When you share your PDB alongside your assembly, your users who debug with Source Server support enabled will automatically be able to step into your source code. # Troubleshooting @@ -134,7 +133,7 @@ GitLink supports the following providers out of the box (will auto-detect based * GitHub * Custom Provider (custom urls) -Providers currently being worked on: +Providers that could be supported with the help of the community: * Assembla * Beanstalk diff --git a/src/GitLink/Linker.cs b/src/GitLink/Linker.cs index 859a8ca..045c3d5 100644 --- a/src/GitLink/Linker.cs +++ b/src/GitLink/Linker.cs @@ -187,13 +187,14 @@ public static class Linker private static void CreateSrcSrv(string srcsrvFile, SrcSrvContext srcSrvContext) { - Argument.IsNotNull(() => srcSrvContext); - Argument.IsNotNullOrWhitespace(() => srcSrvContext.RawUrl); - Argument.IsNotNullOrWhitespace(() => srcSrvContext.Revision); - Argument.IsNotNullOrWhitespace(() => srcsrvFile); + Argument.IsNotNull(nameof(srcSrvContext), srcSrvContext); + Argument.IsNotNullOrWhitespace(nameof(srcSrvContext) + "." + nameof(srcSrvContext.RawUrl), srcSrvContext.RawUrl); + Argument.IsNotNullOrWhitespace(nameof(srcSrvContext) + "." + nameof(srcSrvContext.Revision), srcSrvContext.Revision); + Argument.IsNotNullOrWhitespace(nameof(srcsrvFile), srcsrvFile); if (srcSrvContext.VstsData.Count != 0) { + Log.Debug("Writing VSTS specific bytes to srcsrv file because VstsData was not empty."); File.WriteAllBytes(srcsrvFile, SrcSrv.CreateVsts(srcSrvContext.Revision, srcSrvContext.Paths, srcSrvContext.VstsData)); } else @@ -224,6 +225,12 @@ private static string GetNormalizedPath(string path, string gitRepoRootDir) { string segment = segments[i]; var next = currentDir.GetFileSystemInfos(segment).FirstOrDefault(); + if (next == null) + { + Log.Error($"Unable to find path \"{path}\" on disk."); + return path; + } + segments[i] = next.Name; // get canonical capitalization currentDir = next as DirectoryInfo; } From 0a08eb6c583732f761285722c7f6efd03c9c5ea6 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Wed, 30 Nov 2016 07:33:10 -0800 Subject: [PATCH 59/85] Only run gitlink during Release builds --- src/GitLinkTask/GitLink.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GitLinkTask/GitLink.props b/src/GitLinkTask/GitLink.props index 40dad90..0b81bcd 100644 --- a/src/GitLinkTask/GitLink.props +++ b/src/GitLinkTask/GitLink.props @@ -1,7 +1,7 @@ - true + true true From bf8f1604a32e8f919e8bcd7aea7eefedaa958e29 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Wed, 30 Nov 2016 07:36:24 -0800 Subject: [PATCH 60/85] Fix .targets reference to LinkPdbToGitRemote --- src/GitLinkTask/GitLink.targets | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/GitLinkTask/GitLink.targets b/src/GitLinkTask/GitLink.targets index 0829efb..2daf151 100644 --- a/src/GitLinkTask/GitLink.targets +++ b/src/GitLinkTask/GitLink.targets @@ -1,12 +1,12 @@  - + - Date: Sat, 3 Dec 2016 22:49:49 -0800 Subject: [PATCH 61/85] fixed missing ----- around file headers --- src/GitLink.Tests/Providers/CustomUrlProviderFacts.cs | 4 +++- src/GitLink/Pdb/PdbFile.cs | 4 +++- src/GitLink/Providers/CustomRawUrlProvider.cs | 4 +++- src/GitLink/Providers/CustomUrlProvider.cs | 4 +++- 4 files changed, 12 insertions(+), 4 deletions(-) diff --git a/src/GitLink.Tests/Providers/CustomUrlProviderFacts.cs b/src/GitLink.Tests/Providers/CustomUrlProviderFacts.cs index 27060fe..43891c2 100644 --- a/src/GitLink.Tests/Providers/CustomUrlProviderFacts.cs +++ b/src/GitLink.Tests/Providers/CustomUrlProviderFacts.cs @@ -1,6 +1,8 @@ -// +// -------------------------------------------------------------------------------------------------------------------- +// // Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. // +// -------------------------------------------------------------------------------------------------------------------- namespace GitLink.Tests.Providers { diff --git a/src/GitLink/Pdb/PdbFile.cs b/src/GitLink/Pdb/PdbFile.cs index d4e82d8..4bc16d4 100644 --- a/src/GitLink/Pdb/PdbFile.cs +++ b/src/GitLink/Pdb/PdbFile.cs @@ -1,6 +1,8 @@ -// +// -------------------------------------------------------------------------------------------------------------------- +// // Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. // +// -------------------------------------------------------------------------------------------------------------------- namespace GitLink.Pdb { diff --git a/src/GitLink/Providers/CustomRawUrlProvider.cs b/src/GitLink/Providers/CustomRawUrlProvider.cs index c597fa2..ff43263 100644 --- a/src/GitLink/Providers/CustomRawUrlProvider.cs +++ b/src/GitLink/Providers/CustomRawUrlProvider.cs @@ -1,6 +1,8 @@ -// +// -------------------------------------------------------------------------------------------------------------------- +// // Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. // +// -------------------------------------------------------------------------------------------------------------------- namespace GitLink.Providers { diff --git a/src/GitLink/Providers/CustomUrlProvider.cs b/src/GitLink/Providers/CustomUrlProvider.cs index 236d7a1..07b1cbd 100644 --- a/src/GitLink/Providers/CustomUrlProvider.cs +++ b/src/GitLink/Providers/CustomUrlProvider.cs @@ -1,6 +1,8 @@ -// +// -------------------------------------------------------------------------------------------------------------------- +// // Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. // +// -------------------------------------------------------------------------------------------------------------------- namespace GitLink.Providers { From 10d779c68588712ea99ba1c9cd4732a0ec539e72 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Wed, 7 Dec 2016 22:13:38 -0800 Subject: [PATCH 62/85] Fix README usage and target name --- README.md | 6 +++--- src/GitLinkTask/GitLink.targets | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 5eaf98d..0a7832e 100644 --- a/README.md +++ b/README.md @@ -63,13 +63,13 @@ Using GitLink via the command line is very simple: When working with a content proxy or an alternative git VCS system that supports direct HTTP access to specific file revisions use the `-u` parameter with the custom raw content root URL - GitLink.exe -u https://raw.githubusercontent.com/catel/catel + GitLink.exe -u https://raw.githubusercontent.com/catel/catel The custom url will be used to fill in the following pattern `{customUrl}/{revision}/{relativeFilePath}` when generating the source mapping. When working with a repository using uncommon URL you can use placeholders to specify where the filename and revision hash should be, use `-u` parameter with the custom URL - GitLink.exe -u "https://host/projects/catel/repos/catel/browse/{filename}?at={revision}&raw" + GitLink.exe -u "https://host/projects/catel/repos/catel/browse/{filename}?at={revision}&raw" The custom url will be used to fill the placeholders with the relative file path and the revision hash. @@ -77,7 +77,7 @@ The custom url will be used to fill the placeholders with the relative file path When you need to log the information to a file, use the following command line: - GitLink.exe -l GitLinkLog.log + GitLink.exe -l GitLinkLog.log ### More options diff --git a/src/GitLinkTask/GitLink.targets b/src/GitLinkTask/GitLink.targets index 2daf151..2e25ce8 100644 --- a/src/GitLinkTask/GitLink.targets +++ b/src/GitLinkTask/GitLink.targets @@ -3,7 +3,7 @@ - Date: Thu, 8 Dec 2016 11:56:21 -0800 Subject: [PATCH 63/85] Remove log switch doc from README --- README.md | 6 ------ 1 file changed, 6 deletions(-) diff --git a/README.md b/README.md index 0a7832e..e4ab939 100644 --- a/README.md +++ b/README.md @@ -73,12 +73,6 @@ When working with a repository using uncommon URL you can use placeholders to sp The custom url will be used to fill the placeholders with the relative file path and the revision hash. -### Logging to a file - -When you need to log the information to a file, use the following command line: - - GitLink.exe -l GitLinkLog.log - ### More options There are many more parameters you can use. Display the usage doc with the following command line: From edc6a3f669f635b8475715478576d643edd5b140 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Sun, 11 Dec 2016 21:55:28 -0800 Subject: [PATCH 64/85] Dramatically improve perf of path normalization --- .../Extensions/RepositoryExtensionFacts.cs | 93 +++++++++++++++++++ src/GitLink.Tests/GitLink.Tests.csproj | 4 + .../Extensions/RepositoryExtensions.cs | 80 ++++++++++++++++ src/GitLink/GitLink.csproj | 1 + src/GitLink/Linker.cs | 12 +-- 5 files changed, 179 insertions(+), 11 deletions(-) create mode 100644 src/GitLink.Tests/Extensions/RepositoryExtensionFacts.cs create mode 100644 src/GitLink/Extensions/RepositoryExtensions.cs diff --git a/src/GitLink.Tests/Extensions/RepositoryExtensionFacts.cs b/src/GitLink.Tests/Extensions/RepositoryExtensionFacts.cs new file mode 100644 index 0000000..7effb82 --- /dev/null +++ b/src/GitLink.Tests/Extensions/RepositoryExtensionFacts.cs @@ -0,0 +1,93 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2016 Andrew Arnott. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace GitLink.Tests.Extensions +{ + using System.IO; + using GitTools.Git; + using LibGit2Sharp; + using NUnit.Framework; + + [TestFixture] + public class RepositoryExtensionFacts + { + private static readonly char[] PathSeparators = new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }; + private Repository repo; + + [SetUp] + public void SetUp() + { + string repositoryDirectory = GitDirFinder.TreeWalkForGitDir(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)); + repo = new Repository(repositoryDirectory); + } + + [Theory, Pairwise] + public void NormalizeFileAtRoot(bool scrambleCase, bool absolutePath, bool emptySegments, bool forwardSlashes) + { + string expected = "LICENSE"; + string input = GetPathToTest(expected, scrambleCase, absolutePath, emptySegments, forwardSlashes); + string actual = repo.GetNormalizedPath(input); + Assert.AreEqual(expected, actual); + } + + [Theory, Pairwise] + public void NormalizeFileOneDirDeep(bool scrambleCase, bool absolutePath, bool emptySegments, bool forwardSlashes) + { + string expected = "src/EnlistmentInfo.targets"; + string input = GetPathToTest(expected, scrambleCase, absolutePath, emptySegments, forwardSlashes); + string actual = repo.GetNormalizedPath(input); + Assert.AreEqual(expected, actual); + } + + [Theory, Pairwise] + public void NormalizeFileTwoDirsDeep(bool scrambleCase, bool absolutePath, bool emptySegments, bool forwardSlashes) + { + string expected = "src/GitLink/Linker.cs"; + string input = GetPathToTest(expected, scrambleCase, absolutePath, emptySegments, forwardSlashes); + string actual = repo.GetNormalizedPath(input); + Assert.AreEqual(expected, actual); + } + + [Theory] + public void NormalizeMissingFile([Values("T/N", "T\\N", "T", "T/n/C")]string path) + { + Assert.AreEqual(path, repo.GetNormalizedPath(path)); + } + + private string GetPathToTest(string expected, bool scrambleCase, bool absolutePath, bool emptySegments, bool forwardSlashes) + { + string actual = expected; + if (scrambleCase) + { + actual = actual.ToUpperInvariant(); + if (actual == expected) + { + actual = actual.ToLowerInvariant(); + } + } + + if (absolutePath) + { + actual = Path.Combine(repo.Info.WorkingDirectory, actual); + } + + if (emptySegments) + { + if (actual.IndexOfAny(PathSeparators) >= 0) + { + actual = actual.Replace(Path.DirectorySeparatorChar.ToString(), Path.DirectorySeparatorChar.ToString() + Path.DirectorySeparatorChar.ToString()) + .Replace(Path.AltDirectorySeparatorChar.ToString(), Path.AltDirectorySeparatorChar.ToString() + Path.AltDirectorySeparatorChar.ToString()); + } + } + + actual = forwardSlashes + ? actual.Replace('\\', '/') + : actual.Replace('/', '\\'); + + return actual; + } + } +} diff --git a/src/GitLink.Tests/GitLink.Tests.csproj b/src/GitLink.Tests/GitLink.Tests.csproj index 002abcf..bfd5785 100644 --- a/src/GitLink.Tests/GitLink.Tests.csproj +++ b/src/GitLink.Tests/GitLink.Tests.csproj @@ -56,7 +56,11 @@ + + Extensions\RepositoryExtensions.cs + + diff --git a/src/GitLink/Extensions/RepositoryExtensions.cs b/src/GitLink/Extensions/RepositoryExtensions.cs new file mode 100644 index 0000000..cd6c205 --- /dev/null +++ b/src/GitLink/Extensions/RepositoryExtensions.cs @@ -0,0 +1,80 @@ +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2016 Andrew Arnott. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + +namespace GitLink +{ + using System; + using System.IO; + using System.Linq; + using Catel; + using Catel.Logging; + using LibGit2Sharp; + + internal static class RepositoryExtensions + { + private static readonly ILog Log = LogManager.GetCurrentClassLogger(); + private static readonly char[] PathSeparators = new char[] { Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar }; + + internal static string GetNormalizedPath(this Repository repository, string path) + { + Argument.IsNotNull(nameof(repository), repository); + Argument.IsNotNullOrEmpty(nameof(path), path); + + string relativePath = GetRelativePath(path, repository.Info.WorkingDirectory); + string[] relativePathSegments = relativePath.Split(PathSeparators, StringSplitOptions.RemoveEmptyEntries); + var tree = repository.Commits.FirstOrDefault()?.Tree; + if (tree == null) + { + // Throw an exception that will cause our caller to fallback to poor man's normalization. + throw new RepositoryNotFoundException(); + } + + for (int i = 0; i < relativePathSegments.Length; i++) + { + string segment = relativePathSegments[i]; + TreeEntry entry = tree[segment] ?? tree.FirstOrDefault(te => string.Equals(te.Name, segment, StringComparison.OrdinalIgnoreCase)); + + if (entry == null) + { + Log.Warning("Unable to find file in git."); + return path; + } + + if (entry.TargetType == TreeEntryTargetType.Tree) + { + tree = (Tree)entry.Target; + } + else + { + if (i < relativePathSegments.Length - 1) + { + Log.Error("Found a file where we expected to find a directory."); + return path; + } + } + + relativePathSegments[i] = entry.Name; + } + + return string.Join("/", relativePathSegments); + } + + private static string GetRelativePath(string target, string relativeTo) + { + if (!Path.IsPathRooted(target)) + { + // It is already relative. + return target; + } + + target = string.Join(Path.DirectorySeparatorChar.ToString(), target.Split(PathSeparators, StringSplitOptions.RemoveEmptyEntries)); + + Uri baseUri = new Uri(relativeTo, UriKind.Absolute); + Uri targetUri = new Uri(target, UriKind.Absolute); + return baseUri.MakeRelativeUri(targetUri).ToString(); + } + } +} diff --git a/src/GitLink/GitLink.csproj b/src/GitLink/GitLink.csproj index 77ac5b2..897ca2e 100644 --- a/src/GitLink/GitLink.csproj +++ b/src/GitLink/GitLink.csproj @@ -62,6 +62,7 @@ + diff --git a/src/GitLink/Linker.cs b/src/GitLink/Linker.cs index 045c3d5..9bfc675 100644 --- a/src/GitLink/Linker.cs +++ b/src/GitLink/Linker.cs @@ -99,7 +99,7 @@ public static class Linker try { Repository repo = repository.Value; - repoSourceFiles = sourceFiles.ToDictionary(e => e, e => GetNormalizedPath(e, repo)); + repoSourceFiles = sourceFiles.ToDictionary(e => e, e => repo.GetNormalizedPath(e)); } catch (RepositoryNotFoundException) { @@ -203,16 +203,6 @@ private static void CreateSrcSrv(string srcsrvFile, SrcSrvContext srcSrvContext) } } - private static string GetNormalizedPath(string path, Repository repository) - { - Argument.IsNotNull(nameof(repository), repository); - Argument.IsNotNullOrEmpty(nameof(path), path); - - string relativePath = Catel.IO.Path.GetRelativePath(path, repository.Info.WorkingDirectory); - var repoFile = repository.Index.FirstOrDefault(e => string.Equals(e.Path, relativePath, StringComparison.OrdinalIgnoreCase)); - return repoFile?.Path; - } - private static string GetNormalizedPath(string path, string gitRepoRootDir) { Argument.IsNotNullOrEmpty(nameof(path), path); From ced3a7f6e6405405a2720718dd21177d7750434e Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Tue, 20 Dec 2016 13:58:36 -0800 Subject: [PATCH 65/85] Undo the read only source tree changes Per request of GitLink folks as part of merging PR #110 --- src/GitLink.NuGet/project.json | 4 ---- src/GitLink/project.json | 4 ---- 2 files changed, 8 deletions(-) diff --git a/src/GitLink.NuGet/project.json b/src/GitLink.NuGet/project.json index 7826b01..69f1dfd 100644 --- a/src/GitLink.NuGet/project.json +++ b/src/GitLink.NuGet/project.json @@ -1,9 +1,5 @@ { "dependencies": { - "ReadOnlySourceTree": { - "version": "0.1.37-beta", - "suppressParent": "none" - }, "Nerdbank.GitVersioning": { "version": "1.5.46", "suppressParent": "none" diff --git a/src/GitLink/project.json b/src/GitLink/project.json index a793228..86a660f 100644 --- a/src/GitLink/project.json +++ b/src/GitLink/project.json @@ -10,10 +10,6 @@ "version": "1.0.4", "suppressParent": "none" }, - "ReadOnlySourceTree": { - "version": "0.1.37-beta", - "suppressParent": "none" - }, "StyleCop.Analyzers": { "version": "1.0.0", "type": "build" From 39b9bafa343e38119bb369ebb0cbec93b5bc10c1 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Tue, 20 Dec 2016 14:03:28 -0800 Subject: [PATCH 66/85] Package up nupkg even without readonlysourcetree --- appveyor.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 130321d..af0dffb 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -14,5 +14,5 @@ build: parallel: true verbosity: minimal artifacts: -- path: bin\**\*.nupkg +- path: '**\bin\**\*.nupkg' name: NuGet Package From 32efb2b9db111b9fcb3b46e7ca24e00ebfbe377b Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Thu, 22 Dec 2016 10:59:40 -0800 Subject: [PATCH 67/85] Brought back the build scripts at GitLink owners' request I've modified from their original to be more reliable to paths with spaces, require at least MSBuild 14 (because that's the only thing tested), and clean build outputs from the new location --- scripts - Build - Debug.bat | 7 +++++++ scripts - Build - Release.bat | 7 +++++++ scripts - Clean all.bat | 4 ++++ scripts - Restore packages.bat | 3 +++ 4 files changed, 21 insertions(+) create mode 100644 scripts - Build - Debug.bat create mode 100644 scripts - Build - Release.bat create mode 100644 scripts - Clean all.bat create mode 100644 scripts - Restore packages.bat diff --git a/scripts - Build - Debug.bat b/scripts - Build - Debug.bat new file mode 100644 index 0000000..e0e893d --- /dev/null +++ b/scripts - Build - Debug.bat @@ -0,0 +1,7 @@ +@echo off +@setlocal + +IF NOT "%VS140COMNTOOLS%" == "" (call "%VS140COMNTOOLS%vsvars32.bat") + +for /F %%A in ('dir /b src\*.sln') do call devenv "src\%%A" /build "Debug" +pause \ No newline at end of file diff --git a/scripts - Build - Release.bat b/scripts - Build - Release.bat new file mode 100644 index 0000000..8cc9874 --- /dev/null +++ b/scripts - Build - Release.bat @@ -0,0 +1,7 @@ +@echo off +@setlocal + +IF NOT "%VS140COMNTOOLS%" == "" (call "%VS140COMNTOOLS%vsvars32.bat") + +for /F %%A in ('dir /b src\*.sln') do call devenv "src\%%A" /build "Release" +pause \ No newline at end of file diff --git a/scripts - Clean all.bat b/scripts - Clean all.bat new file mode 100644 index 0000000..11df517 --- /dev/null +++ b/scripts - Clean all.bat @@ -0,0 +1,4 @@ +REM Deleting output +FOR %%A in (bin obj\debug obj\release) do IF EXIST "%~dp0%%A" rd /s /q "%~dp0%%A" + +@pause diff --git a/scripts - Restore packages.bat b/scripts - Restore packages.bat new file mode 100644 index 0000000..4500c48 --- /dev/null +++ b/scripts - Restore packages.bat @@ -0,0 +1,3 @@ +call "%~dp0init.cmd" + +@pause From f4429a2d549d5da5f699c8aab5c0179e85133e26 Mon Sep 17 00:00:00 2001 From: Geert van Horrik Date: Wed, 18 Jan 2017 14:06:45 +0100 Subject: [PATCH 68/85] Add GitVersion config file --- GitVersionConfig.yaml | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 GitVersionConfig.yaml diff --git a/GitVersionConfig.yaml b/GitVersionConfig.yaml new file mode 100644 index 0000000..af70400 --- /dev/null +++ b/GitVersionConfig.yaml @@ -0,0 +1,3 @@ +mode: ContinuousDeployment +assembly-versioning-scheme: MajorMinorPatch +next-version: 3.0.0 \ No newline at end of file From 560e446f0fd9972693fcf82a0b94a57c711b9836 Mon Sep 17 00:00:00 2001 From: Geert van Horrik Date: Wed, 18 Jan 2017 14:30:46 +0100 Subject: [PATCH 69/85] Project versioning improvements --- src/.RepoSrcRoot | 0 src/GitLink.NuGet/GitLink.NuGet.nuproj | 2 +- src/GitLink.NuGet/project.json | 4 ---- src/GitLink.sln | 1 - src/version.json | 11 ----------- 5 files changed, 1 insertion(+), 17 deletions(-) delete mode 100644 src/.RepoSrcRoot delete mode 100644 src/version.json diff --git a/src/.RepoSrcRoot b/src/.RepoSrcRoot deleted file mode 100644 index e69de29..0000000 diff --git a/src/GitLink.NuGet/GitLink.NuGet.nuproj b/src/GitLink.NuGet/GitLink.NuGet.nuproj index c320a43..1b130cd 100644 --- a/src/GitLink.NuGet/GitLink.NuGet.nuproj +++ b/src/GitLink.NuGet/GitLink.NuGet.nuproj @@ -22,7 +22,7 @@ GitLink GeertvanHorrik,AArnott GeertvanHorrik,AArnott - GitLink let's users step through your code hosted on any Git hosting service! This makes symbol servers obsolete which saves you both time + GitLink let's users step through your code hosted on any Git hosting service! This makes symbol servers obsolete which saves you both time https://github.com/GitTools/GitLink diff --git a/src/GitLink.NuGet/project.json b/src/GitLink.NuGet/project.json index 69f1dfd..584dba0 100644 --- a/src/GitLink.NuGet/project.json +++ b/src/GitLink.NuGet/project.json @@ -1,9 +1,5 @@ { "dependencies": { - "Nerdbank.GitVersioning": { - "version": "1.5.46", - "suppressParent": "none" - }, "NuProj": "0.11.14-beta" }, "frameworks": { diff --git a/src/GitLink.sln b/src/GitLink.sln index 3e213c6..1845f2b 100644 --- a/src/GitLink.sln +++ b/src/GitLink.sln @@ -17,7 +17,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution EnlistmentInfo.targets = EnlistmentInfo.targets nuget.config = nuget.config ..\README.md = ..\README.md - version.json = version.json EndProjectSection EndProject Project("{FF286327-C783-4F7A-AB73-9BCBAD0D4460}") = "GitLink.NuGet", "GitLink.NuGet\GitLink.NuGet.nuproj", "{29E78909-B7FB-472C-A9A0-1749A8BE9A9A}" diff --git a/src/version.json b/src/version.json deleted file mode 100644 index 3be50ff..0000000 --- a/src/version.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "version": "3.0-beta", - "publicReleaseRefSpec": [ - "^refs/heads/master$" - ], - "cloudBuild": { - "buildNumber": { - "enabled": true - } - } -} \ No newline at end of file From 9e3c2aa7c1c620197d29b8095b7c9ae1be41d84b Mon Sep 17 00:00:00 2001 From: Geert van Horrik Date: Wed, 18 Jan 2017 14:31:09 +0100 Subject: [PATCH 70/85] Separate method calls into separate lines --- src/GitLink.Tests/Extensions/RepositoryExtensionFacts.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/GitLink.Tests/Extensions/RepositoryExtensionFacts.cs b/src/GitLink.Tests/Extensions/RepositoryExtensionFacts.cs index 7effb82..6f23ff1 100644 --- a/src/GitLink.Tests/Extensions/RepositoryExtensionFacts.cs +++ b/src/GitLink.Tests/Extensions/RepositoryExtensionFacts.cs @@ -20,7 +20,10 @@ public class RepositoryExtensionFacts [SetUp] public void SetUp() { - string repositoryDirectory = GitDirFinder.TreeWalkForGitDir(Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location)); + var assemblyLocation = System.Reflection.Assembly.GetExecutingAssembly().Location; + var assemblyDirectory = Path.GetDirectoryName(assemblyLocation); + + string repositoryDirectory = GitDirFinder.TreeWalkForGitDir(assemblyDirectory); repo = new Repository(repositoryDirectory); } From 6eff01454dfa0cebeb1597100571ea42fc3e5475 Mon Sep 17 00:00:00 2001 From: Geert van Horrik Date: Wed, 18 Jan 2017 14:35:15 +0100 Subject: [PATCH 71/85] Update to .NET 4.6.2 --- src/GitLink.NuGet/project.json | 2 +- src/GitLink.Tests/GitLink.Tests.csproj | 3 ++- src/GitLink.Tests/project.json | 2 +- src/GitLink/GitLink.csproj | 3 ++- src/GitLink/app.config | 3 +++ src/GitLink/project.json | 2 +- src/GitLinkTask/GitLinkTask.csproj | 3 ++- src/GitLinkTask/project.json | 2 +- 8 files changed, 13 insertions(+), 7 deletions(-) create mode 100644 src/GitLink/app.config diff --git a/src/GitLink.NuGet/project.json b/src/GitLink.NuGet/project.json index 584dba0..e0b23bc 100644 --- a/src/GitLink.NuGet/project.json +++ b/src/GitLink.NuGet/project.json @@ -3,7 +3,7 @@ "NuProj": "0.11.14-beta" }, "frameworks": { - "net451": {} + "net462": {} }, "runtimes": { "win": {} diff --git a/src/GitLink.Tests/GitLink.Tests.csproj b/src/GitLink.Tests/GitLink.Tests.csproj index d008f77..fd364ec 100644 --- a/src/GitLink.Tests/GitLink.Tests.csproj +++ b/src/GitLink.Tests/GitLink.Tests.csproj @@ -8,7 +8,7 @@ Properties GitLink.Tests GitLink.Tests - v4.5 + v4.6.2 512 {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 10.0 @@ -18,6 +18,7 @@ UnitTest + true diff --git a/src/GitLink.Tests/project.json b/src/GitLink.Tests/project.json index 556abd5..a571ef2 100644 --- a/src/GitLink.Tests/project.json +++ b/src/GitLink.Tests/project.json @@ -6,7 +6,7 @@ "NUnit3TestAdapter": "3.4.1" }, "frameworks": { - "net45": {} + "net462": {} }, "runtimes": { "win": {} diff --git a/src/GitLink/GitLink.csproj b/src/GitLink/GitLink.csproj index f7d723b..f422e72 100644 --- a/src/GitLink/GitLink.csproj +++ b/src/GitLink/GitLink.csproj @@ -9,7 +9,7 @@ Properties GitLink GitLink - v4.5 + v4.6.2 512 @@ -90,6 +90,7 @@ + PreserveNewest diff --git a/src/GitLink/app.config b/src/GitLink/app.config new file mode 100644 index 0000000..2a0024f --- /dev/null +++ b/src/GitLink/app.config @@ -0,0 +1,3 @@ + + + diff --git a/src/GitLink/project.json b/src/GitLink/project.json index 86a660f..51da0d1 100644 --- a/src/GitLink/project.json +++ b/src/GitLink/project.json @@ -20,7 +20,7 @@ } }, "frameworks": { - "net45": {} + "net462": {} }, "runtimes": { "win": {} diff --git a/src/GitLinkTask/GitLinkTask.csproj b/src/GitLinkTask/GitLinkTask.csproj index d427497..35ea065 100644 --- a/src/GitLinkTask/GitLinkTask.csproj +++ b/src/GitLinkTask/GitLinkTask.csproj @@ -9,8 +9,9 @@ Properties GitLinkTask GitLinkTask - v4.5 + v4.6.2 512 + AnyCPU diff --git a/src/GitLinkTask/project.json b/src/GitLinkTask/project.json index 25ed17a..3fcd4c8 100644 --- a/src/GitLinkTask/project.json +++ b/src/GitLinkTask/project.json @@ -3,7 +3,7 @@ "NuProj.Common": "0.11.14-beta" }, "frameworks": { - "net45": {} + "net462": {} }, "runtimes": { "win": {} From e56fb70ad66886290a6e990358a9df15af86bba9 Mon Sep 17 00:00:00 2001 From: Geert van Horrik Date: Wed, 18 Jan 2017 14:44:16 +0100 Subject: [PATCH 72/85] Use GitVersionTask --- src/GitLink/Properties/AssemblyInfo.cs | 10 +++------- src/GitLink/project.json | 15 ++++++--------- src/GitLinkTask/Properties/AssemblyInfo.cs | 14 +++++++------- src/GitLinkTask/project.json | 1 + 4 files changed, 17 insertions(+), 23 deletions(-) diff --git a/src/GitLink/Properties/AssemblyInfo.cs b/src/GitLink/Properties/AssemblyInfo.cs index f902610..7e0c82d 100644 --- a/src/GitLink/Properties/AssemblyInfo.cs +++ b/src/GitLink/Properties/AssemblyInfo.cs @@ -1,16 +1,12 @@ // -------------------------------------------------------------------------------------------------------------------- // -// Copyright (c) 2014 - 2016 CatenaLogic. All rights reserved. +// Copyright (c) 2014 - 2017 CatenaLogic. All rights reserved. // // -------------------------------------------------------------------------------------------------------------------- + using System.Reflection; -using System.Runtime.InteropServices; [assembly: AssemblyTitle("GitLink")] [assembly: AssemblyProduct("GitLink")] -[assembly: AssemblyDescription("GitLink library")] - -#if !PCL -[assembly: ComVisible(false)] -#endif +[assembly: AssemblyDescription("GitLink library")] \ No newline at end of file diff --git a/src/GitLink/project.json b/src/GitLink/project.json index 51da0d1..26cd5f6 100644 --- a/src/GitLink/project.json +++ b/src/GitLink/project.json @@ -1,23 +1,20 @@ { "dependencies": { "Catel.Core": "4.5.3", - "GitTools.Core": "1.0.0-unstable0021", - "ImpromptuInterface": "6.2.2", - "LibGit2Sharp": "0.21.0.176", - "System.CommandLine": "0.1.0-e161008-1", - "NuProj.Common": "0.11.14-beta", "EnlistmentInfo": { "version": "1.0.4", "suppressParent": "none" }, + "GitTools.Core": "1.0.0-unstable0021", + "GitVersionTask": "4.0.0-beta0009", + "ImpromptuInterface": "6.2.2", + "LibGit2Sharp": "0.21.0.176", + "NuProj.Common": "0.11.14-beta", "StyleCop.Analyzers": { "version": "1.0.0", "type": "build" }, - "Nerdbank.GitVersioning": { - "version": "1.5.46", - "suppressParent": "none" - } + "System.CommandLine": "0.1.0-e161008-1" }, "frameworks": { "net462": {} diff --git a/src/GitLinkTask/Properties/AssemblyInfo.cs b/src/GitLinkTask/Properties/AssemblyInfo.cs index e554b1d..ae23dfa 100644 --- a/src/GitLinkTask/Properties/AssemblyInfo.cs +++ b/src/GitLinkTask/Properties/AssemblyInfo.cs @@ -1,12 +1,12 @@ -// Copyright (c) Andrew Arnott. All rights reserved. +// -------------------------------------------------------------------------------------------------------------------- +// +// Copyright (c) 2014 - 2017 CatenaLogic. All rights reserved. +// +// -------------------------------------------------------------------------------------------------------------------- + using System.Reflection; -using System.Runtime.InteropServices; [assembly: AssemblyTitle("GitLinkTask")] [assembly: AssemblyProduct("GitLinkTask")] -[assembly: AssemblyDescription("GitLinkTask library")] - -#if !PCL -[assembly: ComVisible(false)] -#endif \ No newline at end of file +[assembly: AssemblyDescription("GitLinkTask library")] \ No newline at end of file diff --git a/src/GitLinkTask/project.json b/src/GitLinkTask/project.json index 3fcd4c8..8942d02 100644 --- a/src/GitLinkTask/project.json +++ b/src/GitLinkTask/project.json @@ -1,5 +1,6 @@ { "dependencies": { + "GitVersionTask": "4.0.0-beta0009", "NuProj.Common": "0.11.14-beta" }, "frameworks": { From 97edfb113d09ec53e62d8800c8fed1d26076de5a Mon Sep 17 00:00:00 2001 From: Geert van Horrik Date: Wed, 18 Jan 2017 14:50:58 +0100 Subject: [PATCH 73/85] Fix CI build shield --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index b055ff9..28d2b3d 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ GitLink ========== -[![Build status](https://ci.appveyor.com/api/projects/status/g56b4xyl4q0ean8i/branch/develop?svg=true)](https://ci.appveyor.com/project/AArnott/gitlink/branch/develop) +[![Build status](https://ci.appveyor.com/api/projects/status/y3yvwpvk4kmw0hsg/branch/develop?svg=true)](https://ci.appveyor.com/project/gittools/gitlink/branch/develop) [![Join the chat at https://gitter.im/GitTools/GitLink](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/GitTools/GitLink?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) ![License](https://img.shields.io/github/license/gittools/gitlink.svg) From 4aaa0ee2549e8c43e0bcee859bd0c526a8a76142 Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Mon, 6 Feb 2017 13:48:02 -0800 Subject: [PATCH 74/85] Revert "Update to .NET 4.6.2" This reverts commit 6eff01454dfa0cebeb1597100571ea42fc3e5475. This was just wrong for the NuProj project as it must target net451. Besides that, requiring .NET 4.6.2 in the redist tooling means fewer developers can run the tooling without an upgrade. Until we need to upgrade, let's not. --- src/GitLink.NuGet/project.json | 2 +- src/GitLink.Tests/GitLink.Tests.csproj | 3 +-- src/GitLink.Tests/project.json | 2 +- src/GitLink/GitLink.csproj | 3 +-- src/GitLink/app.config | 3 --- src/GitLink/project.json | 2 +- src/GitLinkTask/GitLinkTask.csproj | 3 +-- src/GitLinkTask/project.json | 2 +- 8 files changed, 7 insertions(+), 13 deletions(-) delete mode 100644 src/GitLink/app.config diff --git a/src/GitLink.NuGet/project.json b/src/GitLink.NuGet/project.json index e0b23bc..584dba0 100644 --- a/src/GitLink.NuGet/project.json +++ b/src/GitLink.NuGet/project.json @@ -3,7 +3,7 @@ "NuProj": "0.11.14-beta" }, "frameworks": { - "net462": {} + "net451": {} }, "runtimes": { "win": {} diff --git a/src/GitLink.Tests/GitLink.Tests.csproj b/src/GitLink.Tests/GitLink.Tests.csproj index fd364ec..d008f77 100644 --- a/src/GitLink.Tests/GitLink.Tests.csproj +++ b/src/GitLink.Tests/GitLink.Tests.csproj @@ -8,7 +8,7 @@ Properties GitLink.Tests GitLink.Tests - v4.6.2 + v4.5 512 {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 10.0 @@ -18,7 +18,6 @@ UnitTest - true diff --git a/src/GitLink.Tests/project.json b/src/GitLink.Tests/project.json index a571ef2..556abd5 100644 --- a/src/GitLink.Tests/project.json +++ b/src/GitLink.Tests/project.json @@ -6,7 +6,7 @@ "NUnit3TestAdapter": "3.4.1" }, "frameworks": { - "net462": {} + "net45": {} }, "runtimes": { "win": {} diff --git a/src/GitLink/GitLink.csproj b/src/GitLink/GitLink.csproj index f422e72..f7d723b 100644 --- a/src/GitLink/GitLink.csproj +++ b/src/GitLink/GitLink.csproj @@ -9,7 +9,7 @@ Properties GitLink GitLink - v4.6.2 + v4.5 512 @@ -90,7 +90,6 @@ - PreserveNewest diff --git a/src/GitLink/app.config b/src/GitLink/app.config deleted file mode 100644 index 2a0024f..0000000 --- a/src/GitLink/app.config +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/GitLink/project.json b/src/GitLink/project.json index 26cd5f6..2c78d6f 100644 --- a/src/GitLink/project.json +++ b/src/GitLink/project.json @@ -17,7 +17,7 @@ "System.CommandLine": "0.1.0-e161008-1" }, "frameworks": { - "net462": {} + "net45": {} }, "runtimes": { "win": {} diff --git a/src/GitLinkTask/GitLinkTask.csproj b/src/GitLinkTask/GitLinkTask.csproj index 35ea065..d427497 100644 --- a/src/GitLinkTask/GitLinkTask.csproj +++ b/src/GitLinkTask/GitLinkTask.csproj @@ -9,9 +9,8 @@ Properties GitLinkTask GitLinkTask - v4.6.2 + v4.5 512 - AnyCPU diff --git a/src/GitLinkTask/project.json b/src/GitLinkTask/project.json index 8942d02..6c9ca4f 100644 --- a/src/GitLinkTask/project.json +++ b/src/GitLinkTask/project.json @@ -4,7 +4,7 @@ "NuProj.Common": "0.11.14-beta" }, "frameworks": { - "net462": {} + "net45": {} }, "runtimes": { "win": {} From cf2ba13475c1d7b4f006cd2884fff18c0e886196 Mon Sep 17 00:00:00 2001 From: Jason Date: Mon, 13 Feb 2017 21:08:37 +0800 Subject: [PATCH 75/85] fix cannot support ? in raw issue --- src/GitLink/Pdb/SrcSrv.cs | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/GitLink/Pdb/SrcSrv.cs b/src/GitLink/Pdb/SrcSrv.cs index 019aee3..66f0755 100644 --- a/src/GitLink/Pdb/SrcSrv.cs +++ b/src/GitLink/Pdb/SrcSrv.cs @@ -35,9 +35,16 @@ internal static byte[] Create(string rawUrl, string revision, IEnumerable Date: Sun, 19 Feb 2017 10:50:11 +0100 Subject: [PATCH 76/85] Testing variable replacement in nuproj --- appveyor.yml | 11 ++++++++++- src/GitLink.NuGet/GitLink.NuGet.nuproj | 1 + src/GitLink.NuGet/tools/LICENSE.txt | 2 +- 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index af0dffb..d6c6ee9 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,18 +1,27 @@ -version: 1.0.{build} +assembly_info: + patch: false + skip_tags: true + image: Visual Studio 2015 + configuration: Release + environment: VisualStudioVersion: 14.0 + cache: - '%USERPROFILE%\.nuget\packages -> **\project.json' - 'obj\tools -> tools\**' + before_build: - ps: .\init.ps1 + build: project: src\GitLink.sln parallel: true verbosity: minimal + artifacts: - path: '**\bin\**\*.nupkg' name: NuGet Package diff --git a/src/GitLink.NuGet/GitLink.NuGet.nuproj b/src/GitLink.NuGet/GitLink.NuGet.nuproj index 1b130cd..cdbe524 100644 --- a/src/GitLink.NuGet/GitLink.NuGet.nuproj +++ b/src/GitLink.NuGet/GitLink.NuGet.nuproj @@ -19,6 +19,7 @@ GitLink + $(APPVEYOR_BUILD_VERSION) GitLink GeertvanHorrik,AArnott GeertvanHorrik,AArnott diff --git a/src/GitLink.NuGet/tools/LICENSE.txt b/src/GitLink.NuGet/tools/LICENSE.txt index 8da68fb..2e12320 100644 --- a/src/GitLink.NuGet/tools/LICENSE.txt +++ b/src/GitLink.NuGet/tools/LICENSE.txt @@ -4,7 +4,7 @@ LICENSE The MIT License (MIT) -Copyright (c) 2014 - 2016 CatenaLogic +Copyright (c) 2014 - 2017 CatenaLogic Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal From 30c1175ec55d0ebc4f2f98e7135532871990f6c6 Mon Sep 17 00:00:00 2001 From: Geert van Horrik Date: Sun, 19 Feb 2017 11:02:59 +0100 Subject: [PATCH 77/85] Build script improvements --- appveyor.yml | 7 ++++++- src/GitLink.NuGet/GitLink.NuGet.nuproj | 6 +++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/appveyor.yml b/appveyor.yml index d6c6ee9..0977ac7 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,3 +1,6 @@ +install: + - choco install gitversion.portable -pre -y + assembly_info: patch: false @@ -15,7 +18,9 @@ cache: - 'obj\tools -> tools\**' before_build: -- ps: .\init.ps1 + - ps: .\init.ps1 + - nuget restore + - ps: gitversion /l console /output buildserver /updateAssemblyInfo build: project: src\GitLink.sln diff --git a/src/GitLink.NuGet/GitLink.NuGet.nuproj b/src/GitLink.NuGet/GitLink.NuGet.nuproj index cdbe524..e292dc4 100644 --- a/src/GitLink.NuGet/GitLink.NuGet.nuproj +++ b/src/GitLink.NuGet/GitLink.NuGet.nuproj @@ -19,7 +19,7 @@ GitLink - $(APPVEYOR_BUILD_VERSION) + $(GitVersion_NuGetVersion) GitLink GeertvanHorrik,AArnott GeertvanHorrik,AArnott @@ -27,10 +27,10 @@ https://github.com/GitTools/GitLink - https://github.com/GitTools/GitLink/blob/$GitCommitIdShort$/LICENSE + https://github.com/GitTools/GitLink/blob/$(APPVEYOR_REPO_COMMIT)/LICENSE git pdb true - https://raw.githubusercontent.com/GitTools/GitLink/$GitCommitIdShort$/design/logo/logo_64.png + https://raw.githubusercontent.com/GitTools/GitLink/$(APPVEYOR_REPO_COMMIT)/design/logo/logo_64.png GetDeployableOutputs true From 102fa429412f9f0b428c1f117a9d42acc84d797a Mon Sep 17 00:00:00 2001 From: Geert van Horrik Date: Sun, 19 Feb 2017 11:05:28 +0100 Subject: [PATCH 78/85] Remove manual nuget restore --- appveyor.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/appveyor.yml b/appveyor.yml index 0977ac7..6f8b018 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -19,7 +19,6 @@ cache: before_build: - ps: .\init.ps1 - - nuget restore - ps: gitversion /l console /output buildserver /updateAssemblyInfo build: From b597b597bea3049a019608c7379ac755fbe54d7e Mon Sep 17 00:00:00 2001 From: Andrew Arnott Date: Wed, 1 Mar 2017 21:19:42 -0800 Subject: [PATCH 79/85] Improve logging on missing files --- src/GitLink/Extensions/RepositoryExtensions.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/GitLink/Extensions/RepositoryExtensions.cs b/src/GitLink/Extensions/RepositoryExtensions.cs index cd6c205..b6d58c8 100644 --- a/src/GitLink/Extensions/RepositoryExtensions.cs +++ b/src/GitLink/Extensions/RepositoryExtensions.cs @@ -39,7 +39,7 @@ internal static string GetNormalizedPath(this Repository repository, string path if (entry == null) { - Log.Warning("Unable to find file in git."); + Log.Warning($"Unable to find file in git: \"{path}\"."); return path; } @@ -51,7 +51,7 @@ internal static string GetNormalizedPath(this Repository repository, string path { if (i < relativePathSegments.Length - 1) { - Log.Error("Found a file where we expected to find a directory."); + Log.Error($"Found a file where we expected to find a directory: \"{path}\"."); return path; } } From d0bf800dbc70ec49b9af798992fe132922dd908e Mon Sep 17 00:00:00 2001 From: Jason Date: Thu, 23 Mar 2017 22:16:37 +0800 Subject: [PATCH 80/85] fix issue 150 --- src/GitLink/Pdb/SrcSrv.cs | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/GitLink/Pdb/SrcSrv.cs b/src/GitLink/Pdb/SrcSrv.cs index 66f0755..ad5b66e 100644 --- a/src/GitLink/Pdb/SrcSrv.cs +++ b/src/GitLink/Pdb/SrcSrv.cs @@ -35,16 +35,9 @@ internal static byte[] Create(string rawUrl, string revision, IEnumerable Date: Wed, 24 May 2017 09:02:27 -0700 Subject: [PATCH 81/85] Exit with 1 on failure When GitLink fails to modify the PDB it should exit with a failure code. The behavior of exiting with 0 on success makes it impossible to detect GitLink breaks in infrastructure. closes #156 --- src/GitLink/Program.cs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/GitLink/Program.cs b/src/GitLink/Program.cs index 025ab5c..e8ff243 100644 --- a/src/GitLink/Program.cs +++ b/src/GitLink/Program.cs @@ -67,7 +67,11 @@ private static int Main(string[] args) Method = method, }; - Linker.Link(pdbPath, options); + if (!Linker.Link(pdbPath, options)) + { + return 1; + } + WaitForKeyPressWhenDebugging(); return 0; } From 6b8e375ff15456572aa91ce08fbc9fcefe3bb9c7 Mon Sep 17 00:00:00 2001 From: Paulo Morgado Date: Wed, 31 May 2017 09:52:17 +0100 Subject: [PATCH 82/85] Added exclusion for Live Unit Test builds --- src/GitLinkTask/GitLink.props | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GitLinkTask/GitLink.props b/src/GitLinkTask/GitLink.props index 0b81bcd..f8131b3 100644 --- a/src/GitLinkTask/GitLink.props +++ b/src/GitLinkTask/GitLink.props @@ -1,7 +1,7 @@ - true + true true From 7fb3a93fb41e79c2ac480ce5f5db6c34538e2ab9 Mon Sep 17 00:00:00 2001 From: unknown Date: Sun, 25 Jun 2017 02:41:33 +0300 Subject: [PATCH 83/85] Small change to VisualStudio.com regex Some build systems add an extra slash in the path. Modified the VSTS regex to account for that --- src/GitLink/Providers/VisualStudioTeamServicesProvider.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GitLink/Providers/VisualStudioTeamServicesProvider.cs b/src/GitLink/Providers/VisualStudioTeamServicesProvider.cs index 799b43b..f891079 100644 --- a/src/GitLink/Providers/VisualStudioTeamServicesProvider.cs +++ b/src/GitLink/Providers/VisualStudioTeamServicesProvider.cs @@ -13,7 +13,7 @@ namespace GitLink.Providers public class VisualStudioTeamServicesProvider : ProviderBase { - private static readonly Regex HostingUrlPattern = new Regex(@"(?(?(?:https://)?(?([a-zA-Z0-9\-\.]*)?)\.visualstudio\.com/)(?[a-zA-Z0-9\-\.]*)/?_git/(?[^/]+))"); + private static readonly Regex HostingUrlPattern = new Regex(@"(?(?(?:https://)?(?([a-zA-Z0-9\-\.]*)?)\.visualstudio\.com/)(?[a-zA-Z0-9\-\.]*)/?_git//?(?[^/]+))"); public VisualStudioTeamServicesProvider() : base(new GitPreparer()) From 0f67340c676280827f09c5a6d3ebe7bfc06f43cc Mon Sep 17 00:00:00 2001 From: Jared Parsons Date: Thu, 6 Jul 2017 08:33:51 -0700 Subject: [PATCH 84/85] Respect the env variable NUGET_PACKAGES The environment variable `%NUGET_PACKAGES%` can be used to specify a location where NuGet packages should restore. This is respected during the restore of this repo. Yet the build file GitLink.NuGet.nuproj assumes packages will always be restored to `%USERPROFILE%\.nuget\packages`. Fix is to respect `%NUGET_PACKAGES%` in GitLink.NuGet.nuproj closes #162 --- src/GitLink.NuGet/GitLink.NuGet.nuproj | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/GitLink.NuGet/GitLink.NuGet.nuproj b/src/GitLink.NuGet/GitLink.NuGet.nuproj index e292dc4..338674f 100644 --- a/src/GitLink.NuGet/GitLink.NuGet.nuproj +++ b/src/GitLink.NuGet/GitLink.NuGet.nuproj @@ -14,7 +14,9 @@ 29e78909-b7fb-472c-a9a0-1749a8be9a9a - $(UserProfile)\.nuget\packages\NuProj\0.11.14-beta\tools + <_NuGetRoot>$(UserProfile)\.nuget\packages + <_NuGetRoot Condition="'$(NUGET_PACKAGES)' != ''">$(NUGET_PACKAGES) + $(_NuGetRoot)\NuProj\0.11.14-beta\tools @@ -47,4 +49,4 @@ - \ No newline at end of file + From 26fb807b7d12556a7837dd34278eeda30bf6734e Mon Sep 17 00:00:00 2001 From: Jared Parsons Date: Thu, 6 Jul 2017 08:37:25 -0700 Subject: [PATCH 85/85] Handle files with a backtick in the name GitLink uses the `Uri` APIs in order to calculate relative file paths. This API will escape a number of characters, including backtick, when converting from `string` to `Uri`. These characters are not unescaped when converting from `Uri` to `string`. Hence the paths produced from GetRelativePath are incorrect and GitLink can't process the files. Fixed to unescape the characters once the relative path is calculated. closes #163 --- src/GitLink/Extensions/RepositoryExtensions.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/GitLink/Extensions/RepositoryExtensions.cs b/src/GitLink/Extensions/RepositoryExtensions.cs index b6d58c8..22db728 100644 --- a/src/GitLink/Extensions/RepositoryExtensions.cs +++ b/src/GitLink/Extensions/RepositoryExtensions.cs @@ -74,7 +74,7 @@ private static string GetRelativePath(string target, string relativeTo) Uri baseUri = new Uri(relativeTo, UriKind.Absolute); Uri targetUri = new Uri(target, UriKind.Absolute); - return baseUri.MakeRelativeUri(targetUri).ToString(); + return Uri.UnescapeDataString(baseUri.MakeRelativeUri(targetUri).ToString()); } } }