.csproj
files patchingEvery build goes through the following steps:
init
scriptscd
to clone folderinstall
scriptsAssemblyInfo
and .csproj
fileshosts
filesbefore_build
scriptsbuild_script
)after_build
scriptsbefore_test
scriptstest_script
)after_test
scriptsbefore_deploy
scriptsafter_deploy
scriptson_success
scriptson_failure
scriptson_finish
scriptsNote that you can forcibly terminate build with success from script with appveyor exit
cmd
command or Exit-AppVeyorBuild
PS
command. This can be done from any script except Finalize ones (on_success
, on_failure
and on_finish
).
If you need to can forcibly terminate build with failure you can run any command with non-zero exit code, for example exit 1
cmd
command or throw
PS
command.
All plans have 60 minutes quota per build job.
Project builds can be configured by either appveyor.yml
or on the user interface.
appveyor.yml
is a project configuration file in YAML format that should be placed in the root of your repository. At a minimum appveyor.yml
is just an empty file.
Each method has pros and cons. Via the User interface one can control every aspect of the build process without ever touching the repository. On the other hand, YAML may seem more sophisticated and familiar for those coming from a Linux platform. Another thing to consider is that when you fork/clone a project with its configuration stored in appveyor.yml
, you simply add a new project in AppVeyor referencing repo and you are good to go.
It’s worth noticing that both appveyor.yml
and UI configuration are mutually exclusive. It’s always either YAML or UI - the settings from each are not merged. If you have appveyor.yml
in your repo it will override all settings made on the UI unless explicitly disabled by Ignore appveyor.yml. There are few exceptions, which are:
appveyor.yml
. Variable values with the same names are getting overridden with values from UI.appveyor.yml
.appveyor.yml
.Some build configuration settings can be configured only with UI. They are not exposed with appveyor.yml
for security reasons. It is possible to have both appveyor.yml
and any of those UI setting. Those settings are in projects settings General tab. They are:
context
appveyor.yml
AppVeyor supports dot-file-style YAML named .appveyor.yml
as is. Another custom name like experimental.yml
is also possible, and can be specified in Custom configuration .yml file name setting.
It is possible to keep YAML file outside of repository. For that place YAML file as a plain text (Content-Type: text/plain) and anonymously accessible at some HTTP (or HTTPS) location. If using some web hosting, let file has .txt
extension for it to get correct content type. However better option is to use permalink to GitHub gist raw file, and take advantage of keeping file change history on GitHub. After that place URL to YAML file to Custom configuration .yml file name setting. Needless to say that secure variables should be used for secrets in YAML file.
Appveyor attempts to acquire appveyor.yml (or custom YAML name) from the repository before starting the build. This happens on central servers (not build workers) before any git clone happens. At that moment AppVeyor only needs the content of that one configuration file and so a full clone would be too expensive on central servers which are scheduling thousands of builds. Generic git does not have an option to check out an individual file, therefore we are using the APIs of source control providers who support this directly (like “Get contents” from GitHub). At the moment those supported are: GitHub (hosted and on-premise), Bitbucket (hosted and on-premise), GitLab (hosted and on-premise), Azure DevOps, Kiln and Gitea. If you are using any other git source control provider, you will need to use Alternative YAML file location described above.
While working with YAML there are few important points to be aware of:
Every time you push changes into repo or click the New build button AppVeyor starts a new build with an incremented build number.
You may use the build number for versioning purposes (assemblies version patching, naming artifacts, etc.) or just use it for reference.
AppVeyor uses the version value for naming builds. You can have “through” builds numbering in which major and minor parts of the version are changing and the build number is never reset or you can reset the build number for every new version. In any case, while the build number could be reset to any previously used value, the version must be unique across all builds.
You can specify version format in appveyor.yml
:
version: '1.0.{build}'
AppVeyor has a built-in task for setting values in AssemblyInfo
files during the build. This “patching” process is disabled by default for newly added projects.
You can enable patching on the General tab of project settings or in appveyor.yml
:
assembly_info:
patch: true
file: MyVersion.cs
assembly_version: '{version}'
assembly_file_version: '{version}'
assembly_informational_version: '{version}-rc1'
Note that specific attribute like AssemblyInformationalVersion
should exist in AssemblyInfo
file to be patched.
Variables {version}
, {build}
, {branch}
are shortcuts implemented specifically to use with patching. Use them exactly as described (in {}
curly brackets). You can use all other environment variables substitution in file name and version formats using standard environment variable notation, for example:
package_version: $(APPVEYOR_BUILD_VERSION)
.csproj
files patchingLike with AssemblyInfo patching, AppVeyor can patch .NET Core .csproj
files. This also applies to .NET Standard and ASP.NET Core .csproj
files, which has the same new structure, different from classic .NET .csproj
files. The main practical reason to patch those files is to set version to be used by nuget packaging of .NET Core and .NET Standard libraries (created in Visual Studio 2017, and probably later versions).
You can enable patching on the General tab of project settings or in appveyor.yml
:
dotnet_csproj:
patch: true
file: '**\*.csproj'
version: '{version}'
package_version: '{version}'
assembly_version: '{version}'
file_version: '{version}'
informational_version: '{version}'
This section can also be used to patch *.props
, .fsproj
and .xml
files side-by-side with .csproj
:
dotnet_csproj:
patch: true
file: '**\*.csproj;**\*.props;**\*.fsproj;**\*.xml'
...
Note that specific attribute like PackageVersion
should exist in .csproj
file to be patched. Note that the xmlns must not be defined or else the file won’t be processed.
Variables {version}
, {build}
, {branch}
are shortcuts implemented specifically to use with patching. Use them exactly as described (in {}
curly brackets). You can use all other environment variables substitution in file name and version formats using standard environment variable notation, for example:
package_version: $(APPVEYOR_BUILD_VERSION)
The new .csproj
format along with the dotnet CLI have added improved semantic versioning support for version suffixes.
With the command line:
dotnet build My.sln --version-suffix "preview"
and .csproj
containing:
<PropertyGroup>
<VersionPrefix>1.0.0</VersionPrefix>
</PropertyGroup>
enables the output of a project with the semantic version of 1.0.0-preview
while keeping the assembly version, file version, etc. in the correct format.
You can patch <VersionPrefix>
element with the following configuration in YAML file:
dotnet_csproj:
patch: true
file: '**\*.csproj'
version_prefix: '{version}'
The format of the default directory on the build machine for cloning a repository is c:\projects\<project-slug>
on Windows and /home/appveyor/projects/
on Ubuntu.
If required by your project (say, if absolute paths are used to reference its parts) you can change the clone directory path on the Environment tab of project settings or in appveyor.yml
:
clone_folder: c:\projects\myproject
Immediately after cloning the repo on the build worker AppVeyor sets standard environment variables.
Custom environment variables can be set on the Environment tab of project settings or in appveyor.yml
:
environment:
variable1: value
variable2: value
AppVeyor allows you to choose between Command scripting and PowerShell scripting. If you are using the Command interpreter and batch files then prefix your script with cmd:
as shown below. Do not use command:
.
test_script:
- cmd: ECHO this is batch
If you are using the PowerShell then prefix your script with ps:
as shown below. Do not use powershell:
.
test_script:
- ps: Write-Host "This is PowerShell"
Important: If you are using a PowerShell script file like so:
- ps: build.ps1
It is important to be aware that the default behaviour in PowerShell is to continue on non-terminating errors. An example of a non-terminating error is when an external command returns a non-zero exit code. For example, you could be calling dotnet.exe
in your script. If dotnet.exe
fails, the script will continue and the build will still be reported as successful. To ensure the build fails when the script produces non-terminating errors, add the following line at the top of your script:
$ErrorActionPreference = "Stop";
CMD:
set MY_VARIABLE=value
Once a variable is set for a batch file you access it by %MY_VARIABLE%
.
PowerShell:
$env:MY_VARIABLE="value"
Once a PowerShell variable is set you access it by $env:MY_VARIABLE
. Do not use PowerShell syntax of $MY_VARIABLE
.
When you work with OSS projects and you’d like to hide some sensitive data from everyone’s eyes you can use secure variables in appveyor.yml
.
AppVeyor generates a unique encryption key for every account. To encrypt variable values go to Account → Encrypt YAML page.
To use encrypted variable in appveyor.yml
:
environment:
my_variable:
secure: <encrypt_value>
“Secure” variables means you can safely put them into appveyor.yml
that is visible to others. Other than that they are just regular environment variables in a build session that could be easily displayed in a build log by simple Get-ChildItem env:
.
However, secure variables are not decoded during Pull Request builds which prevents someone from submitting PR with malicious build script displaying those variables. In more controlled environment through with a trusted team and private GitHub repositories there is an option on General tab of project settings to allow secure variables for PRs.
The build phase can operate in multiple modes. By default, AppVeyor works in MSBuild
mode. This causes AppVeyor to look for a Visual Studio project or solution file in the root directory of your project, and use that to do the build. The behavior of an MSBuild
mode build can be controlled by the build:
entry in appveyor.yml
.
The alternative to MSBuild
mode is Script
mode. This mode lets you do the build by running arbitrary scripted actions instead of building a Visual Studio project. Script
mode can be activated in appveyor.yml
by adding a build_script:
section instead of a build:
section. Alternatively, you can set build: off
to disable MSBuild
mode without providing an alternate custom build script.
If you are using AppVeyor UI (not YAML) configuration, all three options (MSBuild
, Script
and Off
) are available in Build
settings section.
There are a lot of places in configuration where you can inject your custom logic like “install” scripts, “before build”, “after tests”, “deploy” scripts, etc.
Every script could be authored either as a batch or PowerShell snippet.
When you set Cmd script on the UI its body will be split into lines and executed as separate commands with an exit code check after each line. For example, consider the following “install” script:
If exit code of the first command (gem update --system
) is different from a 0 script execution will be terminated and entire build will return as failed.
When PS (PowerShell) or PS CORE (PowerShell Core) is selected the entire body is treated as a single (PowerShell or PowerShell Core) script, so you can use control flow logic inside it. For example:
PowerShell script is considered successful if it finishes without exception.
If you need to check execution results of commands in the middle of PowerShell script you can verify $LastExitCode
after calling a command and terminate earlier, for example:
vstest.console myassembly1.dll
if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) }
vstest.console myassembly2.dll
if ($LastExitCode -ne 0) { $host.SetShouldExit($LastExitCode) }
There are a few ways of configuring scripts in appveyor.yml
.
For example, this is the same configuration as shown on the first screenshot:
install:
- gem update --system
- gem install rake
By default, script line is treated as batch command, but you can specify script engine explicitly:
before_build:
- cmd: ECHO this is batch
- ps: Write-Host "This is PowerShell"
- pwsh: Write-Host "This is PowerShell Core"
To add multi-line PowerShell script covering an entire event:
- ps: |
should be at least 4 spaces longeron_success:
- ps: |
if ($true)
{
Write-Host "Success"
}
This syntax is also valid:
on_success:
- ps: >-
if ($true)
{
Write-Host "Success"
}
Every build runs on a pristine virtual machine that is not shared with any other builds. VM state is not preserved between builds.
You can install additional software on the build machine using Chocolatey, Web Platform Installer (Web PI) or PowerShell.
install: <script>
or
install:
- cmd: rd temp
- ps: Write-Host “Hello!”
install:
- cinst <package>
install:
- ps: (new-object net.webclient).DownloadFile('https://mysite.com/mypackage.msi', 'mypackage.msi')
- ps: msiexec /i mypackage.msi /quiet /qn /norestart /log install.log PROPERTY1=value1 PROPERTY2=value2
install:
- WebpiCmd /Install /Products:<Title or ID>
hosts:
localhost: 127.0.0.1
db.server.com: 127.0.0.2
Every AppVeyor build consists of one or more jobs. A build is considered successful if all jobs are successful. A build immediately fails when any of its jobs fail.
AppVeyor enables easy testing against multiple combinations of platforms, configurations and environments. Specify which operating systems, build configurations and platforms you would like to include into the build matrix and AppVeyor will create a build with multiple jobs for all possible combinations.
Build matrix supports the following dimensions:
For example, selecting x86, Any CPU for Platform and Debug, Release for Configuration yields the build with the following jobs:
To configure build matrix in appveyor.yml
:
environment:
# these variables are common to all jobs
common_var1: value1
common_var2: value2
matrix:
# first group
- db: mysql
provider: mysql
# second group
- db: mssql
provider: mssql
password:
secure: DHEU39J6X9VD376==
platform:
- x86
- Any CPU
configuration:
- Debug
- Release
matrix:
fast_finish: true
By default AppVeyor runs all build jobs. If at least one job has failed the entire build is marked as failed. Sometimes, you want the build fail immediately once one of the job fails. To enable fast fail strategy add fast_finish
setting into appveyor.yml
:
matrix:
fast_finish: true
You can configure AppVeyor to allow certain build matrix rows to fail and still continue with the rest of the build. The result of these failing jobs is not counted towards build status. This may be useful if you are experimenting with running tests on the latest version of a platform or framework, e.g. Node.js 0.11.
To allow failing jobs add matrix.allow_failures
section into appveyor.yml
(the feature is not available on UI):
matrix:
allow_failures:
- <condition>: <value>
<condition>
can be os
, image
, configuration
, platform
, test_category
or the name of environment variable.
For example, to allow job failing on Node.js 0.11 (see Node.js instructions):
matrix:
allow_failures:
- nodejs_version: 0.11
The following example allows failure for platform=x86, configuration=Debug
and platform=x64, configuration=Release
jobs:
matrix:
allow_failures:
- platform: x86
configuration: Debug
- platform: x64
configuration: Release
The matrix is already optimized for fast failing. The logic is as follows:
See complete appveyor.yml reference for full syntax.
It is possible to exclude configuration from the matrix. Syntax is the same as for allow_failures
(this feature is also YAML-only and not available in UI currently).
Please consider the following example:
configuration:
- Debug
- Release
environment:
matrix:
- MY_VAR: A
- MY_VAR: B
matrix:
exclude:
- configuration: Debug
MY_VAR: B
Here we have 2 matrix dimensions: configurations and variables and each has 2 values. Therefore by default it should be 2X2=4 build jobs. But if combination of configuration: Debug
and MY_VAR: B
is not needed, we can exclude it. In comparison with allow_failures
build will not be even started for this combination.
We still recommend use allow_failures
for “unstable” cases which should be built but should not affect build results, and use exclude
for cases where build should be completely skipped.
By default, all build matrix jobs share the same configuration. However there are cases when build scenario should be different for each matrix job. This become especially handful for multi-platform builds. For example, build should run in MSBuild
mode for all build worker images, but run a build script for Ubuntu
one. This can be done with the following YAML configuration:
image:
- Visual Studio 2017
- Ubuntu
build:
project: src
publish_wap: true
verbosity: minimal
for:
-
matrix:
only:
- image: Ubuntu
build_script:
- echo Ubuntu build script
Also except
syntax is supported:
configuration:
- Debug
- Release
platform:
- x86
- Any CPU
test_script:
- echo common script
for:
-
matrix:
except:
- configuration: Debug
platform: x86
test_script:
- echo matrix except script
YAML syntax to describe specific matrix job is the same as allow_failures
and exclude
, but it should be placed under the for
construct, similar to sharing common configuration between branches.
Settings to be ignored:
Here is a list of settings which will be ignored if placed under for.matrix.only/.except
construct:
version
allow_failures
, exclude
and fast_finish
for
constructpull_requests.do_not_increment_build_number
max_jobs
nuget.account_feed.project_feed.disable_publish_on_pr
nuget.account_feed.project_feed.publish_wap_octopus
Therefore this YAML will be executed, but no special configuration for any matrix job will be formed:
configuration:
- Debug
- Release
for:
-
version: 22.33.{build}
matrix:
only:
- configuration: Debug
fast_finish: true
allow_failures:
- nodejs_version: 0.11
pull_requests:
do_not_increment_build_number: true
Settings to be merged:
With specializing matrix job configuration you can also conditionally skip specific matrix jobs. It is possible because branches white- and blacklisting, tags filtering with skip_tags: true
or skip_non_tags: true
, and all commit filtering settings are valid in for.matrix.only/.except
construct. Examples:
Skip build job with configuration Config1
on commits to master
:
configuration:
- Config1
- Config2
- Config3
for:
-
matrix:
only:
- configuration: Config1
branches:
except:
- master
Skip build job with image Ubuntu
when all changes files are in docs
folder:
image:
- Visual Studio 2017
- Ubuntu
for:
-
matrix:
only:
- image: Ubuntu
skip_commits:
files:
- docs/*
“Rolling builds” are great for very active OSS projects with lengthy queue. Whenever you do a new commit to the same branch OR pull request all current queued/running builds for that branch or PR are cancelled and the new one is queued. Other words, rolling builds make sure that only the most recent commit is built.
For example, you do commit A
to master
branch - it’s being queued and then run. Then you do B
commit to master
branch while A
is running - build of A
will be cancelled immediately and B
is queued. If you do another C
commit while B
is queued it will be cancelled and C
queued.
(Note that “Rolling builds” can only be enabled in the settings UI of a project and not via appveyor.yml
and that the presence of appveyor.yml
does not disable this UI setting.)
AppVeyor uses NCrontab library to calculate a build schedule.
Schedule hour values should be UTC.
External links:
Sometimes you may wonder why your build is not being run immediately or remains in Queued
state longer than usual.
This is how builds scheduling works.
First, the maximum number of build jobs running simultaneously is defined by account plan. For example, for Free, Basic and Pro it’s 1 concurrent job, for Premium it’s 2. Also, there are a lot of additional plans with greater number of concurrent build jobs available on billing page.
It is important to understand that concurrent build jobs limit is account level setting. So, if some project consumes all concurrent build jobs defined in plan, builds for other projects in the same account wait in queue. To prevent specific project from consuming number of jobs higher than specific number, please use Max jobs (UI) or max_jobs
(YAML) setting.
If there are no running builds under your account, you might see the build is in Starting
state for longer than usual time. It can happen that VM assigned to a build is still being provisioned. We maintain our own Hyper-V based infrastructure with pre-heated VMs, and if a build is assigned to pre-provisioned machine it should start almost instantly. However, if own infrastructure is under heavy load or we do some servicing work, then new builds start on backup cloud which we run on Google Cloud Engine. Also some specific build images as Visual Studio 2013
and different Ubuntu
images run mostly on Google Cloud Engine. In this case VM is being created from scratch. Usually, it should take no longer than 3-4 minutes.
When a lot of commits are made by your team during the day there are multiple builds awaiting in “queued” state. It is possible to move up and down certain builds in a queue by changing their projects’ priority. Build priority setting is located on General tab of project settings. When priority is not set builds are not prioritized. When priority is set builds in a queue are in ascending order. The higher number - the lower priority; the highest priority is 1. Builds with priority has precedence over those ones without it.
As a real-world example, suppose we have three projects: A, B and C. We want builds of project A always come first, then builds of project B and the rest in FIFO order. We set project A build priority to 1 (the highest priority) and project B priority to 2. Then we did commits in the following order (provided our account allows only one concurrent job and first build of project C is still running):
C (no priority) - 1 - running
C (no priority) - 2 - queued
B (priority 2) - 1 - queued
A (priority 1) - 1 - queued
B (priority 2) - 2 - queued
Build queue will look as below:
C (no priority) - 1 - running
A (priority 1) - 1 - queued <-- this build comes next
B (priority 2) - 1 - queued
B (priority 2) - 2 - queued
C (no priority) - 2 - queued
Default timeout for build job is 60 minutes. If you need to decrease it, you can set Build timeout, minutes to the smaller value. Note that decrease to less than 5 minutes will not apply. If you run your builds on private build cloud, you can increase build timeout, otherwise increase will not apply.