├── .github
├── dependabot.yml
└── workflows
│ └── build.yml
├── .gitignore
├── LICENSE
├── LocalAppVeyor.sln
├── README.md
├── appveyor.yml
├── src
├── LocalAppVeyor.Engine
│ ├── Configuration
│ │ ├── AllowedJobFailureConditions.cs
│ │ ├── AssemblyInfo.cs
│ │ ├── Build.cs
│ │ ├── BuildConfiguration.cs
│ │ ├── BuildVerbosity.cs
│ │ ├── EnvironmentVariables.cs
│ │ ├── ExpandableString.cs
│ │ ├── Matrix.cs
│ │ ├── Reader
│ │ │ ├── BuildConfigurationYamlFileReader.cs
│ │ │ ├── BuildConfigurationYamlStringReader.cs
│ │ │ ├── IBuildConfigurationReader.cs
│ │ │ └── Internal
│ │ │ │ ├── Converters
│ │ │ │ ├── AllowedFailuresYamlTypeConverter.cs
│ │ │ │ ├── EnvironmentVariablesYamlTypeConverter.cs
│ │ │ │ └── VariableTypeConverter.cs
│ │ │ │ └── Model
│ │ │ │ ├── AllowedFailuresCollection.cs
│ │ │ │ ├── InternalAssemblyVersion.cs
│ │ │ │ ├── InternalBuild.cs
│ │ │ │ ├── InternalBuildConfiguration.cs
│ │ │ │ ├── InternalBuildVerbosity.cs
│ │ │ │ ├── InternalConfigurations.cs
│ │ │ │ ├── InternalEnvironmentVariables.cs
│ │ │ │ ├── InternalMatrix.cs
│ │ │ │ ├── InternalOperatingSystems.cs
│ │ │ │ ├── InternalPlatforms.cs
│ │ │ │ ├── InternalScriptBlock.cs
│ │ │ │ ├── InternalScriptLine.cs
│ │ │ │ └── InternalVariable.cs
│ │ ├── ScriptBlock.cs
│ │ ├── ScriptLine.cs
│ │ ├── ScriptType.cs
│ │ └── Variable.cs
│ ├── Engine.cs
│ ├── EngineConfiguration.cs
│ ├── IPipelineOutputter.cs
│ ├── Internal
│ │ ├── BashScriptExecuter.cs
│ │ ├── BatchScriptExecuter.cs
│ │ ├── BuildPipelineExecuter.cs
│ │ ├── ExecutionContext.cs
│ │ ├── IEngineStep.cs
│ │ ├── KnownExceptions
│ │ │ └── SolutionNotFoundException.cs
│ │ ├── PipelineOutputterMsBuildLogger.cs
│ │ ├── Platform.cs
│ │ ├── PowerShellScriptExecuter.cs
│ │ └── Steps
│ │ │ ├── AfterBuildStep.cs
│ │ │ ├── AssemblyInfoRewriteStep.cs
│ │ │ ├── BeforeBuildStep.cs
│ │ │ ├── BuildScriptStep.cs
│ │ │ ├── BuildStep.cs
│ │ │ ├── CloneFolderStep.cs
│ │ │ ├── InitStandardEnvironmentVariablesStep.cs
│ │ │ ├── InitStep.cs
│ │ │ ├── InstallStep.cs
│ │ │ ├── OnFailureStep.cs
│ │ │ ├── OnFinishStep.cs
│ │ │ ├── OnSuccessStep.cs
│ │ │ ├── ScriptBlockExecuterStep.cs
│ │ │ └── TestScriptStep.cs
│ ├── JobEndedEventArgs.cs
│ ├── JobExecutionResult.cs
│ ├── JobExecutionResultType.cs
│ ├── JobStartingEventArgs.cs
│ ├── LocalAppVeyor.Engine.csproj
│ ├── LocalAppVeyorException.cs
│ ├── MatrixJob.cs
│ └── Properties
│ │ └── AssemblyInfo.cs
└── LocalAppVeyor
│ ├── Commands
│ ├── BuildConsoleCommand.cs
│ ├── ConsoleCommand.cs
│ ├── JobsConsoleCommand.cs
│ └── LintConsoleCommand.cs
│ ├── ConsoleOutputter.cs
│ ├── LocalAppVeyor.csproj
│ ├── Program.cs
│ └── Properties
│ └── launchSettings.json
└── tests
└── LocalAppVeyor.Engine.UnitTests
├── Configuration
├── AssemblyInfoTests.cs
├── BuildScriptTests.Unix.cs
├── BuildScriptTests.Window.cs
├── BuildScriptTests.cs
├── EnvironmentTests.cs
├── ExpandableStringTests.cs
└── MatrixTests.cs
├── Engine
├── AssemblyInfoRewriteStepTests.cs
├── EngineTests.Unix.cs
├── EngineTests.Windows.cs
└── EngineTests.cs
├── LocalAppVeyor.Engine.UnitTests.csproj
├── TestUtilities
├── UnixOnlyFactAttribute.cs
└── WindowsOnlyFactAttribute.cs
├── packages.lock.json
└── xunit.runner.json
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 | updates:
3 | - package-ecosystem: nuget
4 | directory: "/"
5 | schedule:
6 | interval: daily
7 | open-pull-requests-limit: 10
8 | labels:
9 | - dependencies
10 | - package-ecosystem: "github-actions"
11 | directory: "/"
12 | schedule:
13 | interval: daily
--------------------------------------------------------------------------------
/.github/workflows/build.yml:
--------------------------------------------------------------------------------
1 | name: Build
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 |
7 | build:
8 | name: ${{ matrix.os }} (${{ matrix.dotnet }})
9 | runs-on: ${{ matrix.os }}
10 | strategy:
11 | matrix:
12 | os: [ubuntu-latest, windows-latest, macos-latest]
13 | dotnet: ['8.0.100']
14 |
15 | steps:
16 | - uses: actions/checkout@master
17 |
18 | - uses: actions/setup-dotnet@v4
19 | with:
20 | dotnet-version: ${{ matrix.dotnet }}
21 |
22 | - run: dotnet build --configuration Release
23 |
--------------------------------------------------------------------------------
/.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 | *.suo
8 | *.user
9 | *.userosscache
10 | *.sln.docstates
11 |
12 | # User-specific files (MonoDevelop/Xamarin Studio)
13 | *.userprefs
14 |
15 | # Build results
16 | [Dd]ebug/
17 | [Dd]ebugPublic/
18 | [Rr]elease/
19 | [Rr]eleases/
20 | x64/
21 | x86/
22 | bld/
23 | [Bb]in/
24 | [Oo]bj/
25 | [Ll]og/
26 |
27 | # Visual Studio 2015/2017 cache/options directory
28 | .vs/
29 | # Uncomment if you have tasks that create the project's static files in wwwroot
30 | #wwwroot/
31 |
32 | # Visual Studio 2017 auto generated files
33 | Generated\ Files/
34 |
35 | # MSTest test Results
36 | [Tt]est[Rr]esult*/
37 | [Bb]uild[Ll]og.*
38 |
39 | # NUNIT
40 | *.VisualState.xml
41 | TestResult.xml
42 |
43 | # Build Results of an ATL Project
44 | [Dd]ebugPS/
45 | [Rr]eleasePS/
46 | dlldata.c
47 |
48 | # Benchmark Results
49 | BenchmarkDotNet.Artifacts/
50 |
51 | # .NET Core
52 | project.lock.json
53 | project.fragment.lock.json
54 | artifacts/
55 |
56 | # StyleCop
57 | StyleCopReport.xml
58 |
59 | # Files built by Visual Studio
60 | *_i.c
61 | *_p.c
62 | *_i.h
63 | *.ilk
64 | *.meta
65 | *.obj
66 | *.iobj
67 | *.pch
68 | *.pdb
69 | *.ipdb
70 | *.pgc
71 | *.pgd
72 | *.rsp
73 | *.sbr
74 | *.tlb
75 | *.tli
76 | *.tlh
77 | *.tmp
78 | *.tmp_proj
79 | *.log
80 | *.vspscc
81 | *.vssscc
82 | .builds
83 | *.pidb
84 | *.svclog
85 | *.scc
86 |
87 | # Chutzpah Test files
88 | _Chutzpah*
89 |
90 | # Visual C++ cache files
91 | ipch/
92 | *.aps
93 | *.ncb
94 | *.opendb
95 | *.opensdf
96 | *.sdf
97 | *.cachefile
98 | *.VC.db
99 | *.VC.VC.opendb
100 |
101 | # Visual Studio profiler
102 | *.psess
103 | *.vsp
104 | *.vspx
105 | *.sap
106 |
107 | # Visual Studio Trace Files
108 | *.e2e
109 |
110 | # TFS 2012 Local Workspace
111 | $tf/
112 |
113 | # Guidance Automation Toolkit
114 | *.gpState
115 |
116 | # ReSharper is a .NET coding add-in
117 | _ReSharper*/
118 | *.[Rr]e[Ss]harper
119 | *.DotSettings.user
120 |
121 | # JustCode is a .NET coding add-in
122 | .JustCode
123 |
124 | # TeamCity is a build add-in
125 | _TeamCity*
126 |
127 | # DotCover is a Code Coverage Tool
128 | *.dotCover
129 |
130 | # AxoCover is a Code Coverage Tool
131 | .axoCover/*
132 | !.axoCover/settings.json
133 |
134 | # Visual Studio code coverage results
135 | *.coverage
136 | *.coveragexml
137 |
138 | # NCrunch
139 | _NCrunch_*
140 | .*crunch*.local.xml
141 | nCrunchTemp_*
142 |
143 | # MightyMoose
144 | *.mm.*
145 | AutoTest.Net/
146 |
147 | # Web workbench (sass)
148 | .sass-cache/
149 |
150 | # Installshield output folder
151 | [Ee]xpress/
152 |
153 | # DocProject is a documentation generator add-in
154 | DocProject/buildhelp/
155 | DocProject/Help/*.HxT
156 | DocProject/Help/*.HxC
157 | DocProject/Help/*.hhc
158 | DocProject/Help/*.hhk
159 | DocProject/Help/*.hhp
160 | DocProject/Help/Html2
161 | DocProject/Help/html
162 |
163 | # Click-Once directory
164 | publish/
165 |
166 | # Publish Web Output
167 | *.[Pp]ublish.xml
168 | *.azurePubxml
169 | # Note: Comment the next line if you want to checkin your web deploy settings,
170 | # but database connection strings (with potential passwords) will be unencrypted
171 | *.pubxml
172 | *.publishproj
173 |
174 | # Microsoft Azure Web App publish settings. Comment the next line if you want to
175 | # checkin your Azure Web App publish settings, but sensitive information contained
176 | # in these scripts will be unencrypted
177 | PublishScripts/
178 |
179 | # NuGet Packages
180 | *.nupkg
181 | # The packages folder can be ignored because of Package Restore
182 | **/[Pp]ackages/*
183 | # except build/, which is used as an MSBuild target.
184 | !**/[Pp]ackages/build/
185 | # Uncomment if necessary however generally it will be regenerated when needed
186 | #!**/[Pp]ackages/repositories.config
187 | # NuGet v3's project.json files produces more ignorable files
188 | *.nuget.props
189 | *.nuget.targets
190 |
191 | # Microsoft Azure Build Output
192 | csx/
193 | *.build.csdef
194 |
195 | # Microsoft Azure Emulator
196 | ecf/
197 | rcf/
198 |
199 | # Windows Store app package directories and files
200 | AppPackages/
201 | BundleArtifacts/
202 | Package.StoreAssociation.xml
203 | _pkginfo.txt
204 | *.appx
205 |
206 | # Visual Studio cache files
207 | # files ending in .cache can be ignored
208 | *.[Cc]ache
209 | # but keep track of directories ending in .cache
210 | !*.[Cc]ache/
211 |
212 | # Others
213 | ClientBin/
214 | ~$*
215 | *~
216 | *.dbmdl
217 | *.dbproj.schemaview
218 | *.jfm
219 | *.pfx
220 | *.publishsettings
221 | orleans.codegen.cs
222 |
223 | # Including strong name files can present a security risk
224 | # (https://github.com/github/gitignore/pull/2483#issue-259490424)
225 | #*.snk
226 |
227 | # Since there are multiple workflows, uncomment next line to ignore bower_components
228 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
229 | #bower_components/
230 |
231 | # RIA/Silverlight projects
232 | Generated_Code/
233 |
234 | # Backup & report files from converting an old project file
235 | # to a newer Visual Studio version. Backup files are not needed,
236 | # because we have git ;-)
237 | _UpgradeReport_Files/
238 | Backup*/
239 | UpgradeLog*.XML
240 | UpgradeLog*.htm
241 | ServiceFabricBackup/
242 | *.rptproj.bak
243 |
244 | # SQL Server files
245 | *.mdf
246 | *.ldf
247 | *.ndf
248 |
249 | # Business Intelligence projects
250 | *.rdl.data
251 | *.bim.layout
252 | *.bim_*.settings
253 | *.rptproj.rsuser
254 |
255 | # Microsoft Fakes
256 | FakesAssemblies/
257 |
258 | # GhostDoc plugin setting file
259 | *.GhostDoc.xml
260 |
261 | # Node.js Tools for Visual Studio
262 | .ntvs_analysis.dat
263 | node_modules/
264 |
265 | # Visual Studio 6 build log
266 | *.plg
267 |
268 | # Visual Studio 6 workspace options file
269 | *.opt
270 |
271 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
272 | *.vbw
273 |
274 | # Visual Studio LightSwitch build output
275 | **/*.HTMLClient/GeneratedArtifacts
276 | **/*.DesktopClient/GeneratedArtifacts
277 | **/*.DesktopClient/ModelManifest.xml
278 | **/*.Server/GeneratedArtifacts
279 | **/*.Server/ModelManifest.xml
280 | _Pvt_Extensions
281 |
282 | # Paket dependency manager
283 | .paket/paket.exe
284 | paket-files/
285 |
286 | # FAKE - F# Make
287 | .fake/
288 |
289 | # JetBrains Rider
290 | .idea/
291 | *.sln.iml
292 |
293 | # CodeRush
294 | .cr/
295 |
296 | # Python Tools for Visual Studio (PTVS)
297 | __pycache__/
298 | *.pyc
299 |
300 | # Cake - Uncomment if you are using it
301 | # tools/**
302 | # !tools/packages.config
303 |
304 | # Tabs Studio
305 | *.tss
306 |
307 | # Telerik's JustMock configuration file
308 | *.jmconfig
309 |
310 | # BizTalk build output
311 | *.btp.cs
312 | *.btm.cs
313 | *.odx.cs
314 | *.xsd.cs
315 |
316 | # OpenCover UI analysis results
317 | OpenCover/
318 |
319 | # Azure Stream Analytics local run output
320 | ASALocalRun/
321 |
322 | # MSBuild Binary and Structured Log
323 | *.binlog
324 |
325 | # NVidia Nsight GPU debugger configuration file
326 | *.nvuser
327 |
328 | # MFractors (Xamarin productivity tool) working folder
329 | .mfractor/
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2016 Joao Correia (@joaope)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
--------------------------------------------------------------------------------
/LocalAppVeyor.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.29306.81
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{90C2048F-14A3-4CB7-86DD-B96708B01507}"
7 | ProjectSection(SolutionItems) = preProject
8 | .gitignore = .gitignore
9 | appveyor.yml = appveyor.yml
10 | LICENSE = LICENSE
11 | README.md = README.md
12 | EndProjectSection
13 | EndProject
14 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LocalAppVeyor.Engine", "src\LocalAppVeyor.Engine\LocalAppVeyor.Engine.csproj", "{86203454-E52C-49B4-BEDB-0BE025EA1D87}"
15 | EndProject
16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LocalAppVeyor", "src\LocalAppVeyor\LocalAppVeyor.csproj", "{9A050F7B-1A34-47FD-805D-C8A0FEE102D0}"
17 | EndProject
18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LocalAppVeyor.Engine.UnitTests", "tests\LocalAppVeyor.Engine.UnitTests\LocalAppVeyor.Engine.UnitTests.csproj", "{9C8D6864-9557-45E5-BB69-4658C7B66775}"
19 | EndProject
20 | Global
21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
22 | Debug|Any CPU = Debug|Any CPU
23 | Release|Any CPU = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
26 | {86203454-E52C-49B4-BEDB-0BE025EA1D87}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {86203454-E52C-49B4-BEDB-0BE025EA1D87}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {86203454-E52C-49B4-BEDB-0BE025EA1D87}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {86203454-E52C-49B4-BEDB-0BE025EA1D87}.Release|Any CPU.Build.0 = Release|Any CPU
30 | {9A050F7B-1A34-47FD-805D-C8A0FEE102D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
31 | {9A050F7B-1A34-47FD-805D-C8A0FEE102D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
32 | {9A050F7B-1A34-47FD-805D-C8A0FEE102D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
33 | {9A050F7B-1A34-47FD-805D-C8A0FEE102D0}.Release|Any CPU.Build.0 = Release|Any CPU
34 | {9C8D6864-9557-45E5-BB69-4658C7B66775}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
35 | {9C8D6864-9557-45E5-BB69-4658C7B66775}.Debug|Any CPU.Build.0 = Debug|Any CPU
36 | {9C8D6864-9557-45E5-BB69-4658C7B66775}.Release|Any CPU.ActiveCfg = Release|Any CPU
37 | {9C8D6864-9557-45E5-BB69-4658C7B66775}.Release|Any CPU.Build.0 = Release|Any CPU
38 | EndGlobalSection
39 | GlobalSection(SolutionProperties) = preSolution
40 | HideSolutionNode = FALSE
41 | EndGlobalSection
42 | GlobalSection(ExtensibilityGlobals) = postSolution
43 | SolutionGuid = {B9111348-CC78-4DF2-8AF6-65634522D441}
44 | EndGlobalSection
45 | EndGlobal
46 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | .NET Core global tool which brings _**appveyor.yml**_ to the center of your build process by making possible to execute
2 | its build jobs, locally.
3 |
4 | | Windows | OS X / Linux | Nuget |
5 | | ------------- |:-------------:| ----- |
6 | |[](https://ci.appveyor.com/project/joaope/localappveyor)|[](https://github.com/joaope/LocalAppVeyor/actions)|[](https://www.nuget.org/packages/LocalAppVeyor/)|
7 |
8 | - [How it works](#how-it-works)
9 | - [Install](#install)
10 | - [Usage](#usage)
11 | - [• `build` command](#%e2%80%a2-build-command)
12 | - [• `jobs` command](#%e2%80%a2-jobs-command)
13 | - [• `lint` command](#%e2%80%a2-lint-command)
14 | - [Supported build steps](#supported-build-steps)
15 |
16 | ## How it works
17 | LocalAppVeyor tries to strictly follow same [build pipeline](https://www.appveyor.com/docs/build-configuration/#build-pipeline)
18 | as [AppVeyor CI](https://appveyor.com) itself.
19 |
20 | 1. Grabs _appveyor.yml_'s build configuration from current (or specified) local repository folder.
21 | 2. Reads [supported build steps](#supported-build-steps) from it.
22 | 3. Executes [build pipeline](https://www.appveyor.com/docs/build-configuration/#build-pipeline) for each job (or specified ones)
23 | on the [build matrix](https://www.appveyor.com/docs/build-configuration/#build-matrix).
24 |
25 | Build engine tries to be the less intrusive as possible printing only what it comes from the build output.
26 |
27 | ## Install
28 |
29 | Install LocalAppVeyor as a [.NET Core CLI](https://docs.microsoft.com/en-us/dotnet/core/tools/?tabs=netcore2x) global tool using the following command:
30 | ```console
31 | dotnet tool install -g localappveyor
32 | ```
33 | You have it now available on your command line:
34 |
35 | ```console
36 | LocalAppVeyor --help
37 | ```
38 |
39 | *Requires [.NET Core 3.1](https://www.microsoft.com/net/download) or higher.*
40 |
41 | ## Usage
42 | ```
43 | Usage: LocalAppVeyor [options] [command]
44 |
45 | Options:
46 | -?|-h|--help Show help information
47 | -v|--version Show version information
48 |
49 | Commands:
50 | build Executes one or all build jobs on specified repository directory
51 | jobs List all build jobs available to execution.
52 | lint Validates appveyor.yml YAML configuration. It requires internet connection.
53 |
54 | Use "LocalAppVeyor [command] --help" for more information about a command.
55 | ```
56 |
57 | ### • `build` command
58 |
59 | This is the main console command which allows one to execute all or a smaller set of jobs from the
60 | [build matrix](https://www.appveyor.com/docs/build-configuration/#build-matrix). `--job` command should be followed by a integer
61 | corresponding to job index as listed on `jobs` command
62 |
63 | ```
64 | Usage: LocalAppVeyor build [options]
65 |
66 | Options:
67 | -?|-h|--help Show help information
68 | -d|--dir Local repository directory where appveyor.yml sits. If not specified current directory is used
69 | -j|--job Job to build. You can specify multiple jobs. Use 'jobs' command to list all jobs
70 | -s|--skip Step to skip from the build pipeline. You can specify multiple steps.
71 | ```
72 |
73 | ### • `jobs` command
74 |
75 | Lists all available jobs on the specified appveyor YAML configuration file build matrix.
76 |
77 | ```
78 | Usage: LocalAppVeyor jobs [options]
79 |
80 | Options:
81 | -?|-h|--help Show help information
82 | -d|--dir Local repository directory where appveyor.yml sits. If not specified current directory is used
83 | ```
84 |
85 | ### • `lint` command
86 |
87 | Validates appveyor.yml YAML configuration. It requires an active internet connection as it uses AppVeyor web API for a real and up to date validation.
88 |
89 | ```
90 | Usage: LocalAppVeyor lint [options]
91 |
92 | Options:
93 | -?|-h|--help Show help information
94 | -t|--token AppVeyor account API token. If not specified it tries to get it from LOCALAPPVEYOR_API_TOKEN environment variable. You can find it here: https://ci.appveyor.com/api-token
95 | -d|--dir Local repository directory where appveyor.yml sits. If not specified current directory is used
96 | ```
97 |
98 | ## Supported build steps
99 | Due to LocalAppVeyor's nature only a subset of [AppVeyor build steps](https://www.appveyor.com/docs/build-configuration/#build-pipeline)
100 | are supported. Some of them might get some support later in time, after consideration, but others most likely won't ever be part
101 | of the build pipeline.
102 |
103 | :white_check_mark: Fully supported :large_blue_circle: Partially supported :red_circle: Not yet supported
104 |
105 | | Step \ Option | Support | Notes |
106 | | ------------- |:-------------:| ----- |
107 | | version | :white_check_mark: | `{build}` placeholder is replaced by `0`
108 | | environment | :white_check_mark: | As for the [standard AppVeyor variables](https://www.appveyor.com/docs/environment-variables/) these are the ones supported: `APPVEYOR`, `CI`, `APPVEYOR_BUILD_FOLDER`, `APPVEYOR_BUILD_NUMBER`, `APPVEYOR_BUILD_VERSION`, `PLATFORM` and `CONFIGURATION` |
109 | | configuration | :white_check_mark: | |
110 | | platform | :white_check_mark: | |
111 | | os | :white_check_mark: | Relatively undocumented option but it exists apparently. It's usually a single value so it serves nothing other than to build the matrix job name. |
112 | | init | :white_check_mark: | |
113 | | clone_folder | :white_check_mark: | Tries first to clone to specified `clone_folder`, if any; otherwise it creates a random directory in user's temp folder. From this step on all scripts will be executed as the clone folder being the working directory. |
114 | | matrix | :white_check_mark: | |
115 | | install | :white_check_mark: | |
116 | | assembly_info | :white_check_mark: | |
117 | | before_build | :white_check_mark: | |
118 | | build | :white_check_mark: | |
119 | | build_script | :white_check_mark: | |
120 | | after_build | :white_check_mark: | |
121 | | before_test | :red_circle: | |
122 | | test | :red_circle: | |
123 | | test_script | :large_blue_circle: | It will always execute if it exists, no matter if other tests options are specified. |
124 | | after_test | :red_circle: | |
125 | | on_success | :white_check_mark: | |
126 | | on_failure | :white_check_mark: | |
127 | | on_finish | :white_check_mark: | |
128 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | image: Visual Studio 2022
2 |
3 | version: 0.6.0+appveyor.{build}
4 |
5 | init:
6 | - dotnet --info
7 | - git config --global core.autocrlf true
8 |
9 | configuration: release
10 |
11 | build_script:
12 | - dotnet build src\LocalAppVeyor.Engine --configuration %configuration%
13 | - dotnet build src\LocalAppVeyor --configuration %configuration%
14 |
15 | after_build:
16 | - dotnet pack src\LocalAppVeyor.Engine --configuration %configuration% --output out_engine
17 | - dotnet pack src\LocalAppVeyor --configuration %configuration% --output out_console
18 |
19 | test_script:
20 | - dotnet test tests\LocalAppVeyor.Engine.UnitTests\LocalAppVeyor.Engine.UnitTests.csproj --configuration %configuration%
21 |
22 | artifacts:
23 | - path: out_engine\*.nupkg
24 | name: engine_packages
25 | - path: out_console\*.nupkg
26 | name: console_packages
27 |
28 | deploy:
29 | - provider: NuGet
30 | api_key:
31 | secure: 44crHq8PCXTO+ybHAUJjVHOT0PvFsqlcWH/m+/htVFXHihsmYj22WMnz1NBsDXWR
32 | on:
33 | branch: master
34 | appveyor_repo_tag: true
35 | configuration: release
36 | skip_symbols: true
37 | artifact: engine_packages
38 |
39 | - provider: NuGet
40 | api_key:
41 | secure: c1ZZc9Hqh92gxYb1pskP22xA+ppb5a3KQ6rESYDJnTn5jZv9PBjfxaRlDtWs765F
42 | on:
43 | branch: master
44 | appveyor_repo_tag: true
45 | configuration: release
46 | skip_symbols: true
47 | artifact: console_packages
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Configuration/AllowedJobFailureConditions.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Collections.ObjectModel;
3 | using System.Linq;
4 |
5 | namespace LocalAppVeyor.Engine.Configuration;
6 |
7 | public class AllowedJobFailureConditions
8 | {
9 | public string OperatingSystem { get; }
10 |
11 | public string Configuration { get; }
12 |
13 | public string Platform { get; }
14 |
15 | public string TestCategory { get; }
16 |
17 | public ReadOnlyCollection Variables { get; }
18 |
19 | public AllowedJobFailureConditions(
20 | string operatingSystem,
21 | string configuration,
22 | string platform,
23 | string testCategory,
24 | IEnumerable variables)
25 | {
26 | OperatingSystem = operatingSystem;
27 | Configuration = configuration;
28 | Platform = platform;
29 | TestCategory = testCategory;
30 | Variables = new ReadOnlyCollection(variables?.ToList() ?? new List());
31 | }
32 |
33 | public bool AreConditionsMetForJob(MatrixJob job)
34 | {
35 | if (job == null)
36 | {
37 | return false;
38 | }
39 |
40 | if (!string.IsNullOrEmpty(OperatingSystem))
41 | {
42 | if (OperatingSystem != job.OperatingSystem)
43 | {
44 | return false;
45 | }
46 | }
47 |
48 | if (!string.IsNullOrEmpty(Configuration))
49 | {
50 | if (Configuration != job.Configuration)
51 | {
52 | return false;
53 | }
54 | }
55 |
56 | if (!string.IsNullOrEmpty(Platform))
57 | {
58 | if (Platform != job.Platform)
59 | {
60 | return false;
61 | }
62 | }
63 |
64 | if (Variables.Count > 0)
65 | {
66 | if (!Variables.All(v => job.Variables.Contains(v)))
67 | {
68 | return false;
69 | }
70 | }
71 |
72 | return true;
73 | }
74 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Configuration/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | namespace LocalAppVeyor.Engine.Configuration;
2 |
3 | public class AssemblyInfo
4 | {
5 | public bool Patch { get; }
6 |
7 | public ExpandableString File { get; }
8 |
9 | public ExpandableString AssemblyVersion { get; }
10 |
11 | public ExpandableString AssemblyFileVersion { get; }
12 |
13 | public ExpandableString AssemblyInformationalVersion { get; }
14 |
15 | public AssemblyInfo()
16 | : this(
17 | false,
18 | null,
19 | null,
20 | null,
21 | null)
22 | {
23 | }
24 |
25 | public AssemblyInfo(
26 | bool patch,
27 | ExpandableString file,
28 | ExpandableString assemblyVersion,
29 | ExpandableString assemblyFileVersion,
30 | ExpandableString assemblyInformationalVersion)
31 | {
32 | Patch = patch;
33 | File = file;
34 | AssemblyVersion = assemblyVersion;
35 | AssemblyFileVersion = assemblyFileVersion;
36 | AssemblyInformationalVersion = assemblyInformationalVersion;
37 | }
38 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Configuration/Build.cs:
--------------------------------------------------------------------------------
1 | namespace LocalAppVeyor.Engine.Configuration;
2 |
3 | public class Build
4 | {
5 | public bool IsAutomaticBuildOff { get; }
6 |
7 | public bool IsParallel { get; }
8 |
9 | public ExpandableString SolutionFile { get; }
10 |
11 | public BuildVerbosity Verbosity { get; }
12 |
13 | public Build()
14 | : this(true, false, null, BuildVerbosity.Normal)
15 | {
16 | }
17 |
18 | public Build(
19 | bool isAutomaticBuildOff,
20 | bool isParallel,
21 | ExpandableString solutionFile,
22 | BuildVerbosity verbosity)
23 | {
24 | IsAutomaticBuildOff = isAutomaticBuildOff;
25 | IsParallel = isParallel;
26 | SolutionFile = solutionFile;
27 | Verbosity = verbosity;
28 | }
29 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Configuration/BuildConfiguration.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Collections.ObjectModel;
3 | using System.Linq;
4 |
5 | namespace LocalAppVeyor.Engine.Configuration;
6 |
7 | public class BuildConfiguration
8 | {
9 | public static BuildConfiguration Default => new BuildConfiguration();
10 |
11 | public ExpandableString Version { get; }
12 |
13 | public ScriptBlock InitializationScript { get; }
14 |
15 | public ExpandableString CloneFolder { get; }
16 |
17 | public Matrix Matrix { get; }
18 |
19 | public ScriptBlock InstallScript { get; }
20 |
21 | public AssemblyInfo AssemblyInfo { get; }
22 |
23 | public ReadOnlyCollection OperatingSystems { get; }
24 |
25 | public EnvironmentVariables EnvironmentVariables { get; }
26 |
27 | public ReadOnlyCollection Platforms { get; }
28 |
29 | public ReadOnlyCollection Configurations { get; }
30 |
31 | public Build Build { get; }
32 |
33 | public ScriptBlock BeforeBuildScript { get; }
34 |
35 | public ScriptBlock BuildScript { get; }
36 |
37 | public ScriptBlock AfterBuildScript { get; }
38 |
39 | public ScriptBlock TestScript { get; }
40 |
41 | public ScriptBlock OnSuccessScript { get; }
42 |
43 | public ScriptBlock OnFailureScript { get; }
44 |
45 | public ScriptBlock OnFinishScript { get; }
46 |
47 | private string[] _skipSteps = new string[0];
48 |
49 | public string[] SkipSteps
50 | {
51 | get => _skipSteps;
52 | set => _skipSteps = value ?? new string[0];
53 | }
54 |
55 | public BuildConfiguration()
56 | : this(
57 | null,
58 | null,
59 | null,
60 | null,
61 | null,
62 | new string[0],
63 | null,
64 | null,
65 | new string[0],
66 | new string[0],
67 | null,
68 | null,
69 | null,
70 | null,
71 | null,
72 | null,
73 | null,
74 | null)
75 | {
76 | }
77 |
78 | public BuildConfiguration(
79 | ExpandableString version,
80 | ScriptBlock initializationScript,
81 | ExpandableString cloneFolder,
82 | ScriptBlock installScript,
83 | AssemblyInfo assemblyInfo,
84 | IEnumerable operatingSystems,
85 | EnvironmentVariables environmentVariables,
86 | Matrix matrix,
87 | IEnumerable platforms,
88 | IEnumerable configurations,
89 | Build build,
90 | ScriptBlock beforeBuildScript,
91 | ScriptBlock buildScript,
92 | ScriptBlock afterBuildScript,
93 | ScriptBlock testScript,
94 | ScriptBlock onSuccess,
95 | ScriptBlock onFailure,
96 | ScriptBlock onFinish)
97 | {
98 | Version = version;
99 | InitializationScript = initializationScript ?? new ScriptBlock();
100 | CloneFolder = cloneFolder;
101 | InstallScript = installScript ?? new ScriptBlock();
102 | AssemblyInfo = assemblyInfo ?? new AssemblyInfo();
103 | OperatingSystems = new ReadOnlyCollection(operatingSystems?.ToList() ?? new List());
104 | EnvironmentVariables = environmentVariables ?? new EnvironmentVariables();
105 | Matrix = matrix ?? new Matrix();
106 | Platforms = new ReadOnlyCollection(platforms?.ToList() ?? new List());
107 | Configurations = new ReadOnlyCollection(configurations?.ToList() ?? new List());
108 | Build = build ?? new Build();
109 | BeforeBuildScript = beforeBuildScript ?? new ScriptBlock();
110 | BuildScript = buildScript ?? new ScriptBlock();
111 | AfterBuildScript = afterBuildScript ?? new ScriptBlock();
112 | TestScript = testScript ?? new ScriptBlock();
113 | OnSuccessScript = onSuccess ?? new ScriptBlock();
114 | OnFailureScript = onFailure ?? new ScriptBlock();
115 | OnFinishScript = onFinish ?? new ScriptBlock();
116 | }
117 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Configuration/BuildVerbosity.cs:
--------------------------------------------------------------------------------
1 | namespace LocalAppVeyor.Engine.Configuration;
2 |
3 | public enum BuildVerbosity
4 | {
5 | Quiet,
6 | Minimal,
7 | Normal,
8 | Detailed
9 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Configuration/EnvironmentVariables.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Collections.ObjectModel;
3 | using System.Linq;
4 |
5 | namespace LocalAppVeyor.Engine.Configuration;
6 |
7 | public class EnvironmentVariables
8 | {
9 | public ReadOnlyCollection CommonVariables { get; }
10 |
11 | public ReadOnlyCollection> Matrix { get; }
12 |
13 | public EnvironmentVariables()
14 | : this(new Variable[0], new List>())
15 | {
16 | }
17 |
18 | public EnvironmentVariables(IEnumerable commonVariables)
19 | : this(commonVariables, null)
20 | {
21 | }
22 |
23 | public EnvironmentVariables(IEnumerable> matrixVariables)
24 | : this(null, matrixVariables)
25 | {
26 | }
27 |
28 | public EnvironmentVariables(
29 | IEnumerable commonVariables,
30 | IEnumerable> matrixVariables)
31 | {
32 | CommonVariables = new ReadOnlyCollection(commonVariables?.ToList() ?? new List());
33 | Matrix = new ReadOnlyCollection>(matrixVariables?.ToList() ?? new List>());
34 | }
35 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Configuration/ExpandableString.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text.RegularExpressions;
3 |
4 | namespace LocalAppVeyor.Engine.Configuration;
5 |
6 | public struct ExpandableString
7 | {
8 | private static readonly Regex VarPattern = new Regex(@"\$\([\w-]+\)", RegexOptions.Compiled);
9 |
10 | private readonly string _internalStr;
11 |
12 | public static ExpandableString Empty => new ExpandableString(string.Empty);
13 |
14 | public ExpandableString(string str)
15 | {
16 | _internalStr = str;
17 | }
18 |
19 | public static implicit operator ExpandableString(string str)
20 | {
21 | return new ExpandableString(str);
22 | }
23 |
24 | public static implicit operator string(ExpandableString expandable)
25 | {
26 | if (string.IsNullOrEmpty(expandable._internalStr))
27 | {
28 | return expandable._internalStr;
29 | }
30 |
31 | return VarPattern
32 | .Replace(expandable._internalStr, m => Environment.GetEnvironmentVariable(m.Value.Substring(2, m.Value.Length - 3)))
33 | .Replace("{build}", "0")
34 | .Replace("{version}", Environment.GetEnvironmentVariable("APPVEYOR_BUILD_VERSION"));
35 | }
36 |
37 | public override bool Equals(object obj)
38 | {
39 | switch (obj)
40 | {
41 | case null:
42 | return false;
43 | case string s:
44 | return this == s;
45 | case ExpandableString expandableString:
46 | return expandableString._internalStr == _internalStr;
47 | }
48 |
49 | return false;
50 | }
51 |
52 | public override int GetHashCode()
53 | {
54 | return _internalStr.GetHashCode();
55 | }
56 |
57 | public override string ToString()
58 | {
59 | return this;
60 | }
61 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Configuration/Matrix.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Collections.ObjectModel;
3 | using System.Linq;
4 |
5 | namespace LocalAppVeyor.Engine.Configuration;
6 |
7 | public class Matrix
8 | {
9 | public bool IsFastFinish { get; }
10 |
11 | public ReadOnlyCollection AllowedFailures { get; }
12 |
13 | public Matrix()
14 | : this(false, new AllowedJobFailureConditions[0])
15 | {
16 | }
17 |
18 | public Matrix(
19 | bool isFastFinish,
20 | IEnumerable allowedFailures)
21 | {
22 | IsFastFinish = isFastFinish;
23 | AllowedFailures = new ReadOnlyCollection(allowedFailures?.ToList() ?? new List());
24 | }
25 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Configuration/Reader/BuildConfigurationYamlFileReader.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO.Abstractions;
3 |
4 | namespace LocalAppVeyor.Engine.Configuration.Reader;
5 |
6 | public class BuildConfigurationYamlFileReader : IBuildConfigurationReader
7 | {
8 | private const string AppVeyorBuildFileName = "appveyor.yml";
9 |
10 | public string YamlFilePath { get; }
11 |
12 | private readonly FileSystem _fileSystem;
13 |
14 | public BuildConfigurationYamlFileReader(string yamlFilePathOrDirectory)
15 | : this(new FileSystem(), yamlFilePathOrDirectory)
16 | {
17 | }
18 |
19 | public BuildConfigurationYamlFileReader(
20 | FileSystem fileSystem,
21 | string yamlFilePathOrDirectory)
22 | {
23 | if (string.IsNullOrEmpty(yamlFilePathOrDirectory)) throw new ArgumentNullException(nameof(yamlFilePathOrDirectory));
24 |
25 | _fileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem));
26 |
27 | string yamlFile;
28 |
29 | if (fileSystem.File.Exists(yamlFilePathOrDirectory))
30 | {
31 | YamlFilePath = yamlFilePathOrDirectory;
32 | }
33 | else if (fileSystem.Directory.Exists(yamlFilePathOrDirectory) &&
34 | fileSystem.File.Exists(yamlFile = fileSystem.Path.Combine(yamlFilePathOrDirectory, AppVeyorBuildFileName)))
35 | {
36 | YamlFilePath = yamlFile;
37 | }
38 | else
39 | {
40 | throw new LocalAppVeyorException($"'{AppVeyorBuildFileName}' file not found.");
41 | }
42 | }
43 |
44 | public BuildConfiguration GetBuildConfiguration()
45 | {
46 | string yaml;
47 |
48 | try
49 | {
50 | yaml = _fileSystem.File.ReadAllText(YamlFilePath);
51 | }
52 | catch (Exception e)
53 | {
54 | throw new LocalAppVeyorException("Error while reading YAML file.", e);
55 | }
56 |
57 | return new BuildConfigurationYamlStringReader(yaml).GetBuildConfiguration();
58 | }
59 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Configuration/Reader/BuildConfigurationYamlStringReader.cs:
--------------------------------------------------------------------------------
1 | using LocalAppVeyor.Engine.Configuration.Reader.Internal.Converters;
2 | using LocalAppVeyor.Engine.Configuration.Reader.Internal.Model;
3 | using YamlDotNet.Core;
4 | using YamlDotNet.Serialization;
5 |
6 | namespace LocalAppVeyor.Engine.Configuration.Reader;
7 |
8 | public class BuildConfigurationYamlStringReader : IBuildConfigurationReader
9 | {
10 | public string Yaml { get; }
11 |
12 | public BuildConfigurationYamlStringReader(string yaml)
13 | {
14 | Yaml = yaml;
15 | }
16 |
17 | public BuildConfiguration GetBuildConfiguration()
18 | {
19 | if (string.IsNullOrEmpty(Yaml))
20 | {
21 | return BuildConfiguration.Default;
22 | }
23 |
24 | var yamlDeserializer = new DeserializerBuilder()
25 | .IgnoreUnmatchedProperties()
26 | .WithTypeConverter(new EnvironmentVariablesYamlTypeConverter())
27 | .WithTypeConverter(new VariableTypeConverter())
28 | .WithTypeConverter(new AllowedFailuresYamlTypeConverter())
29 | .Build();
30 |
31 | try
32 | {
33 | var conf = yamlDeserializer.Deserialize(Yaml);
34 |
35 | return conf?.ToBuildConfiguration() ?? BuildConfiguration.Default;
36 | }
37 | catch (YamlException e)
38 | {
39 | throw new LocalAppVeyorException("Error while parsing YAML.", e);
40 | }
41 | }
42 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Configuration/Reader/IBuildConfigurationReader.cs:
--------------------------------------------------------------------------------
1 | namespace LocalAppVeyor.Engine.Configuration.Reader;
2 |
3 | public interface IBuildConfigurationReader
4 | {
5 | BuildConfiguration GetBuildConfiguration();
6 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Configuration/Reader/Internal/Converters/AllowedFailuresYamlTypeConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using LocalAppVeyor.Engine.Configuration.Reader.Internal.Model;
4 | using YamlDotNet.Core;
5 | using YamlDotNet.Core.Events;
6 | using YamlDotNet.Serialization;
7 |
8 | namespace LocalAppVeyor.Engine.Configuration.Reader.Internal.Converters;
9 |
10 | internal class AllowedFailuresYamlTypeConverter : IYamlTypeConverter
11 | {
12 | private readonly IDeserializer _deserializer;
13 |
14 | public AllowedFailuresYamlTypeConverter()
15 | {
16 | _deserializer = new DeserializerBuilder()
17 | .IgnoreUnmatchedProperties()
18 | .WithTypeConverter(new VariableTypeConverter())
19 | .Build();
20 | }
21 |
22 | public bool Accepts(Type type)
23 | {
24 | return type == typeof(AllowedFailuresCollection);
25 | }
26 |
27 | public object ReadYaml(IParser parser, Type type)
28 | {
29 | var allowedFailuresCollection = new AllowedFailuresCollection();
30 |
31 | // discard SequenceStart
32 | parser.Consume();
33 |
34 | do
35 | {
36 | string os = null;
37 | string configuration = null;
38 | string platform = null;
39 | string testCategory = null;
40 | var variables = new List();
41 |
42 | parser.Consume();
43 |
44 | do
45 | {
46 | var possibleVar = _deserializer.Deserialize(parser);
47 |
48 | switch (possibleVar.Name)
49 | {
50 | case "os":
51 | os = possibleVar.Value;
52 | break;
53 | case "configuration":
54 | configuration = possibleVar.Value;
55 | break;
56 | case "platform":
57 | platform = possibleVar.Value;
58 | break;
59 | case "test_category":
60 | testCategory = possibleVar.Value;
61 | break;
62 | default:
63 | variables.Add(possibleVar.ToVariable());
64 | break;
65 | }
66 | } while (!parser.Accept(out _));
67 |
68 | parser.Consume();
69 |
70 | allowedFailuresCollection.Add(
71 | new AllowedJobFailureConditions(os, configuration, platform, testCategory, variables.AsReadOnly()));
72 |
73 | } while (!parser.Accept(out _));
74 |
75 | parser.Consume();
76 |
77 | return allowedFailuresCollection;
78 | }
79 |
80 | public void WriteYaml(IEmitter emitter, object value, Type type)
81 | {
82 | throw new NotImplementedException();
83 | }
84 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Configuration/Reader/Internal/Converters/EnvironmentVariablesYamlTypeConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using LocalAppVeyor.Engine.Configuration.Reader.Internal.Model;
4 | using YamlDotNet.Core;
5 | using YamlDotNet.Core.Events;
6 | using YamlDotNet.Serialization;
7 |
8 | namespace LocalAppVeyor.Engine.Configuration.Reader.Internal.Converters;
9 |
10 | internal class EnvironmentVariablesYamlTypeConverter : IYamlTypeConverter
11 | {
12 | private readonly IDeserializer _deserializer;
13 |
14 | public EnvironmentVariablesYamlTypeConverter()
15 | {
16 | _deserializer = new DeserializerBuilder()
17 | .IgnoreUnmatchedProperties()
18 | .WithTypeConverter(new VariableTypeConverter())
19 | .Build();
20 | }
21 |
22 | public bool Accepts(Type type)
23 | {
24 | return type == typeof(InternalEnvironmentVariables);
25 | }
26 |
27 | public object ReadYaml(IParser parser, Type type)
28 | {
29 | var env = new InternalEnvironmentVariables();
30 |
31 | parser.Consume();
32 |
33 | do
34 | {
35 | parser.Accept(out var scalar);
36 |
37 | if (scalar != null)
38 | {
39 | if (scalar.Value == "global")
40 | {
41 | // discard "global" value itself
42 | parser.Consume();
43 |
44 | // read global variables (common to all matrix items)
45 | parser.Consume();
46 |
47 | do
48 | {
49 | env.InternalCommonVariables.Add(_deserializer.Deserialize(parser));
50 |
51 | } while (!parser.Accept(out _));
52 |
53 | parser.Consume();
54 |
55 | }
56 | else if (scalar.Value == "matrix")
57 | {
58 | // discard "matrix" value itself
59 | parser.Consume();
60 |
61 | // discard SequenceStart
62 | parser.Consume();
63 |
64 | do
65 | {
66 | var matrixItemVariables = new List();
67 |
68 | parser.Consume();
69 |
70 | do
71 | {
72 | matrixItemVariables.Add(_deserializer.Deserialize(parser));
73 |
74 | } while (!parser.Accept(out _));
75 |
76 | parser.Consume();
77 |
78 | env.InternalMatrix.Add(matrixItemVariables.AsReadOnly());
79 |
80 | } while (!parser.Accept(out _));
81 |
82 | parser.Consume();
83 | }
84 | else
85 | {
86 | var variable = _deserializer.Deserialize(parser);
87 | env.InternalCommonVariables.Add(variable);
88 | }
89 | }
90 |
91 |
92 | } while (!parser.Accept(out _));
93 |
94 | parser.Consume();
95 |
96 | return env;
97 | }
98 |
99 | public void WriteYaml(IEmitter emitter, object value, Type type)
100 | {
101 | throw new NotImplementedException();
102 | }
103 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Configuration/Reader/Internal/Converters/VariableTypeConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using LocalAppVeyor.Engine.Configuration.Reader.Internal.Model;
3 | using YamlDotNet.Core;
4 | using YamlDotNet.Core.Events;
5 | using YamlDotNet.Serialization;
6 |
7 | namespace LocalAppVeyor.Engine.Configuration.Reader.Internal.Converters;
8 |
9 | internal class VariableTypeConverter : IYamlTypeConverter
10 | {
11 | public bool Accepts(Type type)
12 | {
13 | return type == typeof(InternalVariable);
14 | }
15 |
16 | public object ReadYaml(IParser parser, Type type)
17 | {
18 | var name = parser.Consume().Value;
19 |
20 | parser.TryConsume(out var mappingStart);
21 |
22 | if (mappingStart != null)
23 | {
24 | var secureNode = parser.Consume();
25 |
26 | if (secureNode != null && secureNode.Value == "secure")
27 | {
28 | var secureValue = parser.Consume().Value;
29 |
30 | parser.Consume();
31 |
32 | return new InternalVariable(name, secureValue, true);
33 | }
34 |
35 | throw new YamlException("error parsing environment variables");
36 | }
37 |
38 | return new InternalVariable(name, parser.Consume().Value, false);
39 | }
40 |
41 | public void WriteYaml(IEmitter emitter, object value, Type type)
42 | {
43 | throw new NotImplementedException();
44 | }
45 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Configuration/Reader/Internal/Model/AllowedFailuresCollection.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.ObjectModel;
2 |
3 | namespace LocalAppVeyor.Engine.Configuration.Reader.Internal.Model;
4 |
5 | internal class AllowedFailuresCollection : Collection
6 | {
7 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Configuration/Reader/Internal/Model/InternalAssemblyVersion.cs:
--------------------------------------------------------------------------------
1 | using YamlDotNet.Serialization;
2 |
3 | namespace LocalAppVeyor.Engine.Configuration.Reader.Internal.Model;
4 |
5 | internal class InternalAssemblyVersion
6 | {
7 | [YamlMember(Alias = "patch")]
8 | public bool Patch { get; set; }
9 |
10 | [YamlMember(Alias = "file")]
11 | public string File { get; set; }
12 |
13 | [YamlMember(Alias = "assembly_version")]
14 | public string AssemblyVersion { get; set; }
15 |
16 | [YamlMember(Alias = "assembly_file_version")]
17 | public string AssemblyFileVersion { get; set; }
18 |
19 | [YamlMember(Alias = "assembly_informational_version")]
20 | public string AssemblyInformationalVersion { get; set; }
21 |
22 | public AssemblyInfo ToAssemblyInfo()
23 | {
24 | return new AssemblyInfo(
25 | Patch,
26 | File,
27 | AssemblyVersion,
28 | AssemblyFileVersion,
29 | AssemblyInformationalVersion);
30 | }
31 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Configuration/Reader/Internal/Model/InternalBuild.cs:
--------------------------------------------------------------------------------
1 | using YamlDotNet.Serialization;
2 |
3 | namespace LocalAppVeyor.Engine.Configuration.Reader.Internal.Model;
4 |
5 | internal class InternalBuild
6 | {
7 | [YamlIgnore]
8 | public bool IsAutomaticBuildOff { get; set; }
9 |
10 | [YamlMember(Alias = "parallel")]
11 | public bool IsParallel { get; set; }
12 |
13 | [YamlMember(Alias = "project")]
14 | public string SolutionFile { get; set; }
15 |
16 | public InternalBuildVerbosity? Verbosity { get; set; }
17 |
18 | public static implicit operator InternalBuild(string offString)
19 | {
20 | if (!string.IsNullOrEmpty(offString) && offString == "off")
21 | {
22 | return new InternalBuild
23 | {
24 | IsAutomaticBuildOff = true
25 | };
26 | }
27 |
28 | return null;
29 | }
30 |
31 | public Build ToBuild()
32 | {
33 | return new Build(
34 | IsAutomaticBuildOff,
35 | IsParallel,
36 | SolutionFile,
37 | Verbosity?.ToBuildVerbosity() ?? BuildVerbosity.Normal);
38 | }
39 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Configuration/Reader/Internal/Model/InternalBuildConfiguration.cs:
--------------------------------------------------------------------------------
1 | using YamlDotNet.Serialization;
2 |
3 | namespace LocalAppVeyor.Engine.Configuration.Reader.Internal.Model;
4 |
5 | internal class InternalBuildConfiguration
6 | {
7 | [YamlMember(Alias = "version")]
8 | public string Version { get; set; }
9 |
10 | [YamlMember(Alias = "init")]
11 | public InternalScriptBlock InitializationScript { get; set; }
12 |
13 | [YamlMember(Alias = "clone_folder")]
14 | public string CloneFolder { get; set; }
15 |
16 | [YamlMember(Alias = "os")]
17 | public InternalOperatingSystems OperatingSystems { get; set; }
18 |
19 | [YamlMember(Alias = "environment")]
20 | public InternalEnvironmentVariables EnvironmentVariables { get; set; }
21 |
22 | [YamlMember(Alias = "matrix")]
23 | public InternalMatrix Matrix { get; set; }
24 |
25 | [YamlMember(Alias = "install")]
26 | public InternalScriptBlock InstallScript { get; set; }
27 |
28 | [YamlMember(Alias = "assembly_info")]
29 | public InternalAssemblyVersion AssemblyVersion { get; set; }
30 |
31 | [YamlMember(Alias = "platform")]
32 | public InternalPlatforms Platforms { get; set; }
33 |
34 | [YamlMember(Alias = "configuration")]
35 | public InternalConfigurations Configurations { get; set; }
36 |
37 | [YamlMember(Alias = "build")]
38 | public InternalBuild Build { get; set; }
39 |
40 | [YamlMember(Alias = "before_build")]
41 | public InternalScriptBlock BeforeBuildScript { get; set; }
42 |
43 | [YamlMember(Alias = "after_build")]
44 | public InternalScriptBlock AfterBuildScript { get; set; }
45 |
46 | [YamlMember(Alias = "build_script")]
47 | public InternalScriptBlock BuildScript { get; set; }
48 |
49 | [YamlMember(Alias = "test_script")]
50 | public InternalScriptBlock TestScript { get; set; }
51 |
52 | [YamlMember(Alias = "on_success")]
53 | public InternalScriptBlock OnSuccessScript { get; set; }
54 |
55 | [YamlMember(Alias = "on_failure")]
56 | public InternalScriptBlock OnFailureScript { get; set; }
57 |
58 | [YamlMember(Alias = "on_finish")]
59 | public InternalScriptBlock OnFinishScript { get; set; }
60 |
61 | public BuildConfiguration ToBuildConfiguration()
62 | {
63 | return new BuildConfiguration(
64 | Version,
65 | InitializationScript?.ToScriptBlock(),
66 | CloneFolder,
67 | InstallScript?.ToScriptBlock(),
68 | AssemblyVersion?.ToAssemblyInfo(),
69 | OperatingSystems,
70 | EnvironmentVariables?.ToEnvironmentVariables(),
71 | Matrix?.ToMatrix(),
72 | Platforms,
73 | Configurations,
74 | Build?.ToBuild(),
75 | BeforeBuildScript?.ToScriptBlock(),
76 | BuildScript?.ToScriptBlock(),
77 | AfterBuildScript?.ToScriptBlock(),
78 | TestScript?.ToScriptBlock(),
79 | OnSuccessScript?.ToScriptBlock(),
80 | OnFailureScript?.ToScriptBlock(),
81 | OnFinishScript?.ToScriptBlock());
82 | }
83 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Configuration/Reader/Internal/Model/InternalBuildVerbosity.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace LocalAppVeyor.Engine.Configuration.Reader.Internal.Model;
4 |
5 | internal enum InternalBuildVerbosity
6 | {
7 | Quiet,
8 | Minimal,
9 | Normal,
10 | Detailed
11 | }
12 |
13 | internal static class InternalBuildVerbosityExtensions
14 | {
15 | public static BuildVerbosity ToBuildVerbosity(this InternalBuildVerbosity buildVerbosity)
16 | {
17 | switch (buildVerbosity)
18 | {
19 | case InternalBuildVerbosity.Quiet:
20 | return BuildVerbosity.Quiet;
21 | case InternalBuildVerbosity.Minimal:
22 | return BuildVerbosity.Minimal;
23 | case InternalBuildVerbosity.Normal:
24 | return BuildVerbosity.Normal;
25 | case InternalBuildVerbosity.Detailed:
26 | return BuildVerbosity.Detailed;
27 | default:
28 | throw new ArgumentOutOfRangeException(nameof(buildVerbosity), buildVerbosity, null);
29 | }
30 | }
31 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Configuration/Reader/Internal/Model/InternalConfigurations.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace LocalAppVeyor.Engine.Configuration.Reader.Internal.Model;
4 |
5 | internal class InternalConfigurations : List
6 | {
7 | public static implicit operator InternalConfigurations(string platform)
8 | {
9 | return new InternalConfigurations
10 | {
11 | platform
12 | };
13 | }
14 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Configuration/Reader/Internal/Model/InternalEnvironmentVariables.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.ObjectModel;
2 | using System.Linq;
3 |
4 | namespace LocalAppVeyor.Engine.Configuration.Reader.Internal.Model;
5 |
6 | internal class InternalEnvironmentVariables
7 | {
8 | public readonly Collection InternalCommonVariables = new Collection();
9 |
10 | public readonly Collection> InternalMatrix =
11 | new Collection>();
12 |
13 | public EnvironmentVariables ToEnvironmentVariables()
14 | {
15 | return new EnvironmentVariables(
16 | InternalCommonVariables.Select(v => v.ToVariable()),
17 | InternalMatrix.Select(m => new ReadOnlyCollection(m.Select(v => v.ToVariable()).ToList())));
18 | }
19 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Configuration/Reader/Internal/Model/InternalMatrix.cs:
--------------------------------------------------------------------------------
1 | using YamlDotNet.Serialization;
2 |
3 | namespace LocalAppVeyor.Engine.Configuration.Reader.Internal.Model;
4 |
5 | internal class InternalMatrix
6 | {
7 | [YamlMember(Alias = "fast_finish")]
8 | public bool IsFastFinish { get; set; }
9 |
10 | [YamlMember(Alias = "allow_failures")]
11 | public AllowedFailuresCollection AllowedFailures { get; set; }
12 |
13 | public Matrix ToMatrix()
14 | {
15 | return new Matrix(IsFastFinish, AllowedFailures);
16 | }
17 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Configuration/Reader/Internal/Model/InternalOperatingSystems.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace LocalAppVeyor.Engine.Configuration.Reader.Internal.Model;
4 |
5 | internal class InternalOperatingSystems : List
6 | {
7 | public static implicit operator InternalOperatingSystems(string os)
8 | {
9 | return new InternalOperatingSystems
10 | {
11 | os
12 | };
13 | }
14 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Configuration/Reader/Internal/Model/InternalPlatforms.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace LocalAppVeyor.Engine.Configuration.Reader.Internal.Model;
4 |
5 | internal class InternalPlatforms : List
6 | {
7 | public static implicit operator InternalPlatforms(string platform)
8 | {
9 | return new InternalPlatforms
10 | {
11 | platform
12 | };
13 | }
14 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Configuration/Reader/Internal/Model/InternalScriptBlock.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 |
4 | namespace LocalAppVeyor.Engine.Configuration.Reader.Internal.Model;
5 |
6 | internal class InternalScriptBlock : List
7 | {
8 | public ScriptBlock ToScriptBlock()
9 | {
10 | return new ScriptBlock(this.Select(l => l.ToScriptLine()));
11 | }
12 |
13 | public static implicit operator InternalScriptBlock(string _)
14 | {
15 | return new InternalScriptBlock();
16 | }
17 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Configuration/Reader/Internal/Model/InternalScriptLine.cs:
--------------------------------------------------------------------------------
1 | using LocalAppVeyor.Engine.Internal;
2 | using YamlDotNet.Serialization;
3 |
4 | namespace LocalAppVeyor.Engine.Configuration.Reader.Internal.Model;
5 |
6 | internal class InternalScriptLine
7 | {
8 | [YamlMember(Alias = "cmd")]
9 | public string Batch { get; set; }
10 |
11 | [YamlMember(Alias = "ps")]
12 | public string PowerShell { get; set; }
13 |
14 | [YamlMember(Alias = "sh")]
15 | public string Bash { get; set; }
16 |
17 | public static implicit operator InternalScriptLine(string scriptLine)
18 | {
19 | if (Platform.IsUnix)
20 | {
21 | return new InternalScriptLine
22 | {
23 | Bash = scriptLine
24 | };
25 | }
26 |
27 | return new InternalScriptLine
28 | {
29 | Batch = scriptLine
30 | };
31 | }
32 |
33 | public ScriptLine ToScriptLine()
34 | {
35 | var scriptType = string.IsNullOrEmpty(PowerShell)
36 | ? string.IsNullOrEmpty(Batch)
37 | ? ScriptType.Bash
38 | : ScriptType.Batch
39 | : ScriptType.PowerShell;
40 |
41 | return new ScriptLine(
42 | scriptType,
43 | scriptType == ScriptType.PowerShell
44 | ? PowerShell
45 | : scriptType == ScriptType.Batch
46 | ? Batch
47 | : Bash);
48 | }
49 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Configuration/Reader/Internal/Model/InternalVariable.cs:
--------------------------------------------------------------------------------
1 | namespace LocalAppVeyor.Engine.Configuration.Reader.Internal.Model;
2 |
3 | internal class InternalVariable
4 | {
5 | public string Name { get; }
6 |
7 | public bool IsSecuredValue { get; }
8 |
9 | public string Value { get; }
10 |
11 | public InternalVariable(string name, string value, bool isSecuredValue)
12 | {
13 | Name = name;
14 | Value = value;
15 | IsSecuredValue = isSecuredValue;
16 | }
17 |
18 | public Variable ToVariable()
19 | {
20 | return new Variable(Name, Value, IsSecuredValue);
21 | }
22 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Configuration/ScriptBlock.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace LocalAppVeyor.Engine.Configuration;
4 |
5 | public class ScriptBlock : List
6 | {
7 | public ScriptBlock()
8 | : this(new ScriptLine[0])
9 | {
10 | }
11 |
12 | public ScriptBlock(IEnumerable scriptLines)
13 | : base(scriptLines)
14 | {
15 | }
16 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Configuration/ScriptLine.cs:
--------------------------------------------------------------------------------
1 | namespace LocalAppVeyor.Engine.Configuration;
2 |
3 | public class ScriptLine
4 | {
5 | public ScriptType ScriptType { get; }
6 |
7 | public string Script { get; }
8 |
9 | public ScriptLine(ScriptType scriptType, string script)
10 | {
11 | ScriptType = scriptType;
12 | Script = script;
13 | }
14 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Configuration/ScriptType.cs:
--------------------------------------------------------------------------------
1 | namespace LocalAppVeyor.Engine.Configuration;
2 |
3 | public enum ScriptType
4 | {
5 | Batch,
6 | PowerShell,
7 | Bash
8 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Configuration/Variable.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace LocalAppVeyor.Engine.Configuration;
4 |
5 | public class Variable : IEquatable
6 | {
7 | public string Name { get; }
8 |
9 | public bool IsSecuredValue { get; }
10 |
11 | public ExpandableString Value { get; }
12 |
13 | public Variable(string name, string value, bool isSecuredValue)
14 | {
15 | Name = name;
16 | Value = value;
17 | IsSecuredValue = isSecuredValue;
18 | }
19 |
20 | public bool Equals(Variable other)
21 | {
22 | if (other == null)
23 | {
24 | return false;
25 | }
26 |
27 | return Name == other.Name &&
28 | Value == other.Value &&
29 | IsSecuredValue == other.IsSecuredValue;
30 | }
31 |
32 | public override string ToString()
33 | {
34 | return $"{Name}={Value}";
35 | }
36 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Engine.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using LocalAppVeyor.Engine.Configuration;
5 | using LocalAppVeyor.Engine.Configuration.Reader;
6 | using LocalAppVeyor.Engine.Internal;
7 |
8 | namespace LocalAppVeyor.Engine;
9 |
10 | public sealed class Engine
11 | {
12 | public event EventHandler JobStarting = delegate { };
13 |
14 | public event EventHandler JobEnded = delegate { };
15 |
16 | private readonly BuildConfiguration _buildConfiguration;
17 |
18 | private readonly EngineConfiguration _engineConfiguration;
19 |
20 | private MatrixJob[] _jobs;
21 |
22 | public MatrixJob[] Jobs
23 | {
24 | get
25 | {
26 | if (_jobs != null)
27 | {
28 | return _jobs;
29 | }
30 |
31 | var environmentsVariables = _buildConfiguration.EnvironmentVariables.Matrix.Count > 0
32 | ? _buildConfiguration.EnvironmentVariables.Matrix.ToArray()
33 | : new IReadOnlyCollection[] { null };
34 | var configurations = _buildConfiguration.Configurations.Count > 0
35 | ? _buildConfiguration.Configurations.ToArray()
36 | : new string[] { null };
37 | var platforms = _buildConfiguration.Platforms.Count > 0
38 | ? _buildConfiguration.Platforms.ToArray()
39 | : new string[] { null };
40 | var oses = _buildConfiguration.OperatingSystems.Count > 0
41 | ? _buildConfiguration.OperatingSystems.ToArray()
42 | : new string[] { null };
43 |
44 | _jobs = (
45 | from environmentVariables in environmentsVariables
46 | from configuration in configurations
47 | from platform in platforms
48 | from os in oses
49 | select new MatrixJob(os, environmentVariables, configuration, platform))
50 | .ToArray();
51 |
52 | return _jobs;
53 | }
54 | }
55 |
56 | public Engine(
57 | EngineConfiguration engineConfiguration,
58 | IBuildConfigurationReader buildConfigurationReader)
59 | : this(engineConfiguration, buildConfigurationReader.GetBuildConfiguration())
60 | {
61 | }
62 |
63 | public Engine(
64 | EngineConfiguration engineConfiguration,
65 | BuildConfiguration buildConfiguration)
66 | {
67 | _buildConfiguration = buildConfiguration ?? throw new ArgumentNullException(nameof(buildConfiguration));
68 | _engineConfiguration = engineConfiguration ?? throw new ArgumentNullException(nameof(engineConfiguration));
69 | }
70 |
71 | public JobExecutionResult ExecuteJob(int jobIndex)
72 | {
73 | if (jobIndex < 0 || jobIndex >= Jobs.Length)
74 | {
75 | var result = JobExecutionResult.CreateJobNotFound();
76 | JobEnded?.Invoke(this, new JobEndedEventArgs(null, result));
77 | return result;
78 | }
79 |
80 | return ExecuteJob(Jobs[jobIndex]);
81 | }
82 |
83 | public JobExecutionResult ExecuteJob(MatrixJob job)
84 | {
85 | JobStarting?.Invoke(this, new JobStartingEventArgs(job));
86 |
87 | var executionContext = new ExecutionContext(
88 | job,
89 | _buildConfiguration,
90 | _engineConfiguration.Outputter,
91 | _engineConfiguration.RepositoryDirectoryPath,
92 | !string.IsNullOrEmpty(_buildConfiguration.CloneFolder)
93 | ? _buildConfiguration.CloneFolder
94 | : new ExpandableString(_engineConfiguration.FallbackCloneDirectoryPath),
95 | _engineConfiguration.FileSystem);
96 |
97 | var executionResult = new BuildPipelineExecuter(executionContext).Execute();
98 |
99 | JobEnded?.Invoke(this, new JobEndedEventArgs(job, executionResult));
100 |
101 | return executionResult;
102 | }
103 |
104 | public JobExecutionResult[] ExecuteAllJobs()
105 | {
106 | var results = new JobExecutionResult[Jobs.Length];
107 |
108 | for (var i = 0; i < Jobs.Length; i++)
109 | {
110 | var job = Jobs[i];
111 |
112 | results[i] = ExecuteJob(job);
113 |
114 | // if success, or job is on the allowed failures matrix, continue on to next one
115 | if (results[i].IsSuccessfulExecution ||
116 | _buildConfiguration.Matrix.AllowedFailures.Any(a => a.AreConditionsMetForJob(job)))
117 | {
118 | continue;
119 | }
120 |
121 | // if fast_finish is on mark remaining jobs as NotExecuted and leave build
122 | if (_buildConfiguration.Matrix.IsFastFinish)
123 | {
124 | for (++i; i < Jobs.Length; i++)
125 | {
126 | results[i] = JobExecutionResult.CreateNotExecuted();
127 | }
128 |
129 | break;
130 | }
131 | }
132 |
133 | return results;
134 | }
135 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/EngineConfiguration.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO.Abstractions;
3 |
4 | namespace LocalAppVeyor.Engine;
5 |
6 | public class EngineConfiguration
7 | {
8 | public string RepositoryDirectoryPath { get; }
9 |
10 | public string FallbackCloneDirectoryPath { get; }
11 |
12 | public IPipelineOutputter Outputter { get; }
13 |
14 | public IFileSystem FileSystem { get; }
15 |
16 | public EngineConfiguration(
17 | string repositoryDirectoryPath,
18 | string fallbackCloneDirectoryPath,
19 | IPipelineOutputter outputter,
20 | IFileSystem fileSystem)
21 | {
22 | if (string.IsNullOrEmpty(repositoryDirectoryPath)) throw new ArgumentNullException(nameof(repositoryDirectoryPath));
23 | if (string.IsNullOrEmpty(fallbackCloneDirectoryPath)) throw new ArgumentNullException(nameof(fallbackCloneDirectoryPath));
24 |
25 | RepositoryDirectoryPath = repositoryDirectoryPath;
26 | FallbackCloneDirectoryPath = fallbackCloneDirectoryPath;
27 | Outputter = outputter ?? throw new ArgumentNullException(nameof(outputter));
28 | FileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem));
29 | }
30 |
31 | public EngineConfiguration(
32 | string repositoryDirectoryPath,
33 | IPipelineOutputter outputter,
34 | IFileSystem fileSystem)
35 | : this(repositoryDirectoryPath, GetFallbackTemporaryCloningFolder(fileSystem), outputter, fileSystem)
36 | {
37 | if (string.IsNullOrEmpty(repositoryDirectoryPath)) throw new ArgumentNullException(nameof(repositoryDirectoryPath));
38 |
39 | RepositoryDirectoryPath = repositoryDirectoryPath;
40 | Outputter = outputter ?? throw new ArgumentNullException(nameof(outputter));
41 | FileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem));
42 | }
43 |
44 | public EngineConfiguration(
45 | string repositoryDirectoryPath,
46 | IPipelineOutputter outputter)
47 | : this(repositoryDirectoryPath, outputter, new FileSystem())
48 | {
49 | }
50 |
51 | private static string GetFallbackTemporaryCloningFolder(IFileSystem fileSystem)
52 | {
53 | if (fileSystem == null) throw new ArgumentNullException(nameof(fileSystem));
54 |
55 | string tempDirectory;
56 |
57 | do
58 | {
59 | tempDirectory = fileSystem.Path.Combine(fileSystem.Path.GetTempPath(), fileSystem.Path.GetRandomFileName());
60 | } while (fileSystem.Directory.Exists(tempDirectory));
61 |
62 | return tempDirectory;
63 | }
64 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/IPipelineOutputter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace LocalAppVeyor.Engine;
4 |
5 | public interface IPipelineOutputter
6 | {
7 | void SetColor(ConsoleColor color);
8 |
9 | void ResetColor();
10 |
11 | void Write(string message);
12 |
13 | void WriteSuccess(string successMessage);
14 |
15 | void WriteWarning(string warningMessage);
16 |
17 | void WriteError(string errorMessage);
18 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Internal/BashScriptExecuter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 |
4 | namespace LocalAppVeyor.Engine.Internal;
5 |
6 | internal static class BashScriptExecuter
7 | {
8 | public static bool Execute(
9 | string script,
10 | Action onOutputDataReceived,
11 | Action onErrorDataReceived)
12 | {
13 | if (string.IsNullOrEmpty(script))
14 | {
15 | return true;
16 | }
17 |
18 | using (var process = Process.Start(new ProcessStartInfo
19 | {
20 | FileName = "/bin/bash",
21 | Arguments = $"-c \"{script.Replace("\"", "\\\"")}\"",
22 | CreateNoWindow = true,
23 | UseShellExecute = false,
24 | RedirectStandardError = true,
25 | RedirectStandardOutput = true
26 | }))
27 | {
28 | process.OutputDataReceived += (s, e) =>
29 | {
30 | onOutputDataReceived?.Invoke(e.Data);
31 | };
32 | process.ErrorDataReceived += (s, e) =>
33 | {
34 | onErrorDataReceived?.Invoke(e.Data);
35 | };
36 |
37 | process.BeginOutputReadLine();
38 | process.BeginErrorReadLine();
39 |
40 | process.WaitForExit();
41 |
42 | return process.ExitCode == 0;
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Internal/BatchScriptExecuter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.IO.Abstractions;
4 |
5 | namespace LocalAppVeyor.Engine.Internal;
6 |
7 | internal static class BatchScriptExecuter
8 | {
9 | public static bool Execute(
10 | IFileSystem fileSystem,
11 | string workingDirectory,
12 | string script,
13 | Action onOutputDataReceived,
14 | Action onErrorDataReceived)
15 | {
16 | if (string.IsNullOrEmpty(script))
17 | {
18 | return true;
19 | }
20 |
21 | var batchFile = fileSystem.Path.Combine(fileSystem.Path.GetTempPath(), $"{Guid.NewGuid()}.bat");
22 | fileSystem.File.WriteAllText(batchFile, script);
23 |
24 | using (var process = Process.Start(new ProcessStartInfo("cmd.exe", $"/c {batchFile}")
25 | {
26 | CreateNoWindow = true,
27 | UseShellExecute = false,
28 | RedirectStandardError = true,
29 | RedirectStandardOutput = true,
30 | WorkingDirectory = workingDirectory
31 | }))
32 | {
33 | process.OutputDataReceived += (s, e) =>
34 | {
35 | onOutputDataReceived?.Invoke(e.Data);
36 | };
37 | process.ErrorDataReceived += (s, e) =>
38 | {
39 | onErrorDataReceived?.Invoke(e.Data);
40 | };
41 |
42 | process.BeginOutputReadLine();
43 | process.BeginErrorReadLine();
44 |
45 | process.WaitForExit();
46 |
47 | return process.ExitCode == 0;
48 | }
49 | }
50 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Internal/BuildPipelineExecuter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using LocalAppVeyor.Engine.Internal.KnownExceptions;
4 | using LocalAppVeyor.Engine.Internal.Steps;
5 |
6 | namespace LocalAppVeyor.Engine.Internal;
7 |
8 | internal sealed class BuildPipelineExecuter
9 | {
10 | private readonly ExecutionContext _executionContext;
11 |
12 | private readonly InitStandardEnvironmentVariablesStep _environmentStep;
13 | private readonly InitStep _initStep;
14 | private readonly CloneFolderStep _cloneStep;
15 | private readonly InstallStep _installStep;
16 | private readonly AssemblyInfoRewriteStep _assemblyInfoStep;
17 | private readonly BeforeBuildStep _beforeBuildStep;
18 | private readonly BuildScriptStep _buildScriptStep;
19 | private readonly BuildStep _buildStep;
20 | private readonly AfterBuildStep _afterBuildStep;
21 | private readonly TestScriptStep _testScriptStep;
22 |
23 | private readonly OnSuccessStep _onSuccessStep;
24 | private readonly OnFailureStep _onFailureStep;
25 | private readonly OnFinishStep _onFinishStep;
26 |
27 | public BuildPipelineExecuter(ExecutionContext executionContext)
28 | {
29 | _executionContext = executionContext;
30 |
31 | _environmentStep = new InitStandardEnvironmentVariablesStep();
32 | _initStep = new InitStep(executionContext.RepositoryDirectory, executionContext.BuildConfiguration.InitializationScript);
33 | _cloneStep = new CloneFolderStep(executionContext.FileSystem);
34 | _installStep = new InstallStep(executionContext.CloneDirectory, executionContext.BuildConfiguration.InstallScript);
35 | _assemblyInfoStep = new AssemblyInfoRewriteStep();
36 | _beforeBuildStep = new BeforeBuildStep(executionContext.CloneDirectory, executionContext.BuildConfiguration.BeforeBuildScript);
37 | _buildScriptStep = new BuildScriptStep(executionContext.CloneDirectory, executionContext.BuildConfiguration.BuildScript);
38 | _buildStep = new BuildStep();
39 | _afterBuildStep = new AfterBuildStep(executionContext.CloneDirectory, executionContext.BuildConfiguration.AfterBuildScript);
40 | _testScriptStep = new TestScriptStep(executionContext.CloneDirectory, executionContext.BuildConfiguration.TestScript);
41 |
42 | _onSuccessStep = new OnSuccessStep(executionContext.CloneDirectory, executionContext.BuildConfiguration.OnSuccessScript);
43 | _onFailureStep = new OnFailureStep(executionContext.CloneDirectory, executionContext.BuildConfiguration.OnFailureScript);
44 | _onFinishStep = new OnFinishStep(executionContext.CloneDirectory, executionContext.BuildConfiguration.OnFinishScript);
45 | }
46 |
47 | public JobExecutionResult Execute()
48 | {
49 | JobExecutionResult executionResult;
50 |
51 | try
52 | {
53 | var isSuccess = ExecuteBuildPipeline(_executionContext);
54 |
55 | // on_success / on_failure only happens here, after we know the build status
56 | // they do intervene on build final status though
57 | isSuccess = isSuccess
58 | ? Execute(_onSuccessStep, _executionContext)
59 | : Execute(_onFailureStep, _executionContext);
60 |
61 | return isSuccess
62 | ? JobExecutionResult.CreateSuccess()
63 | : JobExecutionResult.CreateFailure();
64 | }
65 | catch (SolutionNotFoundException)
66 | {
67 | executionResult = JobExecutionResult.CreateSolutionNotFound();
68 | }
69 | catch (Exception e)
70 | {
71 | executionResult = JobExecutionResult.CreateUnhandledException(e);
72 | }
73 | finally
74 | {
75 | // on_finish don't influence build final status so we just run it
76 | Execute(_onFinishStep, _executionContext);
77 | }
78 |
79 | return executionResult;
80 | }
81 |
82 | private bool ExecuteBuildPipeline(ExecutionContext executionContext)
83 | {
84 | if (!Execute(_environmentStep, executionContext))
85 | {
86 | return false;
87 | }
88 |
89 | if (!Execute(_initStep, executionContext))
90 | {
91 | return false;
92 | }
93 |
94 | if (!Execute(_cloneStep, executionContext))
95 | {
96 | return false;
97 | }
98 |
99 | if (!Execute(_installStep, executionContext))
100 | {
101 | return false;
102 | }
103 |
104 | if (!Execute(_assemblyInfoStep, executionContext))
105 | {
106 | return false;
107 | }
108 |
109 | // Before build
110 | if (!Execute(_beforeBuildStep, executionContext))
111 | {
112 | return false;
113 | }
114 |
115 | // Build
116 | if (executionContext.BuildConfiguration.Build.IsAutomaticBuildOff)
117 | {
118 | if (!Execute(_buildScriptStep, executionContext))
119 | {
120 | return false;
121 | }
122 | }
123 | else
124 | {
125 | if (!Execute(_buildStep, executionContext))
126 | {
127 | return false;
128 | }
129 | }
130 |
131 | // After Build
132 | if (!Execute(_afterBuildStep, executionContext))
133 | {
134 | return false;
135 | }
136 |
137 | // Test script
138 | if (!Execute(_testScriptStep, executionContext))
139 | {
140 | return false;
141 | }
142 |
143 | return true;
144 | }
145 |
146 | private bool Execute(IEngineStep step, ExecutionContext executionContext)
147 | {
148 | if (executionContext.BuildConfiguration.SkipSteps.Contains(step.Name, StringComparer.InvariantCultureIgnoreCase))
149 | {
150 | executionContext.Outputter.Write($"Skipped '{step.Name}' step.");
151 | return true;
152 | }
153 |
154 | return step.Execute(executionContext);
155 | }
156 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Internal/ExecutionContext.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO.Abstractions;
3 | using LocalAppVeyor.Engine.Configuration;
4 |
5 | namespace LocalAppVeyor.Engine.Internal;
6 |
7 | internal class ExecutionContext
8 | {
9 | public MatrixJob CurrentJob { get; }
10 |
11 | public string RepositoryDirectory { get; }
12 |
13 | public ExpandableString CloneDirectory { get; }
14 |
15 | public IFileSystem FileSystem { get; }
16 |
17 | public BuildConfiguration BuildConfiguration { get; }
18 |
19 | public IPipelineOutputter Outputter { get; }
20 |
21 | public ExecutionContext(
22 | MatrixJob currentJob,
23 | BuildConfiguration buildConfiguration,
24 | IPipelineOutputter outputter,
25 | string repositoryDirectory,
26 | ExpandableString cloneDirectory,
27 | IFileSystem fileSystem)
28 | {
29 | CurrentJob = currentJob ?? throw new ArgumentNullException(nameof(currentJob));
30 | BuildConfiguration = buildConfiguration ?? throw new ArgumentNullException(nameof(buildConfiguration));
31 | Outputter = outputter ?? throw new ArgumentNullException(nameof(outputter));
32 | RepositoryDirectory = repositoryDirectory ?? throw new ArgumentNullException(nameof(repositoryDirectory));
33 | CloneDirectory = cloneDirectory;
34 | FileSystem = fileSystem ?? throw new ArgumentNullException(nameof(fileSystem));
35 | }
36 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Internal/IEngineStep.cs:
--------------------------------------------------------------------------------
1 | namespace LocalAppVeyor.Engine.Internal;
2 |
3 | internal interface IEngineStep
4 | {
5 | string Name { get; }
6 |
7 | bool Execute(ExecutionContext executionContext);
8 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Internal/KnownExceptions/SolutionNotFoundException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace LocalAppVeyor.Engine.Internal.KnownExceptions;
4 |
5 | public class SolutionNotFoundException : Exception
6 | {
7 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Internal/PipelineOutputterMsBuildLogger.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using LocalAppVeyor.Engine.Configuration;
3 | using Microsoft.Build.Framework;
4 | using Microsoft.Build.Logging;
5 |
6 | namespace LocalAppVeyor.Engine.Internal;
7 |
8 | internal class PipelineOutputterMsBuildLogger : ConsoleLogger
9 | {
10 | public PipelineOutputterMsBuildLogger(BuildVerbosity verbosity, IPipelineOutputter outputter)
11 | : base(
12 | TransformToLoggerVerbosity(verbosity),
13 | outputter.Write,
14 | outputter.SetColor,
15 | outputter.ResetColor
16 | )
17 | {
18 | }
19 |
20 | private static LoggerVerbosity TransformToLoggerVerbosity(BuildVerbosity verbosity)
21 | {
22 | switch (verbosity)
23 | {
24 | case BuildVerbosity.Quiet:
25 | return LoggerVerbosity.Quiet;
26 | case BuildVerbosity.Minimal:
27 | return LoggerVerbosity.Minimal;
28 | case BuildVerbosity.Normal:
29 | return LoggerVerbosity.Normal;
30 | case BuildVerbosity.Detailed:
31 | return LoggerVerbosity.Detailed;
32 | default:
33 | throw new ArgumentOutOfRangeException(nameof(verbosity), verbosity, null);
34 | }
35 | }
36 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Internal/Platform.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace LocalAppVeyor.Engine.Internal;
4 |
5 | public static class Platform
6 | {
7 | public static bool IsWindow { get; }
8 |
9 | public static bool IsUnix { get; }
10 |
11 | static Platform()
12 | {
13 | IsUnix = Path.DirectorySeparatorChar == '/';
14 | IsWindow = !IsUnix;
15 | }
16 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Internal/PowerShellScriptExecuter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Management.Automation;
3 |
4 | namespace LocalAppVeyor.Engine.Internal;
5 |
6 | internal static class PowerShellScriptExecuter
7 | {
8 | public static bool Execute(
9 | string script,
10 | Action onOutputDataReceived,
11 | Action onErrorDataReceived)
12 | {
13 | using var powerShell = PowerShell.Create();
14 | var success = true;
15 |
16 | powerShell.AddScript(script);
17 |
18 | powerShell.Streams.Information.DataAdded += (sender, args) =>
19 | {
20 | onOutputDataReceived(powerShell.Streams.Information[args.Index]);
21 | };
22 |
23 | powerShell.Streams.Error.DataAdded += (sender, args) =>
24 | {
25 | onErrorDataReceived(powerShell.Streams.Error[args.Index]);
26 | success = false;
27 | };
28 |
29 | powerShell.Invoke();
30 |
31 | return success;
32 | }
33 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Internal/Steps/AfterBuildStep.cs:
--------------------------------------------------------------------------------
1 | using LocalAppVeyor.Engine.Configuration;
2 |
3 | namespace LocalAppVeyor.Engine.Internal.Steps;
4 |
5 | internal sealed class AfterBuildStep : ScriptBlockExecuterStep
6 | {
7 | public override string Name => "after_build";
8 |
9 | public AfterBuildStep(string workigDirectory, ScriptBlock scriptBlock)
10 | : base(workigDirectory, scriptBlock)
11 | {
12 | }
13 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Internal/Steps/AssemblyInfoRewriteStep.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 | using System.Text.RegularExpressions;
3 |
4 | namespace LocalAppVeyor.Engine.Internal.Steps;
5 |
6 | internal class AssemblyInfoRewriteStep : IEngineStep
7 | {
8 | public string Name => "assembly_info";
9 |
10 | private static readonly Regex AssemblyVersionPattern = new Regex(@"AssemblyVersion\("".+""\)", RegexOptions.Compiled);
11 |
12 | private static readonly Regex AssemblyFileVersionPattern = new Regex(@"AssemblyFileVersion\("".+""\)", RegexOptions.Compiled);
13 |
14 | private static readonly Regex AssemblyInformationalVersionPattern = new Regex(@"AssemblyInformationalVersion\("".+""\)", RegexOptions.Compiled);
15 |
16 | public bool Execute(ExecutionContext executionContext)
17 | {
18 | if (!executionContext.BuildConfiguration.AssemblyInfo.Patch)
19 | {
20 | return true;
21 | }
22 |
23 | if (string.IsNullOrEmpty(executionContext.BuildConfiguration.AssemblyInfo.File))
24 | {
25 | executionContext.Outputter.WriteWarning("No assembly info files specified.");
26 | return true;
27 | }
28 |
29 | if (string.IsNullOrEmpty(executionContext.BuildConfiguration.AssemblyInfo.AssemblyFileVersion) &&
30 | string.IsNullOrEmpty(executionContext.BuildConfiguration.AssemblyInfo.AssemblyVersion) &&
31 | string.IsNullOrEmpty(executionContext.BuildConfiguration.AssemblyInfo.AssemblyInformationalVersion))
32 | {
33 | executionContext.Outputter.WriteWarning("No versioning information provided to re-write AssemlyInfo files with.");
34 | return false;
35 | }
36 |
37 | foreach (var assemblyInfoFile in executionContext.FileSystem.Directory.EnumerateFiles(
38 | executionContext.CloneDirectory,
39 | executionContext.BuildConfiguration.AssemblyInfo.File,
40 | SearchOption.AllDirectories))
41 | {
42 | if (!RewriteAssemblyInfoFile(executionContext, assemblyInfoFile))
43 | {
44 | return false;
45 | }
46 | }
47 |
48 | return true;
49 | }
50 |
51 | private bool RewriteAssemblyInfoFile(ExecutionContext executionContext, string filePath)
52 | {
53 | executionContext.Outputter.Write($"Re-writing '{filePath}'...");
54 |
55 | var fileContent = executionContext.FileSystem.File.ReadAllText(filePath);
56 |
57 | if (!string.IsNullOrEmpty(executionContext.BuildConfiguration.AssemblyInfo.AssemblyVersion))
58 | {
59 | fileContent = AssemblyVersionPattern.Replace(
60 | fileContent,
61 | $@"AssemblyVersion(""{executionContext.BuildConfiguration.AssemblyInfo.AssemblyVersion}"")");
62 | }
63 |
64 | if (!string.IsNullOrEmpty(executionContext.BuildConfiguration.AssemblyInfo.AssemblyFileVersion))
65 | {
66 | fileContent = AssemblyFileVersionPattern.Replace(
67 | fileContent,
68 | $@"AssemblyFileVersion(""{executionContext.BuildConfiguration.AssemblyInfo.AssemblyFileVersion}"")");
69 | }
70 |
71 | if (!string.IsNullOrEmpty(executionContext.BuildConfiguration.AssemblyInfo.AssemblyInformationalVersion))
72 | {
73 | fileContent = AssemblyInformationalVersionPattern.Replace(
74 | fileContent,
75 | $@"AssemblyInformationalVersion(""{executionContext.BuildConfiguration.AssemblyInfo.AssemblyInformationalVersion}"")");
76 | }
77 |
78 | executionContext.FileSystem.File.WriteAllText(filePath, fileContent);
79 |
80 | return true;
81 | }
82 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Internal/Steps/BeforeBuildStep.cs:
--------------------------------------------------------------------------------
1 | using LocalAppVeyor.Engine.Configuration;
2 |
3 | namespace LocalAppVeyor.Engine.Internal.Steps;
4 |
5 | internal sealed class BeforeBuildStep : ScriptBlockExecuterStep
6 | {
7 | public BeforeBuildStep(string workigDirectory, ScriptBlock scriptBlock)
8 | : base(workigDirectory, scriptBlock)
9 | {
10 | }
11 |
12 | public override string Name => "before_build";
13 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Internal/Steps/BuildScriptStep.cs:
--------------------------------------------------------------------------------
1 | using LocalAppVeyor.Engine.Configuration;
2 |
3 | namespace LocalAppVeyor.Engine.Internal.Steps;
4 |
5 | internal sealed class BuildScriptStep : ScriptBlockExecuterStep
6 | {
7 | public override string Name => "build_script";
8 |
9 | public BuildScriptStep(string workigDirectory, ScriptBlock scriptBlock)
10 | : base(workigDirectory, scriptBlock)
11 | {
12 | }
13 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Internal/Steps/BuildStep.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using LocalAppVeyor.Engine.Internal.KnownExceptions;
5 | using Microsoft.Build.Execution;
6 | using Microsoft.Build.Framework;
7 |
8 | namespace LocalAppVeyor.Engine.Internal.Steps;
9 |
10 | internal class BuildStep : IEngineStep
11 | {
12 | public string Name => "build";
13 |
14 | public bool Execute(ExecutionContext executionContext)
15 | {
16 | var platform = executionContext.CurrentJob.Platform;
17 | var configuration = executionContext.CurrentJob.Configuration;
18 | string slnProjFile = null;
19 |
20 | if (executionContext.FileSystem.File.Exists(executionContext.BuildConfiguration.Build.SolutionFile))
21 | {
22 | slnProjFile = executionContext.BuildConfiguration.Build.SolutionFile;
23 | }
24 | else if (!string.IsNullOrEmpty(executionContext.BuildConfiguration.Build.SolutionFile))
25 | {
26 | if (
27 | !executionContext.FileSystem.File.Exists(
28 | slnProjFile =
29 | executionContext.FileSystem.Path.Combine(executionContext.CloneDirectory,
30 | executionContext.BuildConfiguration.Build.SolutionFile)))
31 | {
32 | slnProjFile = null;
33 | }
34 | }
35 |
36 | if (string.IsNullOrEmpty(slnProjFile))
37 | {
38 | slnProjFile = GetProjectOrSolutionFileRecursively(executionContext);
39 |
40 | if (string.IsNullOrEmpty(slnProjFile))
41 | {
42 | throw new SolutionNotFoundException();
43 | }
44 | }
45 |
46 | // MSBuild
47 |
48 | var globalProperties = new Dictionary();
49 |
50 | if (!string.IsNullOrEmpty(platform))
51 | {
52 | globalProperties.Add("Platform", platform);
53 | }
54 |
55 | if (!string.IsNullOrEmpty(configuration))
56 | {
57 | globalProperties.Add("Configuration", configuration);
58 | }
59 |
60 | var buildRequest = new BuildRequestData(slnProjFile, globalProperties, null, new[] {"Build"}, null);
61 | var buildParameters = new BuildParameters
62 | {
63 | Loggers = new ILogger[]
64 | {
65 | new PipelineOutputterMsBuildLogger(
66 | executionContext.BuildConfiguration.Build.Verbosity,
67 | executionContext.Outputter)
68 | }
69 | };
70 |
71 | var buildResult = BuildManager.DefaultBuildManager.Build(buildParameters, buildRequest);
72 |
73 | return buildResult.OverallResult == BuildResultCode.Success;
74 | }
75 |
76 | private string GetProjectOrSolutionFileRecursively(ExecutionContext executionContext)
77 | {
78 | // first tries .sln file
79 | var possibleHit = executionContext.FileSystem.Directory
80 | .EnumerateFiles(executionContext.CloneDirectory, "*.sln")
81 | .FirstOrDefault(f => f.EndsWith("*.sln", StringComparison.OrdinalIgnoreCase));
82 |
83 | if (!string.IsNullOrEmpty(possibleHit))
84 | {
85 | return possibleHit;
86 | }
87 |
88 | // finally tries .csproj files
89 | return executionContext.FileSystem
90 | .Directory
91 | .EnumerateFiles(executionContext.CloneDirectory, "*.csproj")
92 | .FirstOrDefault(f => f.EndsWith("*.csproj", StringComparison.OrdinalIgnoreCase));
93 | }
94 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Internal/Steps/CloneFolderStep.cs:
--------------------------------------------------------------------------------
1 | using System.IO.Abstractions;
2 |
3 | namespace LocalAppVeyor.Engine.Internal.Steps;
4 |
5 | internal class CloneFolderStep : IEngineStep
6 | {
7 | public string Name => "clone_folder";
8 |
9 | private readonly IFileSystem _fileSystem;
10 |
11 | public CloneFolderStep(IFileSystem fileSystem)
12 | {
13 | _fileSystem = fileSystem;
14 | }
15 |
16 | public bool Execute(ExecutionContext executionContext)
17 | {
18 | executionContext.Outputter.Write($"Cloning '{executionContext.RepositoryDirectory}' in to '{executionContext.CloneDirectory}'...");
19 | Clone(executionContext.RepositoryDirectory, executionContext.CloneDirectory);
20 | executionContext.Outputter.Write("Cloning finished.");
21 |
22 | return true;
23 | }
24 |
25 | private void Clone(string source, string destination)
26 | {
27 | var dirSource = _fileSystem.DirectoryInfo.New(source);
28 | var dirDestination = _fileSystem.DirectoryInfo.New(destination);
29 |
30 | if (!dirDestination.Exists)
31 | {
32 | _fileSystem.Directory.CreateDirectory(dirDestination.FullName);
33 | }
34 |
35 | // empty destination
36 | foreach (var fileInfo in dirDestination.GetFiles())
37 | {
38 | fileInfo.Delete();
39 | }
40 |
41 | foreach (var directoryInfo in dirDestination.GetDirectories())
42 | {
43 | directoryInfo.Delete(true);
44 | }
45 |
46 | CopyAll(dirSource, dirDestination);
47 | }
48 |
49 | private void CopyAll(IDirectoryInfo source, IDirectoryInfo destination)
50 | {
51 | // copy each file into destination
52 | foreach (var file in source.GetFiles())
53 | {
54 | file.CopyTo(_fileSystem.Path.Combine(destination.FullName, file.Name), true);
55 | }
56 |
57 | // copy each subdirectory using recursion
58 | foreach (var diSourceSubDir in source.GetDirectories())
59 | {
60 | var nextTargetSubDir = destination.CreateSubdirectory(diSourceSubDir.Name);
61 | CopyAll(diSourceSubDir, nextTargetSubDir);
62 | }
63 | }
64 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Internal/Steps/InitStandardEnvironmentVariablesStep.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 |
4 | namespace LocalAppVeyor.Engine.Internal.Steps;
5 |
6 | internal class InitStandardEnvironmentVariablesStep : IEngineStep
7 | {
8 | public string Name => "environment";
9 |
10 | public bool Execute(ExecutionContext executionContext)
11 | {
12 | executionContext.Outputter.Write("Initializing environment variables...");
13 |
14 | Environment.SetEnvironmentVariable("APPVEYOR_BUILD_NUMBER", "0");
15 | Environment.SetEnvironmentVariable("APPVEYOR_BUILD_VERSION", executionContext.BuildConfiguration.Version);
16 | Environment.SetEnvironmentVariable("APPVEYOR_BUILD_FOLDER", executionContext.CloneDirectory);
17 | Environment.SetEnvironmentVariable("CI", "False");
18 | Environment.SetEnvironmentVariable("APPVEYOR", "False");
19 |
20 | if (!string.IsNullOrEmpty(executionContext.CurrentJob.Configuration))
21 | {
22 | Environment.SetEnvironmentVariable("CONFIGURATION", executionContext.CurrentJob.Configuration);
23 | }
24 |
25 | if (!string.IsNullOrEmpty(executionContext.CurrentJob.Platform))
26 | {
27 | Environment.SetEnvironmentVariable("PLATFORM", executionContext.CurrentJob.Platform);
28 | }
29 |
30 | foreach (
31 | var variable
32 | in executionContext.BuildConfiguration.EnvironmentVariables.CommonVariables.Concat(executionContext.CurrentJob.Variables))
33 | {
34 | Environment.SetEnvironmentVariable(variable.Name, variable.Value);
35 | }
36 |
37 | executionContext.Outputter.Write("Environment variables initialized.");
38 |
39 | return true;
40 | }
41 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Internal/Steps/InitStep.cs:
--------------------------------------------------------------------------------
1 | using LocalAppVeyor.Engine.Configuration;
2 |
3 | namespace LocalAppVeyor.Engine.Internal.Steps;
4 |
5 | internal class InitStep : ScriptBlockExecuterStep
6 | {
7 | public override string Name => "init";
8 |
9 | public InitStep(string workingDirectory, ScriptBlock scriptBlock)
10 | : base(workingDirectory, scriptBlock)
11 | {
12 | }
13 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Internal/Steps/InstallStep.cs:
--------------------------------------------------------------------------------
1 | using LocalAppVeyor.Engine.Configuration;
2 |
3 | namespace LocalAppVeyor.Engine.Internal.Steps;
4 |
5 | internal class InstallStep : ScriptBlockExecuterStep
6 | {
7 | public override string Name => "install";
8 |
9 | public InstallStep(string workigDirectory, ScriptBlock scriptBlock)
10 | : base(workigDirectory, scriptBlock)
11 | {
12 | }
13 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Internal/Steps/OnFailureStep.cs:
--------------------------------------------------------------------------------
1 | using LocalAppVeyor.Engine.Configuration;
2 |
3 | namespace LocalAppVeyor.Engine.Internal.Steps;
4 |
5 | internal sealed class OnFailureStep : ScriptBlockExecuterStep
6 | {
7 | public override string Name => "on_failure";
8 |
9 | public OnFailureStep(string workigDirectory, ScriptBlock scriptBlock)
10 | : base(workigDirectory, scriptBlock)
11 | {
12 | }
13 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Internal/Steps/OnFinishStep.cs:
--------------------------------------------------------------------------------
1 | using LocalAppVeyor.Engine.Configuration;
2 |
3 | namespace LocalAppVeyor.Engine.Internal.Steps;
4 |
5 | internal sealed class OnFinishStep : ScriptBlockExecuterStep
6 | {
7 | public override string Name => "on_finish";
8 |
9 | public OnFinishStep(string workigDirectory, ScriptBlock scriptBlock)
10 | : base(workigDirectory, scriptBlock)
11 | {
12 | }
13 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Internal/Steps/OnSuccessStep.cs:
--------------------------------------------------------------------------------
1 | using LocalAppVeyor.Engine.Configuration;
2 |
3 | namespace LocalAppVeyor.Engine.Internal.Steps;
4 |
5 | internal sealed class OnSuccessStep : ScriptBlockExecuterStep
6 | {
7 | public override string Name => "on_success";
8 |
9 | public OnSuccessStep(string workigDirectory, ScriptBlock scriptBlock)
10 | : base(workigDirectory, scriptBlock)
11 | {
12 | }
13 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Internal/Steps/ScriptBlockExecuterStep.cs:
--------------------------------------------------------------------------------
1 | using LocalAppVeyor.Engine.Configuration;
2 |
3 | namespace LocalAppVeyor.Engine.Internal.Steps;
4 |
5 | internal abstract class ScriptBlockExecuterStep : IEngineStep
6 | {
7 | public abstract string Name { get; }
8 |
9 | private readonly ScriptBlock _scriptBlock;
10 |
11 | private readonly string _workingDirectory;
12 |
13 | protected ScriptBlockExecuterStep(
14 | string workingDirectory,
15 | ScriptBlock scriptBlock)
16 | {
17 | _scriptBlock = scriptBlock;
18 | _workingDirectory = workingDirectory;
19 | }
20 |
21 | public bool Execute(ExecutionContext executionContext)
22 | {
23 | if (_scriptBlock != null)
24 | {
25 | foreach (var scriptLine in _scriptBlock)
26 | {
27 | if (string.IsNullOrEmpty(scriptLine.Script))
28 | {
29 | continue;
30 | }
31 |
32 | if (scriptLine.ScriptType == ScriptType.Bash && Platform.IsWindow)
33 | {
34 | executionContext.Outputter.Write("Script line skipped - Bash (sh) scripts cannot be executed in Windows platforms.");
35 | continue;
36 | }
37 |
38 | if (scriptLine.ScriptType == ScriptType.Batch && Platform.IsUnix)
39 | {
40 | executionContext.Outputter.Write("Script line skipped - Batch (cmd) scripts cannot be executed in Unix platforms.");
41 | continue;
42 | }
43 |
44 | var status = true;
45 |
46 | switch (scriptLine.ScriptType)
47 | {
48 | case ScriptType.Batch:
49 | status = ExecuteBatchScript(executionContext, scriptLine.Script);
50 | break;
51 | case ScriptType.PowerShell:
52 | status = ExecutePowerShellScript(executionContext, scriptLine.Script);
53 | break;
54 | case ScriptType.Bash:
55 | status = ExecuteBashScript(executionContext, scriptLine.Script);
56 | break;
57 | }
58 |
59 | if (!status)
60 | {
61 | return false;
62 | }
63 | }
64 | }
65 |
66 | return true;
67 | }
68 |
69 | private bool ExecuteBatchScript(ExecutionContext executionContext, string script)
70 | {
71 | return BatchScriptExecuter.Execute(
72 | executionContext.FileSystem,
73 | _workingDirectory,
74 | script,
75 | data =>
76 | {
77 | if (data != null)
78 | {
79 | executionContext.Outputter.Write(data);
80 | }
81 | },
82 | data =>
83 | {
84 | if (data != null)
85 | {
86 | executionContext.Outputter.WriteError(data);
87 | }
88 | });
89 | }
90 |
91 | private static bool ExecutePowerShellScript(ExecutionContext executionContext, string script)
92 | {
93 | return PowerShellScriptExecuter.Execute(
94 | script,
95 | data =>
96 | {
97 | if (data != null)
98 | {
99 | executionContext.Outputter.Write(data?.ToString() ?? "PowerShell: ");
100 | }
101 | },
102 | data =>
103 | {
104 | if (data != null)
105 | {
106 | executionContext.Outputter.WriteError(data?.ToString() ?? "PowerShell: Error while executing command.");
107 | }
108 | });
109 | }
110 |
111 | private static bool ExecuteBashScript(ExecutionContext executionContext, string script)
112 | {
113 | return BashScriptExecuter.Execute(
114 | script,
115 | data =>
116 | {
117 | if (data != null)
118 | {
119 | executionContext.Outputter.Write(data);
120 | }
121 | },
122 | data =>
123 | {
124 | if (data != null)
125 | {
126 | executionContext.Outputter.WriteError(data);
127 | }
128 | });
129 | }
130 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Internal/Steps/TestScriptStep.cs:
--------------------------------------------------------------------------------
1 | using LocalAppVeyor.Engine.Configuration;
2 |
3 | namespace LocalAppVeyor.Engine.Internal.Steps;
4 |
5 | internal sealed class TestScriptStep : ScriptBlockExecuterStep
6 | {
7 | public override string Name => "test_script";
8 |
9 | public TestScriptStep(string workigDirectory, ScriptBlock scriptBlock)
10 | : base(workigDirectory, scriptBlock)
11 | {
12 | }
13 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/JobEndedEventArgs.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace LocalAppVeyor.Engine;
4 |
5 | public sealed class JobEndedEventArgs : EventArgs
6 | {
7 | public MatrixJob Job { get; }
8 |
9 | public JobExecutionResult ExecutionResult { get; }
10 |
11 | public JobEndedEventArgs(MatrixJob job, JobExecutionResult executionResult)
12 | {
13 | Job = job;
14 | ExecutionResult = executionResult;
15 | }
16 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/JobExecutionResult.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace LocalAppVeyor.Engine;
4 |
5 | public sealed class JobExecutionResult
6 | {
7 | public JobExecutionResultType ResultType { get; private set; }
8 |
9 | public bool IsSuccessfulExecution => ResultType == JobExecutionResultType.Success;
10 |
11 | public Exception UnhandledException { get; private set; }
12 |
13 | private JobExecutionResult ()
14 | {
15 | }
16 |
17 | internal static JobExecutionResult CreateSuccess()
18 | {
19 | return new JobExecutionResult
20 | {
21 | ResultType = JobExecutionResultType.Success
22 | };
23 | }
24 |
25 | internal static JobExecutionResult CreateFailure()
26 | {
27 | return new JobExecutionResult
28 | {
29 | ResultType = JobExecutionResultType.Failure
30 | };
31 | }
32 |
33 | internal static JobExecutionResult CreateNotExecuted()
34 | {
35 | return new JobExecutionResult
36 | {
37 | ResultType = JobExecutionResultType.NotExecuted
38 | };
39 | }
40 |
41 | internal static JobExecutionResult CreateUnhandledException(Exception exception)
42 | {
43 | return new JobExecutionResult
44 | {
45 | ResultType = JobExecutionResultType.UnhandledException,
46 | UnhandledException = exception
47 | };
48 | }
49 |
50 | internal static JobExecutionResult CreateSolutionNotFound()
51 | {
52 | return new JobExecutionResult
53 | {
54 | ResultType = JobExecutionResultType.SolutionFileNotFound
55 | };
56 | }
57 |
58 | internal static JobExecutionResult CreateJobNotFound()
59 | {
60 | return new JobExecutionResult
61 | {
62 | ResultType = JobExecutionResultType.JobNotFound
63 | };
64 | }
65 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/JobExecutionResultType.cs:
--------------------------------------------------------------------------------
1 | namespace LocalAppVeyor.Engine;
2 |
3 | public enum JobExecutionResultType
4 | {
5 | Success,
6 | Failure,
7 |
8 | NotExecuted,
9 | JobNotFound,
10 | SolutionFileNotFound,
11 | UnhandledException
12 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/JobStartingEventArgs.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace LocalAppVeyor.Engine;
4 |
5 | public sealed class JobStartingEventArgs : EventArgs
6 | {
7 | public MatrixJob Job { get; }
8 |
9 | public JobStartingEventArgs(MatrixJob job)
10 | {
11 | Job = job;
12 | }
13 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/LocalAppVeyor.Engine.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Allows one to execute AppVeyor build pipeline programatically, for a given local repository and configuration.
5 | joaope 2020
6 | LocalAppVeyor.Engine
7 | 0.6.0
8 | joaope
9 | net8.0
10 | true
11 | anycpu
12 | latest
13 | portable
14 | LocalAppVeyor.Engine
15 | LocalAppVeyor.Engine
16 | console;appveyor;local;build;api
17 | https://github.com/joaope/LocalAppVeyor
18 | MIT
19 | git
20 | https://github.com/joaope/LocalAppVeyor.git
21 | NU1605
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/LocalAppVeyorException.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using YamlDotNet.Core;
3 |
4 | namespace LocalAppVeyor.Engine;
5 |
6 | public class LocalAppVeyorException : Exception
7 | {
8 | public Mark Start { get; }
9 |
10 | public Mark End { get; }
11 |
12 | public LocalAppVeyorException()
13 | {
14 | }
15 |
16 | public LocalAppVeyorException(string message)
17 | : base(message)
18 | {
19 | }
20 |
21 | public LocalAppVeyorException(string message, Exception innerException)
22 | : base(message, innerException)
23 | {
24 | if (!(innerException is YamlException yamlEx))
25 | {
26 | return;
27 | }
28 |
29 | Start = new Mark(yamlEx.Start.Line, yamlEx.Start.Column);
30 | End = new Mark(yamlEx.End.Line, yamlEx.End.Column);
31 | }
32 |
33 | public sealed class Mark
34 | {
35 | public int Line { get; }
36 |
37 | public int Column { get; }
38 |
39 | public Mark(int line, int column)
40 | {
41 | Line = line;
42 | Column = column;
43 | }
44 |
45 | public override string ToString()
46 | {
47 | return $"(Line: {Line}, Column: {Column})";
48 | }
49 | }
50 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/MatrixJob.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using LocalAppVeyor.Engine.Configuration;
4 |
5 | namespace LocalAppVeyor.Engine;
6 |
7 | public class MatrixJob
8 | {
9 | public string OperatingSystem { get; }
10 |
11 | public string Platform { get; }
12 |
13 | public string Configuration { get; }
14 |
15 | public IReadOnlyCollection Variables { get; }
16 |
17 | private string _name;
18 |
19 | public string Name
20 | {
21 | get
22 | {
23 | if (_name != null)
24 | {
25 | return _name;
26 | }
27 |
28 | if (string.IsNullOrEmpty(OperatingSystem) &&
29 | string.IsNullOrEmpty(Platform) &&
30 | string.IsNullOrEmpty(Configuration) &&
31 | Variables.Count == 0)
32 | {
33 | return "Default Job";
34 | }
35 |
36 | var nameParts = new List();
37 |
38 | if (!string.IsNullOrEmpty(OperatingSystem))
39 | {
40 | nameParts.Add($"OS: {OperatingSystem}");
41 | }
42 |
43 | if (Variables.Count > 0)
44 | {
45 | nameParts.Add($"Environment: {string.Join(", ", Variables.Select(v => v.ToString()))}");
46 | }
47 |
48 | if (!string.IsNullOrEmpty(Configuration))
49 | {
50 | nameParts.Add($"Configuration: {Configuration}");
51 | }
52 |
53 | if (!string.IsNullOrEmpty(Platform))
54 | {
55 | nameParts.Add($"Platform: {Platform}");
56 | }
57 |
58 | return _name = string.Join("; ", nameParts);
59 | }
60 | }
61 |
62 | public MatrixJob(
63 | string operatingSystem,
64 | IReadOnlyCollection variables,
65 | string configuration,
66 | string platform)
67 | {
68 | OperatingSystem = operatingSystem;
69 | Platform = platform;
70 | Configuration = configuration;
71 | Variables = variables ?? new Variable[0];
72 | }
73 |
74 | public override string ToString()
75 | {
76 | return Name;
77 | }
78 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor.Engine/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Runtime.CompilerServices;
2 |
3 | [assembly: InternalsVisibleTo("LocalAppVeyor.Engine.UnitTests")]
--------------------------------------------------------------------------------
/src/LocalAppVeyor/Commands/BuildConsoleCommand.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 | using LocalAppVeyor.Engine;
7 | using LocalAppVeyor.Engine.Configuration;
8 | using LocalAppVeyor.Engine.Configuration.Reader;
9 | using McMaster.Extensions.CommandLineUtils;
10 |
11 | namespace LocalAppVeyor.Commands;
12 |
13 | internal class BuildConsoleCommand : ConsoleCommand
14 | {
15 | public override string Name => "build";
16 |
17 | protected override string Description => "Executes appveyor.yml's build jobs from specified repository directory";
18 |
19 | private CommandOption _repositoryPathOption;
20 |
21 | private CommandOption _jobsIndexesOption;
22 |
23 | private CommandOption _skipStepsOptions;
24 |
25 | public BuildConsoleCommand(IPipelineOutputter outputter)
26 | : base(outputter)
27 | {
28 | }
29 |
30 | protected override void SetUpAdditionalCommandOptions(CommandLineApplication app)
31 | {
32 | _repositoryPathOption = app.Option(
33 | "-d|--dir",
34 | "Local repository directory where appveyor.yml sits. If not specified current directory is used",
35 | CommandOptionType.SingleValue);
36 |
37 | _jobsIndexesOption = app.Option(
38 | "-j|--job",
39 | "Job to build. You can specify multiple jobs. Use 'jobs' command to list them all",
40 | CommandOptionType.MultipleValue);
41 |
42 | _skipStepsOptions = app.Option(
43 | "-s|--skip",
44 | "Step to skip from the build pipeline step. You can specify multiple steps.",
45 | CommandOptionType.MultipleValue);
46 | }
47 |
48 | protected override Task OnExecute(CommandLineApplication app)
49 | {
50 | var engineConfiguration = TryGetEngineConfigurationOrTerminate(_repositoryPathOption.Value());
51 | var buildConfiguration = TryGetBuildConfigurationOrTerminate(engineConfiguration.RepositoryDirectoryPath);
52 |
53 | var engine = new Engine.Engine(
54 | engineConfiguration,
55 | buildConfiguration);
56 |
57 | engine.JobStarting += (sender, args) =>
58 | {
59 | Outputter.Write($"Starting '{args.Job.Name}'...");
60 | };
61 |
62 | engine.JobEnded += (sender, args) =>
63 | {
64 | switch (args.ExecutionResult.ResultType)
65 | {
66 | case JobExecutionResultType.Success:
67 | Outputter.WriteSuccess($"Job '{args.Job.Name}' successfully executed.");
68 | break;
69 | case JobExecutionResultType.Failure:
70 | Outputter.WriteError($"Job '{args.Job.Name}' failed.");
71 | break;
72 | case JobExecutionResultType.NotExecuted:
73 | Outputter.WriteError($"Job '{args.Job.Name}' will not be executed.");
74 | break;
75 | case JobExecutionResultType.JobNotFound:
76 | Outputter.WriteError("Specified job index not found. Use 'jobs' command to list available jobs.");
77 | break;
78 | case JobExecutionResultType.SolutionFileNotFound:
79 | Outputter.WriteError("Solution was not found.");
80 | break;
81 | case JobExecutionResultType.UnhandledException:
82 | Outputter.WriteError($"Unhandled exception while executing '{args.Job.Name}': " +
83 | $"{args.ExecutionResult.UnhandledException.Message} {args.ExecutionResult.UnhandledException.StackTrace}");
84 | break;
85 | default:
86 | throw new ArgumentOutOfRangeException();
87 | }
88 | };
89 |
90 | int[] jobs;
91 |
92 | try
93 | {
94 | jobs = _jobsIndexesOption
95 | .Values
96 | .Select(int.Parse)
97 | .ToArray();
98 | }
99 | catch (Exception)
100 | {
101 | Outputter.WriteError("Job option receives a integer as input. Use 'jobs' command to list all available jobs.");
102 | return Task.FromResult(1);
103 | }
104 |
105 | var jobsResults = new List();
106 |
107 | if (jobs.Length == 0)
108 | {
109 | jobsResults = engine.ExecuteAllJobs().ToList();
110 | }
111 | else
112 | {
113 | foreach (var jobIndex in jobs)
114 | {
115 | jobsResults.Add(engine.ExecuteJob(jobIndex));
116 | }
117 | }
118 |
119 | PrintFinalResults(jobsResults);
120 | return Task.FromResult(0);
121 | }
122 |
123 | private static string ToFinalResultsString(JobExecutionResultType jobResult)
124 | {
125 | switch (jobResult)
126 | {
127 | case JobExecutionResultType.Success:
128 | return "Succeeded";
129 | case JobExecutionResultType.Failure:
130 | return "Failed";
131 | case JobExecutionResultType.NotExecuted:
132 | return "Not executed";
133 | case JobExecutionResultType.JobNotFound:
134 | return "Job not found";
135 | case JobExecutionResultType.SolutionFileNotFound:
136 | return "No solution file";
137 | case JobExecutionResultType.UnhandledException:
138 | return "Unhandled exception";
139 | default:
140 | throw new ArgumentOutOfRangeException(nameof(jobResult), jobResult, null);
141 | }
142 | }
143 |
144 | private void PrintFinalResults(IReadOnlyCollection jobsResults)
145 | {
146 | if (jobsResults.Count == 0)
147 | {
148 | Outputter.Write("Execution finished.");
149 | return;
150 | }
151 |
152 | Outputter.Write("Execution finished:");
153 |
154 | var groupedResults = jobsResults
155 | .GroupBy(r => r.ResultType)
156 | .Select(g => $"{ToFinalResultsString(g.Key)}: {g.Count()}");
157 |
158 | Outputter.Write(
159 | $"=== Execution Result: {string.Join(", ", groupedResults)} ===");
160 | }
161 |
162 | private BuildConfiguration TryGetBuildConfigurationOrTerminate(string repositoryPathStr)
163 | {
164 | var appVeyorYml = Path.Combine(repositoryPathStr, "appveyor.yml");
165 |
166 | if (!File.Exists(appVeyorYml))
167 | {
168 | Outputter.WriteError("appveyor.yml file not found on repository path. Trying '.appveyor.yml'...");
169 |
170 | appVeyorYml = Path.Combine(repositoryPathStr, ".appveyor.yml");
171 |
172 | if (!File.Exists(appVeyorYml))
173 | {
174 | Outputter.WriteError(".appveyor.yml file not found on repository path. Build aborted.");
175 | Environment.Exit(1);
176 | }
177 | }
178 |
179 | BuildConfiguration configuration = null;
180 |
181 | try
182 | {
183 | configuration = new BuildConfigurationYamlFileReader(appVeyorYml)
184 | .GetBuildConfiguration();
185 | }
186 | catch (LocalAppVeyorException exception)
187 | {
188 | Outputter.WriteError(GetDetailedErrorMessage(appVeyorYml, exception));
189 | Environment.Exit(1);
190 | }
191 |
192 | if (_skipStepsOptions.Values != null)
193 | {
194 | configuration.SkipSteps = _skipStepsOptions.Values.ToArray();
195 | }
196 |
197 | return configuration;
198 | }
199 |
200 | private static string GetDetailedErrorMessage(string appVeyorYml, LocalAppVeyorException exception)
201 | {
202 | string marksMessage = null;
203 |
204 | if (exception.Start != null && exception.End != null)
205 | {
206 | marksMessage = $"{exception.Start} to {exception.End}";
207 | }
208 |
209 | return $"Error while parsing '{appVeyorYml}' file{(marksMessage != null ? $" {marksMessage}." : ".")}";
210 | }
211 |
212 | private EngineConfiguration TryGetEngineConfigurationOrTerminate(string repositoryPath)
213 | {
214 | if (!string.IsNullOrEmpty(repositoryPath))
215 | {
216 | if (!Directory.Exists(repositoryPath))
217 | {
218 | Outputter.WriteError($"Repository directory '{repositoryPath}' not found. Build aborted.");
219 | Environment.Exit(1);
220 | }
221 | }
222 | else
223 | {
224 | repositoryPath = Directory.GetCurrentDirectory();
225 | }
226 |
227 | return new EngineConfiguration(repositoryPath, Outputter);
228 | }
229 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor/Commands/ConsoleCommand.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using LocalAppVeyor.Engine;
3 | using McMaster.Extensions.CommandLineUtils;
4 |
5 | namespace LocalAppVeyor.Commands;
6 |
7 | internal abstract class ConsoleCommand
8 | {
9 | public abstract string Name { get; }
10 |
11 | protected abstract string Description { get; }
12 |
13 | protected IPipelineOutputter Outputter { get; }
14 |
15 | protected ConsoleCommand(IPipelineOutputter outputter)
16 | {
17 | Outputter = outputter;
18 | }
19 |
20 | public void SetUp(CommandLineApplication app)
21 | {
22 | app.Description = Description;
23 | app.HelpOption("-?|-h|--help");
24 |
25 | SetUpAdditionalCommandOptions(app);
26 |
27 | app.OnExecuteAsync(token => OnExecute(app));
28 | }
29 |
30 | protected virtual void SetUpAdditionalCommandOptions(CommandLineApplication app)
31 | {
32 | }
33 |
34 | protected abstract Task OnExecute(CommandLineApplication app);
35 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor/Commands/JobsConsoleCommand.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Threading.Tasks;
4 | using LocalAppVeyor.Engine;
5 | using LocalAppVeyor.Engine.Configuration;
6 | using LocalAppVeyor.Engine.Configuration.Reader;
7 | using McMaster.Extensions.CommandLineUtils;
8 |
9 | namespace LocalAppVeyor.Commands;
10 |
11 | internal class JobsConsoleCommand : ConsoleCommand
12 | {
13 | private CommandOption _repositoryPathOption;
14 |
15 | public override string Name => "jobs";
16 |
17 | protected override string Description => "List all build jobs available to execution";
18 |
19 | public JobsConsoleCommand(IPipelineOutputter outputter)
20 | : base(outputter)
21 | {
22 | }
23 |
24 | protected override void SetUpAdditionalCommandOptions(CommandLineApplication app)
25 | {
26 | _repositoryPathOption = app.Option(
27 | "-d|--dir",
28 | "Local repository directory where appveyor.yml sits. If not specified current directory is used",
29 | CommandOptionType.SingleValue);
30 | }
31 |
32 | protected override Task OnExecute(CommandLineApplication app)
33 | {
34 | var engineConfiguration = TryGetEngineConfigurationOrTerminate(_repositoryPathOption.Value());
35 | var buildConfiguration = TryGetBuildConfigurationOrTerminate(engineConfiguration.RepositoryDirectoryPath);
36 |
37 | var engine = new Engine.Engine(
38 | engineConfiguration,
39 | buildConfiguration);
40 |
41 | engineConfiguration.Outputter.Write("Available jobs:");
42 | for (var i = 0; i < engine.Jobs.Length; i++)
43 | {
44 | engineConfiguration.Outputter.Write(
45 | $"[{i}]: {engine.Jobs[i].Name}");
46 | }
47 |
48 | return Task.FromResult(0);
49 | }
50 |
51 | private BuildConfiguration TryGetBuildConfigurationOrTerminate(string repositoryPathStr)
52 | {
53 | var appVeyorYml = Path.Combine(repositoryPathStr, "appveyor.yml");
54 |
55 | if (!File.Exists(appVeyorYml))
56 | {
57 | Outputter.Write("appveyor.yml file not found on repository path. Trying '.appveyor.yml'...");
58 |
59 | appVeyorYml = Path.Combine(repositoryPathStr, ".appveyor.yml");
60 |
61 | if (!File.Exists(appVeyorYml))
62 | {
63 | Outputter.WriteError(".appveyor.yml file not found on repository path.");
64 | Environment.Exit(1);
65 | }
66 | }
67 |
68 | BuildConfiguration configuration = null;
69 |
70 | try
71 | {
72 | configuration = new BuildConfigurationYamlFileReader(appVeyorYml)
73 | .GetBuildConfiguration();
74 | }
75 | catch (LocalAppVeyorException)
76 | {
77 | Outputter.WriteError($"Error while parsing '{appVeyorYml}' file.");
78 | Environment.Exit(1);
79 | }
80 |
81 | return configuration;
82 | }
83 |
84 | private EngineConfiguration TryGetEngineConfigurationOrTerminate(string repositoryPath)
85 | {
86 | if (!string.IsNullOrEmpty(repositoryPath))
87 | {
88 | if (!Directory.Exists(repositoryPath))
89 | {
90 | Outputter.WriteError($"Repository directory '{repositoryPath}' not found.");
91 | Environment.Exit(1);
92 | }
93 | }
94 | else
95 | {
96 | repositoryPath = Directory.GetCurrentDirectory();
97 | }
98 |
99 | return new EngineConfiguration(repositoryPath, Outputter);
100 | }
101 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor/Commands/LintConsoleCommand.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Net;
4 | using System.Net.Http;
5 | using System.Net.Http.Headers;
6 | using System.Security;
7 | using System.Text.Json;
8 | using System.Threading.Tasks;
9 | using LocalAppVeyor.Engine;
10 | using McMaster.Extensions.CommandLineUtils;
11 |
12 | namespace LocalAppVeyor.Commands;
13 |
14 | internal sealed class LintConsoleCommand : ConsoleCommand
15 | {
16 | private const string TokenEnvironmentVariableName = "LOCALAPPVEYOR_API_TOKEN";
17 |
18 | private const string YmlValidationUrl = "https://ci.appveyor.com/api/projects/validate-yaml";
19 |
20 | private CommandOption _repositoryPathOption;
21 |
22 | private CommandOption _apiTokenOption;
23 |
24 | public override string Name => "lint";
25 |
26 | protected override string Description => "Validates appveyor.yml YAML configuration. It requires internet connection.";
27 |
28 | public LintConsoleCommand(IPipelineOutputter outputter)
29 | : base(outputter)
30 | {
31 | }
32 |
33 | protected override void SetUpAdditionalCommandOptions(CommandLineApplication app)
34 | {
35 | _apiTokenOption = app.Option(
36 | "-t|--token",
37 | $"Your AppVeyor account API token. If not specified it tries to get it from {TokenEnvironmentVariableName} " +
38 | "environment variable. You can find your API token here: https://ci.appveyor.com/api-token.",
39 | CommandOptionType.SingleValue);
40 |
41 | _repositoryPathOption = app.Option(
42 | "-d|--dir",
43 | "Local repository directory where appveyor.yml sits. If not specified current directory is used",
44 | CommandOptionType.SingleValue);
45 | }
46 |
47 | protected override async Task OnExecute(CommandLineApplication app)
48 | {
49 | var apiToken = _apiTokenOption.Value();
50 |
51 | if (string.IsNullOrEmpty(apiToken))
52 | {
53 | apiToken = Environment.GetEnvironmentVariable(
54 | TokenEnvironmentVariableName,
55 | EnvironmentVariableTarget.User);
56 |
57 | if (string.IsNullOrEmpty(apiToken))
58 | {
59 | Outputter.WriteError("AppVeyor API token is required. Either specify it using --token " +
60 | $"option or {TokenEnvironmentVariableName} environment variable.");
61 | Environment.Exit(1);
62 | }
63 | }
64 |
65 | var repositoryPath = TryGetRepositoryDirectoryPathOrTerminate(_repositoryPathOption.Value());
66 | var (yamlFilePath, ymlFileContent) = TryGetAppVeyorFileContentOrTerminate(repositoryPath);
67 |
68 | Outputter.Write($"Validating '{yamlFilePath}'...");
69 |
70 | using var client = new HttpClient();
71 | client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
72 | client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiToken);
73 |
74 | Outputter.Write("Connecting to AppVeyor validation API...");
75 |
76 | try
77 | {
78 | using var response = await client.PostAsync(YmlValidationUrl, new StringContent(ymlFileContent));
79 | var responseContent = await response.Content.ReadAsStringAsync();
80 | var responseObj = JsonSerializer.Deserialize(responseContent);
81 |
82 | switch (response.StatusCode)
83 | {
84 | case HttpStatusCode.OK when (bool)responseObj.isValid:
85 | Outputter.WriteSuccess("YAML configuration file is valid.");
86 | return 0;
87 | case HttpStatusCode.OK:
88 | Outputter.WriteError((string)responseObj.errorMessage);
89 | break;
90 | case HttpStatusCode.Unauthorized:
91 | Outputter.WriteError("Authorization failed. Make sure you're specifying an updated API token.");
92 | break;
93 | default:
94 | var msg = (string) responseObj.message;
95 | Outputter.WriteError(!string.IsNullOrEmpty(msg)
96 | ? $"Validation failed with status code {response.StatusCode}. Message: {msg}"
97 | : $"Validation failed with status code {response.StatusCode}.");
98 |
99 | break;
100 | }
101 | }
102 | catch (HttpRequestException)
103 | {
104 | Outputter.WriteError("Error connecting to AppVeyor validation API. Check your interner connection.");
105 | }
106 |
107 | return 1;
108 | }
109 |
110 | private (string YamlFilePath, string YmlFileContent) TryGetAppVeyorFileContentOrTerminate(string repositoryPath)
111 | {
112 | var appVeyorYml = Path.Combine(repositoryPath, "appveyor.yml");
113 |
114 | if (!File.Exists(appVeyorYml))
115 | {
116 | Outputter.Write("appveyor.yml file not found on repository path. Trying '.appveyor.yml'...");
117 |
118 | appVeyorYml = Path.Combine(repositoryPath, ".appveyor.yml");
119 |
120 | if (!File.Exists(appVeyorYml))
121 | {
122 | Outputter.WriteError(".appveyor.yml file not found on repository path. Validation stopped.");
123 | Environment.Exit(1);
124 | }
125 | }
126 |
127 | string exceptionReason;
128 |
129 | try
130 | {
131 | return (appVeyorYml, File.ReadAllText(appVeyorYml));
132 | }
133 | catch (PathTooLongException)
134 | {
135 | exceptionReason = "Path too long";
136 | }
137 | catch (DirectoryNotFoundException)
138 | {
139 | exceptionReason = "Directory not found";
140 | }
141 | catch (FileNotFoundException)
142 | {
143 | exceptionReason = "File not found";
144 | }
145 | catch (NotSupportedException)
146 | {
147 | exceptionReason = "Path is in an invalid format";
148 | }
149 | catch (IOException e)
150 | {
151 | exceptionReason = e.Message;
152 | }
153 | catch (UnauthorizedAccessException)
154 | {
155 | exceptionReason = "No permissions to read configuration file";
156 | }
157 | catch (SecurityException)
158 | {
159 | exceptionReason = "The caller does not have the required permission";
160 | }
161 |
162 | Outputter.WriteError($"Error while trying to read '{appVeyorYml}' file. {exceptionReason}. Validation aborted.");
163 | Environment.Exit(1);
164 | return (null, null);
165 | }
166 |
167 | private string TryGetRepositoryDirectoryPathOrTerminate(string repositoryPath)
168 | {
169 | if (!string.IsNullOrEmpty(repositoryPath))
170 | {
171 | if (!Directory.Exists(repositoryPath))
172 | {
173 | Outputter.WriteError($"Repository directory '{repositoryPath}' not found. Validation aborted.");
174 | Environment.Exit(1);
175 | }
176 |
177 | return repositoryPath;
178 | }
179 |
180 | return Directory.GetCurrentDirectory();
181 | }
182 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor/ConsoleOutputter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using LocalAppVeyor.Engine;
3 |
4 | namespace LocalAppVeyor;
5 |
6 | internal sealed class ConsoleOutputter : IPipelineOutputter
7 | {
8 | public void SetColor(ConsoleColor color)
9 | {
10 | Console.ForegroundColor = TransformColor(color, Console.BackgroundColor);
11 | }
12 |
13 | public void ResetColor()
14 | {
15 | Console.ResetColor();
16 | }
17 |
18 | private static ConsoleColor TransformColor(
19 | ConsoleColor foreground,
20 | ConsoleColor background)
21 | {
22 | if (foreground != background)
23 | {
24 | return foreground;
25 | }
26 |
27 | return background != ConsoleColor.Black
28 | ? ConsoleColor.Black
29 | : ConsoleColor.Gray;
30 | }
31 |
32 | public void Write(string message)
33 | {
34 | Console.WriteLine($"{message ?? ""}");
35 | }
36 |
37 | public void WriteSuccess(string successMessage)
38 | {
39 | SetColor(ConsoleColor.Green);
40 | Console.WriteLine($"{successMessage ?? ""}");
41 | ResetColor();
42 | }
43 |
44 | public void WriteWarning(string warningMessage)
45 | {
46 | SetColor(ConsoleColor.Yellow);
47 | Console.WriteLine($"{warningMessage ?? ""}");
48 | ResetColor();
49 | }
50 |
51 | public void WriteError(string errorMessage)
52 | {
53 | SetColor(ConsoleColor.Red);
54 | Console.WriteLine($"{errorMessage ?? ""}");
55 | ResetColor();
56 | }
57 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor/LocalAppVeyor.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Run your AppVeyor builds, locally.
5 | joaope 2020
6 | 0.6.0
7 | joaope
8 | net8.0
9 | true
10 | latest
11 | Exe
12 | true
13 | true
14 | localappveyor
15 | localappveyor
16 | console;appveyor;local;build;api
17 | https://github.com/joaope/LocalAppVeyor
18 | MIT
19 | git
20 | https://github.com/joaope/LocalAppVeyor.git
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------
/src/LocalAppVeyor/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Reflection;
3 | using LocalAppVeyor.Commands;
4 | using LocalAppVeyor.Engine;
5 | using McMaster.Extensions.CommandLineUtils;
6 |
7 | namespace LocalAppVeyor;
8 |
9 | public static class Program
10 | {
11 | private static readonly IPipelineOutputter PipelineOutputter = new ConsoleOutputter();
12 |
13 | private static readonly BuildConsoleCommand BuildCommand = new BuildConsoleCommand(PipelineOutputter);
14 |
15 | private static readonly JobsConsoleCommand JobsCommand = new JobsConsoleCommand(PipelineOutputter);
16 |
17 | private static readonly LintConsoleCommand LintCommand = new LintConsoleCommand(PipelineOutputter);
18 |
19 | public static void Main(string[] args)
20 | {
21 | var app = new CommandLineApplication
22 | {
23 | Name = "LocalAppVeyor",
24 | FullName = "LocalAppVeyor",
25 | Description = "LocalAppVeyor allows one to run an appveyor.yml build script locally",
26 | UnrecognizedArgumentHandling = UnrecognizedArgumentHandling.StopParsingAndCollect
27 | };
28 |
29 | var (shortFormVersion, longFormVersion) = GetShortAndLongVersion();
30 |
31 | app.HelpOption("-?|-h|--help");
32 | app.VersionOption("-v|--version", shortFormVersion, longFormVersion);
33 |
34 | app.Command(BuildCommand.Name, conf => { BuildCommand.SetUp(conf); });
35 | app.Command(JobsCommand.Name, conf => { JobsCommand.SetUp(conf); });
36 | app.Command(LintCommand.Name, conf => { LintCommand.SetUp(conf); });
37 |
38 | app.OnExecute(() =>
39 | {
40 | app.ShowHelp();
41 | return 0;
42 | });
43 |
44 | app.Execute(args);
45 | }
46 |
47 | private static (string ShortFormVersion, string LongFormVersion) GetShortAndLongVersion()
48 | {
49 | string GetVersionFromTypeInfo(Type typeInfo)
50 | {
51 | var infoVersion = typeInfo.Assembly.GetCustomAttribute()
52 | .InformationalVersion;
53 |
54 | if (string.IsNullOrEmpty(infoVersion))
55 | {
56 | infoVersion = typeInfo.Assembly.GetName().Version.ToString();
57 | }
58 |
59 | return infoVersion;
60 | }
61 |
62 | var consoleVer = GetVersionFromTypeInfo(typeof(Program).GetTypeInfo());
63 | var engineVer = GetVersionFromTypeInfo(typeof(Engine.Engine).GetTypeInfo());
64 |
65 | return (consoleVer, $"{consoleVer} (engine: {engineVer})");
66 | }
67 | }
--------------------------------------------------------------------------------
/src/LocalAppVeyor/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "profiles": {
3 | "LocalAppVeyor": {
4 | "commandName": "Project"
5 | },
6 | "LocalAppVeyor (jobs)": {
7 | "commandName": "Project",
8 | "commandLineArgs": "jobs"
9 | },
10 | "LocalAppVeyor (build)": {
11 | "commandName": "Project",
12 | "commandLineArgs": "build"
13 | },
14 | "LocalAppVeyor (lint)": {
15 | "commandName": "Project",
16 | "commandLineArgs": "lint"
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/tests/LocalAppVeyor.Engine.UnitTests/Configuration/AssemblyInfoTests.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using LocalAppVeyor.Engine.Configuration;
3 | using LocalAppVeyor.Engine.Configuration.Reader;
4 | using Xunit;
5 |
6 | namespace LocalAppVeyor.Engine.UnitTests.Configuration
7 | {
8 | public class AssemblyInfoTests
9 | {
10 | [Fact]
11 | public void ShouldReadAssemblyInfoStep()
12 | {
13 | const string yaml = @"
14 | assembly_info:
15 | patch: true
16 | file: AssemblyInfo.*
17 | assembly_version: ""2.2.{build}""
18 | assembly_file_version: ""{version}""
19 | assembly_informational_version: ""{version}""
20 | ";
21 |
22 | var conf = new BuildConfigurationYamlStringReader(yaml).GetBuildConfiguration();
23 |
24 | conf.AssemblyInfo.Should().BeEquivalentTo(new AssemblyInfo(
25 | true,
26 | "AssemblyInfo.*",
27 | "2.2.{build}",
28 | "{version}",
29 | "{version}"));
30 | }
31 |
32 | [Fact]
33 | public void ShouldBePatchFalseForAssemblyInfoWhenNotSpecified()
34 | {
35 | var conf = new BuildConfigurationYamlStringReader(string.Empty).GetBuildConfiguration();
36 |
37 | conf.AssemblyInfo.Should().BeEquivalentTo(new AssemblyInfo());
38 | conf.AssemblyInfo.Should().BeEquivalentTo(new AssemblyInfo(
39 | false,
40 | null,
41 | null,
42 | null,
43 | null));
44 | }
45 | }
46 | }
--------------------------------------------------------------------------------
/tests/LocalAppVeyor.Engine.UnitTests/Configuration/BuildScriptTests.Unix.cs:
--------------------------------------------------------------------------------
1 | using LocalAppVeyor.Engine.Configuration;
2 | using LocalAppVeyor.Engine.Configuration.Reader;
3 | using LocalAppVeyor.Engine.UnitTests.TestUtilities;
4 | using Xunit;
5 |
6 | namespace LocalAppVeyor.Engine.UnitTests.Configuration
7 | {
8 | public partial class BuildScriptTests
9 | {
10 | [UnixOnlyFact]
11 | public void Unix_ShouldReadBuildScriptAsAScriptBlockWithMultipleDifferentTypeScripts()
12 | {
13 | const string yaml = @"
14 | build_script:
15 | # by default, all script lines are interpreted as bash
16 | - echo This is bash
17 | # to run script as a PowerShell command prepend it with ps:
18 | - ps: Write-Host 'This is PowerShell'
19 | # bash commands start from cmd:
20 | - cmd: echo This is bash again
21 | - cmd: set MY_VAR=12345
22 | ";
23 |
24 | var conf = new BuildConfigurationYamlStringReader(yaml).GetBuildConfiguration();
25 |
26 | Assert.Equal(4, conf.BuildScript.Count);
27 | Assert.Equal("echo This is bash", conf.BuildScript[0].Script);
28 | Assert.Equal(ScriptType.Bash, conf.BuildScript[0].ScriptType);
29 | Assert.Equal("Write-Host 'This is PowerShell'", conf.BuildScript[1].Script);
30 | Assert.Equal(ScriptType.PowerShell, conf.BuildScript[1].ScriptType);
31 | Assert.Equal("echo This is bash again", conf.BuildScript[2].Script);
32 | Assert.Equal(ScriptType.Batch, conf.BuildScript[2].ScriptType);
33 | Assert.Equal("set MY_VAR=12345", conf.BuildScript[3].Script);
34 | Assert.Equal(ScriptType.Batch, conf.BuildScript[3].ScriptType);
35 | }
36 |
37 | [UnixOnlyFact]
38 | public void Unix_ShouldReadBuildScriptAsAScriptBlockWithSplittedLinesScripts()
39 | {
40 | const string yaml = @"
41 | build_script:
42 | - |-
43 | echo --------------------------------------------------------------------------------
44 | echo Build tinyformat
45 | mkdir build
46 | cd build
47 | cmake -G ""%COMPILER%"" ..
48 | cmake --build . --config %CONFIGURATION%
49 | ";
50 |
51 | var conf = new BuildConfigurationYamlStringReader(yaml).GetBuildConfiguration();
52 |
53 | Assert.Single(conf.BuildScript);
54 | Assert.Equal("echo --------------------------------------------------------------------------------\n" +
55 | "echo Build tinyformat\n" +
56 | "mkdir build\n" +
57 | "cd build\n" +
58 | "cmake -G \"%COMPILER%\" ..\n" +
59 | "cmake --build . --config %CONFIGURATION%",
60 | conf.BuildScript[0].Script);
61 | Assert.Equal(ScriptType.Bash, conf.BuildScript[0].ScriptType);
62 | }
63 |
64 | [UnixOnlyFact]
65 | public void Unix_ShouldReadBuildScriptAsAScriptBlockWithSplittedLinesScripts_AlternativeBlockStyle()
66 | {
67 | const string yaml = @"
68 | build_script:
69 | - sh: |-
70 | echo --------------------------------------------------------------------------------
71 | echo Build tinyformat
72 | mkdir build
73 | cd build
74 | cmake -G ""%COMPILER%"" ..
75 | cmake --build . --config %CONFIGURATION%
76 | ";
77 |
78 | var conf = new BuildConfigurationYamlStringReader(yaml).GetBuildConfiguration();
79 |
80 | Assert.Single(conf.BuildScript);
81 | Assert.Equal("echo --------------------------------------------------------------------------------\n" +
82 | "echo Build tinyformat\n" +
83 | "mkdir build\n" +
84 | "cd build\n" +
85 | "cmake -G \"%COMPILER%\" ..\n" +
86 | "cmake --build . --config %CONFIGURATION%",
87 | conf.BuildScript[0].Script);
88 | Assert.Equal(ScriptType.Bash, conf.BuildScript[0].ScriptType);
89 | }
90 | }
91 | }
--------------------------------------------------------------------------------
/tests/LocalAppVeyor.Engine.UnitTests/Configuration/BuildScriptTests.Window.cs:
--------------------------------------------------------------------------------
1 | using LocalAppVeyor.Engine.Configuration;
2 | using LocalAppVeyor.Engine.Configuration.Reader;
3 | using LocalAppVeyor.Engine.UnitTests.TestUtilities;
4 | using Xunit;
5 |
6 | namespace LocalAppVeyor.Engine.UnitTests.Configuration
7 | {
8 | public partial class BuildScriptTests
9 | {
10 | [WindowsOnlyFact]
11 | public void Windows_ShouldReadBuildScriptAsAScriptBlockWithMultipleDifferentTypeScripts()
12 | {
13 | const string yaml = @"
14 | build_script:
15 | # by default, all script lines are interpreted as batch
16 | - echo This is batch
17 | # to run script as a PowerShell command prepend it with ps:
18 | - ps: Write-Host 'This is PowerShell'
19 | # batch commands start from cmd:
20 | - cmd: echo This is batch again
21 | - cmd: set MY_VAR=12345
22 | ";
23 |
24 | var conf = new BuildConfigurationYamlStringReader(yaml).GetBuildConfiguration();
25 |
26 | Assert.Equal(4, conf.BuildScript.Count);
27 | Assert.Equal("echo This is batch", conf.BuildScript[0].Script);
28 | Assert.Equal(ScriptType.Batch, conf.BuildScript[0].ScriptType);
29 | Assert.Equal("Write-Host 'This is PowerShell'", conf.BuildScript[1].Script);
30 | Assert.Equal(ScriptType.PowerShell, conf.BuildScript[1].ScriptType);
31 | Assert.Equal("echo This is batch again", conf.BuildScript[2].Script);
32 | Assert.Equal(ScriptType.Batch, conf.BuildScript[2].ScriptType);
33 | Assert.Equal("set MY_VAR=12345", conf.BuildScript[3].Script);
34 | Assert.Equal(ScriptType.Batch, conf.BuildScript[3].ScriptType);
35 | }
36 |
37 | [WindowsOnlyFact]
38 | public void Windows_ShouldReadBuildScriptAsAScriptBlockWithSplittedLinesScripts()
39 | {
40 | const string yaml = @"
41 | build_script:
42 | - |-
43 | echo --------------------------------------------------------------------------------
44 | echo Build tinyformat
45 | mkdir build
46 | cd build
47 | cmake -G ""%COMPILER%"" ..
48 | cmake --build . --config %CONFIGURATION%
49 | ";
50 |
51 | var conf = new BuildConfigurationYamlStringReader(yaml).GetBuildConfiguration();
52 |
53 | Assert.Single(conf.BuildScript);
54 | Assert.Equal("echo --------------------------------------------------------------------------------\n" +
55 | "echo Build tinyformat\n" +
56 | "mkdir build\n" +
57 | "cd build\n" +
58 | "cmake -G \"%COMPILER%\" ..\n" +
59 | "cmake --build . --config %CONFIGURATION%",
60 | conf.BuildScript[0].Script);
61 | Assert.Equal(ScriptType.Batch, conf.BuildScript[0].ScriptType);
62 | }
63 |
64 | [WindowsOnlyFact]
65 | public void Windows_ShouldReadBuildScriptAsAScriptBlockWithSplittedLinesScripts_AlternativeBlockStyle()
66 | {
67 | const string yaml = @"
68 | build_script:
69 | - cmd: |-
70 | echo --------------------------------------------------------------------------------
71 | echo Build tinyformat
72 | mkdir build
73 | cd build
74 | cmake -G ""%COMPILER%"" ..
75 | cmake --build . --config %CONFIGURATION%
76 | ";
77 |
78 | var conf = new BuildConfigurationYamlStringReader(yaml).GetBuildConfiguration();
79 |
80 | Assert.Single(conf.BuildScript);
81 | Assert.Equal("echo --------------------------------------------------------------------------------\n" +
82 | "echo Build tinyformat\n" +
83 | "mkdir build\n" +
84 | "cd build\n" +
85 | "cmake -G \"%COMPILER%\" ..\n" +
86 | "cmake --build . --config %CONFIGURATION%",
87 | conf.BuildScript[0].Script);
88 | Assert.Equal(ScriptType.Batch, conf.BuildScript[0].ScriptType);
89 | }
90 | }
91 | }
--------------------------------------------------------------------------------
/tests/LocalAppVeyor.Engine.UnitTests/Configuration/BuildScriptTests.cs:
--------------------------------------------------------------------------------
1 | using LocalAppVeyor.Engine.Configuration;
2 | using LocalAppVeyor.Engine.Configuration.Reader;
3 | using Xunit;
4 |
5 | namespace LocalAppVeyor.Engine.UnitTests.Configuration
6 | {
7 | public partial class BuildScriptTests
8 | {
9 | [Fact]
10 | public void ShouldReadBuildScriptAsAScriptBlockWithSplittedLinesScripts_AlternativeBlockStylePowershell()
11 | {
12 | const string yaml = @"
13 | build_script:
14 | - ps: |-
15 | echo --------------------------------------------------------------------------------
16 | echo Build tinyformat
17 | mkdir build
18 | cd build
19 | cmake -G ""%COMPILER%"" ..
20 | cmake --build . --config %CONFIGURATION%
21 | ";
22 |
23 | var conf = new BuildConfigurationYamlStringReader(yaml).GetBuildConfiguration();
24 |
25 | Assert.Single(conf.BuildScript);
26 | Assert.Equal("echo --------------------------------------------------------------------------------\n" +
27 | "echo Build tinyformat\n" +
28 | "mkdir build\n" +
29 | "cd build\n" +
30 | "cmake -G \"%COMPILER%\" ..\n" +
31 | "cmake --build . --config %CONFIGURATION%",
32 | conf.BuildScript[0].Script);
33 | Assert.Equal(ScriptType.PowerShell, conf.BuildScript[0].ScriptType);
34 | }
35 |
36 | [Fact]
37 | public void ShouldGracefullyReadAnInvalidScriptBlock_AndMakeItAnEmptyScript()
38 | {
39 | const string yaml = @"
40 | build_script: true;
41 | ";
42 |
43 | var conf = new BuildConfigurationYamlStringReader(yaml).GetBuildConfiguration();
44 |
45 | Assert.Empty(conf.BuildScript);
46 | }
47 | }
48 | }
--------------------------------------------------------------------------------
/tests/LocalAppVeyor.Engine.UnitTests/Configuration/EnvironmentTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.ObjectModel;
4 | using FluentAssertions;
5 | using LocalAppVeyor.Engine.Configuration;
6 | using LocalAppVeyor.Engine.Configuration.Reader;
7 | using Xunit;
8 |
9 | namespace LocalAppVeyor.Engine.UnitTests.Configuration
10 | {
11 | public class EnvironmentTests
12 | {
13 | [Fact]
14 | public void ShouldReadEnvironmentWithCommonAndMatrixVariables()
15 | {
16 | const string yaml = @"
17 | environment:
18 | common_var1: common_value1
19 | common_var2:
20 | secure: common_value2_secured
21 | matrix:
22 | - db: mysql
23 | password: mysql_password
24 | - db: sqlserver
25 | password:
26 | secure: sqlserver_secured_password
27 | ";
28 |
29 | var conf = new BuildConfigurationYamlStringReader(yaml).GetBuildConfiguration();
30 |
31 | conf.EnvironmentVariables.Should().BeEquivalentTo(
32 | new EnvironmentVariables(
33 | new List
34 | {
35 | new Variable("common_var1", "common_value1", false),
36 | new Variable("common_var2", "common_value2_secured", true)
37 | },
38 | new List>
39 | {
40 | new ReadOnlyCollection(new List
41 | {
42 | new Variable("db", "mysql", false),
43 | new Variable("password", "mysql_password", false)
44 | }),
45 | new ReadOnlyCollection(new List
46 | {
47 | new Variable("db", "sqlserver", false),
48 | new Variable("password", "sqlserver_secured_password", true)
49 | })
50 | }));
51 | }
52 |
53 | [Fact]
54 | public void ShouldReadCommonVariablesInsideGlobal()
55 | {
56 | const string yaml = @"
57 | environment:
58 | global:
59 | common_var1: common_value1
60 | common_var2:
61 | secure: common_value2_secured
62 | matrix:
63 | - db: mysql
64 | password: mysql_password
65 | - db: sqlserver
66 | password:
67 | secure: sqlserver_secured_password
68 | ";
69 |
70 | var conf = new BuildConfigurationYamlStringReader(yaml).GetBuildConfiguration();
71 |
72 | conf.EnvironmentVariables.Should().BeEquivalentTo(
73 | new EnvironmentVariables(
74 | new List
75 | {
76 | new Variable("common_var1", "common_value1", false),
77 | new Variable("common_var2", "common_value2_secured", true)
78 | },
79 | new List>
80 | {
81 | new ReadOnlyCollection(new List
82 | {
83 | new Variable("db", "mysql", false),
84 | new Variable("password", "mysql_password", false)
85 | }),
86 | new ReadOnlyCollection(new List
87 | {
88 | new Variable("db", "sqlserver", false),
89 | new Variable("password", "sqlserver_secured_password", true)
90 | })
91 | }));
92 | }
93 |
94 | [Fact]
95 | public void ShouldReadOnlyMatrixVariables()
96 | {
97 | const string yaml = @"
98 | environment:
99 | matrix:
100 | - db: mysql
101 | password: mysql_password
102 | - db: sqlserver
103 | password:
104 | secure: sqlserver_secured_password
105 | ";
106 |
107 | var conf = new BuildConfigurationYamlStringReader(yaml).GetBuildConfiguration();
108 |
109 | conf.EnvironmentVariables.CommonVariables.Should().BeEmpty();
110 | conf.EnvironmentVariables.Should().BeEquivalentTo(
111 | new EnvironmentVariables(
112 | null,
113 | new List>
114 | {
115 | new ReadOnlyCollection(new List
116 | {
117 | new Variable("db", "mysql", false),
118 | new Variable("password", "mysql_password", false)
119 | }),
120 | new ReadOnlyCollection(new List
121 | {
122 | new Variable("db", "sqlserver", false),
123 | new Variable("password", "sqlserver_secured_password", true)
124 | })
125 | }));
126 | }
127 |
128 | [Fact]
129 | public void ShouldReadNoEnvironmentButPropertiesShouldNotBeNullOnlyEmpty()
130 | {
131 | var conf = new BuildConfigurationYamlStringReader("").GetBuildConfiguration();
132 |
133 | conf.EnvironmentVariables.Should().NotBeNull();
134 | conf.EnvironmentVariables.CommonVariables.Should().BeEmpty();
135 | conf.EnvironmentVariables.Matrix.Should().BeEmpty();
136 | }
137 |
138 | [Fact]
139 | public void ShouldExpandVariableValueWhenUsed()
140 | {
141 | Environment.SetEnvironmentVariable("ENV_VAR", "my env value");
142 |
143 | const string yaml = @"
144 | environment:
145 | common_var1: common_value1 $(ENV_VAR)
146 | ";
147 |
148 | var conf = new BuildConfigurationYamlStringReader(yaml).GetBuildConfiguration();
149 |
150 | conf.EnvironmentVariables.Should().BeEquivalentTo(
151 | new EnvironmentVariables(
152 | new List
153 | {
154 | new Variable("common_var1", "common_value1 $(ENV_VAR)", false)
155 | }));
156 | conf.EnvironmentVariables.CommonVariables[0].Value.Should().Be("common_value1 my env value");
157 | conf.EnvironmentVariables.Matrix.Should().BeEmpty();
158 | }
159 | }
160 | }
161 |
--------------------------------------------------------------------------------
/tests/LocalAppVeyor.Engine.UnitTests/Configuration/ExpandableStringTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using FluentAssertions;
3 | using LocalAppVeyor.Engine.Configuration;
4 | using LocalAppVeyor.Engine.Configuration.Reader;
5 | using Xunit;
6 |
7 | namespace LocalAppVeyor.Engine.UnitTests.Configuration
8 | {
9 | public class ExpandableStringTests
10 | {
11 | [Fact]
12 | public void ExpandEnvironmentVariablesOnString()
13 | {
14 | ExpandableString str = "this a string $(VAR1) and also $(VAR2)";
15 |
16 | Environment.SetEnvironmentVariable("VAR1", "VAR1_VALUE1");
17 | Environment.SetEnvironmentVariable("VAR2", "VAR2_VALUE2");
18 |
19 | str.Should().Be("this a string VAR1_VALUE1 and also VAR2_VALUE2");
20 | }
21 |
22 | [Fact]
23 | public void DontExpandVariablesWhenOpenButNotClosingBrace()
24 | {
25 | ExpandableString str = "this a string $(VAR1 and also $(VAR2";
26 |
27 | Environment.SetEnvironmentVariable("VAR1", "VAR1_VALUE1");
28 | Environment.SetEnvironmentVariable("VAR2", "VAR2_VALUE2");
29 |
30 | str.Should().Be("this a string $(VAR1 and also $(VAR2");
31 | }
32 |
33 | [Fact]
34 | public void DontExpandVariablesWithInvalidCharacters()
35 | {
36 | ExpandableString str = "this a string $(VAR 1) and also $(VAR2)";
37 |
38 | Environment.SetEnvironmentVariable("VAR1", "VAR1_VALUE1");
39 | Environment.SetEnvironmentVariable("VAR2", "VAR2_VALUE2");
40 |
41 | str.Should().Be("this a string $(VAR 1) and also VAR2_VALUE2");
42 | }
43 |
44 | [Fact]
45 | public void Expand()
46 | {
47 | ExpandableString str = "this a string $(VAR1) and also $(VAR2)";
48 |
49 | Environment.SetEnvironmentVariable("VAR1", "VAR1_VALUE1");
50 | Environment.SetEnvironmentVariable("VAR2", "VAR2_VALUE2");
51 |
52 | str.Should().Be("this a string VAR1_VALUE1 and also VAR2_VALUE2");
53 | }
54 |
55 | [Fact]
56 | public void ShouldExpandCloneFolderWithVersionAndBuildNumber()
57 | {
58 | Environment.SetEnvironmentVariable("APPVEYOR_BUILD_BUILD", "0");
59 | Environment.SetEnvironmentVariable("APPVEYOR_BUILD_VERSION", "1.0.0-0");
60 |
61 | const string yaml = @"
62 | version: 1.0.0-{build}
63 | clone_folder: c:\folder\with\version_{version}_here
64 | ";
65 |
66 | var conf = new BuildConfigurationYamlStringReader(yaml).GetBuildConfiguration();
67 |
68 | conf.Version.Should().Be("1.0.0-0");
69 | conf.CloneFolder.Should().Be(@"c:\folder\with\version_1.0.0-0_here");
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/tests/LocalAppVeyor.Engine.UnitTests/Configuration/MatrixTests.cs:
--------------------------------------------------------------------------------
1 | using FluentAssertions;
2 | using LocalAppVeyor.Engine.Configuration;
3 | using LocalAppVeyor.Engine.Configuration.Reader;
4 | using Xunit;
5 |
6 | namespace LocalAppVeyor.Engine.UnitTests.Configuration
7 | {
8 | public class MatrixTests
9 | {
10 | [Fact]
11 | public void MatrixShouldReadAllowFailuresSectionSuccessfully()
12 | {
13 | const string yaml = @"
14 | matrix:
15 | allow_failures:
16 | - platform: x86
17 | configuration: Debug
18 | - platform: x64
19 | configuration: Release
20 | ";
21 |
22 | var conf = new BuildConfigurationYamlStringReader(yaml).GetBuildConfiguration();
23 |
24 | conf.Matrix.Should().BeEquivalentTo(new Matrix(
25 | false,
26 | new[]
27 | {
28 | new AllowedJobFailureConditions(null, "Debug", "x86", null, new Variable[0]),
29 | new AllowedJobFailureConditions(null, "Release", "x64", null, new Variable[0])
30 | }));
31 |
32 | conf.Matrix.AllowedFailures.Should().HaveCount(2);
33 | }
34 |
35 | [Fact]
36 | public void AllowFailuresSectionShouldBeEmpty()
37 | {
38 | const string yaml = @"
39 | matrix:
40 | ";
41 |
42 | var conf = new BuildConfigurationYamlStringReader(yaml).GetBuildConfiguration();
43 |
44 | conf.Matrix.Should().BeEquivalentTo(new Matrix(
45 | false,
46 | new AllowedJobFailureConditions[0]));
47 |
48 | conf.Matrix.IsFastFinish.Should().BeFalse();
49 | conf.Matrix.AllowedFailures.Should().BeEmpty();
50 | }
51 |
52 | [Fact]
53 | public void MatrixShouldHaveDefaultValuesWhenNotSpecified()
54 | {
55 | var conf = new BuildConfigurationYamlStringReader(string.Empty).GetBuildConfiguration();
56 |
57 | conf.Matrix.Should().BeEquivalentTo(new Matrix());
58 | conf.Matrix.IsFastFinish.Should().BeFalse();
59 | conf.Matrix.AllowedFailures.Should().BeEmpty();
60 | }
61 |
62 | [Fact]
63 | public void MatrixShouldHaveFastFinishAsTrueAndEmptyAllowedFailures()
64 | {
65 | const string yaml = @"
66 | matrix:
67 | fast_finish: true
68 | ";
69 | var conf = new BuildConfigurationYamlStringReader(yaml).GetBuildConfiguration();
70 |
71 | conf.Matrix.Should().BeEquivalentTo(new Matrix(
72 | true,
73 | new AllowedJobFailureConditions[0]));
74 | conf.Matrix.IsFastFinish.Should().BeTrue();
75 | conf.Matrix.AllowedFailures.Should().BeEmpty();
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/tests/LocalAppVeyor.Engine.UnitTests/Engine/AssemblyInfoRewriteStepTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO.Abstractions.TestingHelpers;
4 | using LocalAppVeyor.Engine.Configuration;
5 | using LocalAppVeyor.Engine.Configuration.Reader;
6 | using LocalAppVeyor.Engine.Internal;
7 | using LocalAppVeyor.Engine.Internal.Steps;
8 | using Moq;
9 | using Xunit;
10 |
11 | namespace LocalAppVeyor.Engine.UnitTests.Engine
12 | {
13 | public class AssemblyInfoRewriteStepTests
14 | {
15 | [Fact]
16 | public void AssemblyInfoReWriteShouldReplaceAllVersionAttributes()
17 | {
18 | const string yaml = @"
19 | version: 2.3.{build}
20 | assembly_info:
21 | patch: true
22 | file: AssemblyInfo.*
23 | assembly_version: ""1.1.{build}""
24 | assembly_file_version: ""{version}""
25 | assembly_informational_version: ""{version}""
26 | ";
27 |
28 | const string originalAssemblyInfoContent = @"
29 | [assembly: AssemblyTitle(""PatchAssemblyInfoFiles"")]
30 | [assembly: AssemblyDescription("""")]
31 | [assembly: AssemblyVersion(""1.2.3.4"")]
32 | [assembly: AssemblyFileVersion(""1.2.3.4"")]
33 | [assembly: AssemblyInformationalVersion(""foo bar baz 1 2 3 4"")]
34 | ";
35 |
36 | const string rewrittenAssemblyInfoContent = @"
37 | [assembly: AssemblyTitle(""PatchAssemblyInfoFiles"")]
38 | [assembly: AssemblyDescription("""")]
39 | [assembly: AssemblyVersion(""1.1.0"")]
40 | [assembly: AssemblyFileVersion(""2.3.0"")]
41 | [assembly: AssemblyInformationalVersion(""2.3.0"")]
42 | ";
43 |
44 | var fileSystem = new MockFileSystem();
45 | var cloneDirectory = fileSystem.Path.Combine(fileSystem.Path.GetTempPath(), fileSystem.Path.GetRandomFileName());
46 | var repoDirectory = fileSystem.Path.Combine(fileSystem.Path.GetTempPath(), fileSystem.Path.GetRandomFileName());
47 | var assemblyInfoFilename = fileSystem.Path.Combine(cloneDirectory, "AssemblyInfo.cs");
48 | fileSystem.AddDirectory(repoDirectory);
49 | fileSystem.AddDirectory(cloneDirectory);
50 | fileSystem.AddFile(assemblyInfoFilename, new MockFileData(originalAssemblyInfoContent));
51 | fileSystem.Directory.SetCurrentDirectory(repoDirectory);
52 |
53 | var executionContext = new ExecutionContext(
54 | new MatrixJob("os", new List(), "conf", "platform"),
55 | new BuildConfigurationYamlStringReader(yaml).GetBuildConfiguration(),
56 | new Mock().Object,
57 | repoDirectory,
58 | cloneDirectory,
59 | fileSystem);
60 |
61 | Environment.SetEnvironmentVariable("APPVEYOR_BUILD_VERSION", new ExpandableString("2.3.{build}"));
62 |
63 | var rewriteStep = new AssemblyInfoRewriteStep();
64 | var executionResult = rewriteStep.Execute(executionContext);
65 |
66 | Assert.True(executionResult);
67 | Assert.Equal(rewrittenAssemblyInfoContent, fileSystem.File.ReadAllText(assemblyInfoFilename));
68 | }
69 | }
70 | }
71 |
72 |
--------------------------------------------------------------------------------
/tests/LocalAppVeyor.Engine.UnitTests/Engine/EngineTests.Unix.cs:
--------------------------------------------------------------------------------
1 | using LocalAppVeyor.Engine.Configuration;
2 | using LocalAppVeyor.Engine.UnitTests.TestUtilities;
3 | using Moq;
4 | using Xunit;
5 |
6 | namespace LocalAppVeyor.Engine.UnitTests.Engine
7 | {
8 | public partial class EngineTests
9 | {
10 | [UnixOnlyFact]
11 | public void Linux_ShouldRunInitializationPowershellScript()
12 | {
13 | var buildConfiguration = new BuildConfiguration
14 | {
15 | InitializationScript =
16 | {
17 | new ScriptLine(ScriptType.PowerShell, "Write-Host 'This is a test'")
18 | }
19 | };
20 |
21 | var jobResult = new LocalAppVeyor.Engine.Engine(_engineConfiguration, buildConfiguration).ExecuteJob(0);
22 |
23 | _outputterMock.Verify(outputter => outputter.Write(It.Is(m => m == "This is a test")), Times.Once);
24 | Assert.True(jobResult.IsSuccessfulExecution);
25 | }
26 |
27 | [UnixOnlyFact]
28 | public void Linux_ShouldRunInitializationPowershellScriptWithMultipleLines()
29 | {
30 | var buildConfiguration = new BuildConfiguration
31 | {
32 | InitializationScript =
33 | {
34 | new ScriptLine(ScriptType.PowerShell, "Write-Host 'This is a test - first line'"),
35 | new ScriptLine(ScriptType.PowerShell, "Write-Host 'This is a test - second line'")
36 | }
37 | };
38 |
39 | var jobResult = new LocalAppVeyor.Engine.Engine(_engineConfiguration, buildConfiguration).ExecuteJob(0);
40 |
41 | _outputterMock.Verify(outputter => outputter.Write(It.Is(m => m == "This is a test - first line")), Times.Once);
42 | _outputterMock.Verify(outputter => outputter.Write(It.Is(m => m == "This is a test - second line")), Times.Once);
43 | _outputterMock.Verify(outputter => outputter.WriteError(It.IsAny()), Times.Never);
44 | Assert.True(jobResult.IsSuccessfulExecution);
45 | }
46 |
47 | [UnixOnlyFact]
48 | public void Linux_ShouldRunInitializationBashScript()
49 | {
50 | var buildConfiguration = new BuildConfiguration
51 | {
52 | InitializationScript =
53 | {
54 | new ScriptLine(ScriptType.Bash, "echo This is a test")
55 | }
56 | };
57 |
58 | var jobResult = new LocalAppVeyor.Engine.Engine(_engineConfiguration, buildConfiguration).ExecuteJob(0);
59 |
60 | _outputterMock.Verify(outputter => outputter.Write(It.Is(m => m == "This is a test")), Times.Once);
61 | _outputterMock.Verify(outputter => outputter.WriteError(It.IsAny()), Times.Never);
62 | Assert.True(jobResult.IsSuccessfulExecution);
63 | }
64 |
65 | [UnixOnlyFact]
66 | public void Linux_ShouldRunInitializationBashScriptWithMultipleLines()
67 | {
68 | var buildConfiguration = new BuildConfiguration
69 | {
70 | InitializationScript =
71 | {
72 | new ScriptLine(ScriptType.Bash, "echo This is a test - first line"),
73 | new ScriptLine(ScriptType.Bash, "echo This is a test - second line")
74 | }
75 | };
76 |
77 | var jobResult = new LocalAppVeyor.Engine.Engine(_engineConfiguration, buildConfiguration).ExecuteJob(0);
78 |
79 | _outputterMock.Verify(outputter => outputter.Write(It.Is(m => m == "This is a test - first line")), Times.Once);
80 | _outputterMock.Verify(outputter => outputter.Write(It.Is(m => m == "This is a test - second line")), Times.Once);
81 | _outputterMock.Verify(outputter => outputter.WriteError(It.IsAny()), Times.Never);
82 | Assert.True(jobResult.IsSuccessfulExecution);
83 | }
84 |
85 | [UnixOnlyFact]
86 | public void Linux_ShouldRunInitializationScriptWithMixedPowershellAndBashScripts()
87 | {
88 | var buildConfiguration = new BuildConfiguration
89 | {
90 | InitializationScript =
91 | {
92 | new ScriptLine(ScriptType.Bash, "echo This is 1 bash test"),
93 | new ScriptLine(ScriptType.PowerShell, "Write-Host 'This is 1 powershell test'"),
94 | new ScriptLine(ScriptType.Bash, "echo This is 2 bash test"),
95 | new ScriptLine(ScriptType.PowerShell, "Write-Host 'This is 2 powershell test'"),
96 | new ScriptLine(ScriptType.PowerShell, "Write-Host 'This is 3 powershell test'"),
97 | }
98 | };
99 |
100 | var jobResult = new LocalAppVeyor.Engine.Engine(_engineConfiguration, buildConfiguration).ExecuteJob(0);
101 |
102 | _outputterMock.Verify(outputter => outputter.Write(It.Is(m => m == "This is 1 bash test")), Times.Once);
103 | _outputterMock.Verify(outputter => outputter.Write(It.Is(m => m == "This is 1 powershell test")), Times.Once);
104 | _outputterMock.Verify(outputter => outputter.Write(It.Is(m => m == "This is 2 bash test")), Times.Once);
105 | _outputterMock.Verify(outputter => outputter.Write(It.Is(m => m == "This is 2 powershell test")), Times.Once);
106 | _outputterMock.Verify(outputter => outputter.Write(It.Is(m => m == "This is 3 powershell test")), Times.Once);
107 | _outputterMock.Verify(outputter => outputter.WriteError(It.IsAny()), Times.Never);
108 | Assert.True(jobResult.IsSuccessfulExecution);
109 | }
110 |
111 | [UnixOnlyFact]
112 | public void Linux_ShouldRunInitializationScriptPowershellBlockScript()
113 | {
114 | var buildConfiguration = new BuildConfiguration
115 | {
116 | InitializationScript =
117 | {
118 | new ScriptLine(ScriptType.PowerShell, @"
119 | Write-Host 'This is line one'
120 | Write-Host 'And this is line two'")
121 | }
122 | };
123 |
124 | var jobResult = new LocalAppVeyor.Engine.Engine(_engineConfiguration, buildConfiguration).ExecuteJob(0);
125 |
126 | _outputterMock.Verify(outputter => outputter.Write(It.Is(m => m == "This is line one")), Times.Once);
127 | _outputterMock.Verify(outputter => outputter.Write(It.Is(m => m == "And this is line two")), Times.Once);
128 | _outputterMock.Verify(outputter => outputter.WriteError(It.IsAny()), Times.Never);
129 | Assert.True(jobResult.IsSuccessfulExecution);
130 | }
131 | }
132 | }
--------------------------------------------------------------------------------
/tests/LocalAppVeyor.Engine.UnitTests/Engine/EngineTests.Windows.cs:
--------------------------------------------------------------------------------
1 | using LocalAppVeyor.Engine.Configuration;
2 | using LocalAppVeyor.Engine.UnitTests.TestUtilities;
3 | using Moq;
4 | using Xunit;
5 |
6 | namespace LocalAppVeyor.Engine.UnitTests.Engine
7 | {
8 | public partial class EngineTests
9 | {
10 | [WindowsOnlyFact]
11 | public void Windows_ShouldRunInitializationPowershellScript()
12 | {
13 | var buildConfiguration = new BuildConfiguration
14 | {
15 | InitializationScript =
16 | {
17 | new ScriptLine(ScriptType.PowerShell, "Write-Host 'This is a test'")
18 | }
19 | };
20 |
21 | var jobResult = new LocalAppVeyor.Engine.Engine(_engineConfiguration, buildConfiguration).ExecuteJob(0);
22 |
23 | _outputterMock.Verify(outputter => outputter.Write(It.Is(m => m == "This is a test")), Times.Once);
24 | Assert.True(jobResult.IsSuccessfulExecution);
25 | }
26 |
27 | [WindowsOnlyFact]
28 | public void Windows_ShouldRunInitializationPowershellScriptWithMultipleLines()
29 | {
30 | var buildConfiguration = new BuildConfiguration
31 | {
32 | InitializationScript =
33 | {
34 | new ScriptLine(ScriptType.PowerShell, "Write-Host 'This is a test - first line'"),
35 | new ScriptLine(ScriptType.PowerShell, "Write-Host 'This is a test - second line'")
36 | }
37 | };
38 |
39 | var jobResult = new LocalAppVeyor.Engine.Engine(_engineConfiguration, buildConfiguration).ExecuteJob(0);
40 |
41 | _outputterMock.Verify(outputter => outputter.Write(It.Is(m => m == "This is a test - first line")), Times.Once);
42 | _outputterMock.Verify(outputter => outputter.Write(It.Is(m => m == "This is a test - second line")), Times.Once);
43 | _outputterMock.Verify(outputter => outputter.WriteError(It.IsAny()), Times.Never);
44 | Assert.True(jobResult.IsSuccessfulExecution);
45 | }
46 |
47 | [WindowsOnlyFact]
48 | public void Windows_ShouldRunInitializationBatchScript()
49 | {
50 | var buildConfiguration = new BuildConfiguration
51 | {
52 | InitializationScript =
53 | {
54 | new ScriptLine(ScriptType.Batch, "echo This is a test")
55 | }
56 | };
57 |
58 | var jobResult = new LocalAppVeyor.Engine.Engine(_engineConfiguration, buildConfiguration).ExecuteJob(0);
59 |
60 | _outputterMock.Verify(outputter => outputter.Write(It.Is(m => m == "This is a test")), Times.Once);
61 | _outputterMock.Verify(outputter => outputter.WriteError(It.IsAny()), Times.Never);
62 | Assert.True(jobResult.IsSuccessfulExecution);
63 | }
64 |
65 | [WindowsOnlyFact]
66 | public void Windows_ShouldRunInitializationBatchScriptWithMultipleLines()
67 | {
68 | var buildConfiguration = new BuildConfiguration
69 | {
70 | InitializationScript =
71 | {
72 | new ScriptLine(ScriptType.Batch, "echo This is a test - first line"),
73 | new ScriptLine(ScriptType.Batch, "echo This is a test - second line")
74 | }
75 | };
76 |
77 | var jobResult = new LocalAppVeyor.Engine.Engine(_engineConfiguration, buildConfiguration).ExecuteJob(0);
78 |
79 | _outputterMock.Verify(outputter => outputter.Write(It.Is(m => m == "This is a test - first line")), Times.Once);
80 | _outputterMock.Verify(outputter => outputter.Write(It.Is(m => m == "This is a test - second line")), Times.Once);
81 | _outputterMock.Verify(outputter => outputter.WriteError(It.IsAny()), Times.Never);
82 | Assert.True(jobResult.IsSuccessfulExecution);
83 | }
84 |
85 | [WindowsOnlyFact]
86 | public void Windows_ShouldRunInitializationScriptWithMixedPowershellAndBatchScripts()
87 | {
88 | var buildConfiguration = new BuildConfiguration
89 | {
90 | InitializationScript =
91 | {
92 | new ScriptLine(ScriptType.Batch, "echo This is 1 batch test"),
93 | new ScriptLine(ScriptType.PowerShell, "Write-Host 'This is 1 powershell test'"),
94 | new ScriptLine(ScriptType.Batch, "echo This is 2 batch test"),
95 | new ScriptLine(ScriptType.PowerShell, "Write-Host 'This is 2 powershell test'"),
96 | new ScriptLine(ScriptType.PowerShell, "Write-Host 'This is 3 powershell test'"),
97 | }
98 | };
99 |
100 | var jobResult = new LocalAppVeyor.Engine.Engine(_engineConfiguration, buildConfiguration).ExecuteJob(0);
101 |
102 | _outputterMock.Verify(outputter => outputter.Write(It.Is(m => m == "This is 1 batch test")), Times.Once);
103 | _outputterMock.Verify(outputter => outputter.Write(It.Is(m => m == "This is 1 powershell test")), Times.Once);
104 | _outputterMock.Verify(outputter => outputter.Write(It.Is(m => m == "This is 2 batch test")), Times.Once);
105 | _outputterMock.Verify(outputter => outputter.Write(It.Is(m => m == "This is 2 powershell test")), Times.Once);
106 | _outputterMock.Verify(outputter => outputter.Write(It.Is(m => m == "This is 3 powershell test")), Times.Once);
107 | _outputterMock.Verify(outputter => outputter.WriteError(It.IsAny()), Times.Never);
108 | Assert.True(jobResult.IsSuccessfulExecution);
109 | }
110 |
111 | [WindowsOnlyFact]
112 | public void Windows_ShouldRunInitializationScriptPowershellBlockScript()
113 | {
114 | var buildConfiguration = new BuildConfiguration
115 | {
116 | InitializationScript =
117 | {
118 | new ScriptLine(ScriptType.PowerShell, @"
119 | Write-Host 'This is line one'
120 | Write-Host 'And this is line two'")
121 | }
122 | };
123 |
124 | var jobResult = new LocalAppVeyor.Engine.Engine(_engineConfiguration, buildConfiguration).ExecuteJob(0);
125 |
126 | _outputterMock.Verify(outputter => outputter.Write(It.Is(m => m == "This is line one")), Times.Once);
127 | _outputterMock.Verify(outputter => outputter.Write(It.Is(m => m == "And this is line two")), Times.Once);
128 | _outputterMock.Verify(outputter => outputter.WriteError(It.IsAny()), Times.Never);
129 | Assert.True(jobResult.IsSuccessfulExecution);
130 | }
131 | }
132 | }
--------------------------------------------------------------------------------
/tests/LocalAppVeyor.Engine.UnitTests/Engine/EngineTests.cs:
--------------------------------------------------------------------------------
1 | using System.IO.Abstractions;
2 | using LocalAppVeyor.Engine.Configuration;
3 | using Moq;
4 | using Xunit;
5 |
6 | namespace LocalAppVeyor.Engine.UnitTests.Engine
7 | {
8 | public partial class EngineTests
9 | {
10 | private readonly Mock _outputterMock = new Mock();
11 | private readonly EngineConfiguration _engineConfiguration;
12 | private readonly IFileSystem _fileSystem = new FileSystem();
13 |
14 | public EngineTests()
15 | {
16 | var currentDirectory =
17 | _fileSystem.Path.Combine(_fileSystem.Path.GetTempPath(), _fileSystem.Path.GetRandomFileName());
18 |
19 | if (!_fileSystem.Directory.Exists(currentDirectory))
20 | {
21 | _fileSystem.Directory.CreateDirectory(currentDirectory);
22 | }
23 |
24 | _fileSystem.Directory.SetCurrentDirectory(currentDirectory);
25 |
26 | _engineConfiguration = new EngineConfiguration(_fileSystem.Directory.GetCurrentDirectory(), _outputterMock.Object, _fileSystem);
27 | }
28 |
29 | [Fact]
30 | public void ShouldSkipSpecifiedBuildStep()
31 | {
32 | var buildConfiguration = new BuildConfiguration
33 | {
34 | InitializationScript =
35 | {
36 | new ScriptLine(ScriptType.PowerShell, "Write-Host 'This is init step'")
37 | },
38 | InstallScript =
39 | {
40 | new ScriptLine(ScriptType.PowerShell, "Write-Host 'This is install step'")
41 | },
42 | BuildScript =
43 | {
44 | new ScriptLine(ScriptType.PowerShell, "Write-Host 'This is build step'")
45 | },
46 | SkipSteps = new [] { "install" }
47 | };
48 |
49 | var jobResult = new LocalAppVeyor.Engine.Engine(_engineConfiguration, buildConfiguration).ExecuteJob(0);
50 |
51 | _outputterMock.Verify(outputter => outputter.Write(It.Is(m => m == "This is init step")), Times.Once);
52 | _outputterMock.Verify(outputter => outputter.Write(It.Is(m => m == "This is install step")), Times.Never);
53 | _outputterMock.Verify(outputter => outputter.Write(It.Is(m => m == "This is build step")), Times.Once);
54 | Assert.True(jobResult.IsSuccessfulExecution);
55 | }
56 |
57 | [Fact]
58 | public void ShouldSkipSpecifiedBuildSteps()
59 | {
60 | var buildConfiguration = new BuildConfiguration
61 | {
62 | InitializationScript =
63 | {
64 | new ScriptLine(ScriptType.PowerShell, "Write-Host 'This is init step'")
65 | },
66 | InstallScript =
67 | {
68 | new ScriptLine(ScriptType.PowerShell, "Write-Host 'This is install step'")
69 | },
70 | BuildScript =
71 | {
72 | new ScriptLine(ScriptType.PowerShell, "Write-Host 'This is build step'")
73 | },
74 | SkipSteps = new[] { "install", "build_script" }
75 | };
76 |
77 | var jobResult = new LocalAppVeyor.Engine.Engine(_engineConfiguration, buildConfiguration).ExecuteJob(0);
78 |
79 | _outputterMock.Verify(outputter => outputter.Write(It.Is(m => m == "This is init step")), Times.Once);
80 | _outputterMock.Verify(outputter => outputter.Write(It.Is(m => m == "This is install step")), Times.Never);
81 | _outputterMock.Verify(outputter => outputter.Write(It.Is(m => m == "This is build step")), Times.Never);
82 | Assert.True(jobResult.IsSuccessfulExecution);
83 | }
84 | }
85 | }
--------------------------------------------------------------------------------
/tests/LocalAppVeyor.Engine.UnitTests/LocalAppVeyor.Engine.UnitTests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net8.0
5 | true
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | PreserveNewest
27 |
28 |
29 |
30 |
31 |
--------------------------------------------------------------------------------
/tests/LocalAppVeyor.Engine.UnitTests/TestUtilities/UnixOnlyFactAttribute.cs:
--------------------------------------------------------------------------------
1 | using System.IO.Abstractions.TestingHelpers;
2 | using Xunit;
3 |
4 | namespace LocalAppVeyor.Engine.UnitTests.TestUtilities
5 | {
6 | internal sealed class UnixOnlyFactAttribute : FactAttribute
7 | {
8 | public UnixOnlyFactAttribute()
9 | {
10 | if (!MockUnixSupport.IsUnixPlatform())
11 | {
12 | Skip = "Unix-only test";
13 | }
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/tests/LocalAppVeyor.Engine.UnitTests/TestUtilities/WindowsOnlyFactAttribute.cs:
--------------------------------------------------------------------------------
1 | using System.IO.Abstractions.TestingHelpers;
2 | using Xunit;
3 |
4 | namespace LocalAppVeyor.Engine.UnitTests.TestUtilities
5 | {
6 | internal sealed class WindowsOnlyFactAttribute : FactAttribute
7 | {
8 | public WindowsOnlyFactAttribute()
9 | {
10 | if (!MockUnixSupport.IsWindowsPlatform())
11 | {
12 | Skip = "Windows-only test";
13 | }
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/tests/LocalAppVeyor.Engine.UnitTests/xunit.runner.json:
--------------------------------------------------------------------------------
1 | {
2 | "parallelizeAssembly": false,
3 | "parallelizeTestCollections": false
4 | }
--------------------------------------------------------------------------------