├── .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 | [](https://dev.azure.com/golavr/ConfigurationTransform/_build/latest?definitionId=4&branchName=master)
4 | [](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 | 
36 |
37 | Notice that you have new App.$$$.config files under App.config
38 |
39 | 
40 |
41 | corresponding to build configurations
42 |
43 | 
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 | 
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 | 
81 |
82 | Notice that you have new App.$$$.config files under App.config
83 |
84 | 
85 |
86 | corresponding to source App.config files
87 |
88 | 3. Continue with step 3 above...
89 |
90 | ### Preview: ###
91 |
92 | 
93 |
94 | 
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 |
51 |
52 |
53 |
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