├── .gitattributes ├── .gitignore ├── README.md ├── azure-pipelines.yml ├── extension-manifest.json ├── resources ├── add_as_linked_config.png ├── app_configs.png ├── configuration_manager.png ├── package_preview.png ├── preview_diff.png └── preview_solution_explorer.png └── src ├── ConfigurationTransform.Test ├── ConfigTransformManagerTest.cs ├── ConfigurationTransform.Test.csproj ├── DeleteFileOnCloseBaseTest.cs ├── DeleteFileOnDocumentCloseTest.cs ├── DeleteFileOnWindowFrameCloseTest.cs ├── MockHelper.cs ├── ProjectItemExtensionsTest.cs ├── ProjectItemsExtensionsTest.cs ├── ProjectProperties.cs ├── Properties │ └── AssemblyInfo.cs ├── TestProject.xml ├── TestProjectWithLinkFiles.xml ├── VsProjectXmlTransformTest.cs ├── VsServicesTest.cs ├── XmlTransformTest.cs └── app.config ├── ConfigurationTransform.sln ├── ConfigurationTransform ├── ConfigurationTransform.csproj ├── ConfigurationTransform.vsct ├── ConfigurationTransformPackage.cs ├── GlobalSuppressions.cs ├── Guids.cs ├── Key.snk ├── PkgCmdID.cs ├── Properties │ └── AssemblyInfo.cs ├── Remove │ └── RemoveCommand.cs ├── Resources │ ├── Images.bmp │ └── Package.ico ├── Services │ ├── DeleteFileOnClose.cs │ ├── DeleteFileOnDocumentClose.cs │ ├── DeleteFileOnWindowFrameClose.cs │ ├── Extensions │ │ ├── DTEExtensions.cs │ │ ├── ProjectExtensions.cs │ │ ├── ProjectItemExtensions.cs │ │ ├── ProjectItemsExtensions.cs │ │ ├── ProjectsExtensions.cs │ │ ├── PropertiesExtensions.cs │ │ ├── PropertyExtensions.cs │ │ ├── SelectedItemsExtensions.cs │ │ └── VsServicesExtensions.cs │ ├── Helpers │ │ └── PathHelper.cs │ ├── IDeleteFileOnClose.cs │ ├── IDeleteFileOnWindowFrameClose.cs │ ├── IVsServices.cs │ ├── MessageBoxResult.cs │ ├── VsDiffOpt.cs │ └── VsServices.cs ├── Transform │ ├── AfterTargets.cs │ ├── ConfigTransformManager.cs │ ├── Extensions.cs │ ├── TargetTransformArgs.cs │ ├── VsProjectXmlTransform.cs │ ├── XmlExtension.cs │ └── XmlTransform.cs ├── VSPackage.resx ├── Wrappers │ ├── FileWrapper.cs │ ├── IFileWrapper.cs │ ├── IStreamManager.cs │ ├── IStreamWriterWrapper.cs │ ├── StreamManager.cs │ └── StreamWriterWrapper.cs ├── license.txt ├── package_icon.png ├── package_preview.png └── source.extension.vsixmanifest └── packages ├── FluentAssertions.4.19.4 ├── FluentAssertions.4.19.4.nupkg └── lib │ ├── net40 │ ├── FluentAssertions.Core.dll │ ├── FluentAssertions.Core.xml │ ├── FluentAssertions.dll │ └── FluentAssertions.xml │ ├── net45 │ ├── FluentAssertions.Core.dll │ ├── FluentAssertions.Core.xml │ ├── FluentAssertions.dll │ └── FluentAssertions.xml │ ├── netstandard1.3 │ ├── FluentAssertions.Core.XML │ ├── FluentAssertions.Core.dll │ ├── FluentAssertions.dll │ └── FluentAssertions.xml │ ├── portable-net40+sl5+win8+wp8+wpa81 │ ├── FluentAssertions.Core.dll │ ├── FluentAssertions.Core.xml │ ├── FluentAssertions.XML │ └── FluentAssertions.dll │ ├── portable-win81+wpa81 │ ├── FluentAssertions.Core.dll │ ├── FluentAssertions.Core.xml │ ├── FluentAssertions.dll │ ├── FluentAssertions.pri │ └── FluentAssertions.xml │ ├── sl5 │ ├── FluentAssertions.Core.dll │ ├── FluentAssertions.Core.xml │ ├── FluentAssertions.dll │ ├── FluentAssertions.xml │ ├── Microsoft.CSharp.dll │ ├── Microsoft.CSharp.xml │ ├── Microsoft.VisualStudio.QualityTools.UnitTesting.Silverlight.dll │ ├── Microsoft.VisualStudio.QualityTools.UnitTesting.Silverlight.xml │ ├── System.Xml.Linq.dll │ ├── System.Xml.Linq.xml │ ├── de │ │ ├── Microsoft.CSharp.resources.dll │ │ └── System.Xml.Linq.resources.dll │ ├── es │ │ ├── Microsoft.CSharp.resources.dll │ │ └── System.Xml.Linq.resources.dll │ ├── fr │ │ ├── Microsoft.CSharp.resources.dll │ │ └── System.Xml.Linq.resources.dll │ ├── it │ │ ├── Microsoft.CSharp.resources.dll │ │ └── System.Xml.Linq.resources.dll │ ├── ja │ │ ├── Microsoft.CSharp.resources.dll │ │ └── System.Xml.Linq.resources.dll │ ├── ko │ │ ├── Microsoft.CSharp.resources.dll │ │ └── System.Xml.Linq.resources.dll │ ├── ru │ │ ├── Microsoft.CSharp.resources.dll │ │ └── System.Xml.Linq.resources.dll │ ├── zh-Hans │ │ ├── Microsoft.CSharp.resources.dll │ │ └── System.Xml.Linq.resources.dll │ └── zh-Hant │ │ ├── Microsoft.CSharp.resources.dll │ │ └── System.Xml.Linq.resources.dll │ ├── uap10.0 │ ├── FluentAssertions.Core.XML │ ├── FluentAssertions.Core.dll │ ├── FluentAssertions.dll │ └── FluentAssertions.xml │ ├── win81 │ ├── FluentAssertions.Core.dll │ ├── FluentAssertions.Core.xml │ ├── FluentAssertions.dll │ ├── FluentAssertions.pri │ └── FluentAssertions.xml │ ├── wp8 │ ├── FluentAssertions.Core.dll │ ├── FluentAssertions.Core.xml │ ├── FluentAssertions.dll │ └── FluentAssertions.xml │ └── wpa81 │ ├── FluentAssertions.Core.dll │ ├── FluentAssertions.Core.xml │ ├── FluentAssertions.dll │ ├── FluentAssertions.pri │ └── FluentAssertions.xml └── repositories.config /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.rsuser 8 | *.suo 9 | *.user 10 | *.userosscache 11 | *.sln.docstates 12 | 13 | # User-specific files (MonoDevelop/Xamarin Studio) 14 | *.userprefs 15 | 16 | # Mono auto generated files 17 | mono_crash.* 18 | 19 | # Build results 20 | [Dd]ebug/ 21 | [Dd]ebugPublic/ 22 | [Rr]elease/ 23 | [Rr]eleases/ 24 | x64/ 25 | x86/ 26 | [Aa][Rr][Mm]/ 27 | [Aa][Rr][Mm]64/ 28 | bld/ 29 | [Bb]in/ 30 | [Oo]bj/ 31 | [Ll]og/ 32 | 33 | # Visual Studio 2015/2017 cache/options directory 34 | .vs/ 35 | # Uncomment if you have tasks that create the project's static files in wwwroot 36 | #wwwroot/ 37 | 38 | # Visual Studio 2017 auto generated files 39 | Generated\ Files/ 40 | 41 | # MSTest test Results 42 | [Tt]est[Rr]esult*/ 43 | [Bb]uild[Ll]og.* 44 | 45 | # NUNIT 46 | *.VisualState.xml 47 | TestResult.xml 48 | 49 | # Build Results of an ATL Project 50 | [Dd]ebugPS/ 51 | [Rr]eleasePS/ 52 | dlldata.c 53 | 54 | # Benchmark Results 55 | BenchmarkDotNet.Artifacts/ 56 | 57 | # .NET Core 58 | project.lock.json 59 | project.fragment.lock.json 60 | artifacts/ 61 | 62 | # StyleCop 63 | StyleCopReport.xml 64 | 65 | # Files built by Visual Studio 66 | *_i.c 67 | *_p.c 68 | *_h.h 69 | *.ilk 70 | *.meta 71 | *.obj 72 | *.iobj 73 | *.pch 74 | *.pdb 75 | *.ipdb 76 | *.pgc 77 | *.pgd 78 | *.rsp 79 | *.sbr 80 | *.tlb 81 | *.tli 82 | *.tlh 83 | *.tmp 84 | *.tmp_proj 85 | *_wpftmp.csproj 86 | *.log 87 | *.vspscc 88 | *.vssscc 89 | .builds 90 | *.pidb 91 | *.svclog 92 | *.scc 93 | 94 | # Chutzpah Test files 95 | _Chutzpah* 96 | 97 | # Visual C++ cache files 98 | ipch/ 99 | *.aps 100 | *.ncb 101 | *.opendb 102 | *.opensdf 103 | *.sdf 104 | *.cachefile 105 | *.VC.db 106 | *.VC.VC.opendb 107 | 108 | # Visual Studio profiler 109 | *.psess 110 | *.vsp 111 | *.vspx 112 | *.sap 113 | 114 | # Visual Studio Trace Files 115 | *.e2e 116 | 117 | # TFS 2012 Local Workspace 118 | $tf/ 119 | 120 | # Guidance Automation Toolkit 121 | *.gpState 122 | 123 | # ReSharper is a .NET coding add-in 124 | _ReSharper*/ 125 | *.[Rr]e[Ss]harper 126 | *.DotSettings.user 127 | 128 | # JustCode is a .NET coding add-in 129 | .JustCode 130 | 131 | # TeamCity is a build add-in 132 | _TeamCity* 133 | 134 | # DotCover is a Code Coverage Tool 135 | *.dotCover 136 | 137 | # AxoCover is a Code Coverage Tool 138 | .axoCover/* 139 | !.axoCover/settings.json 140 | 141 | # Visual Studio code coverage results 142 | *.coverage 143 | *.coveragexml 144 | 145 | # NCrunch 146 | _NCrunch_* 147 | .*crunch*.local.xml 148 | nCrunchTemp_* 149 | 150 | # MightyMoose 151 | *.mm.* 152 | AutoTest.Net/ 153 | 154 | # Web workbench (sass) 155 | .sass-cache/ 156 | 157 | # Installshield output folder 158 | [Ee]xpress/ 159 | 160 | # DocProject is a documentation generator add-in 161 | DocProject/buildhelp/ 162 | DocProject/Help/*.HxT 163 | DocProject/Help/*.HxC 164 | DocProject/Help/*.hhc 165 | DocProject/Help/*.hhk 166 | DocProject/Help/*.hhp 167 | DocProject/Help/Html2 168 | DocProject/Help/html 169 | 170 | # Click-Once directory 171 | publish/ 172 | 173 | # Publish Web Output 174 | *.[Pp]ublish.xml 175 | *.azurePubxml 176 | # Note: Comment the next line if you want to checkin your web deploy settings, 177 | # but database connection strings (with potential passwords) will be unencrypted 178 | *.pubxml 179 | *.publishproj 180 | 181 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 182 | # checkin your Azure Web App publish settings, but sensitive information contained 183 | # in these scripts will be unencrypted 184 | PublishScripts/ 185 | 186 | # NuGet Packages 187 | *.nupkg 188 | # The packages folder can be ignored because of Package Restore 189 | **/[Pp]ackages/* 190 | # except build/, which is used as an MSBuild target. 191 | !**/[Pp]ackages/build/ 192 | # Uncomment if necessary however generally it will be regenerated when needed 193 | #!**/[Pp]ackages/repositories.config 194 | # NuGet v3's project.json files produces more ignorable files 195 | *.nuget.props 196 | *.nuget.targets 197 | 198 | # Microsoft Azure Build Output 199 | csx/ 200 | *.build.csdef 201 | 202 | # Microsoft Azure Emulator 203 | ecf/ 204 | rcf/ 205 | 206 | # Windows Store app package directories and files 207 | AppPackages/ 208 | BundleArtifacts/ 209 | Package.StoreAssociation.xml 210 | _pkginfo.txt 211 | *.appx 212 | *.appxbundle 213 | *.appxupload 214 | 215 | # Visual Studio cache files 216 | # files ending in .cache can be ignored 217 | *.[Cc]ache 218 | # but keep track of directories ending in .cache 219 | !?*.[Cc]ache/ 220 | 221 | # Others 222 | ClientBin/ 223 | ~$* 224 | *~ 225 | *.dbmdl 226 | *.dbproj.schemaview 227 | *.jfm 228 | *.pfx 229 | *.publishsettings 230 | orleans.codegen.cs 231 | 232 | # Including strong name files can present a security risk 233 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 234 | #*.snk 235 | 236 | # Since there are multiple workflows, uncomment next line to ignore bower_components 237 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 238 | #bower_components/ 239 | 240 | # RIA/Silverlight projects 241 | Generated_Code/ 242 | 243 | # Backup & report files from converting an old project file 244 | # to a newer Visual Studio version. Backup files are not needed, 245 | # because we have git ;-) 246 | _UpgradeReport_Files/ 247 | Backup*/ 248 | UpgradeLog*.XML 249 | UpgradeLog*.htm 250 | ServiceFabricBackup/ 251 | *.rptproj.bak 252 | 253 | # SQL Server files 254 | *.mdf 255 | *.ldf 256 | *.ndf 257 | 258 | # Business Intelligence projects 259 | *.rdl.data 260 | *.bim.layout 261 | *.bim_*.settings 262 | *.rptproj.rsuser 263 | *- Backup*.rdl 264 | 265 | # Microsoft Fakes 266 | FakesAssemblies/ 267 | 268 | # GhostDoc plugin setting file 269 | *.GhostDoc.xml 270 | 271 | # Node.js Tools for Visual Studio 272 | .ntvs_analysis.dat 273 | node_modules/ 274 | 275 | # Visual Studio 6 build log 276 | *.plg 277 | 278 | # Visual Studio 6 workspace options file 279 | *.opt 280 | 281 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 282 | *.vbw 283 | 284 | # Visual Studio LightSwitch build output 285 | **/*.HTMLClient/GeneratedArtifacts 286 | **/*.DesktopClient/GeneratedArtifacts 287 | **/*.DesktopClient/ModelManifest.xml 288 | **/*.Server/GeneratedArtifacts 289 | **/*.Server/ModelManifest.xml 290 | _Pvt_Extensions 291 | 292 | # Paket dependency manager 293 | .paket/paket.exe 294 | paket-files/ 295 | 296 | # FAKE - F# Make 297 | .fake/ 298 | 299 | # CodeRush personal settings 300 | .cr/personal 301 | 302 | # Python Tools for Visual Studio (PTVS) 303 | __pycache__/ 304 | *.pyc 305 | 306 | # Cake - Uncomment if you are using it 307 | # tools/** 308 | # !tools/packages.config 309 | 310 | # Tabs Studio 311 | *.tss 312 | 313 | # Telerik's JustMock configuration file 314 | *.jmconfig 315 | 316 | # BizTalk build output 317 | *.btp.cs 318 | *.btm.cs 319 | *.odx.cs 320 | *.xsd.cs 321 | 322 | # OpenCover UI analysis results 323 | OpenCover/ 324 | 325 | # Azure Stream Analytics local run output 326 | ASALocalRun/ 327 | 328 | # MSBuild Binary and Structured Log 329 | *.binlog 330 | 331 | # NVidia Nsight GPU debugger configuration file 332 | *.nvuser 333 | 334 | # MFractors (Xamarin productivity tool) working folder 335 | .mfractor/ 336 | 337 | # Local History for Visual Studio 338 | .localhistory/ 339 | 340 | # BeatPulse healthcheck temp database 341 | healthchecksdb 342 | 343 | # Backup folder for Package Reference Convert tool in Visual Studio 2017 344 | MigrationBackup/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # [ARCHIVED] ConfigurationTransform 2 | 3 | [![Build Status](https://dev.azure.com/golavr/ConfigurationTransform/_apis/build/status/golavr.ConfigurationTransform?branchName=master)](https://dev.azure.com/golavr/ConfigurationTransform/_build/latest?definitionId=4&branchName=master) 4 | [![GitHub release](https://img.shields.io/github/release/golavr/configurationtransform.svg?label=GitHub%20Release&logo=github&style=flat)](https://github.com/golavr/ConfigurationTransform/releases) 5 | 6 | #### IMPORTANT! This work has been superseded by https://docs.microsoft.com/en-us/dotnet/core/extensions/configuration 7 | As part of the archival process, I'm closing all open issues and pull requests. 8 | 9 | --- 10 | 11 | Microsoft has a neat out of the box solution for Web Application Projects configuration transform which gives you the ability to have multiple configurations for each build environment (i.e. web.config). Somehow this ability is not included in other project types. 12 | 13 | When you have multiple projects with many configurations the dev->staging->production task becomes very unpleasant. I remember back then having one app.config file with different commented configurations for each environment struggling to comment uncomment the right ones. Well I'm happy those days are over. After using app.config transformations for two years doing it the hard way by editing the project file and adding the configuration files manually, I thought it would be nice to write extension to automate this task. 14 | 15 | There are many articles and solutions out there, some of them used as the inspiration for this extension. 16 | 17 | When I developed this extension I had a few things in mind: 18 | 19 | * Multiple configuration files for different environments (e.g. dev/staging/production). 20 | * ClickOnce support. 21 | * Self-contained solution - no need to install the extension on all dev/ build server machines. 22 | * Simple few clicks and you are set. 23 | 24 | For more information how to use XDT (XML document transform) see [http://go.microsoft.com/fwlink/?LinkId=125889](http://go.microsoft.com/fwlink/?LinkId=125889) 25 | 26 | **GitHub Sample** 27 | [ConfigurationTransformSample](https://github.com/golavr/ConfigurationTransformSample "ConfigurationTransformSample") 28 | 29 | ### Step by Step Instructions: ### 30 | 31 | 1. Add **App.config** file to project 32 | 33 | 2. Right click **App.config** file in solution explorer and select **_Add Config Transforms_** 34 | 35 | ![](resources/package_preview.png) 36 | 37 | Notice that you have new App.$$$.config files under App.config 38 | 39 | ![](resources/app_configs.png) 40 | 41 | corresponding to build configurations 42 | 43 | ![](resources/configuration_manager.png) 44 | 45 | 3. Set the source **App.config** 46 | 47 | ```xml 48 | 49 | 50 | 51 | 52 | 53 | 54 | ``` 55 | 56 | 4. Set the target **App.Debug.config** 57 | 58 | ```xml 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | ``` 67 | 68 | 5. Build project. 69 | 70 | ### Linked Configs Step by Step Instructions: ### 71 | 72 | 1. Add link(Existing Item...) **App.config** file to project 73 | 74 | 2. Right click link **App.config** file in solution explorer and select **_Add Config Transforms_** 75 | 76 | ![](resources/package_preview.png) 77 | 78 | Click _**Yes**_ button, if you select _**No**_ button concrete(not linked) config files will be created which suits certain use case 79 | 80 | ![](resources/add_as_linked_config.png) 81 | 82 | Notice that you have new App.$$$.config files under App.config 83 | 84 | ![](resources/app_configs.png) 85 | 86 | corresponding to source App.config files 87 | 88 | 3. Continue with step 3 above... 89 | 90 | ### Preview: ### 91 | 92 | ![](resources/preview_solution_explorer.png) 93 | 94 | ![](resources/preview_diff.png) 95 | 96 | ### Changelog: ### 97 | 98 | From v3.2 and above [Release Notes](https://github.com/golavr/ConfigurationTransform/wiki/Release-Notes) 99 | 100 | * **3.1 - Aug 19, 2017** 101 | * Add support for Visual Studio 2017. 102 | 103 | * **3.0 - Feb 27, 2016** 104 | * Add support for any config file (*.config) not just app.config 105 | * Update [demo solution](https://onedrive.live.com/redir?resid=C235883F28DBC48C!139&authkey=!AE6qGnmd99lwsdg&ithint=folder%2c "Demo solution"). 106 | 107 | * **2.2 - Feb 09, 2016** 108 | * Add support for Visual Studio 2015. 109 | 110 | * **2.1 - Sep 29, 2014** 111 | * Hot fix for v2.0. Remove 'Microsoft.VisualStudio.Shell.12.0 dependency. 112 | 113 | * **2.0 - Sep 28, 2014** 114 | * Add preview support. 115 | 116 | * **1.6 - May 28, 2014** 117 | * Fix minor bug. 118 | 119 | * **1.5 - May 24, 2014** 120 | * Fix linked configs relative path. 121 | * Add support for source linked configs not included in project. 122 | 123 | * **1.4 - Apr 13, 2014** 124 | * Fix linked configs creation when source config is located in solution sub folder. 125 | 126 | * **1.3 - Oct 05, 2013** 127 | * Add support for Visual Studio 2013. 128 | * Add support for linked configs transformations. 129 | * Add deployment(Click-Once) support for class library projects. 130 | 131 | * **1.2 - May 03, 2013** 132 | * Fix Visual Studio 2012 compatibility issue. 133 | 134 | * **1.1 - Nov 24, 2012** 135 | * Add support for Visual Studio 2012. 136 | * Fix typo in xml element. 137 | 138 | * **1.0 - May 27, 2012** 139 | * First release. 140 | -------------------------------------------------------------------------------- /azure-pipelines.yml: -------------------------------------------------------------------------------- 1 | # .NET Desktop 2 | # Build and run tests for .NET Desktop or Windows classic desktop solutions. 3 | # Add steps that publish symbols, save build artifacts, and more: 4 | # https://docs.microsoft.com/azure/devops/pipelines/apps/windows/dot-net 5 | 6 | trigger: 7 | - master 8 | 9 | pool: 10 | vmImage: 'VS2017-Win2016' 11 | 12 | variables: 13 | solution: '**/*.sln' 14 | buildPlatform: 'Any CPU' 15 | buildConfiguration: 'Release' 16 | 17 | steps: 18 | - task: NuGetToolInstaller@0 19 | displayName: 'Use NuGet >=4.5.0' 20 | inputs: 21 | versionSpec: '>=4.5.0' 22 | 23 | - task: NuGetCommand@2 24 | displayName: 'NuGet restore' 25 | inputs: 26 | restoreSolution: '$(solution)' 27 | 28 | - task: VSBuild@1 29 | displayName: 'Build solution' 30 | inputs: 31 | solution: '$(solution)' 32 | msbuildArgs: '/p:DeployExtension=false' 33 | platform: '$(buildPlatform)' 34 | configuration: '$(buildConfiguration)' 35 | 36 | - task: VSTest@2 37 | displayName: 'Test solution' 38 | inputs: 39 | platform: '$(buildPlatform)' 40 | configuration: '$(buildConfiguration)' 41 | 42 | - task: CopyFiles@2 43 | displayName: 'Stage VSIX artifact' 44 | inputs: 45 | SourceFolder: '$(Build.SourcesDirectory)\src\ConfigurationTransform\bin\$(buildConfiguration)' 46 | Contents: ConfigurationTransform.vsix 47 | TargetFolder: '$(Build.ArtifactStagingDirectory)' 48 | 49 | - task: CopyFiles@2 50 | displayName: 'Stage marketplace publish artifacts' 51 | inputs: 52 | SourceFolder: '$(Build.SourcesDirectory)' 53 | Contents: | 54 | resources\** 55 | extension-manifest.json 56 | README.md 57 | TargetFolder: '$(Build.ArtifactStagingDirectory)' 58 | 59 | - task: PublishBuildArtifacts@1 60 | displayName: 'Publish Artifact: drop' 61 | 62 | -------------------------------------------------------------------------------- /extension-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "http://json.schemastore.org/vsix-publish", 3 | "categories": [ "build", "setup and deployment", "team development" ], 4 | "identity": { 5 | "internalName": "ConfigurationTransform" 6 | }, 7 | "overview": "README.md", 8 | "priceCategory": "free", 9 | "publisher": "GolanAvraham", 10 | "private": false, 11 | "qna": true, 12 | "repo": "https://github.com/golavr/ConfigurationTransform", 13 | "assetFiles": [ 14 | { 15 | "pathOnDisk": "resources\\add_as_linked_config.png", 16 | "targetPath": "resources/add_as_linked_config.png" 17 | }, 18 | { 19 | "pathOnDisk": "resources\\app_configs.png", 20 | "targetPath": "resources/app_configs.png" 21 | }, 22 | { 23 | "pathOnDisk": "resources\\configuration_manager.png", 24 | "targetPath": "resources/configuration_manager.png" 25 | }, 26 | { 27 | "pathOnDisk": "resources\\package_preview.png", 28 | "targetPath": "resources/package_preview.png" 29 | }, 30 | { 31 | "pathOnDisk": "resources\\preview_solution_explorer.png", 32 | "targetPath": "resources/preview_solution_explorer.png" 33 | }, 34 | { 35 | "pathOnDisk": "resources\\preview_diff.png", 36 | "targetPath": "resources/preview_diff.png" 37 | } 38 | ] 39 | } -------------------------------------------------------------------------------- /resources/add_as_linked_config.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/resources/add_as_linked_config.png -------------------------------------------------------------------------------- /resources/app_configs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/resources/app_configs.png -------------------------------------------------------------------------------- /resources/configuration_manager.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/resources/configuration_manager.png -------------------------------------------------------------------------------- /resources/package_preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/resources/package_preview.png -------------------------------------------------------------------------------- /resources/preview_diff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/resources/preview_diff.png -------------------------------------------------------------------------------- /resources/preview_solution_explorer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/resources/preview_solution_explorer.png -------------------------------------------------------------------------------- /src/ConfigurationTransform.Test/ConfigurationTransform.Test.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Debug 6 | AnyCPU 7 | {FD8CFB45-F75B-40DC-899A-F07233BA64E7} 8 | Library 9 | Properties 10 | ConfigurationTransform.Test 11 | ConfigurationTransform.Test 12 | v4.7.2 13 | 512 14 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 15 | 15.0 16 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 17 | $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages 18 | False 19 | UnitTest 20 | 21 | 22 | 23 | 24 | true 25 | full 26 | false 27 | bin\Debug\ 28 | DEBUG;TRACE 29 | prompt 30 | 4 31 | 32 | 33 | pdbonly 34 | true 35 | bin\Release\ 36 | TRACE 37 | prompt 38 | 4 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | Always 57 | Designer 58 | 59 | 60 | Always 61 | Designer 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | {e4538816-241f-40b4-b962-65ccf294fbba} 70 | ConfigurationTransform 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 5.6.0 92 | 93 | 94 | 8.0.0.1 95 | 96 | 97 | 16.0.428 98 | 99 | 100 | 15.8.243 101 | 102 | 103 | 15.8.243 104 | 105 | 106 | 15.9.3 107 | 108 | 109 | 15.0.16 110 | 111 | 112 | 1.16.30 113 | runtime; build; native; contentfiles; analyzers; buildtransitive 114 | all 115 | 116 | 117 | 15.8.192 118 | 119 | 120 | 4.10.1 121 | 122 | 123 | 1.3.2 124 | 125 | 126 | 1.3.2 127 | 128 | 129 | 130 | 131 | -------------------------------------------------------------------------------- /src/ConfigurationTransform.Test/DeleteFileOnCloseBaseTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using GolanAvraham.ConfigurationTransform.Services; 4 | using GolanAvraham.ConfigurationTransform.Wrappers; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | using Moq; 7 | 8 | namespace ConfigurationTransform.Test 9 | { 10 | [TestClass] 11 | public class DeleteFileOnCloseTest 12 | { 13 | [ExpectedException(typeof(ArgumentException))] 14 | [TestMethod] 15 | public void Constractor_ThrowsArgumentException_WhenFilePathIsNull() 16 | { 17 | //Act 18 | var deleteFileOnWindowFrameClose = new DeleteFileOnClose(null); 19 | } 20 | 21 | [ExpectedException(typeof(ArgumentException))] 22 | [TestMethod] 23 | public void Constractor_ThrowsArgumentException_WhenFilePathIsEmpty() 24 | { 25 | //Act 26 | var deleteFileOnWindowFrameClose = new DeleteFileOnClose(string.Empty); 27 | } 28 | 29 | [ExpectedException(typeof(FileNotFoundException))] 30 | [TestMethod] 31 | public void Constractor_ThrowsException_WhenFilePathIsEmpty() 32 | { 33 | //Arrange 34 | const string path = @"c:\file.xml"; 35 | var mock = new Mock(); 36 | mock.Setup(s => s.Exists(path)).Returns(false); 37 | 38 | //Act 39 | var deleteFileOnWindowFrameClose = new DeleteFileOnClose("file"); 40 | } 41 | 42 | [TestMethod] 43 | public void Delete_FileNotExist_ReturnNoError() 44 | { 45 | //Arrange 46 | const string path = @"c:\file.xml"; 47 | var fileWrapperMock = new Mock(); 48 | fileWrapperMock.Setup(s => s.Exists(path)).Returns(true); 49 | var target = new DeleteFileOnClose(path, fileWrapperMock.Object); 50 | fileWrapperMock.Setup(s => s.Exists(path)).Returns(false); 51 | 52 | //Act 53 | target.DeleteFile(); 54 | 55 | //Assert 56 | fileWrapperMock.VerifyAll(); 57 | fileWrapperMock.Verify(v => v.Delete(path), Times.Never()); 58 | } 59 | } 60 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform.Test/DeleteFileOnDocumentCloseTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using EnvDTE; 3 | using GolanAvraham.ConfigurationTransform.Services; 4 | using GolanAvraham.ConfigurationTransform.Wrappers; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | using Moq; 7 | 8 | namespace ConfigurationTransform.Test 9 | { 10 | [TestClass] 11 | public class DeleteFileOnDocumentCloseTest 12 | { 13 | [TestMethod] 14 | public void DocumentClosing_DeleteFile_UnRegisterEvent() 15 | { 16 | //Arrange 17 | var documentEvents = new Mock(); 18 | const string filePath = @"c:\file.xml"; 19 | var fileWrapperMock = new Mock(); 20 | fileWrapperMock.Setup(s => s.Exists(filePath)).Returns(true); 21 | var documentMock = new Mock(); 22 | documentMock.SetupGet(s => s.FullName).Returns(filePath); 23 | var targetMock = new Mock(filePath, documentEvents.Object, fileWrapperMock.Object); 24 | var deleteFileOnDocumentClose = targetMock.Object; 25 | //Act 26 | documentEvents.Raise(events => 27 | { 28 | events.DocumentClosing += document => { }; 29 | }, documentMock.Object); 30 | 31 | documentEvents.Raise(events => 32 | { 33 | events.DocumentClosing += document => { }; 34 | }, documentMock.Object); 35 | 36 | //Assert 37 | targetMock.Verify(v=>v.DeleteFile(), Times.Exactly(1)); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform.Test/DeleteFileOnWindowFrameCloseTest.cs: -------------------------------------------------------------------------------- 1 | using GolanAvraham.ConfigurationTransform.Services; 2 | using GolanAvraham.ConfigurationTransform.Wrappers; 3 | using Microsoft.VisualStudio.Shell.Interop; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | using Moq; 6 | 7 | namespace ConfigurationTransform.Test 8 | { 9 | [TestClass] 10 | public class DeleteFileOnWindowFrameCloseTest 11 | { 12 | [TestMethod] 13 | public void OnShow_AsClose_Call_DeleteFile() 14 | { 15 | //Arrange 16 | const string path = @"c:\file.xml"; 17 | var fileWrapperMock = new Mock(); 18 | fileWrapperMock.Setup(s => s.Exists(path)).Returns(true); 19 | var targetMock = new Mock(path, fileWrapperMock.Object); 20 | var target = targetMock.Object; 21 | 22 | //Act 23 | target.OnShow((int)__FRAMESHOW.FRAMESHOW_WinClosed); 24 | 25 | //Assert 26 | targetMock.Verify(v => v.DeleteFile()); 27 | } 28 | } 29 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform.Test/MockHelper.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Collections.Generic; 3 | using EnvDTE; 4 | using Moq; 5 | 6 | namespace ConfigurationTransform.Test 7 | { 8 | public static class MockHelper 9 | { 10 | public static Mock MockProjectItems(this IEnumerable projectItems) 11 | { 12 | var projectItemsMock = new Mock(); 13 | projectItemsMock.As().Setup(s => s.GetEnumerator()).Returns(projectItems.GetEnumerator()); 14 | 15 | return projectItemsMock; 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform.Test/ProjectItemExtensionsTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Linq; 4 | using System.Runtime.CompilerServices; 5 | using EnvDTE; 6 | using Microsoft.VisualStudio.TestTools.UnitTesting; 7 | using Moq; 8 | using GolanAvraham.ConfigurationTransform.Services.Extensions; 9 | 10 | namespace ConfigurationTransform.Test 11 | { 12 | [TestClass] 13 | public class ProjectItemExtensionsTest 14 | { 15 | [TestMethod] 16 | public void RelativePath_HigherLevel() 17 | { 18 | //Arrange 19 | const string filePath = @"c:\folder1\folder2\folder3\firstproject\file.txt"; 20 | const string referencePath = @"c:\folder1\folder2\folder3\folder4\secondproject\file.txt"; 21 | var expected = @"..\..\firstproject\file.txt"; 22 | 23 | //Act 24 | var actual = ProjectItemExtensions.RelativePath(filePath, referencePath); 25 | 26 | //Assert 27 | Assert.AreEqual(expected, actual); 28 | } 29 | 30 | [TestMethod] 31 | public void RelativePath_LowerLevel() 32 | { 33 | //Arrange 34 | const string filePath = @"c:\folder1\folder2\folder3\folder4\firstproject\file.txt"; 35 | const string referencePath = @"c:\folder1\folder2\folder3\secondproject\file.txt"; 36 | var expected = @"..\folder4\firstproject\file.txt"; 37 | 38 | //Act 39 | var actual = ProjectItemExtensions.RelativePath(filePath, referencePath); 40 | 41 | //Assert 42 | Assert.AreEqual(expected, actual); 43 | } 44 | 45 | [TestMethod] 46 | public void RelativePath_SameLevel() 47 | { 48 | //Arrange 49 | const string filePath = @"c:\folder1\folder2\folder3\firstproject\file.txt"; 50 | const string referencePath = @"c:\folder1\folder2\folder3\secondproject\file.txt"; 51 | var expected = @"..\firstproject\file.txt"; 52 | 53 | //Act 54 | var actual = ProjectItemExtensions.RelativePath(filePath, referencePath); 55 | 56 | //Assert 57 | Assert.AreEqual(expected, actual); 58 | } 59 | 60 | [TestMethod] 61 | public void RelativeDirectory() 62 | { 63 | //Arrange 64 | var relativePath = @"..\folder4\firstproject\file.txt"; 65 | var expected = @"..\folder4\firstproject"; 66 | 67 | //Act 68 | var actual = ProjectItemExtensions.RelativeDirectory(relativePath); 69 | 70 | //Assert 71 | Assert.AreEqual(expected, actual); 72 | } 73 | 74 | [TestMethod] 75 | public void Parent_Return_ProjectItem() 76 | { 77 | //Arrange 78 | const string expected = "you_should_find_me"; 79 | 80 | var sourceProjectItemMock = new Mock(); 81 | var parentProjectItemMock = new Mock(); 82 | 83 | var mock = new Mock(); 84 | mock.SetupGet(items => items.Count).Returns(0); 85 | mock.SetupGet(parent => parent.Parent).Returns(parentProjectItemMock.Object); 86 | 87 | parentProjectItemMock.SetupProperty(p => p.Name, expected); 88 | 89 | sourceProjectItemMock.SetupGet(c => c.Collection).Returns(mock.Object); 90 | 91 | //Act 92 | var actual = sourceProjectItemMock.Object.ParentProjectItemOrDefault(); 93 | 94 | //Assert 95 | Assert.AreEqual(expected, actual.Name); 96 | } 97 | 98 | [TestMethod] 99 | public void Parent_Return_Null_ProjectItem_For_Project() 100 | { 101 | //Arrange 102 | var sourceProjectItemMock = new Mock(); 103 | var parentProjectMock = new Mock(); 104 | 105 | var mock = new Mock(); 106 | mock.SetupGet(items => items.Count).Returns(0); 107 | mock.SetupGet(parent => parent.Parent).Returns(parentProjectMock.Object); 108 | 109 | //Act 110 | var actual = sourceProjectItemMock.Object.ParentProjectItemOrDefault(); 111 | 112 | //Assert 113 | Assert.IsNull(actual); 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /src/ConfigurationTransform.Test/ProjectItemsExtensionsTest.cs: -------------------------------------------------------------------------------- 1 | using System.Collections; 2 | using System.Linq; 3 | using EnvDTE; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | using Moq; 6 | using GolanAvraham.ConfigurationTransform.Services.Extensions; 7 | 8 | namespace ConfigurationTransform.Test 9 | { 10 | [TestClass] 11 | public class ProjectItemsExtensionsTest 12 | { 13 | [TestMethod] 14 | public void IsItemIncluded_Returns_True() 15 | { 16 | //Arrange 17 | const string mockname1 = "MockName1"; 18 | const string mockvalue = "MockValue"; 19 | const string mockname2 = "MockName2"; 20 | const bool boolvalue = true; 21 | 22 | var prop1 = new Mock(); 23 | prop1.SetupGet(s => s.Name).Returns(mockname1); 24 | prop1.SetupGet(s => s.Value).Returns(mockvalue); 25 | var prop2 = new Mock(); 26 | prop2.SetupGet(s => s.Name).Returns(mockname2); 27 | prop2.SetupGet(s => s.Value).Returns(boolvalue); 28 | 29 | var actualProperties = new[] {prop1.Object, prop2.Object}; 30 | 31 | var target = new Mock() {CallBase = true}; 32 | var targetEnumerable = target.As(); 33 | var projectItem = new Mock(); 34 | 35 | var properties = new Mock(); 36 | var propertiesEnumerable = properties.As(); 37 | propertiesEnumerable.Setup(s => s.GetEnumerator()).Returns(actualProperties.GetEnumerator()); 38 | projectItem.SetupGet(s => s.Properties).Returns(properties.Object); 39 | 40 | var emptyProjectItems = new Mock(); 41 | emptyProjectItems.SetupGet(s => s.Count).Returns(0); 42 | projectItem.SetupGet(s => s.ProjectItems).Returns(emptyProjectItems.Object); 43 | var projectItems = new[] {projectItem.Object}; 44 | 45 | targetEnumerable.Setup(s => s.GetEnumerator()).Returns(projectItems.GetEnumerator()); 46 | 47 | //Act 48 | var actual = 49 | target.Object.IsProjectItemPropertiesIncluded( 50 | b => 51 | b.Count( 52 | property => 53 | (property.Name == mockname1 && property.Value.ToString() == mockvalue) || 54 | (property.Name == mockname2 && property.Value.ToString() == boolvalue.ToString())) == 2); 55 | 56 | //Assert 57 | Assert.IsTrue(actual); 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /src/ConfigurationTransform.Test/ProjectProperties.cs: -------------------------------------------------------------------------------- 1 | namespace UnitTestProject1 2 | { 3 | using System; 4 | using System.Collections.Generic; 5 | using System.ComponentModel.Composition; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using Microsoft.VisualStudio.ProjectSystem; 10 | using Microsoft.VisualStudio.ProjectSystem.Properties; 11 | 12 | [Export] 13 | internal partial class ProjectProperties : StronglyTypedPropertyAccess 14 | { 15 | /// 16 | /// Initializes a new instance of the class. 17 | /// 18 | [ImportingConstructor] 19 | public ProjectProperties(ConfiguredProject configuredProject) 20 | : base(configuredProject) 21 | { 22 | } 23 | 24 | /// 25 | /// Initializes a new instance of the class. 26 | /// 27 | public ProjectProperties(ConfiguredProject configuredProject, string file, string itemType, string itemName) 28 | : base(configuredProject, file, itemType, itemName) 29 | { 30 | } 31 | 32 | /// 33 | /// Initializes a new instance of the class. 34 | /// 35 | public ProjectProperties(ConfiguredProject configuredProject, IProjectPropertiesContext projectPropertiesContext) 36 | : base(configuredProject, projectPropertiesContext) 37 | { 38 | } 39 | 40 | /// 41 | /// Initializes a new instance of the class. 42 | /// 43 | public ProjectProperties(ConfiguredProject configuredProject, UnconfiguredProject unconfiguredProject) 44 | : base(configuredProject, unconfiguredProject) 45 | { 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/ConfigurationTransform.Test/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("ConfigurationTransform.Test")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("Microsoft")] 12 | [assembly: AssemblyProduct("ConfigurationTransform.Test")] 13 | [assembly: AssemblyCopyright("Copyright © Microsoft 2012")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("32e2bb88-c767-4adb-88a9-7002d8adb26b")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /src/ConfigurationTransform.Test/TestProjectWithLinkFiles.xml: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | x86 6 | 8.0.30703 7 | 2.0 8 | {7AC32633-AEB0-4B81-835A-65EAF42AB63A} 9 | Exe 10 | Properties 11 | RTF.Server.Hosting.Console 12 | RTF.Server.Hosting.Console 13 | v4.0 14 | 15 | 16 | 512 17 | SAK 18 | SAK 19 | SAK 20 | SAK 21 | 22 | 23 | x86 24 | true 25 | full 26 | false 27 | bin\x86\Debug\ 28 | DEBUG;TRACE 29 | prompt 30 | 4 31 | 32 | 33 | x86 34 | pdbonly 35 | true 36 | bin\x86\Release\ 37 | TRACE 38 | prompt 39 | 4 40 | 41 | 42 | bin\x86\Integration\ 43 | TRACE 44 | true 45 | pdbonly 46 | x86 47 | bin\x86\Release\RTF.Server.Hosting.Console.exe.CodeAnalysisLog.xml 48 | true 49 | GlobalSuppressions.cs 50 | prompt 51 | MinimumRecommendedRules.ruleset 52 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets 53 | false 54 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules 55 | false 56 | 57 | 58 | bin\x86\IntegrationFM\ 59 | TRACE 60 | true 61 | pdbonly 62 | x86 63 | bin\x86\Release\RTF.Server.Hosting.Console.exe.CodeAnalysisLog.xml 64 | true 65 | GlobalSuppressions.cs 66 | prompt 67 | MinimumRecommendedRules.ruleset 68 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets 69 | false 70 | ;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules 71 | false 72 | 73 | 74 | 75 | False 76 | ..\Solution Items\EntLib50\Microsoft.Practices.EnterpriseLibrary.Common.dll 77 | 78 | 79 | False 80 | ..\Solution Items\EntLib50\Microsoft.Practices.EnterpriseLibrary.Data.dll 81 | 82 | 83 | False 84 | ..\Solution Items\EntLib50\Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.dll 85 | 86 | 87 | False 88 | ..\Solution Items\EntLib50\Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging.dll 89 | 90 | 91 | False 92 | ..\Solution Items\EntLib50\Microsoft.Practices.EnterpriseLibrary.Logging.dll 93 | 94 | 95 | False 96 | ..\Solution Items\EntLib50\Microsoft.Practices.EnterpriseLibrary.Logging.Database.dll 97 | 98 | 99 | False 100 | ..\Solution Items\EntLib50\Microsoft.Practices.ServiceLocation.dll 101 | 102 | 103 | False 104 | ..\Solution Items\EntLib50\Microsoft.Practices.Unity.dll 105 | 106 | 107 | False 108 | ..\Solution Items\EntLib50\Microsoft.Practices.Unity.Interception.dll 109 | 110 | 111 | ..\Solution Items\SWAT\SWAT.Service.Hosting.dll 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | SharedAssemblyInfo.cs 124 | 125 | 126 | 127 | 128 | 129 | 130 | App.config 131 | 132 | 133 | App.Debug.config 134 | App.config 135 | 136 | 137 | App.Integration.config 138 | App.config 139 | 140 | 141 | App.IntegrationFM.config 142 | App.config 143 | 144 | 145 | App.Release.config 146 | App.config 147 | 148 | 149 | Aspose.Cells.lic 150 | Always 151 | 152 | 153 | 154 | 155 | {3EDD3DDE-A0CB-495D-98B3-D28263AF9B56} 156 | RTF.Common 157 | 158 | 159 | {B41CEF5B-5355-490E-A3CD-46D0D7740BAD} 160 | RTF.ExceptionHandling 161 | 162 | 163 | {4CBD9B55-491C-4336-93D2-CB25C3D5D156} 164 | RTF.Logging 165 | 166 | 167 | {2300AFF5-8FD7-4B26-B0D4-846CBFA9F4B1} 168 | RTF.Server.Hosting.BL 169 | 170 | 171 | 172 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | $(TargetFileName).config 188 | 189 | 190 | 191 | -------------------------------------------------------------------------------- /src/ConfigurationTransform.Test/VsProjectXmlTransformTest.cs: -------------------------------------------------------------------------------- 1 | using GolanAvraham.ConfigurationTransform.Services; 2 | using GolanAvraham.ConfigurationTransform.Transform; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | using Moq; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace ConfigurationTransform.Test 12 | { 13 | [TestClass] 14 | public class VsProjectXmlTransformTest 15 | { 16 | const string FileWithMoreDots = "file.with.more.dots.config"; 17 | 18 | private VsProjectXmlTransform vsProjectXmlTransform; 19 | 20 | [TestInitialize] 21 | public void Setup() 22 | { 23 | var vsSerivces = new Mock(); 24 | vsProjectXmlTransform = new VsProjectXmlTransform(vsSerivces.Object); 25 | } 26 | 27 | [TestMethod] 28 | public void GetTargetTransformArgs_WhenMoreDotsConfigName_Success() 29 | { 30 | //Arrange 31 | var configName = FileWithMoreDots; 32 | var relativePrefix = @"..\my.common"; 33 | var expected = new TargetTransformArgs { 34 | ConfigExt = "config", 35 | Transform = @"..\my.common\file.with.more.dots.$(Configuration).config", 36 | Source = @"..\my.common\file.with.more.dots.config", 37 | Destination = @"$(OutputPath)file.with.more.dots.config", 38 | Condition = @"Exists('..\my.common\file.with.more.dots.$(Configuration).config')" 39 | }; 40 | 41 | //Act 42 | var args = vsProjectXmlTransform.GetTargetTransformArgs(configName, relativePrefix, true); 43 | 44 | //Assert 45 | Assert.IsNotNull(args); 46 | Assert.AreEqual(expected.Condition, args.Condition); 47 | Assert.AreEqual(expected.ConfigExt, args.ConfigExt); 48 | Assert.AreEqual(expected.Destination, args.Destination); 49 | Assert.AreEqual(expected.Source, args.Source); 50 | Assert.AreEqual(expected.Transform, args.Transform); 51 | } 52 | 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/ConfigurationTransform.Test/VsServicesTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.Security.AccessControl; 5 | using EnvDTE; 6 | using GolanAvraham.ConfigurationTransform.Services; 7 | using Microsoft.VisualStudio.Shell.Interop; 8 | using Microsoft.VisualStudio.TestTools.UnitTesting; 9 | using Moq; 10 | using GolanAvraham.ConfigurationTransform.Services.Extensions; 11 | 12 | namespace ConfigurationTransform.Test 13 | { 14 | [TestClass] 15 | public class VsServicesTest 16 | { 17 | [TestMethod] 18 | public void IsLinkProjectItem_PropertyExist_Returns_True() 19 | { 20 | //Arrange 21 | var projectItem = new Mock(); 22 | var properties = new Mock(); 23 | var enumerable = properties.As(); 24 | var property = new Mock(); 25 | property.SetupGet(s => s.Name).Returns("IsLink"); 26 | property.SetupGet(s => s.Value).Returns(true); 27 | var propertiesEnumerator = new[] { property.Object }; 28 | enumerable.Setup(s => s.GetEnumerator()).Returns(propertiesEnumerator.GetEnumerator()); 29 | projectItem.SetupGet(s => s.Properties).Returns(properties.Object); 30 | 31 | //Act 32 | var actual = projectItem.Object.IsLink(); 33 | 34 | //Assert 35 | Assert.IsTrue(actual); 36 | } 37 | 38 | [TestMethod] 39 | public void IsLinkProjectItem_PropertyExist_Returns_False() 40 | { 41 | //Arrange 42 | var projectItem = new Mock(); 43 | var properties = new Mock(); 44 | var enumerable = properties.As(); 45 | var property = new Mock(); 46 | property.SetupGet(s => s.Name).Returns("IsLink"); 47 | property.SetupGet(s => s.Value).Returns(false); 48 | var propertiesEnumerator = new[] { property.Object }; 49 | enumerable.Setup(s => s.GetEnumerator()).Returns(propertiesEnumerator.GetEnumerator()); 50 | projectItem.SetupGet(s => s.Properties).Returns(properties.Object); 51 | 52 | //Act 53 | var actual = projectItem.Object.IsLink(); 54 | 55 | //Assert 56 | Assert.IsFalse(actual); 57 | } 58 | 59 | [TestMethod] 60 | public void IsLinkProjectItem_PropertyNotExist_Returns_False() 61 | { 62 | //Arrange 63 | var projectItem = new Mock(); 64 | var properties = new Mock(); 65 | var enumerable = properties.As(); 66 | var property = new Mock(); 67 | property.SetupGet(s => s.Name).Returns("MockPropertyName"); 68 | property.SetupGet(s => s.Value).Returns(false); 69 | var propertiesEnumerator = new[] { property.Object }; 70 | enumerable.Setup(s => s.GetEnumerator()).Returns(propertiesEnumerator.GetEnumerator()); 71 | projectItem.SetupGet(s => s.Properties).Returns(properties.Object); 72 | 73 | //Act 74 | var actual = projectItem.Object.IsLink(); 75 | 76 | //Assert 77 | Assert.IsFalse(actual); 78 | } 79 | 80 | [TestMethod] 81 | public void GetBuildConfigurationNames_Returns_BuildArray() 82 | { 83 | //Arrange 84 | var project = new Mock(); 85 | var configurationManager = new Mock(); 86 | configurationManager.SetupGet(s => s.ConfigurationRowNames).Returns(new object[] {"debug", "release"}); 87 | project.SetupGet(s => s.ConfigurationManager).Returns(configurationManager.Object); 88 | //var target = new Mock(); 89 | 90 | //Act 91 | var actual = project.Object.GetBuildConfigurationNames(); 92 | 93 | //Assert 94 | Assert.AreEqual(2,actual.Length); 95 | Assert.AreEqual("debug", actual[0]); 96 | Assert.AreEqual("release", actual[1]); 97 | } 98 | 99 | [TestMethod] 100 | public void TryRegisterCloseAndDeleteFile_Return_True() 101 | { 102 | //Arrange 103 | const string file = @"c:\file.xml"; 104 | var windowFrameMock = new Mock(); 105 | var windowFrame2Mock = windowFrameMock.As(); 106 | 107 | var deleteHandlerMock = new Mock(); 108 | 109 | //Act 110 | var result = windowFrameMock.Object.TryRegisterCloseAndDeleteFile(file, deleteHandlerMock.Object); 111 | uint cookie; 112 | //Assert 113 | Assert.IsTrue(result); 114 | windowFrame2Mock.Verify(v => v.Advise(deleteHandlerMock.Object, out cookie)); 115 | } 116 | 117 | [TestMethod] 118 | public void TryRegisterCloseAndDeleteFile_NotCastableTo_IVsWindowFrame2_Return_False() 119 | { 120 | //Arrange 121 | const string file = @"c:\file.xml"; 122 | 123 | //Act 124 | var result = VsServicesExtensions.TryRegisterCloseAndDeleteFile(null, file); 125 | 126 | //Assert 127 | Assert.IsFalse(result); 128 | } 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/ConfigurationTransform.Test/XmlTransformTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Xml.Linq; 4 | using System.Xml.Schema; 5 | using FluentAssertions; 6 | using GolanAvraham.ConfigurationTransform.Transform; 7 | using Microsoft.VisualStudio.TestTools.UnitTesting; 8 | 9 | namespace ConfigurationTransform.Test 10 | { 11 | [TestClass] 12 | public class XmlTransformTest 13 | { 14 | [TestMethod] 15 | public void HasUsingTaskTransformXml_Null_ThrowsException() 16 | { 17 | //Arrange 18 | var sut = new XmlTransform(); 19 | 20 | //Act 21 | Action action = () => sut.HasUsingTaskTransformXml(null); 22 | 23 | //Assert 24 | action.Should().Throw(); 25 | } 26 | 27 | [TestMethod] 28 | public void HasUsingTaskTransformXml_NoTransformTask_ReturnsFalse() 29 | { 30 | //Arrange 31 | var root = XElement.Parse(@""); 32 | var sut = new XmlTransform(); 33 | 34 | //Act 35 | var actual = sut.HasUsingTaskTransformXml(root); 36 | 37 | //Assert 38 | actual.Should().BeFalse(); 39 | } 40 | 41 | [TestMethod] 42 | public void HasUsingTaskTransformXml_TransformTask_ReturnsTrue() 43 | { 44 | //Arrange 45 | var root = XElement.Parse(@""); 46 | var sut = new XmlTransform(); 47 | 48 | //Act 49 | var actual = sut.HasUsingTaskTransformXml(root); 50 | 51 | //Assert 52 | actual.Should().BeTrue(); 53 | } 54 | 55 | [TestMethod] 56 | public void GetTargetName_NullConfigName_ThrowsException() 57 | { 58 | //Arrange 59 | var sut = new XmlTransform(); 60 | 61 | //Act 62 | Action action = () => sut.GetTargetName(null, AfterTargets.AfterBuild); 63 | 64 | //Assert 65 | action.Should().Throw(); 66 | } 67 | 68 | [TestMethod] 69 | public void GetTargetName_ValidConfigName_ReturnValue() 70 | { 71 | //Arrange 72 | var sut = new XmlTransform(); 73 | 74 | //Act 75 | var actual = sut.GetTargetName("mock.config", AfterTargets.AfterBuild); 76 | 77 | //Assert 78 | actual.Should().Be("mock_config_AfterBuild"); 79 | } 80 | 81 | [TestMethod] 82 | public void GetTarget_NullRoot_ThrowsException() 83 | { 84 | //Arrange 85 | var sut = new XmlTransform(); 86 | 87 | //Act 88 | Action action = () => sut.GetTarget(null, string.Empty); 89 | 90 | //Assert 91 | action.Should().Throw(); 92 | } 93 | 94 | 95 | [TestMethod] 96 | public void GetTarget_EmptyName_ThrowsException() 97 | { 98 | //Arrange 99 | var root = XElement.Parse(@""); 100 | var sut = new XmlTransform(); 101 | 102 | //Act 103 | Action action = () => sut.GetTarget(root, string.Empty); 104 | 105 | //Assert 106 | action.Should().Throw(); 107 | } 108 | 109 | [TestMethod] 110 | public void GetTarget_NoTarget_ReturnsNull() 111 | { 112 | //Arrange 113 | var root = XElement.Parse(@""); 114 | var sut = new XmlTransform(); 115 | 116 | //Act 117 | var actual = sut.GetTarget(root, "not_mock_target"); 118 | 119 | //Assert 120 | actual.Should().BeNull(); 121 | } 122 | 123 | [TestMethod] 124 | public void GetTarget_TwoSameNameTargets_ThrowsException() 125 | { 126 | //Arrange 127 | var root = XElement.Parse(@"", LoadOptions.SetLineInfo); 128 | var sut = new XmlTransform(); 129 | 130 | //Act 131 | Action action = () => sut.GetTarget(root, "mock_target"); 132 | 133 | //Assert 134 | action.Should().Throw() 135 | .And.LineNumber.Should().BeGreaterThan(0); 136 | } 137 | 138 | [TestMethod] 139 | public void GetTarget_OneTarget_ReturnsValue() 140 | { 141 | //Arrange 142 | var root = XElement.Parse(@""); 143 | var sut = new XmlTransform(); 144 | 145 | //Act 146 | var actual = sut.GetTarget(root, "mock_target"); 147 | 148 | //Assert 149 | actual.Should().NotBeNull(); 150 | } 151 | 152 | [TestMethod] 153 | public void HasTarget_NoTarget_ReturnsFalse() 154 | { 155 | //Arrange 156 | var root = XElement.Parse(@""); 157 | var sut = new XmlTransform(); 158 | 159 | //Act 160 | var actual = sut.HasTarget(root, "mock_target"); 161 | 162 | //Assert 163 | actual.Should().BeFalse(); 164 | } 165 | 166 | [TestMethod] 167 | public void HasTarget_TargetExist_ReturnsTrue() 168 | { 169 | //Arrange 170 | var root = XElement.Parse(@""); 171 | var sut = new XmlTransform(); 172 | 173 | //Act 174 | var actual = sut.HasTarget(root, "mock_target"); 175 | 176 | //Assert 177 | actual.Should().BeTrue(); 178 | } 179 | 180 | [TestMethod] 181 | public void HasAfterPublishTarget_NoAfterPublishTarget_ReturnsFalse() 182 | { 183 | //Arrange 184 | var root = XElement.Parse(@""); 185 | var sut = new XmlTransform(); 186 | 187 | //Act 188 | var actual = sut.HasAfterPublishTarget(root); 189 | 190 | //Assert 191 | actual.Should().BeFalse(); 192 | } 193 | 194 | [TestMethod] 195 | public void HasAfterPublishTarget_OneAfterPublishTarget_ReturnsTrue() 196 | { 197 | //Arrange 198 | var root = XElement.Parse(@""); 199 | var sut = new XmlTransform(); 200 | 201 | //Act 202 | var actual = sut.HasAfterPublishTarget(root); 203 | 204 | //Assert 205 | actual.Should().BeTrue(); 206 | } 207 | 208 | [TestMethod] 209 | public void HasAfterBuildTargetTransformXml_NoTransformXml_ReturnsFalse() 210 | { 211 | //Arrange 212 | var root = XElement.Parse(@""); 213 | var sut = new XmlTransform(); 214 | 215 | //Act 216 | var actual = sut.HasAfterBuildTargetTransformXml(root, "mock.config"); 217 | 218 | //Assert 219 | actual.Should().BeFalse(); 220 | } 221 | 222 | [TestMethod] 223 | public void HasAfterBuildTargetTransformXml_NotEqualTransformXmlSource_ReturnsFalse() 224 | { 225 | //Arrange 226 | var root = XElement.Parse(@""); 227 | var sut = new XmlTransform(); 228 | 229 | //Act 230 | var actual = sut.HasAfterBuildTargetTransformXml(root, "mock.config"); 231 | 232 | //Assert 233 | actual.Should().BeFalse(); 234 | } 235 | 236 | [TestMethod] 237 | public void HasAfterBuildTargetTransformXml_EqualTransformXmlSource_ReturnsTrue() 238 | { 239 | //Arrange 240 | var root = XElement.Parse(@""); 241 | var sut = new XmlTransform(); 242 | 243 | //Act 244 | var actual = sut.HasAfterBuildTargetTransformXml(root, "mock.config"); 245 | 246 | //Assert 247 | actual.Should().BeTrue(); 248 | } 249 | 250 | [TestMethod] 251 | public void HasAfterPublishTargetDeployedConfigDefenition_NoDeployedConfig_ReturnsFalse() 252 | { 253 | //Arrange 254 | var root = XElement.Parse(@""); 255 | var sut = new XmlTransform(); 256 | 257 | //Act 258 | var actual = sut.HasAfterPublishTargetDeployedConfigDefenition(root); 259 | 260 | //Assert 261 | actual.Should().BeFalse(); 262 | } 263 | 264 | [TestMethod] 265 | public void HasAfterPublishTargetDeployedConfigDefenition_WithDeployedConfig_ReturnsTrue() 266 | { 267 | //Arrange 268 | var root = XElement.Parse(@""); 269 | var sut = new XmlTransform(); 270 | 271 | //Act 272 | var actual = sut.HasAfterPublishTargetDeployedConfigDefenition(root); 273 | 274 | //Assert 275 | actual.Should().BeTrue(); 276 | } 277 | 278 | [TestMethod] 279 | public void HasAfterCompileTargetTransformXml_NotEqualTransformXmlSource_ReturnsFalse() 280 | { 281 | //Arrange 282 | var root = XElement.Parse(@""); 283 | var sut = new XmlTransform(); 284 | 285 | //Act 286 | var actual = sut.HasAfterBuildTargetTransformXml(root, "mock.config"); 287 | 288 | //Assert 289 | actual.Should().BeFalse(); 290 | } 291 | 292 | [TestMethod] 293 | public void HasAfterCompileTargetTransformXml_EqualTransformXmlSource_ReturnsTrue() 294 | { 295 | //Arrange 296 | var root = XElement.Parse(@""); 297 | var sut = new XmlTransform(); 298 | 299 | //Act 300 | var actual = sut.HasAfterCompileTargetTransformXml(root, "mock.config"); 301 | 302 | //Assert 303 | actual.Should().BeTrue(); 304 | } 305 | 306 | [TestMethod] 307 | public void CreateAfterCompileContent_AllParams_ReturnsObjectList() 308 | { 309 | //Arrange 310 | var sut = new XmlTransform(); 311 | 312 | //Act 313 | var destination = "destination"; 314 | var actual = sut.CreateAfterCompileContent("source", destination, "transform").ToList(); 315 | 316 | //Assert 317 | actual.Should().HaveCount(4); 318 | actual.ElementAt(0).Should().BeOfType(); 319 | // content already covered in TransformXml tests 320 | actual.ElementAt(1).Should().BeOfType().Which.Name.LocalName.Should().Be("TransformXml"); 321 | actual.ElementAt(2).Should().BeOfType(); 322 | actual.ElementAt(3).Should().BeOfType().Subject.Name.LocalName.Should().Be("ItemGroup"); 323 | actual.ElementAt(3).Should().BeOfType().Subject.Elements().ElementAt(0).Should() 324 | .HaveAttribute("Remove", "App.config") 325 | .And.Subject.Name.LocalName.Should().Be("AppConfigWithTargetPath"); 326 | actual.ElementAt(3).Should().BeOfType().Subject.Elements().ElementAt(1).Should() 327 | .HaveAttribute("Include", destination) 328 | .And.Subject.Name.LocalName.Should().Be("AppConfigWithTargetPath"); 329 | 330 | actual.ElementAt(3).Should().BeOfType() 331 | .Subject.Elements().ElementAt(1).Elements().ElementAt(0).Should() 332 | .HaveValue("$(TargetFileName).config") 333 | .And.Subject.Name.LocalName.Should().Be("TargetPath"); 334 | } 335 | 336 | [TestMethod] 337 | public void CreateAfterPublishContent_AllParams_ReturnsObjectList() 338 | { 339 | //Arrange 340 | var sut = new XmlTransform(); 341 | 342 | //Act 343 | var actual = sut.CreateAfterPublishContent().ToList(); 344 | 345 | //Assert 346 | actual.ElementAt(0).Should().BeOfType().Subject.Name.LocalName.Should().Be("PropertyGroup"); 347 | actual.ElementAt(0).Should().BeOfType().Subject.Elements().ElementAt(0) 348 | .Name.LocalName.Should().Be("DeployedConfig"); 349 | actual.ElementAt(1).Should().BeOfType(); 350 | actual.ElementAt(2).Should().BeOfType().Subject.Should() 351 | .HaveAttribute("Condition", "Exists('$(DeployedConfig)')") 352 | .And.HaveAttribute("SourceFiles", "$(IntermediateOutputPath)$(TargetFileName).config") 353 | .And.HaveAttribute("DestinationFiles", "$(DeployedConfig)") 354 | .And.Subject.Name.LocalName.Should().Be("Copy"); 355 | } 356 | } 357 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform.Test/app.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /src/ConfigurationTransform.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.28803.156 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConfigurationTransform", "ConfigurationTransform\ConfigurationTransform.csproj", "{E4538816-241F-40B4-B962-65CCF294FBBA}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConfigurationTransform.Test", "ConfigurationTransform.Test\ConfigurationTransform.Test.csproj", "{FD8CFB45-F75B-40DC-899A-F07233BA64E7}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {E4538816-241F-40B4-B962-65CCF294FBBA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {E4538816-241F-40B4-B962-65CCF294FBBA}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {E4538816-241F-40B4-B962-65CCF294FBBA}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {E4538816-241F-40B4-B962-65CCF294FBBA}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {FD8CFB45-F75B-40DC-899A-F07233BA64E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {FD8CFB45-F75B-40DC-899A-F07233BA64E7}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {FD8CFB45-F75B-40DC-899A-F07233BA64E7}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {FD8CFB45-F75B-40DC-899A-F07233BA64E7}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {B8CD52A8-418E-4EEB-926F-C44E7656BE63} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /src/ConfigurationTransform/ConfigurationTransform.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 16.0 5 | $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) 6 | 7 | 8 | 9 | 10 | Debug 11 | AnyCPU 12 | 2.0 13 | {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} 14 | {E4538816-241F-40B4-B962-65CCF294FBBA} 15 | Library 16 | Properties 17 | GolanAvraham.ConfigurationTransform 18 | ConfigurationTransform 19 | v4.6 20 | true 21 | true 22 | true 23 | false 24 | false 25 | true 26 | true 27 | Program 28 | $(DevEnvDir)devenv.exe 29 | /rootsuffix Exp 30 | 31 | 32 | true 33 | full 34 | false 35 | bin\Debug\ 36 | DEBUG;TRACE 37 | prompt 38 | 4 39 | 40 | 41 | pdbonly 42 | true 43 | bin\Release\ 44 | TRACE 45 | prompt 46 | 4 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | true 91 | VSPackage 92 | Designer 93 | 94 | 95 | 96 | 97 | Designer 98 | 99 | 100 | 101 | 102 | Menus.ctmenu 103 | Designer 104 | 105 | 106 | 107 | 108 | Always 109 | true 110 | 111 | 112 | 113 | 114 | true 115 | 116 | 117 | Always 118 | true 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 3.0.0 134 | 135 | 136 | 137 | 138 | 145 | -------------------------------------------------------------------------------- /src/ConfigurationTransform/ConfigurationTransform.vsct: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 9 | 10 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 31 | 32 | 39 | 40 | 41 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 62 | 63 | 70 | 71 | 72 | 81 | 82 | 83 | 92 | 93 | 94 | 103 | 104 | 105 | 106 | 107 | 108 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 150 | 151 | 152 | 153 | 154 | -------------------------------------------------------------------------------- /src/ConfigurationTransform/ConfigurationTransformPackage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.Design; 4 | using System.Diagnostics; 5 | using System.Globalization; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Runtime.InteropServices; 9 | using System.Threading; 10 | using EnvDTE; 11 | using GolanAvraham.ConfigurationTransform.Remove; 12 | using GolanAvraham.ConfigurationTransform.Services; 13 | using GolanAvraham.ConfigurationTransform.Transform; 14 | using Microsoft.VisualStudio; 15 | using Microsoft.VisualStudio.Shell; 16 | using Microsoft.VisualStudio.Shell.Interop; 17 | using GolanAvraham.ConfigurationTransform.Services.Extensions; 18 | using Microsoft.VisualStudio.Debugger; 19 | using VSLangProj; 20 | 21 | namespace GolanAvraham.ConfigurationTransform 22 | { 23 | /// 24 | /// This is the class that implements the package exposed by this assembly. 25 | /// 26 | /// The minimum requirement for a class to be considered a valid package for Visual Studio 27 | /// is to implement the IVsPackage interface and register itself with the shell. 28 | /// This package uses the helper classes defined inside the Managed Package Framework (MPF) 29 | /// to do it: it derives from the Package class that provides the implementation of the 30 | /// IVsPackage interface and uses the registration attributes defined in the framework to 31 | /// register itself and its components with the shell. 32 | /// 33 | // This attribute tells the PkgDef creation utility (CreatePkgDef.exe) that this class is 34 | // a package. 35 | [PackageRegistration(UseManagedResourcesOnly = true, AllowsBackgroundLoading = true)] 36 | // This attribute is used to register the informations needed to show the this package 37 | // in the Help/About dialog of Visual Studio. 38 | [InstalledProductRegistration("#110", "#112", "1.3", IconResourceID = 400)] 39 | // This attribute is needed to let the shell know that this package exposes some menus. 40 | [ProvideMenuResource("Menus.ctmenu", 1)] 41 | [Guid(GuidList.guidConfigurationTransformPkgString)] 42 | //Specifies a UI context in which a solution exists. 43 | [ProvideAutoLoad("{f1536ef8-92ec-443c-9ed7-fdadf150da82}", PackageAutoLoadFlags.BackgroundLoad)] 44 | public sealed class ConfigurationTransformPackage : AsyncPackage 45 | { 46 | /// 47 | /// Default constructor of the package. 48 | /// Inside this method you can place any initialization code that does not require 49 | /// any Visual Studio service because at this point the package object is created but 50 | /// not sited yet inside Visual Studio environment. The place to do all the other 51 | /// initialization is the Initialize method. 52 | /// 53 | public ConfigurationTransformPackage() 54 | { 55 | Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering constructor for: {0}", this.ToString())); 56 | } 57 | 58 | ///////////////////////////////////////////////////////////////////////////// 59 | // Overriden Package Implementation 60 | #region Package Members 61 | 62 | /// 63 | /// Initialization of the package; this method is called right after the package is sited, so this is the place 64 | /// where you can put all the initialization code that rely on services provided by VisualStudio. 65 | /// 66 | /// A cancellation token to monitor for initialization cancellation, which can occur when VS is shutting down. 67 | /// A provider for progress updates. 68 | /// A task representing the async work of package initialization, or an already completed task if there is none. Do not return null from this method. 69 | protected override async System.Threading.Tasks.Task InitializeAsync(CancellationToken cancellationToken, IProgress progress) 70 | { 71 | Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering Initialize() of: {0}", this.ToString())); 72 | await base.InitializeAsync(cancellationToken, progress); 73 | 74 | // When initialized asynchronously, the current thread may be a background thread at this point. 75 | // Do any initialization that requires the UI thread after switching to the UI thread. 76 | await this.JoinableTaskFactory.SwitchToMainThreadAsync(cancellationToken); 77 | 78 | // Add our command handlers for menu (commands must exist in the .vsct file) 79 | var mcs = GetService(typeof(IMenuCommandService)) as OleMenuCommandService; 80 | if (null != mcs) 81 | { 82 | // Transform 83 | var menuCommandId = new CommandID(GuidList.guidConfigurationTransformCmdSet, (int)PkgCmdIDList.cmdidAddConfigTransforms); 84 | var oleMenuCommand = new OleMenuCommand(MenuItemCallback, null, BeforeQueryStatus, menuCommandId); 85 | mcs.AddCommand(oleMenuCommand); 86 | 87 | // Preview 88 | var previewCommandId = new CommandID(GuidList.guidConfigurationTransformCmdSet, (int)PkgCmdIDList.cmdidPreviewConfigTransforms); 89 | var previewOleMenuCommand = new OleMenuCommand(PreviewMenuItemCallback, null, PreviewBeforeQueryStatus, previewCommandId); 90 | mcs.AddCommand(previewOleMenuCommand); 91 | 92 | // Remove 93 | RemoveCommand.Create(this, GuidList.ProjectMenuGroupCmdSet, (int) PkgCmdIDList.RemoveCommandId); 94 | } 95 | } 96 | 97 | public override string ToString() 98 | { 99 | return "ConfigurationTransformPackage"; 100 | } 101 | 102 | #endregion 103 | 104 | private void PreviewBeforeQueryStatus(object sender, EventArgs eventArgs) 105 | { 106 | try 107 | { 108 | var menuCommand = sender as OleMenuCommand; 109 | if (menuCommand == null) return; 110 | menuCommand.Visible = false; 111 | 112 | var dte2 = DTEExtensions.GetInstance(); 113 | if (!dte2.HasOneSelectedItem()) return; 114 | var selectedItem = dte2.GetSelectedItem(); 115 | // cache selected config project 116 | _selectedProjectItem = selectedItem.ProjectItem; 117 | //if (!ConfigTransformManager.IsTransformConfigName(_selectedProjectItem.Name)) return; 118 | 119 | menuCommand.Visible = true; 120 | } 121 | catch (Exception e) 122 | { 123 | Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, 124 | "Exception in PreviewBeforeQueryStatus() of: {0}. Exception message: {1}", this, 125 | e.Message)); 126 | VsServices.Instance.OutputLine(e.Message); 127 | } 128 | } 129 | 130 | private void PreviewMenuItemCallback(object sender, EventArgs eventArgs) 131 | { 132 | try 133 | { 134 | ConfigTransformManager.PreviewTransform(_selectedProjectItem); 135 | } 136 | catch (Exception e) 137 | { 138 | Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, 139 | "Exception in PreviewMenuItemCallback() of: {0}. Exception message: {1}", this, e.Message)); 140 | VsServices.Instance.OutputLine(e.Message); 141 | } 142 | } 143 | 144 | private ProjectItem _selectedProjectItem; 145 | 146 | // check if we need to display config transform in context menu 147 | private void BeforeQueryStatus(object sender, EventArgs eventArgs) 148 | { 149 | try 150 | { 151 | var menuCommand = sender as OleMenuCommand; 152 | if (menuCommand == null) return; 153 | menuCommand.Visible = false; 154 | 155 | var dte2 = DTEExtensions.GetInstance(); 156 | if (!dte2.HasOneSelectedItem()) return; 157 | var selectedItem = dte2.GetSelectedItem(); 158 | // cache selected config project 159 | _selectedProjectItem = selectedItem.ProjectItem; 160 | if (_selectedProjectItem == null) return; 161 | //if (!ConfigTransformManager.IsRootConfig(_selectedProjectItem.Name)) return; 162 | 163 | menuCommand.Visible = true; 164 | } 165 | catch (Exception e) 166 | { 167 | Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, 168 | "Exception in BeforeQueryStatus() of: {0}. Exception message: {1}", this, e.Message)); 169 | VsServices.Instance.OutputLine(e.Message); 170 | } 171 | } 172 | 173 | /// 174 | /// This function is the callback used to execute a command when the a menu item is clicked. 175 | /// See the Initialize method to see how the menu item is associated to this function using 176 | /// the OleMenuCommandService service and the MenuCommand class. 177 | /// 178 | private void MenuItemCallback(object sender, EventArgs e) 179 | { 180 | var configName = _selectedProjectItem.Name; 181 | VsServices.Instance.OutputLine($"------ Transform started for {configName}"); 182 | 183 | var editProjectFile = ConfigTransformManager.EditProjectFile(_selectedProjectItem); 184 | const string reloadMessage = @"Changes were made in project file."; 185 | const string noChangeMessage = @"No changes were made."; 186 | var displayMessage = editProjectFile ? reloadMessage : noChangeMessage; 187 | 188 | VsServices.Instance.OutputLine($"------ Transform ended for {configName}"); 189 | // Show a Message Box 190 | VsServices.Instance.ShowMessageBox(displayMessage); 191 | } 192 | 193 | } 194 | } 195 | -------------------------------------------------------------------------------- /src/ConfigurationTransform/GlobalSuppressions.cs: -------------------------------------------------------------------------------- 1 | // This file is used by Code Analysis to maintain SuppressMessage 2 | // attributes that are applied to this project. Project-level 3 | // suppressions either have no target or are given a specific target 4 | // and scoped to a namespace, type, member, etc. 5 | // 6 | // To add a suppression to this file, right-click the message in the 7 | // Error List, point to "Suppress Message(s)", and click "In Project 8 | // Suppression File". You do not need to add suppressions to this 9 | // file manually. 10 | 11 | [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1017:MarkAssembliesWithComVisible")] 12 | -------------------------------------------------------------------------------- /src/ConfigurationTransform/Guids.cs: -------------------------------------------------------------------------------- 1 | // Guids.cs 2 | // MUST match guids.h 3 | using System; 4 | 5 | namespace GolanAvraham.ConfigurationTransform 6 | { 7 | static class GuidList 8 | { 9 | public const string guidConfigurationTransformPkgString = "c347e8c1-66cb-475a-8eb4-a5a2018452fc"; 10 | 11 | public const string guidConfigurationTransformCmdSetString = "349d3566-b19d-4234-997e-1c9e81c9f517"; 12 | 13 | public static readonly Guid guidConfigurationTransformCmdSet = new Guid(guidConfigurationTransformCmdSetString); 14 | 15 | // Project menu group 16 | public static readonly Guid ProjectMenuGroupCmdSet = new Guid("6B8B19D3-D46F-4C88-94D6-E064A475F204"); 17 | }; 18 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform/Key.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/ConfigurationTransform/Key.snk -------------------------------------------------------------------------------- /src/ConfigurationTransform/PkgCmdID.cs: -------------------------------------------------------------------------------- 1 | // PkgCmdID.cs 2 | // MUST match PkgCmdID.h 3 | using System; 4 | 5 | namespace GolanAvraham.ConfigurationTransform 6 | { 7 | static class PkgCmdIDList 8 | { 9 | // Project items commands 10 | public const uint cmdidAddConfigTransforms = 0x100; 11 | public const uint cmdidPreviewConfigTransforms = 0X101; 12 | 13 | // Project menu commands 14 | public const uint RemoveCommandId = 0X110; 15 | } 16 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Reflection; 3 | using System.Resources; 4 | using System.Runtime.CompilerServices; 5 | using System.Runtime.InteropServices; 6 | 7 | // General Information about an assembly is controlled through the following 8 | // set of attributes. Change these attribute values to modify the information 9 | // associated with an assembly. 10 | [assembly: AssemblyTitle("ConfigurationTransform")] 11 | [assembly: AssemblyDescription("")] 12 | [assembly: AssemblyConfiguration("")] 13 | [assembly: AssemblyCompany("Golan Avraham")] 14 | [assembly: AssemblyProduct("ConfigurationTransform")] 15 | [assembly: AssemblyCopyright("")] 16 | [assembly: AssemblyTrademark("")] 17 | [assembly: AssemblyCulture("")] 18 | [assembly: ComVisible(false)] 19 | [assembly: CLSCompliant(false)] 20 | [assembly: NeutralResourcesLanguage("en-US")] 21 | 22 | // Version information for an assembly consists of the following four values: 23 | // 24 | // Major Version 25 | // Minor Version 26 | // Build Number 27 | // Revision 28 | // 29 | // You can specify all the values or you can default the Revision and Build Numbers 30 | // by using the '*' as shown below: 31 | 32 | [assembly: AssemblyVersion("3.5.*")] 33 | //[assembly: AssemblyFileVersion("1.0.0.0")] -------------------------------------------------------------------------------- /src/ConfigurationTransform/Remove/RemoveCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel.Design; 4 | using System.Dynamic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | using System.Xml.Linq; 9 | using GolanAvraham.ConfigurationTransform.Services; 10 | using GolanAvraham.ConfigurationTransform.Services.Extensions; 11 | using GolanAvraham.ConfigurationTransform.Transform; 12 | using Microsoft.VisualStudio.Shell; 13 | using Microsoft.VisualStudio.Shell.Interop; 14 | 15 | namespace GolanAvraham.ConfigurationTransform.Remove 16 | { 17 | internal class RemoveCommand 18 | { 19 | private VsServices _vsServices; 20 | private bool _isDirty; 21 | 22 | public static RemoveCommand Create(IServiceProvider serviceProvider, Guid menuGroup, int commandId) 23 | { 24 | return new RemoveCommand(serviceProvider, menuGroup, commandId); 25 | } 26 | 27 | public RemoveCommand(IServiceProvider serviceProvider, Guid menuGroup, int commandId) 28 | { 29 | _vsServices = VsServices.Instance; 30 | if (serviceProvider == null) 31 | { 32 | throw new ArgumentNullException(nameof(serviceProvider)); 33 | } 34 | 35 | if (serviceProvider.GetService(typeof(IMenuCommandService)) is OleMenuCommandService oleMenuCommandService) 36 | { 37 | var commandIdObject = new CommandID(menuGroup, commandId); 38 | var oleMenuCommand = new OleMenuCommand(MenuItemCallback, commandIdObject); 39 | oleMenuCommandService.AddCommand(oleMenuCommand); 40 | } 41 | } 42 | 43 | private void MenuItemCallback(object sender, EventArgs e) 44 | { 45 | _isDirty = false; 46 | var dte2 = DTEExtensions.GetInstance(); 47 | var selectedItem = dte2.GetSelectedItem(); 48 | var project = selectedItem.Project; 49 | var projectFullName = project.FullName; 50 | Remove(projectFullName); 51 | } 52 | 53 | private void Remove(string fileName) 54 | { 55 | _vsServices.OutputLine($"------ Remove old transformations from project file '{fileName}'"); 56 | try 57 | { 58 | var projectRoot = XElement.Load(fileName, LoadOptions.SetLineInfo); 59 | RemoveUsingTask(projectRoot); 60 | RemoveAfterCompileTarget(projectRoot); 61 | RemoveAfterBuildTarget(projectRoot); 62 | RemoveAfterPublishTarget(projectRoot); 63 | if (_isDirty) 64 | { 65 | projectRoot.Save(fileName); 66 | _vsServices.OutputLine("------ Done removing old transformations from project file"); 67 | } 68 | else 69 | { 70 | _vsServices.OutputLine("------ Project doesn't contains transformations"); 71 | _vsServices.ShowMessageBox( 72 | "Project doesn't contains transformations"); 73 | } 74 | } 75 | catch (Exception e) 76 | { 77 | _vsServices.OutputLine(e.Message); 78 | _vsServices.ShowMessageBox( 79 | "Failed to remove old transformations from project file. Please remove it manually.", 80 | OLEMSGBUTTON.OLEMSGBUTTON_OK, OLEMSGICON.OLEMSGICON_CRITICAL); 81 | } 82 | } 83 | 84 | private void RemoveUsingTask(XElement projectRoot) 85 | { 86 | var usingTask = projectRoot.ElementsAnyNs("UsingTask").FirstOrDefault(w => w.Attribute("TaskName")?.Value == "TransformXml"); 87 | 88 | if (usingTask != null) 89 | { 90 | _vsServices.OutputLine("Remove UsingTask"); 91 | usingTask.Remove(); 92 | _isDirty = true; 93 | } 94 | } 95 | 96 | private void RemoveAfterCompileTarget(XElement projectRoot) 97 | { 98 | var afterCompile = projectRoot.ElementsAnyNs("Target") 99 | .FirstOrDefault(w => w.Attribute("Name")?.Value == "AfterCompile"); 100 | 101 | 102 | if (afterCompile.NotNull(target => target.ElementsAnyNs("TransformXml").Any())) 103 | { 104 | _vsServices.OutputLine("Remove AfterCompile"); 105 | afterCompile.Remove(); 106 | _isDirty = true; 107 | } 108 | 109 | } 110 | 111 | private void RemoveAfterBuildTarget(XElement projectRoot) 112 | { 113 | var afterBuilds = projectRoot.ElementsAnyNs("Target") 114 | .Where(w => w.Attribute("Name")?.Value == "AfterBuild").ToList(); 115 | 116 | for (int i = afterBuilds.Count - 1; i >= 0; i--) 117 | { 118 | var afterBuild = afterBuilds[i]; 119 | if (afterBuild.ElementsAnyNs("TransformXml").Any()) 120 | { 121 | var source = afterBuild.ElementsAnyNs("TransformXml").FirstOrDefault()?.Attribute("Source")?.Value; 122 | _vsServices.OutputLine($"Remove AfterBuild {source}"); 123 | afterBuild.Remove(); 124 | _isDirty = true; 125 | } 126 | } 127 | } 128 | 129 | private void RemoveAfterPublishTarget(XElement projectRoot) 130 | { 131 | var afterPublishComment = projectRoot.DescendantNodes().OfType().FirstOrDefault(w => w.Value.Contains("Override After Publish to support ClickOnce AfterPublish")); 132 | 133 | if (afterPublishComment != null) 134 | { 135 | afterPublishComment.Remove(); 136 | _isDirty = true; 137 | } 138 | 139 | var afterPublish = projectRoot.ElementsAnyNs("Target") 140 | .FirstOrDefault(w => w.Attribute("Name")?.Value == "AfterPublish"); 141 | 142 | 143 | if (afterPublish.NotNull(target => target.ElementsAnyNs("PropertyGroup").NotNull(pg=>pg.ElementsAnyNs("DeployedConfig").Any()))) 144 | { 145 | _vsServices.OutputLine("Remove AfterPublish"); 146 | afterPublish.Remove(); 147 | _isDirty = true; 148 | } 149 | } 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /src/ConfigurationTransform/Resources/Images.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/ConfigurationTransform/Resources/Images.bmp -------------------------------------------------------------------------------- /src/ConfigurationTransform/Resources/Package.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/ConfigurationTransform/Resources/Package.ico -------------------------------------------------------------------------------- /src/ConfigurationTransform/Services/DeleteFileOnClose.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using GolanAvraham.ConfigurationTransform.Wrappers; 4 | 5 | namespace GolanAvraham.ConfigurationTransform.Services 6 | { 7 | public class DeleteFileOnClose : IDeleteFileOnClose 8 | { 9 | private readonly IFileWrapper _fileWrapper; 10 | public string FilePath { get; private set; } 11 | 12 | public DeleteFileOnClose(string filePath) : this(filePath, new FileWrapper()) 13 | { 14 | } 15 | 16 | public DeleteFileOnClose(string filePath, IFileWrapper fileWrapper) 17 | { 18 | _fileWrapper = fileWrapper; 19 | if (string.IsNullOrWhiteSpace(filePath)) throw new ArgumentException(filePath); 20 | if (!_fileWrapper.Exists(filePath)) throw new FileNotFoundException(filePath); 21 | FilePath = filePath; 22 | } 23 | 24 | public virtual void DeleteFile() 25 | { 26 | if (!_fileWrapper.Exists(FilePath)) return; 27 | _fileWrapper.Delete(FilePath); 28 | } 29 | 30 | public override string ToString() 31 | { 32 | return string.Format("FilePath: {0}", FilePath); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform/Services/DeleteFileOnDocumentClose.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using EnvDTE; 3 | using GolanAvraham.ConfigurationTransform.Wrappers; 4 | 5 | namespace GolanAvraham.ConfigurationTransform.Services 6 | { 7 | public class DeleteFileOnDocumentClose : DeleteFileOnClose 8 | { 9 | private readonly DocumentEvents _documentEvents; 10 | 11 | public DeleteFileOnDocumentClose(string filePath, DocumentEvents documentEvents) 12 | : this(filePath, documentEvents, new FileWrapper()) 13 | { 14 | } 15 | 16 | public DeleteFileOnDocumentClose(string filePath, DocumentEvents documentEvents, IFileWrapper fileWrapper) 17 | : base(filePath, fileWrapper) 18 | { 19 | _documentEvents = documentEvents; 20 | documentEvents.DocumentClosing += DocumentEvents_DocumentClosing; 21 | } 22 | 23 | private void DocumentEvents_DocumentClosing(Document document) 24 | { 25 | if (document.FullName != FilePath) return; 26 | _documentEvents.DocumentClosing -= DocumentEvents_DocumentClosing; 27 | DeleteFile(); 28 | } 29 | 30 | public static void Register(string filePath, DocumentEvents documentEvents) 31 | { 32 | new DeleteFileOnDocumentClose(filePath, documentEvents); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform/Services/DeleteFileOnWindowFrameClose.cs: -------------------------------------------------------------------------------- 1 | using GolanAvraham.ConfigurationTransform.Wrappers; 2 | using Microsoft.VisualStudio; 3 | using Microsoft.VisualStudio.Shell.Interop; 4 | 5 | namespace GolanAvraham.ConfigurationTransform.Services 6 | { 7 | public class DeleteFileOnWindowFrameClose : DeleteFileOnClose, IDeleteFileOnWindowFrameClose 8 | { 9 | public DeleteFileOnWindowFrameClose(string filePath) : this(filePath, new FileWrapper()) 10 | { 11 | } 12 | 13 | public DeleteFileOnWindowFrameClose(string filePath, IFileWrapper fileWrapper) : base(filePath, fileWrapper) 14 | { 15 | } 16 | 17 | public int OnShow(int fShow) 18 | { 19 | var frameshow = (__FRAMESHOW) fShow; 20 | switch (frameshow) 21 | { 22 | case __FRAMESHOW.FRAMESHOW_WinClosed: 23 | DeleteFile(); 24 | break; 25 | } 26 | return VSConstants.S_OK; 27 | } 28 | 29 | public int OnMove() 30 | { 31 | return VSConstants.S_OK; 32 | } 33 | 34 | public int OnSize() 35 | { 36 | return VSConstants.S_OK; 37 | } 38 | 39 | public int OnDockableChange(int fDockable) 40 | { 41 | return VSConstants.S_OK; 42 | } 43 | 44 | } 45 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform/Services/Extensions/DTEExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using EnvDTE; 5 | using Microsoft.VisualStudio.Shell; 6 | 7 | namespace GolanAvraham.ConfigurationTransform.Services.Extensions 8 | { 9 | public static class DTEExtensions 10 | { 11 | public static DTE GetInstance() 12 | { 13 | return (DTE)Package.GetGlobalService(typeof(DTE)); 14 | } 15 | 16 | public static SelectedItem GetSelectedItem(this DTE dte) 17 | { 18 | return dte.SelectedItems.AsEnumerable().Single(); 19 | } 20 | 21 | public static bool HasOneSelectedItem(this DTE dte) 22 | { 23 | return dte.SelectedItems.Count == 1; 24 | } 25 | 26 | public static Project FindProjectByProjectItemProperties(this DTE dte, 27 | Predicate> predicate) 28 | { 29 | var projects = dte.Solution.Projects.AsEnumerable(); 30 | var project = 31 | projects.FirstOrDefault(project1 => project1.ProjectItems.IsProjectItemPropertiesIncluded(predicate)); 32 | return project; 33 | } 34 | 35 | public static ProjectItem GetProjectItemHavingProperties(this DTE dte, 36 | Predicate> predicate) 37 | { 38 | foreach ( 39 | var projectItem in 40 | dte.Solution.Projects.AsEnumerable().SelectMany(project => project.ProjectItems.AsEnumerable())) 41 | { 42 | if (projectItem.IsHavingProperties(predicate)) return projectItem; 43 | var projectItemHavingProperties = projectItem.ProjectItems.GetProjectItemHavingProperties(predicate); 44 | if (projectItemHavingProperties != null) return projectItemHavingProperties; 45 | } 46 | return null; 47 | } 48 | 49 | public static ProjectItem GetProjectItemHavingProperties(this DTE dte, 50 | Predicate predicate) 51 | { 52 | foreach ( 53 | var projectItem in 54 | dte.Solution.Projects.AsEnumerable().SelectMany(project => project.ProjectItems.AsEnumerable())) 55 | { 56 | if (projectItem.IsHavingProperties(predicate)) return projectItem; 57 | if (projectItem.ProjectItems == null) 58 | { 59 | if (projectItem.SubProject == null || projectItem.SubProject.ProjectItems == null) continue; 60 | var subProjectItemHavingProperties = 61 | projectItem.SubProject.ProjectItems.GetProjectItemHavingProperties(predicate); 62 | if (subProjectItemHavingProperties != null) return subProjectItemHavingProperties; 63 | continue; 64 | } 65 | var projectItemHavingProperties = projectItem.ProjectItems.GetProjectItemHavingProperties(predicate); 66 | if (projectItemHavingProperties != null) return projectItemHavingProperties; 67 | } 68 | return null; 69 | } 70 | } 71 | 72 | 73 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform/Services/Extensions/ProjectExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using EnvDTE; 4 | 5 | namespace GolanAvraham.ConfigurationTransform.Services.Extensions 6 | { 7 | public static class ProjectExtensions 8 | { 9 | public static int GetProjectOutputType(this Project source) 10 | { 11 | return source.Properties.GetPropertyValue("OutputType"); 12 | } 13 | 14 | public static bool IsProjectOutputTypeExecutable(this Project source) 15 | { 16 | var projectOutputType = GetProjectOutputType(source); 17 | // project is a windows application or console application? 18 | return projectOutputType == 0 || projectOutputType == 1; 19 | } 20 | 21 | public static string[] GetBuildConfigurationNames(this Project source) 22 | { 23 | var configurationRowNames = source.ConfigurationManager.ConfigurationRowNames; 24 | var strings = ((Array)configurationRowNames).Cast().ToArray(); 25 | return strings; 26 | } 27 | 28 | public static void SaveReloadProject(this Project source) 29 | { 30 | var dte = source.DTE; 31 | 32 | if (!source.SelectProjectInExplorer()) return; 33 | 34 | dte.ExecuteCommand("File.SaveAll", string.Empty); 35 | // unload project 36 | dte.ExecuteCommand("Project.UnloadProject", string.Empty); 37 | // reload project 38 | dte.ExecuteCommand("Project.ReloadProject", string.Empty); 39 | } 40 | 41 | public static bool SelectProjectInExplorer(this Project source) 42 | { 43 | var dte = source.DTE; 44 | // Get the the Solution Explorer tree 45 | var solutionExplorer = ((UIHierarchy)dte.Windows.Item(Constants.vsWindowKindSolutionExplorer).Object); 46 | var solutionName = solutionExplorer.UIHierarchyItems.Item(1).Name; 47 | 48 | return SelectProject(solutionExplorer, solutionName, source.Name); 49 | } 50 | 51 | private static bool SelectProject(UIHierarchy solutionExplorer, string solutionName, string projectName) 52 | { 53 | var selectedItems = solutionExplorer.SelectedItems as Array; 54 | if (selectedItems == null) return false; 55 | var selectedItem = selectedItems.GetValue(0) as UIHierarchyItem; 56 | 57 | try 58 | { 59 | // is it the project we are looking for? 60 | if (selectedItem.Name == projectName) return true; 61 | // is it the root? 62 | if (selectedItem.Name == solutionName) return false; 63 | } 64 | // suppress exceptions for items w/o names 65 | // ReSharper disable EmptyGeneralCatchClause 66 | catch 67 | // ReSharper restore EmptyGeneralCatchClause 68 | { 69 | } 70 | // move up in hierarchy 71 | solutionExplorer.SelectUp(vsUISelectionType.vsUISelectionTypeSelect, 1); 72 | return SelectProject(solutionExplorer, solutionName, projectName); 73 | } 74 | } 75 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform/Services/Extensions/ProjectItemExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Windows; 6 | using EnvDTE; 7 | 8 | namespace GolanAvraham.ConfigurationTransform.Services.Extensions 9 | { 10 | public static class ProjectItemExtensions 11 | { 12 | public static bool TryGetPropertyValue(this ProjectItem source, string propertyName, 13 | out TResult propertyValue) 14 | { 15 | propertyValue = default(TResult); 16 | var property = source.Properties.AsEnumerable().SingleOrDefault(s => s.Name == propertyName); 17 | if (property == null) 18 | { 19 | return false; 20 | } 21 | propertyValue = (TResult) property.Value; 22 | return true; 23 | } 24 | 25 | 26 | public static TResult GetPropertyValue(this ProjectItem source, string propertyName) 27 | { 28 | var propertyValue = 29 | source.Properties.AsEnumerable().Where(s => s.Name == propertyName).Select(s => s.Value).Single(); 30 | return (TResult) propertyValue; 31 | } 32 | 33 | public static string GetFullPath(this ProjectItem source) 34 | { 35 | const string fullPath = "FullPath"; 36 | var propertyValue = source.GetPropertyValue(fullPath); 37 | return propertyValue; 38 | } 39 | 40 | /// 41 | /// Returns the parent ProjectItem or null if no parent or if parent is not a ProjectItem. 42 | /// 43 | public static ProjectItem ParentProjectItemOrDefault(this ProjectItem source) 44 | { 45 | if (!(source?.Collection?.Parent is ProjectItem parent)) return null; 46 | return parent; 47 | } 48 | 49 | public static bool IsLink(this ProjectItem source) 50 | { 51 | // is link file? 52 | bool isLink; 53 | return (source.TryGetPropertyValue("IsLink", out isLink) && isLink); 54 | } 55 | 56 | public static bool IsHavingProperties(this ProjectItem source, Predicate> predicate) 57 | { 58 | if (predicate == null) 59 | { 60 | throw new ArgumentNullException("predicate"); 61 | } 62 | var isHavingProperties = predicate(source.Properties.AsEnumerable()); 63 | return isHavingProperties; 64 | } 65 | 66 | public static bool IsHavingProperties(this ProjectItem source, Predicate predicate) 67 | { 68 | if (predicate == null) 69 | { 70 | throw new ArgumentNullException("predicate"); 71 | } 72 | if (source.Properties == null) return false; 73 | var isHavingProperties = predicate(source.Properties); 74 | return isHavingProperties; 75 | } 76 | 77 | public static IEnumerable ContainingProjectItem(this ProjectItem source, Predicate predicate) 78 | { 79 | if (predicate == null) 80 | { 81 | throw new ArgumentNullException("predicate"); 82 | } 83 | if (predicate(source)) 84 | { 85 | yield return source; 86 | yield break; 87 | } 88 | if (source.ProjectItems.Count < 1) yield break; 89 | foreach (var projectItem in source.ProjectItems.AsEnumerable()) 90 | { 91 | if (projectItem.ContainingProjectItem(predicate).Any()) 92 | { 93 | yield return projectItem; 94 | } 95 | } 96 | } 97 | 98 | public static ProjectItem GetProjectItemContainingFullPath(this ProjectItem source, bool isLink = false) 99 | { 100 | // get target app.config full path 101 | var projectItemFullPath = source.GetFullPath(); 102 | var dte = (DTE) source.DTE; 103 | return 104 | dte.GetProjectItemHavingProperties(properties => 105 | properties.GetFullPath() == projectItemFullPath && 106 | properties.GetIsLink() == isLink); 107 | 108 | } 109 | 110 | /// 111 | /// Removes all leading occurrences of second characters specified from the current System.String object. 112 | /// 113 | /// 114 | /// 115 | /// 116 | /// 117 | public static string TrimStart(this string first, string second, string separator) 118 | { 119 | var firstSplit = first.Split(new []{separator}, StringSplitOptions.None); 120 | var secondSplit = second.Split(new[] { separator }, StringSplitOptions.None); 121 | 122 | var firstNewList = firstSplit.AsEnumerable(); 123 | foreach (var s in secondSplit) 124 | { 125 | var chunk = firstNewList.First(); 126 | if (s == chunk) 127 | { 128 | firstNewList = firstNewList.Skip(1); 129 | continue; 130 | } 131 | break; 132 | } 133 | 134 | // flatten list and add removed separator 135 | var firstTrim = firstNewList.Aggregate("", (s, s1) => string.Format(@"{0}{1}{2}", s, separator, s1)); 136 | //var relativePath = sourceNewList.Aggregate(@"..", (s, s1) => string.Format(@"{0}\{1}", s, s1)); 137 | 138 | return firstTrim; 139 | } 140 | 141 | public static string RelativePath(this string filePath, string referencePath) 142 | { 143 | var fileUri = new Uri(filePath); 144 | var referenceUri = new Uri(referencePath); 145 | var relativePath = referenceUri.MakeRelativeUri(fileUri).ToString(); 146 | 147 | relativePath = relativePath.Replace(@"/", @"\"); 148 | 149 | return relativePath; 150 | } 151 | 152 | public static string RelativeDirectory(this string source) 153 | { 154 | var lastIndex = source.LastIndexOf(@"\"); 155 | var relativeDirectory = source.Substring(0, lastIndex); 156 | 157 | return relativeDirectory; 158 | } 159 | 160 | public static string GetRelativePath(this ProjectItem source) 161 | { 162 | var filePath = source.GetFullPath(); 163 | var projectContainingFile = source.ContainingProject.FullName; 164 | var relativePath = filePath.RelativePath(projectContainingFile); 165 | 166 | return relativePath; 167 | } 168 | 169 | public static string GetRelativeDirectory(this ProjectItem source) 170 | { 171 | var relativePath = source.GetRelativePath(); 172 | var relativeDirectory = relativePath.RelativeDirectory(); 173 | 174 | return relativeDirectory; 175 | } 176 | } 177 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform/Services/Extensions/ProjectItemsExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using EnvDTE; 5 | 6 | namespace GolanAvraham.ConfigurationTransform.Services.Extensions 7 | { 8 | public static class ProjectItemsExtensions 9 | { 10 | public static IEnumerable AsEnumerable(this ProjectItems source) 11 | { 12 | return source.Cast(); 13 | } 14 | 15 | public static bool IsProjectItemPropertiesIncluded(this ProjectItems source, Predicate> predicate) 16 | { 17 | if (predicate == null) 18 | { 19 | throw new ArgumentNullException("predicate"); 20 | } 21 | 22 | foreach (var projectItem in source.AsEnumerable()) 23 | { 24 | if (projectItem.ProjectItems.Count > 0) 25 | { 26 | if (projectItem.ProjectItems.IsProjectItemPropertiesIncluded(predicate)) return true; 27 | } 28 | 29 | var isProjectItemPropertiesIncluded = predicate.Invoke(projectItem.Properties.AsEnumerable()); 30 | if (isProjectItemPropertiesIncluded) return true; 31 | } 32 | return false; 33 | } 34 | 35 | public static ProjectItem GetProjectItemHavingProperties(this ProjectItems source, Predicate> predicate) 36 | { 37 | if (predicate == null) 38 | { 39 | throw new ArgumentNullException("predicate"); 40 | } 41 | 42 | foreach (var projectItem in source.AsEnumerable()) 43 | { 44 | // check current leaf 45 | var isProjectItemPropertiesIncluded = projectItem.IsHavingProperties(predicate);//predicate.Invoke(projectItem.Properties.AsEnumerable()); 46 | if (isProjectItemPropertiesIncluded) return projectItem; 47 | 48 | // check if branch 49 | if (projectItem.ProjectItems.Count > 0) 50 | { 51 | // call self 52 | var projectItemPropertiesIncluded = projectItem.ProjectItems.GetProjectItemHavingProperties(predicate); 53 | if (projectItemPropertiesIncluded != null) return projectItemPropertiesIncluded; 54 | } 55 | } 56 | return null; 57 | } 58 | 59 | public static ProjectItem GetProjectItemHavingProperties(this ProjectItems source, Predicate predicate) 60 | { 61 | if (predicate == null) 62 | { 63 | throw new ArgumentNullException("predicate"); 64 | } 65 | 66 | foreach (var projectItem in source.AsEnumerable()) 67 | { 68 | // check current leaf 69 | var isProjectItemPropertiesIncluded = projectItem.IsHavingProperties(predicate);//predicate.Invoke(projectItem.Properties.AsEnumerable()); 70 | if (isProjectItemPropertiesIncluded) return projectItem; 71 | 72 | if (projectItem.ProjectItems == null) 73 | { 74 | if (projectItem.SubProject == null || projectItem.SubProject.ProjectItems == null) continue; 75 | var subProjectItemHavingProperties = projectItem.SubProject.ProjectItems.GetProjectItemHavingProperties(predicate); 76 | if (subProjectItemHavingProperties != null) return subProjectItemHavingProperties; 77 | continue; 78 | } 79 | // check if branch 80 | if (projectItem.ProjectItems.AsEnumerable().Any()) 81 | { 82 | // call self 83 | var projectItemPropertiesIncluded = projectItem.ProjectItems.GetProjectItemHavingProperties(predicate); 84 | if (projectItemPropertiesIncluded != null) return projectItemPropertiesIncluded; 85 | } 86 | } 87 | return null; 88 | } 89 | } 90 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform/Services/Extensions/ProjectsExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using EnvDTE; 4 | 5 | namespace GolanAvraham.ConfigurationTransform.Services.Extensions 6 | { 7 | public static class ProjectsExtensions 8 | { 9 | public static IEnumerable AsEnumerable(this Projects source) 10 | { 11 | return source.Cast(); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform/Services/Extensions/PropertiesExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using EnvDTE; 4 | 5 | namespace GolanAvraham.ConfigurationTransform.Services.Extensions 6 | { 7 | public static class PropertiesExtensions 8 | { 9 | public static IEnumerable AsEnumerable(this Properties source) 10 | { 11 | return source.Cast(); 12 | } 13 | 14 | public static TResult GetPropertyValue(this Properties source, string name) 15 | { 16 | var value = source.AsEnumerable().First(property => property.Name == name).Value; 17 | return (TResult)value; 18 | } 19 | 20 | public static string GetFullPath(this Properties source) 21 | { 22 | var value = source.GetPropertyValue("FullPath"); 23 | return value; 24 | } 25 | 26 | public static bool GetIsLink(this Properties source) 27 | { 28 | var value = source.GetPropertyValue("IsLink"); 29 | return value; 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform/Services/Extensions/PropertyExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using EnvDTE; 5 | 6 | namespace GolanAvraham.ConfigurationTransform.Services.Extensions 7 | { 8 | public static class PropertyExtensions 9 | { 10 | public static bool IsEqual(this Property property, string name, object value) 11 | { 12 | return (property.Name == name && property.Value.Equals(value)); 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform/Services/Extensions/SelectedItemsExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using EnvDTE; 4 | 5 | namespace GolanAvraham.ConfigurationTransform.Services.Extensions 6 | { 7 | public static class SelectedItemsExtensions 8 | { 9 | public static IEnumerable AsEnumerable(this SelectedItems source) 10 | { 11 | return source.Cast(); 12 | } 13 | } 14 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform/Services/Extensions/VsServicesExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Diagnostics; 3 | using EnvDTE; 4 | using Microsoft.VisualStudio.Shell.Interop; 5 | 6 | namespace GolanAvraham.ConfigurationTransform.Services.Extensions 7 | { 8 | public static class VsServicesExtensions 9 | { 10 | public static MessageBoxResult ShowMessageBox(this IVsServices source, string message, 11 | OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK, 12 | OLEMSGICON icon = OLEMSGICON.OLEMSGICON_INFO) 13 | { 14 | var result = source.ShowMessageBox("ConfigurationTransform", message, buttons, icon); 15 | var messageBoxResult = (MessageBoxResult) result; 16 | return messageBoxResult; 17 | } 18 | 19 | public static bool TryRegisterCloseAndDeleteFile(this IVsWindowFrame source, string path, IDeleteFileOnWindowFrameClose deleteHandler = null) 20 | { 21 | try 22 | { 23 | // get window with notifications 24 | var vsWindowFrame = source as IVsWindowFrame2; 25 | 26 | if (vsWindowFrame == null) return false; 27 | // register to window event, delete file when window close 28 | var deleteFileOnWindowFrameClose = deleteHandler ?? new DeleteFileOnWindowFrameClose(path); 29 | vsWindowFrame.Advise(deleteFileOnWindowFrameClose, out _); 30 | } 31 | catch (Exception e) 32 | { 33 | return false; 34 | } 35 | return true; 36 | } 37 | 38 | public static bool TryRegisterCloseAndDeleteFile(this string path) 39 | { 40 | try 41 | { 42 | var dte2 = DTEExtensions.GetInstance(); 43 | DeleteFileOnDocumentClose.Register(path, dte2.Events.DocumentEvents); 44 | } 45 | catch (Exception e) 46 | { 47 | return false; 48 | } 49 | 50 | return true; 51 | } 52 | } 53 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform/Services/Helpers/PathHelper.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace GolanAvraham.ConfigurationTransform.Services.Helpers 4 | { 5 | public static class PathHelper 6 | { 7 | public static string AppendToFileName(string fileName, string append) 8 | { 9 | var prefix = Path.GetFileNameWithoutExtension(fileName); 10 | var extension = Path.GetExtension(fileName); 11 | var newFileName = string.Format("{0}{1}{2}", prefix, append, extension); 12 | return newFileName; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform/Services/IDeleteFileOnClose.cs: -------------------------------------------------------------------------------- 1 | namespace GolanAvraham.ConfigurationTransform.Services 2 | { 3 | public interface IDeleteFileOnClose 4 | { 5 | string FilePath { get; } 6 | 7 | void DeleteFile(); 8 | } 9 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform/Services/IDeleteFileOnWindowFrameClose.cs: -------------------------------------------------------------------------------- 1 | using GolanAvraham.ConfigurationTransform.Wrappers; 2 | using Microsoft.VisualStudio.Shell.Interop; 3 | 4 | namespace GolanAvraham.ConfigurationTransform.Services 5 | { 6 | public interface IDeleteFileOnWindowFrameClose : IVsWindowFrameNotify, IDeleteFileOnClose 7 | { 8 | } 9 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform/Services/IVsServices.cs: -------------------------------------------------------------------------------- 1 | using EnvDTE; 2 | using Microsoft.VisualStudio.Shell.Interop; 3 | 4 | namespace GolanAvraham.ConfigurationTransform.Services 5 | { 6 | public interface IVsServices 7 | { 8 | //void ShowMessageBox(string title, string messageFormat, params object[] messageArgs); 9 | int ShowMessageBox(string title, string message, OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK, OLEMSGICON icon = OLEMSGICON.OLEMSGICON_INFO); 10 | 11 | // 12 | // Summary: 13 | // Opens and displays a file comparison window in Visual Studio. 14 | // 15 | // Parameters: 16 | // leftFileMoniker: 17 | // [in] Path to the file that will be displayed in the left side of the comparison. 18 | // 19 | // rightFileMoniker: 20 | // [in] Path to the file that will be displayed in the right side of the comparison. 21 | // 22 | // caption: 23 | // [in] Caption to display in the document tab. If this parameter is null or 24 | // empty, {0} vs. {1} is shown. 25 | // 26 | // Tooltip: 27 | // [in] Tooltip to display for the document tab. If this parameter is null or 28 | // empty, the default tooltip is used. 29 | // 30 | // leftLabel: 31 | // [in] Label to display above the left view. If this parameter is null or empty, 32 | // then no label is shown. 33 | // 34 | // rightLabel: 35 | // [in] Label to display above the right view. If this parameter is null or 36 | // empty, then no label is shown. 37 | // 38 | // inlineLabel: 39 | // [in] Label to display above the inline view. If this parameter is null or 40 | // empty, then no label is shown. 41 | // 42 | // roles: 43 | // [in] Additional text view roles added to the difference views. 44 | // 45 | // grfDiffOptions: 46 | // [in] Mask of options for the comparison window. 47 | // 48 | // Returns: 49 | // Window frame for the comparison view. 50 | IVsWindowFrame OpenComparisonWindow2(string leftFileMoniker, string rightFileMoniker, string caption, string tooltip, string leftLabel, string rightLabel, string inlineLabel, string roles, uint grfDiffOptions); 51 | void OpenDiff(string leftFile, string rightFile, string leftLabel, string rightLabel); 52 | void OutputLine(string message); 53 | } 54 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform/Services/MessageBoxResult.cs: -------------------------------------------------------------------------------- 1 | namespace GolanAvraham.ConfigurationTransform.Services 2 | { 3 | public enum MessageBoxResult 4 | { 5 | Ok = 1, 6 | Cancel = 2, 7 | Abort = 3, 8 | Retry = 4, 9 | Ignore = 5, 10 | Yes = 6, 11 | No = 7, 12 | } 13 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform/Services/VsDiffOpt.cs: -------------------------------------------------------------------------------- 1 | namespace GolanAvraham.ConfigurationTransform.Services 2 | { 3 | internal enum VsDiffOpt 4 | { 5 | /// 6 | /// Left File Is Temporary 7 | /// 8 | LeftFileIsTemporary = 0x00000010, 9 | /// 10 | /// Right File Is Temporary 11 | /// 12 | RightFileIsTemporary = 0x00000020 13 | } 14 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform/Services/VsServices.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Diagnostics; 4 | using System.Linq; 5 | using System.Net; 6 | using EnvDTE; 7 | using GolanAvraham.ConfigurationTransform.Services.Extensions; 8 | using Microsoft.VisualStudio; 9 | using Microsoft.VisualStudio.Shell; 10 | using Microsoft.VisualStudio.Shell.Interop; 11 | 12 | namespace GolanAvraham.ConfigurationTransform.Services 13 | { 14 | public class VsServices : IVsServices 15 | { 16 | private static readonly VsServices _instance = new VsServices(); 17 | 18 | public static VsServices Instance => _instance; 19 | 20 | // Explicit static constructor to tell C# compiler 21 | // not to mark type as beforefieldinit 22 | static VsServices() 23 | { 24 | } 25 | 26 | protected VsServices() 27 | { 28 | } 29 | 30 | public void OutputLine(string message) 31 | { 32 | // Get the output window 33 | var outputWindow = Package.GetGlobalService(typeof(SVsOutputWindow)) as IVsOutputWindow; 34 | 35 | // Ensure that the desired pane is visible 36 | var paneGuid = VSConstants.OutputWindowPaneGuid.GeneralPane_guid; 37 | IVsOutputWindowPane pane; 38 | outputWindow.CreatePane(paneGuid, "ConfigurationTransform", 1, 0); 39 | outputWindow.GetPane(paneGuid, out pane); 40 | 41 | // Output the message 42 | pane.OutputString($"{message}{Environment.NewLine}"); 43 | } 44 | 45 | public int ShowMessageBox(string title, string message, OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK, OLEMSGICON icon = OLEMSGICON.OLEMSGICON_INFO) 46 | { 47 | var uiShell = (IVsUIShell)Package.GetGlobalService(typeof(SVsUIShell)); 48 | var clsid = Guid.Empty; 49 | int result; 50 | ErrorHandler.ThrowOnFailure(uiShell.ShowMessageBox( 51 | 0, 52 | ref clsid, 53 | title, 54 | message, 55 | string.Empty, 56 | 0, 57 | buttons, 58 | OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST, 59 | icon, 60 | 0, // false 61 | out result)); 62 | return result; 63 | } 64 | 65 | public IVsWindowFrame OpenComparisonWindow2(string leftFileMoniker, string rightFileMoniker, string caption, string tooltip, string leftLabel, string rightLabel, string inlineLabel, string roles, uint grfDiffOptions) 66 | { 67 | // get diff service 68 | if (!(Package.GetGlobalService(typeof(SVsDifferenceService)) is IVsDifferenceService diffService)) 69 | throw new NotSupportedException("IVsDifferenceService"); 70 | 71 | var windowFrame = diffService.OpenComparisonWindow2(leftFileMoniker, rightFileMoniker, caption, tooltip, leftLabel, rightLabel, 72 | inlineLabel, roles, grfDiffOptions); 73 | 74 | return windowFrame; 75 | } 76 | 77 | public IVsWindowFrame OpenComparisonWindow(string leftFile, string rightFile, 78 | string caption, string tooltip, 79 | string leftLabel, string rightLabel) 80 | { 81 | const string inlineLabel = "{0}=>{1}"; 82 | var windowFrame = OpenComparisonWindow2(leftFile, rightFile, caption, tooltip, leftLabel, rightLabel, 83 | inlineLabel, null, (uint)VsDiffOpt.RightFileIsTemporary); 84 | 85 | return windowFrame; 86 | } 87 | 88 | public virtual IVsWindowFrame OpenComparisonWindow(string leftFile, string rightFile, 89 | string leftLabel, string rightLabel) 90 | { 91 | var caption = $"{rightLabel} vs. {leftLabel}"; 92 | var tooltip = $"Diff - {rightLabel}"; 93 | var windowFrame = OpenComparisonWindow(leftFile, rightFile, caption, tooltip, leftLabel, rightLabel); 94 | 95 | return windowFrame; 96 | } 97 | 98 | public void OpenDiff(string leftFile, string rightFile, string leftLabel, string rightLabel) 99 | { 100 | // first try to compare 101 | if (CompareFilesDeleteOnClose(leftFile, rightFile, leftLabel, rightLabel)) return; 102 | OutputLine("Open transformed file as fallback solution"); 103 | // fallback call for document open 104 | var dte2 = DTEExtensions.GetInstance(); 105 | OpenFileDeleteOnClose(rightFile, dte2); 106 | } 107 | 108 | private bool CompareFilesDeleteOnClose(string leftFile, string rightFile, string leftLabel, string rightLabel) 109 | { 110 | try 111 | { 112 | // try to open diff within visual studio 113 | var windowFrame = OpenComparisonWindow(leftFile, rightFile, leftLabel, rightLabel); 114 | try 115 | { 116 | windowFrame.TryRegisterCloseAndDeleteFile(rightFile); 117 | return true; 118 | } 119 | catch (Exception e) 120 | { 121 | var message = $"Cannot register for file delete. File: {rightFile}. Exception message: {e.Message}"; 122 | Trace.WriteLine(message); 123 | OutputLine(message); 124 | } 125 | return true; 126 | } 127 | catch (Exception e) 128 | { 129 | var message = $"Cannot open diff within visual studio. Exception message: {e.Message}"; 130 | Trace.WriteLine(message); 131 | OutputLine(message); 132 | } 133 | return false; 134 | } 135 | 136 | private bool OpenFileDeleteOnClose(string path, DTE dte2) 137 | { 138 | try 139 | { 140 | dte2.Documents.Open(path, "Auto", true); 141 | try 142 | { 143 | path.TryRegisterCloseAndDeleteFile(); 144 | } 145 | catch (Exception e) 146 | { 147 | var message = $"Cannot register for file delete. File: {path}. Exception message: {e.Message}"; 148 | Trace.WriteLine(message); 149 | OutputLine(message); 150 | } 151 | return true; 152 | } 153 | catch (Exception e) 154 | { 155 | var message = $"Cannot open file within visual studio. Exception message: {e.Message}"; 156 | Trace.WriteLine(message); 157 | OutputLine(message); 158 | } 159 | return false; 160 | } 161 | } 162 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform/Transform/AfterTargets.cs: -------------------------------------------------------------------------------- 1 | namespace GolanAvraham.ConfigurationTransform.Transform 2 | { 3 | public enum AfterTargets 4 | { 5 | AfterCompile, 6 | AfterPublish, 7 | AfterBuild 8 | } 9 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform/Transform/ConfigTransformManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Xml; 6 | using EnvDTE; 7 | using GolanAvraham.ConfigurationTransform.Services; 8 | using GolanAvraham.ConfigurationTransform.Services.Extensions; 9 | using GolanAvraham.ConfigurationTransform.Services.Helpers; 10 | using GolanAvraham.ConfigurationTransform.Wrappers; 11 | using Microsoft.VisualStudio.Shell.Interop; 12 | using Microsoft.Web.XmlTransform; 13 | 14 | namespace GolanAvraham.ConfigurationTransform.Transform 15 | { 16 | public class ConfigTransformManager 17 | { 18 | private const string DependencyConfigContent = 19 | @" 20 | 21 | 22 | 23 | "; 24 | 25 | /// 26 | /// replaceable vs service for testing. 27 | /// 28 | public static IVsServices VsService { get; set; } 29 | 30 | public static IVsProjectXmlTransform ProjectXmlTransform { get; set; } 31 | 32 | public static IFileWrapper FileWrapper { get; set; } 33 | public static IStreamManager StreamManager { get; set; } 34 | 35 | static ConfigTransformManager() 36 | { 37 | // add default vs service 38 | VsService = VsServices.Instance; 39 | ProjectXmlTransform = new VsProjectXmlTransform(VsService); 40 | FileWrapper = new FileWrapper(); 41 | StreamManager = new StreamManager(); 42 | } 43 | 44 | //TODO:[Golan] - break this method to small pieces 45 | public static bool EditProjectFile(ProjectItem projectItem) 46 | { 47 | string relativePrefix = null; 48 | 49 | // get dte from project item 50 | var dte = projectItem.DTE; 51 | try 52 | { 53 | // hide UI changes 54 | dte.SuppressUI = true; 55 | 56 | var isLinkAppConfig = projectItem.IsLink(); 57 | 58 | var project = projectItem.ContainingProject; 59 | var createAsLinkedConfig = false; 60 | // check if its linked config 61 | if (isLinkAppConfig) 62 | { 63 | // display yes/no message to user. yes - add as linked configs; no - add as concrete configs 64 | var result = VsService.ShowMessageBox("Add as linked configs?", 65 | OLEMSGBUTTON.OLEMSGBUTTON_YESNO, 66 | OLEMSGICON.OLEMSGICON_QUERY); 67 | 68 | // store relative path 69 | relativePrefix = projectItem.GetRelativeDirectory(); 70 | if (result == MessageBoxResult.Yes) 71 | { 72 | createAsLinkedConfig = true; 73 | } 74 | } 75 | 76 | if (createAsLinkedConfig) 77 | { 78 | VsService.OutputLine("Add LinkedConfigFiles"); 79 | // since it's link files we only need to copy them as like files to project 80 | CreateLinkedConfigFiles(projectItem); 81 | } 82 | else 83 | { 84 | VsService.OutputLine("Add ConfigFiles"); 85 | // create missing config files 86 | CreateConfigFiles(project, projectItem); 87 | } 88 | 89 | // we need to know if we saved the file when displaying message to user 90 | var changed = project.IsDirty; 91 | // save before making external changes to file 92 | if (changed) project.Save(); 93 | 94 | // project file(e.g. c:\myproject\myproject.csproj) 95 | var fileName = project.FullName; 96 | // config name (e.g. app.config or logging.config) 97 | var configName = projectItem.Name; 98 | 99 | ProjectXmlTransform.Open(fileName); 100 | VsService.OutputLine("Add UsingTask"); 101 | ProjectXmlTransform.AddTransformTask(); 102 | if (IsRootAppConfig(configName)) 103 | { 104 | VsService.OutputLine("Add AfterCompileTarget"); 105 | ProjectXmlTransform.AddAfterCompileTarget(configName, relativePrefix, createAsLinkedConfig); 106 | VsService.OutputLine("Add AfterPublishTarget"); 107 | // project is a windows application or console application? if so add click once transform task 108 | ProjectXmlTransform.AddAfterPublishTarget(configName, relativePrefix, createAsLinkedConfig); 109 | } 110 | else 111 | { 112 | VsService.OutputLine("Add AfterBuildTarget"); 113 | ProjectXmlTransform.AddAfterBuildTarget(configName, relativePrefix, createAsLinkedConfig); 114 | } 115 | 116 | VsService.OutputLine("Check if need to save project file"); 117 | // save project file 118 | var isSaved = ProjectXmlTransform.Save(); 119 | // check if need to reload project, remember that we edit the project file externally 120 | if (isSaved) 121 | { 122 | VsService.OutputLine("Done saving project file"); 123 | VsService.OutputLine("Reloading project file"); 124 | project.SaveReloadProject(); 125 | } 126 | else 127 | { 128 | VsService.OutputLine("No changes made in project file"); 129 | } 130 | return changed || isSaved; 131 | } 132 | finally 133 | { 134 | dte.SuppressUI = false; 135 | } 136 | } 137 | 138 | public static void CreateLinkedConfigFiles(ProjectItem targetProjectItem) 139 | { 140 | // get source root config project item 141 | var sourceRootConfig = targetProjectItem.GetProjectItemContainingFullPath(); 142 | 143 | // source config is not included in project 144 | if (sourceRootConfig == null) 145 | { 146 | CreateLikedConfigFilesNotFromProject(targetProjectItem); 147 | } 148 | else 149 | { 150 | if (sourceRootConfig.ProjectItems == null) return; 151 | 152 | CreateLikedConfigFilesFromProject(targetProjectItem, sourceRootConfig); 153 | } 154 | } 155 | 156 | private static void CreateLikedConfigFilesFromProject(ProjectItem targetProjectItem, ProjectItem sourceRootConfig) 157 | { 158 | // iterate source root config items 159 | foreach (var item in sourceRootConfig.ProjectItems.AsEnumerable()) 160 | { 161 | // get source config dependent config file name 162 | var sourceFullPath = item.GetFullPath(); 163 | // get target root config not contains source dependent config? 164 | if (targetProjectItem.ProjectItems.AsEnumerable().All( 165 | projectItem => projectItem.GetFullPath() != sourceFullPath)) 166 | { 167 | // add dependent config to target root config 168 | targetProjectItem.ProjectItems.AddFromFile(sourceFullPath); 169 | } 170 | } 171 | } 172 | 173 | private static void CreateLikedConfigFilesNotFromProject(ProjectItem targetProjectItem) 174 | { 175 | var configName = targetProjectItem.Name; 176 | var sourceConfigPath = targetProjectItem.GetFullPath(); 177 | var project = targetProjectItem.ContainingProject; 178 | var buildConfigurationNames = project.GetBuildConfigurationNames(); 179 | 180 | var sourceConfigDirectory = Directory.GetParent(sourceConfigPath).FullName; 181 | 182 | foreach (var buildConfigurationName in buildConfigurationNames) 183 | { 184 | var dependentConfig = GetTransformConfigName(configName, buildConfigurationName); 185 | var sourceDependentConfigFullPath = Path.Combine(sourceConfigDirectory, dependentConfig); 186 | // check if source config file exist and not exist in target 187 | if (FileWrapper.Exists(sourceDependentConfigFullPath) && 188 | targetProjectItem.ProjectItems.AsEnumerable().All(c => c.Name != dependentConfig)) 189 | { 190 | targetProjectItem.ProjectItems.AddFromFile(sourceDependentConfigFullPath); 191 | } 192 | } 193 | } 194 | 195 | 196 | // disclaimer: visual studio doesn't support adding dependent file under linked file 197 | // so no dependent transformed config under linked app.config in designer 198 | private static void CreateConfigFiles(Project project, ProjectItem projectItem) 199 | { 200 | var appConfigName = projectItem.Name; 201 | var buildConfigurationNames = project.GetBuildConfigurationNames(); 202 | // get app.config directory. new transform config will be created there. 203 | string path; 204 | if (projectItem.IsLink()) 205 | { 206 | path = Directory.GetParent(projectItem.ContainingProject.FullName).FullName; 207 | } 208 | else 209 | { 210 | path = Directory.GetParent(projectItem.GetFullPath()).FullName; 211 | } 212 | 213 | foreach (var buildConfigurationName in buildConfigurationNames) 214 | { 215 | var dependentConfig = GetTransformConfigName(appConfigName, buildConfigurationName); 216 | var dependentConfigFullPath = Path.Combine(path, dependentConfig); 217 | // check if config file exist 218 | if (FileWrapper.Exists(dependentConfigFullPath)) 219 | { 220 | VsService.OutputLine($"File {dependentConfig} already exists"); 221 | } 222 | else 223 | { 224 | using (var file = FileWrapper.AppendText(dependentConfigFullPath)) 225 | { 226 | file.Write(DependencyConfigContent); 227 | } 228 | VsService.OutputLine($"File {dependentConfig} was added"); 229 | } 230 | // add it to project file anyway, in case it was deleted just from project file 231 | projectItem.ProjectItems.AddFromFile(dependentConfigFullPath); 232 | } 233 | } 234 | 235 | public static string GetTransformConfigName(string sourceConfigName, string buildConfigurationName) 236 | { 237 | var splitterIndex = sourceConfigName.LastIndexOf('.'); 238 | if(splitterIndex < 0) 239 | throw new NotSupportedException(sourceConfigName); 240 | 241 | var dependentConfig = $"{sourceConfigName.Substring(0, splitterIndex)}.{buildConfigurationName}.{sourceConfigName.Substring(splitterIndex + 1)}"; 242 | return dependentConfig; 243 | } 244 | 245 | public static bool IsTransformConfigName(string configName) 246 | { 247 | var configSplit = configName.Split('.'); 248 | if (configSplit.Length < 3) return false; 249 | if (!configName.EndsWith(".config")) return false; 250 | 251 | return true; 252 | } 253 | 254 | public static bool IsRootAppConfig(string fileName) 255 | { 256 | if (!fileName.StartsWith("app", StringComparison.OrdinalIgnoreCase)) return false; 257 | return IsRootConfig(fileName); 258 | } 259 | 260 | public static bool IsRootConfig(string fileName) 261 | { 262 | if (!fileName.EndsWith(".config", StringComparison.OrdinalIgnoreCase)) return false; 263 | if (fileName.Split('.').Length > 2) return false; 264 | return true; 265 | } 266 | 267 | /// 268 | /// Return transformed xml string 269 | /// 270 | /// app.config 271 | /// app.debug.config 272 | /// Transformed xml string 273 | public static string GetTransformString(string sourcePath, string targetPath) 274 | { 275 | var xmlDocument = OpenSourceFile(sourcePath); 276 | 277 | var xmlTransformation = new XmlTransformation(targetPath); 278 | xmlTransformation.Apply(xmlDocument); 279 | 280 | var xmlString = xmlDocument.OuterXml; 281 | return xmlString; 282 | } 283 | 284 | private static XmlTransformableDocument OpenSourceFile(string sourceFile) 285 | { 286 | try 287 | { 288 | XmlTransformableDocument transformableDocument = new XmlTransformableDocument(); 289 | transformableDocument.PreserveWhitespace = true; 290 | transformableDocument.Load(sourceFile); 291 | return transformableDocument; 292 | } 293 | //catch (XmlException ex) 294 | //{ 295 | // VsService.OutputLine($"Failed to open {sourceFile}"); 296 | // throw; 297 | //} 298 | catch (Exception e) 299 | { 300 | VsService.OutputLine($"Failed to open {sourceFile}"); 301 | throw; 302 | } 303 | } 304 | 305 | public static void PreviewTransform(ProjectItem projectItem) 306 | { 307 | var parent = projectItem.ParentProjectItemOrDefault(); 308 | if (parent == null) 309 | { 310 | VsServices.Instance.ShowMessageBox("Cannot find source config"); 311 | return; 312 | } 313 | var targetFileName = projectItem.Name; 314 | var sourceLabel = parent.Name; 315 | var sourcePath = parent.GetFullPath(); 316 | var targetLabel = targetFileName; 317 | 318 | // apply transform on file 319 | var xmlString = GetTransformString(sourcePath, projectItem.GetFullPath()); 320 | // create temp file 321 | var tempFilePath = GetTempFilePath(targetFileName); 322 | 323 | var targetPath = tempFilePath; 324 | // write to temp file 325 | WriteToFile(tempFilePath, xmlString); 326 | 327 | VsService.OpenDiff(sourcePath, targetPath, sourceLabel, targetLabel); 328 | } 329 | 330 | private static void WriteToFile(string tempFilePath, string stringToWrite) 331 | { 332 | using (var file = StreamManager.NewStreamWriter(tempFilePath, false, Encoding.UTF8)) 333 | { 334 | file.Write(stringToWrite); 335 | } 336 | } 337 | 338 | private static string GetTempFilePath(string fileName) 339 | { 340 | var tempPath = Path.GetTempPath(); 341 | var tempFileName = PathHelper.AppendToFileName(fileName, string.Format("_{0}", Guid.NewGuid())); 342 | var tempFilePath = Path.Combine(tempPath, tempFileName); 343 | return tempFilePath; 344 | } 345 | 346 | 347 | 348 | 349 | } 350 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform/Transform/Extensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace GolanAvraham.ConfigurationTransform.Transform 4 | { 5 | public static class Extensions 6 | { 7 | public static bool NotNull(this T source, Predicate predicate = null) 8 | { 9 | return source != null && (predicate == null || predicate(source)); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform/Transform/TargetTransformArgs.cs: -------------------------------------------------------------------------------- 1 | namespace GolanAvraham.ConfigurationTransform.Transform 2 | { 3 | public class TargetTransformArgs 4 | { 5 | public string ConfigExt { get; set; } 6 | public string Source { get; set; } 7 | public string Transform { get; set; } 8 | public string Condition { get; set; } 9 | public string Destination { get; set; } 10 | } 11 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform/Transform/VsProjectXmlTransform.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text.RegularExpressions; 6 | using System.Xml.Linq; 7 | using GolanAvraham.ConfigurationTransform.Services; 8 | 9 | namespace GolanAvraham.ConfigurationTransform.Transform 10 | { 11 | public interface IVsProjectXmlTransform 12 | { 13 | void Open(string fileName); 14 | bool Save(); 15 | void AddTransformTask(); 16 | void AddAfterCompileTarget(string appConfigName, string relativePrefix = null, bool transformConfigIsLink = false); 17 | void AddAfterPublishTarget(string appConfigName, string relativePrefix = null, bool transformConfigIsLink = false); 18 | void AddAfterBuildTarget(string anyConfigName, string relativePrefix = null, bool transformConfigIsLink = false); 19 | } 20 | 21 | public class VsProjectXmlTransform : IVsProjectXmlTransform 22 | { 23 | private readonly IVsServices _vsServices; 24 | private string _fileName; 25 | private XElement _projectRoot; 26 | private bool _isDirty; 27 | 28 | private static readonly XNamespace Namespace = "http://schemas.microsoft.com/developer/msbuild/2003"; 29 | private readonly XmlTransform _xmlTransform; 30 | 31 | public VsProjectXmlTransform(IVsServices vsServices) 32 | { 33 | _vsServices = vsServices; 34 | _xmlTransform = new XmlTransform(); 35 | } 36 | 37 | public virtual void Open(string fileName) 38 | { 39 | _fileName = fileName; 40 | _isDirty = false; 41 | _projectRoot = LoadProjectFile(); 42 | } 43 | 44 | protected virtual XElement LoadProjectFile() 45 | { 46 | return XElement.Load(_fileName, LoadOptions.SetLineInfo); 47 | } 48 | 49 | public bool Save() 50 | { 51 | if (!_isDirty) return false; 52 | _projectRoot.Save(_fileName); 53 | return true; 54 | } 55 | 56 | public void AddTransformTask() 57 | { 58 | _vsServices.OutputLine("Check if need to create UsingTask"); 59 | // check if already exists 60 | if (_xmlTransform.HasUsingTaskTransformXml(_projectRoot)) 61 | { 62 | _vsServices.OutputLine("UsingTask already exists"); 63 | return; 64 | } 65 | _isDirty = true; 66 | _vsServices.OutputLine("Creating UsingTask"); 67 | var usingTaskTransformXml = _xmlTransform.CreateUsingTaskTransformXml(); 68 | _projectRoot.Add(usingTaskTransformXml); 69 | _vsServices.OutputLine("Done creating UsingTask"); 70 | } 71 | 72 | public void AddAfterCompileTarget(string appConfigName, string relativePrefix = null, bool transformConfigIsLink = false) 73 | { 74 | var targetName = _xmlTransform.GetTargetName(appConfigName, AfterTargets.AfterCompile); 75 | _vsServices.OutputLine("Check if need to create AfterCompileTarget"); 76 | // check if target exists 77 | if (_xmlTransform.HasTarget(_projectRoot, targetName)) 78 | { 79 | _vsServices.OutputLine("AfterCompileTarget already exists"); 80 | return; 81 | } 82 | _vsServices.OutputLine("Creating AfterCompileTarget"); 83 | // target doesn't not exists, so create it 84 | _isDirty = true; 85 | var args = GetTargetTransformArgs(appConfigName, relativePrefix, transformConfigIsLink); 86 | var afterCompileTarget = _xmlTransform.CreateTarget(targetName, AfterTargets.AfterCompile, args.Condition); 87 | 88 | var destination = $@"$(IntermediateOutputPath)$(TargetFileName).{args.ConfigExt}"; 89 | 90 | // create target content 91 | var afterCompileContent = _xmlTransform.CreateAfterCompileContent( 92 | args.Source, 93 | destination, 94 | args.Transform); 95 | afterCompileTarget.Add(afterCompileContent); 96 | 97 | // add target to project root 98 | _projectRoot.Add(afterCompileTarget); 99 | _vsServices.OutputLine("Done creating AfterCompileTarget"); 100 | } 101 | 102 | public void AddAfterPublishTarget(string appConfigName, string relativePrefix = null, bool transformConfigIsLink = false) 103 | { 104 | var targetName = _xmlTransform.GetTargetName(appConfigName, AfterTargets.AfterPublish); 105 | _vsServices.OutputLine("Check if need to create AfterPublishTarget"); 106 | // check if target exists 107 | if (_xmlTransform.HasTarget(_projectRoot, targetName)) 108 | { 109 | _vsServices.OutputLine("AfterPublishTarget already exists"); 110 | return; 111 | } 112 | _vsServices.OutputLine("Creating AfterPublishTarget"); 113 | // target doesn't not exists, so create it 114 | _isDirty = true; 115 | var condition = GetTargetTransformArgs(appConfigName, relativePrefix, transformConfigIsLink).Condition; 116 | 117 | // create target element 118 | var afterPublishTarget = _xmlTransform.CreateTarget(targetName, AfterTargets.AfterPublish, condition); 119 | 120 | // create target content 121 | var afterPublishContent = _xmlTransform.CreateAfterPublishContent(); 122 | afterPublishTarget.Add(afterPublishContent); 123 | // add comment to project root 124 | _projectRoot.Add(new XComment(@"Override After Publish to support ClickOnce AfterPublish. Target replaces the untransformed config file copied to the deployment directory with the transformed one.")); 125 | // add target to project root 126 | _projectRoot.Add(afterPublishTarget); 127 | _vsServices.OutputLine("Done creating AfterPublishTarget"); 128 | } 129 | 130 | public void AddAfterBuildTarget(string anyConfigName, string relativePrefix = null, 131 | bool transformConfigIsLink = false) 132 | { 133 | var targetName = _xmlTransform.GetTargetName(anyConfigName, AfterTargets.AfterBuild); 134 | _vsServices.OutputLine("Check if need to create AfterBuildTarget"); 135 | // check if target exists 136 | if (_xmlTransform.HasTarget(_projectRoot, targetName)) 137 | { 138 | _vsServices.OutputLine("AfterBuildTarget already exists"); 139 | return; 140 | } 141 | _vsServices.OutputLine("Creating AfterPublishTarget"); 142 | // target doesn't not exists, so create it 143 | _isDirty = true; 144 | 145 | var args = GetTargetTransformArgs(anyConfigName, relativePrefix, transformConfigIsLink); 146 | 147 | // create target element 148 | var afterBuildTarget = _xmlTransform.CreateTarget(targetName, AfterTargets.AfterBuild, args.Condition); 149 | 150 | // create TransformXml element 151 | var transformXml = _xmlTransform.CreateTransformXml( 152 | args.Source, 153 | args.Destination, 154 | args.Transform); 155 | afterBuildTarget.Add(transformXml); 156 | 157 | // add target to project root 158 | _projectRoot.Add(afterBuildTarget); 159 | _vsServices.OutputLine("Done creating AfterPublishTarget"); 160 | } 161 | 162 | public TargetTransformArgs GetTargetTransformArgs(string anyConfigName, string relativePrefix = null, 163 | bool transformConfigIsLink = false) 164 | { 165 | var splitterIndex = anyConfigName.LastIndexOf('.'); 166 | if (splitterIndex < 0) 167 | throw new NotSupportedException(anyConfigName); 168 | 169 | var configName = anyConfigName.Substring(0, splitterIndex); 170 | var configExt = anyConfigName.Substring(splitterIndex + 1); 171 | 172 | if (relativePrefix != null) 173 | { 174 | relativePrefix += @"\"; 175 | } 176 | string transform; 177 | 178 | if (transformConfigIsLink) 179 | { 180 | // ..\Shared\data.$(Configuration).config 181 | transform = $@"{relativePrefix}{configName}.$(Configuration).{configExt}"; 182 | } 183 | else 184 | { 185 | // data.$(Configuration).config 186 | transform = $"{configName}.$(Configuration).{configExt}"; 187 | } 188 | 189 | // Exists('..\Shared\data.$(Configuration).config') 190 | // Exists('data.$(Configuration).config') 191 | var condition = $"Exists('{transform}')"; 192 | 193 | // ..\Shared\data.config 194 | var source = $"{relativePrefix}{anyConfigName}"; 195 | 196 | // $(OutputPath)data.config 197 | var destination = $@"$(OutputPath){configName}.{configExt}"; 198 | 199 | return new TargetTransformArgs 200 | { 201 | Condition = condition, 202 | ConfigExt = configExt, 203 | Destination = destination, 204 | Source = source, 205 | Transform = transform 206 | }; 207 | } 208 | 209 | } 210 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform/Transform/XmlExtension.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Xml.Linq; 4 | 5 | namespace GolanAvraham.ConfigurationTransform.Transform 6 | { 7 | public static class XmlExtension 8 | { 9 | public static IEnumerable ElementsAnyNs(this IEnumerable source, string localName) 10 | where T : XContainer 11 | { 12 | return source.Elements().Where(e => e.Name.LocalName == localName); 13 | } 14 | 15 | public static IEnumerable DescendantsAnyNs(this IEnumerable source, string localName) 16 | where T : XContainer 17 | { 18 | return source.Descendants().Where(e => e.Name.LocalName == localName); 19 | } 20 | 21 | public static IEnumerable ElementsAnyNs(this XElement source, string localName) 22 | { 23 | return source.Elements().Where(e => e.Name.LocalName == localName); 24 | } 25 | 26 | public static IEnumerable DescendantsAnyNs(this XElement source, string localName) 27 | { 28 | return source.Descendants().Where(e => e.Name.LocalName == localName); 29 | } 30 | } 31 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform/Transform/XmlTransform.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Xml; 5 | using System.Xml.Linq; 6 | using System.Xml.Schema; 7 | 8 | namespace GolanAvraham.ConfigurationTransform.Transform 9 | { 10 | public partial class XmlTransform 11 | { 12 | private static readonly XNamespace Namespace = "http://schemas.microsoft.com/developer/msbuild/2003"; 13 | 14 | public string GetTargetName(string configName, AfterTargets afterTargets) 15 | { 16 | if (string.IsNullOrEmpty(configName)) throw new ArgumentNullException(nameof(configName)); 17 | return $"{configName.Replace(".", "_")}_{afterTargets.ToString()}"; 18 | } 19 | 20 | public XElement GetTarget(XElement projectRoot, string targetName) 21 | { 22 | if (projectRoot == null) throw new ArgumentNullException(nameof(projectRoot)); 23 | if (string.IsNullOrEmpty(targetName)) throw new ArgumentNullException(nameof(targetName)); 24 | 25 | var targets = projectRoot.ElementsAnyNs("Target") 26 | .Where(w => w.Attribute("Name")?.Value == targetName).ToList(); 27 | 28 | if (targets.Count <= 1) return targets.FirstOrDefault(); 29 | 30 | var xmlLineInfo = targets.Last() as IXmlLineInfo; 31 | 32 | throw new XmlSchemaValidationException($"Only one Target with {targetName} is allowed in project file", 33 | null, xmlLineInfo.LineNumber, xmlLineInfo.LinePosition); 34 | } 35 | 36 | public XElement GetAfterCompileTarget(XElement projectRoot) 37 | { 38 | return GetTarget(projectRoot, "AfterCompile"); 39 | } 40 | 41 | public bool HasTarget(XElement projectRoot, string targetName) 42 | { 43 | return GetTarget(projectRoot, targetName) 44 | .NotNull(); 45 | } 46 | 47 | public XElement GetAfterBuildTarget(XElement projectRoot) 48 | { 49 | return GetTarget(projectRoot, "AfterBuild"); 50 | } 51 | 52 | public XElement GetAfterPublishTarget(XElement projectRoot) 53 | { 54 | return GetTarget(projectRoot, "AfterPublish"); 55 | } 56 | 57 | public bool HasAfterBuildTarget(XElement projectRoot) 58 | { 59 | return GetAfterBuildTarget(projectRoot) 60 | .NotNull(); 61 | } 62 | 63 | public bool HasAfterPublishTarget(XElement projectRoot) 64 | { 65 | return GetAfterPublishTarget(projectRoot) 66 | .NotNull(); 67 | } 68 | 69 | public bool HasUsingTaskTransformXml(XElement projectRoot) 70 | { 71 | if (projectRoot == null) throw new ArgumentNullException(nameof(projectRoot)); 72 | return projectRoot.ElementsAnyNs("UsingTask").Any(w => w.Attribute("TaskName")?.Value == "TransformXml"); 73 | } 74 | 75 | public bool HasAfterPublishTargetDeployedConfigDefenition(XElement projectRoot) 76 | { 77 | return GetAfterPublishTarget(projectRoot) 78 | .NotNull(target => 79 | target.ElementsAnyNs("PropertyGroup") 80 | .Any(propertyGroup => propertyGroup.ElementsAnyNs("DeployedConfig").Any())); 81 | } 82 | 83 | public bool HasAfterBuildTargetTransformXml(XElement projectRoot, string source) 84 | { 85 | return GetAfterBuildTarget(projectRoot) 86 | .NotNull(target => target.ElementsAnyNs("TransformXml").Any( 87 | transformXml => transformXml.Attribute("Source")?.Value == source)); 88 | } 89 | 90 | public bool HasAfterCompileTargetTransformXml(XElement projectRoot, string source) 91 | { 92 | return GetAfterCompileTarget(projectRoot) 93 | .NotNull(target => target.ElementsAnyNs("TransformXml").Any( 94 | transformXml => transformXml.Attribute("Source")?.Value == source)); 95 | } 96 | 97 | public XElement CreateTarget(string targetName, AfterTargets afterTargets, string condition) 98 | { 99 | return CreateElement("Target", 100 | new XAttribute("Name", targetName), 101 | new XAttribute("AfterTargets", afterTargets.ToString()), 102 | new XAttribute("Condition", condition)); 103 | } 104 | 105 | public XElement CreateTransformXml(string source, string destination, string transform) 106 | { 107 | return CreateElement("TransformXml", 108 | new XAttribute("Source", source), 109 | new XAttribute("Destination", destination), 110 | new XAttribute("Transform", transform)); 111 | } 112 | 113 | public IEnumerable CreateAfterPublishContent() 114 | { 115 | const string publishComment = "Publish copies the untransformed App.config to deployment directory so overwrite it"; 116 | 117 | return new List 118 | { 119 | CreateElement("PropertyGroup", CreateElement("DeployedConfig", @"$(_DeploymentApplicationDir)$(TargetName)$(TargetExt).config$(_DeploymentFileMappingExtension)")), 120 | new XComment(publishComment), 121 | CreateElement("Copy", 122 | new XAttribute("Condition", "Exists('$(DeployedConfig)')"), 123 | new XAttribute("SourceFiles", "$(IntermediateOutputPath)$(TargetFileName).config"), 124 | new XAttribute("DestinationFiles", "$(DeployedConfig)") 125 | ) 126 | }; 127 | } 128 | 129 | public IEnumerable CreateAfterCompileContent(string source, string destination, string transform) 130 | { 131 | const string generateComment = @"Generate transformed app config in the intermediate directory"; 132 | const string forceComment = @"Force build process to use the transformed configuration file from now on."; 133 | 134 | return new List 135 | { 136 | new XComment(generateComment), 137 | CreateTransformXml(source, destination, transform), 138 | new XComment(forceComment), 139 | CreateElement("ItemGroup", 140 | CreateElement("AppConfigWithTargetPath", 141 | new XAttribute("Remove", "App.config")), 142 | CreateElement("AppConfigWithTargetPath", 143 | new XAttribute("Include", destination), 144 | CreateElement("TargetPath", "$(TargetFileName).config"))) 145 | }; 146 | } 147 | 148 | public XElement CreateUsingTaskTransformXml() 149 | { 150 | const string transformAssemblyFile = 151 | @"$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)\Web\Microsoft.Web.Publishing.Tasks.dll"; 152 | 153 | return CreateElement("UsingTask", new XAttribute("TaskName", "TransformXml"), 154 | new XAttribute("AssemblyFile", transformAssemblyFile)); 155 | } 156 | 157 | private XElement CreateElement(string name, params object[] content) 158 | { 159 | return new XElement(Namespace + name, content); 160 | } 161 | } 162 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform/VSPackage.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | ConfigurationTransform 122 | 123 | 124 | Automatically transform app.config during build process. Once the transformation is set, it will run on other build machines without the extension. 125 | 126 | 127 | 128 | resources\package.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a 129 | 130 | -------------------------------------------------------------------------------- /src/ConfigurationTransform/Wrappers/FileWrapper.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using GolanAvraham.ConfigurationTransform.Transform; 3 | 4 | namespace GolanAvraham.ConfigurationTransform.Wrappers 5 | { 6 | public class FileWrapper : IFileWrapper 7 | { 8 | public StreamWriter AppendText(string path) 9 | { 10 | return File.AppendText(path); 11 | } 12 | 13 | public bool Exists(string path) 14 | { 15 | return File.Exists(path); 16 | } 17 | 18 | public void Delete(string path) 19 | { 20 | File.Delete(path); 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform/Wrappers/IFileWrapper.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | 3 | namespace GolanAvraham.ConfigurationTransform.Wrappers 4 | { 5 | public interface IFileWrapper 6 | { 7 | StreamWriter AppendText(string path); 8 | bool Exists(string path); 9 | void Delete(string path); 10 | } 11 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform/Wrappers/IStreamManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | 4 | namespace GolanAvraham.ConfigurationTransform.Wrappers 5 | { 6 | public interface IStreamManager 7 | { 8 | IStreamWriterWrapper NewStreamWriter(string path, bool append, Encoding encoding = null); 9 | } 10 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform/Wrappers/IStreamWriterWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace GolanAvraham.ConfigurationTransform.Wrappers 4 | { 5 | public interface IStreamWriterWrapper : IDisposable 6 | { 7 | void Write(string value); 8 | } 9 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform/Wrappers/StreamManager.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace GolanAvraham.ConfigurationTransform.Wrappers 4 | { 5 | public class StreamManager : IStreamManager 6 | { 7 | public IStreamWriterWrapper NewStreamWriter(string path, bool append, Encoding encoding = null) 8 | { 9 | return new StreamWriterWrapper(path, append, encoding); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform/Wrappers/StreamWriterWrapper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Text; 4 | 5 | namespace GolanAvraham.ConfigurationTransform.Wrappers 6 | { 7 | public sealed class StreamWriterWrapper : IStreamWriterWrapper 8 | { 9 | private readonly StreamWriter _streamWriter; 10 | 11 | public StreamWriterWrapper(string path, bool append, Encoding encoding = null) 12 | { 13 | _streamWriter = new StreamWriter(path, append, encoding ?? Encoding.Default); 14 | } 15 | 16 | public void Write(string value) 17 | { 18 | _streamWriter.Write(value); 19 | } 20 | 21 | public void Dispose() 22 | { 23 | _streamWriter.Dispose(); 24 | } 25 | } 26 | } -------------------------------------------------------------------------------- /src/ConfigurationTransform/license.txt: -------------------------------------------------------------------------------- 1 | Copyright 2012 Golan Avraham 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | -------------------------------------------------------------------------------- /src/ConfigurationTransform/package_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/ConfigurationTransform/package_icon.png -------------------------------------------------------------------------------- /src/ConfigurationTransform/package_preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/ConfigurationTransform/package_preview.png -------------------------------------------------------------------------------- /src/ConfigurationTransform/source.extension.vsixmanifest: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | Configuration Transform 6 | Automatically transform app.config or any other config during build process. Once the transformation is set, it will run on other build machines without the extension. 7 | https://marketplace.visualstudio.com/items?itemName=GolanAvraham.ConfigurationTransform 8 | license.txt 9 | https://github.com/golavr/ConfigurationTransform/wiki/Release-Notes 10 | package_icon.png 11 | package_preview.png 12 | app.config;Build;Configuration;diff;Team Development;Transform;XDT 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/FluentAssertions.4.19.4.nupkg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/FluentAssertions.4.19.4.nupkg -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/net40/FluentAssertions.Core.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/net40/FluentAssertions.Core.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/net40/FluentAssertions.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/net40/FluentAssertions.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/net45/FluentAssertions.Core.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/net45/FluentAssertions.Core.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/net45/FluentAssertions.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/net45/FluentAssertions.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/netstandard1.3/FluentAssertions.Core.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/netstandard1.3/FluentAssertions.Core.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/netstandard1.3/FluentAssertions.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/netstandard1.3/FluentAssertions.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/portable-net40+sl5+win8+wp8+wpa81/FluentAssertions.Core.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/portable-net40+sl5+win8+wp8+wpa81/FluentAssertions.Core.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/portable-net40+sl5+win8+wp8+wpa81/FluentAssertions.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/portable-net40+sl5+win8+wp8+wpa81/FluentAssertions.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/portable-win81+wpa81/FluentAssertions.Core.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/portable-win81+wpa81/FluentAssertions.Core.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/portable-win81+wpa81/FluentAssertions.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/portable-win81+wpa81/FluentAssertions.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/portable-win81+wpa81/FluentAssertions.pri: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/portable-win81+wpa81/FluentAssertions.pri -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/sl5/FluentAssertions.Core.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/sl5/FluentAssertions.Core.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/sl5/FluentAssertions.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/sl5/FluentAssertions.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/sl5/Microsoft.CSharp.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/sl5/Microsoft.CSharp.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/sl5/Microsoft.VisualStudio.QualityTools.UnitTesting.Silverlight.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/sl5/Microsoft.VisualStudio.QualityTools.UnitTesting.Silverlight.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/sl5/System.Xml.Linq.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/sl5/System.Xml.Linq.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/sl5/de/Microsoft.CSharp.resources.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/sl5/de/Microsoft.CSharp.resources.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/sl5/de/System.Xml.Linq.resources.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/sl5/de/System.Xml.Linq.resources.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/sl5/es/Microsoft.CSharp.resources.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/sl5/es/Microsoft.CSharp.resources.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/sl5/es/System.Xml.Linq.resources.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/sl5/es/System.Xml.Linq.resources.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/sl5/fr/Microsoft.CSharp.resources.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/sl5/fr/Microsoft.CSharp.resources.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/sl5/fr/System.Xml.Linq.resources.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/sl5/fr/System.Xml.Linq.resources.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/sl5/it/Microsoft.CSharp.resources.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/sl5/it/Microsoft.CSharp.resources.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/sl5/it/System.Xml.Linq.resources.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/sl5/it/System.Xml.Linq.resources.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/sl5/ja/Microsoft.CSharp.resources.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/sl5/ja/Microsoft.CSharp.resources.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/sl5/ja/System.Xml.Linq.resources.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/sl5/ja/System.Xml.Linq.resources.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/sl5/ko/Microsoft.CSharp.resources.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/sl5/ko/Microsoft.CSharp.resources.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/sl5/ko/System.Xml.Linq.resources.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/sl5/ko/System.Xml.Linq.resources.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/sl5/ru/Microsoft.CSharp.resources.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/sl5/ru/Microsoft.CSharp.resources.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/sl5/ru/System.Xml.Linq.resources.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/sl5/ru/System.Xml.Linq.resources.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/sl5/zh-Hans/Microsoft.CSharp.resources.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/sl5/zh-Hans/Microsoft.CSharp.resources.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/sl5/zh-Hans/System.Xml.Linq.resources.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/sl5/zh-Hans/System.Xml.Linq.resources.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/sl5/zh-Hant/Microsoft.CSharp.resources.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/sl5/zh-Hant/Microsoft.CSharp.resources.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/sl5/zh-Hant/System.Xml.Linq.resources.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/sl5/zh-Hant/System.Xml.Linq.resources.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/uap10.0/FluentAssertions.Core.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/uap10.0/FluentAssertions.Core.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/uap10.0/FluentAssertions.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/uap10.0/FluentAssertions.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/win81/FluentAssertions.Core.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/win81/FluentAssertions.Core.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/win81/FluentAssertions.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/win81/FluentAssertions.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/win81/FluentAssertions.pri: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/win81/FluentAssertions.pri -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/wp8/FluentAssertions.Core.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/wp8/FluentAssertions.Core.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/wp8/FluentAssertions.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/wp8/FluentAssertions.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/wpa81/FluentAssertions.Core.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/wpa81/FluentAssertions.Core.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/wpa81/FluentAssertions.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/wpa81/FluentAssertions.dll -------------------------------------------------------------------------------- /src/packages/FluentAssertions.4.19.4/lib/wpa81/FluentAssertions.pri: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/golavr/ConfigurationTransform/9e78008c2c4c26dc40fc7e44f4b548ab9849540e/src/packages/FluentAssertions.4.19.4/lib/wpa81/FluentAssertions.pri -------------------------------------------------------------------------------- /src/packages/repositories.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | --------------------------------------------------------------------------------