Skip to content

Commit

Permalink
Add CI skip command check template (#74)
Browse files Browse the repository at this point in the history
* Add check-skip template

Signed-off-by: Conor MacBride <conor@macbride.me>

* Add check-skip documentation

Signed-off-by: Conor MacBride <conor@macbride.me>

* Set as executable

Signed-off-by: Conor MacBride <conor@macbride.me>

* Bash displayName to name

Signed-off-by: Conor MacBride <conor@macbride.me>

* Only check on PR trigger

Signed-off-by: Conor MacBride <conor@macbride.me>
  • Loading branch information
ConorMacBride committed Nov 11, 2021
1 parent b273771 commit 14d3f7d
Show file tree
Hide file tree
Showing 5 changed files with 245 additions and 2 deletions.
109 changes: 109 additions & 0 deletions check-skip.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#!/usr/bin/env bash
SKIP_VAR_NAME="found" # variable name to set in Azure job

help () {
echo "Search for a skip command in the commit message and set Azure variable."
echo
echo "Usage: check-skip.sh [merge_commit_message]"
echo
echo "If merge_commit_message is not provided as an argument, its value"
echo "will be taken from a COMMIT_MESSAGE environment variable."
echo "An alternative list of skip commands can be specified in a"
echo "SKIP_COMMANDS environment variable as space separated commands"
echo "with commands containing spaces inside escaped double quotes."
echo
}

require_argument () {
if [[ -z $2 ]]; then
echo "Argument '$1' must be given." 1>&2
exit 1
fi
}

get_skip_commands () {
if [[ -n $SKIP_COMMANDS ]]; then
# "\"[skip ci]\" \"skip tests\" pass" -> "[skip ci]" "skip tests" "pass"
if ! declare -a -g "SKIP_COMMANDS=($SKIP_COMMANDS)"; then
echo "Bash version 4.2 or later required for custom skip commands." 1>&2
exit 1
fi
printf "Using custom skip commands:"
else
SKIP_COMMANDS=(
"[skip ci]" "[ci skip]"
"skip-checks: true" "skip-checks:true"
"[skip azurepipelines]" "[azurepipelines skip]"
"[skip azpipelines]" "[azpipelines skip]"
"[skip azp]" "[azp skip]"
"***NO_CI***"
)
# https://docs.microsoft.com/en-us/azure/devops/pipelines/scripts/git-commands
printf "Using default skip commands:"
fi
for c in "${SKIP_COMMANDS[@]}"; do
printf " '%s'" "$c"
done
printf "\n"
}

get_merge_commit_message () {
if [[ -n "$1" ]]; then
MERGE_MSG=$1
elif [[ -n "$COMMIT_MESSAGE" ]]; then
MERGE_MSG=$COMMIT_MESSAGE
else
help
exit 1
fi
echo "Merge commit being tested: '$MERGE_MSG'"
}

get_commit_hash () {
require_argument merge_commit_message "$1"
if ! [[ $1 =~ ^Merge[[:space:]][0-9a-fA-F]+[[:space:]]into[[:space:]][0-9a-fA-F]+$ ]]; then
echo "Expected commit message to be of form 'Merge HEX into HEX'." 1>&2
echo "Please open an issue: https://github.com/OpenAstronomy/azure-pipelines-templates/issues" 1>&2
exit 1
fi
COMMIT_HASH=$(echo "$1" | awk '{print $2}')
echo "Latest commit hash in PR branch: '$COMMIT_HASH'"
}

get_commit_message () {
require_argument commit_hash "$1"
MSG=$(git log --format=%B -n 1 "$1")
if [ $? -ne 0 ]; then
echo "Error running: git log --format=%B -n 1 $1" 1>&2
exit 1
fi
MSG=$(echo "$MSG" | head -1)
echo "Message being searched for skip commands: '$MSG'"
}

set_skip_var () {
if ! [[ "true false" =~ (^|[[:space:]])$1($|[[:space:]]) ]]; then
echo "Argument must be 'true' or 'false', got '$1'." 1>&2
exit 1
fi
echo "##vso[task.setvariable variable=$SKIP_VAR_NAME;isOutput=true]$1"
}

search_for_skip () {
require_argument message "$1"
for c in "${SKIP_COMMANDS[@]}"; do
if [[ "$1" =~ "$c" ]]; then
echo "Found command '$c' in message."
set_skip_var true
exit 0
fi
done
echo "No skips commands found in message."
set_skip_var false
}

get_merge_commit_message "$1"
get_commit_hash "$MERGE_MSG"
get_commit_message "$COMMIT_HASH"
get_skip_commands
search_for_skip "$MSG"
23 changes: 23 additions & 0 deletions check-skip.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
parameters:
- name: commands
type: string
default: ''

jobs:
- job: check_skip
steps:
- ${{ if eq(variables['Build.Reason'], 'PullRequest') }}:
- checkout: self
path: s/self
- checkout: OpenAstronomy
path: s/azure-pipelines-templates
- bash: $(Pipeline.Workspace)/s/azure-pipelines-templates/check-skip.sh
workingDirectory: $(Pipeline.Workspace)/s/self
name: search
env:
COMMIT_MESSAGE: $(Build.SourceVersionMessage)
${{ if parameters.commands }}:
SKIP_COMMANDS: ${{ parameters.commands }}
- ${{ if ne(variables['Build.Reason'], 'PullRequest') }}:
- bash: echo "##vso[task.setvariable variable=found;isOutput=true]false"
name: search
108 changes: 108 additions & 0 deletions docs/check_skip.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@

CI skip command check template
==============================

This template provides a job which parses the commit message of the pull
request, and if it contains a CI skip command such as ``[skip ci]`` it sets
a variable in the Azure Pipelines run. This variable can be used as a
condition on subsequent stages of the run, such that using a skip command
reduces the amount of building and testing.

For a CI skip command to be recognised for a job, the latest commit to the
pull request branch that triggered the Azure Pipelines run must have a
commit message that contains a recognised CI skip command in its first line.
Recognised commands include ``[skip ci]`` and ``[ci skip]``.
See :ref:`custom-skip-commands` for more details.

Basic setup
-----------

Firstly, ensure you load the templates under the ``OpenAstronomy``
namespace as described in :doc:`common`.
The following code shows how to place the job within a stage.

.. code:: yaml
stages:
- stage: StageOne
jobs:
- template: check-skip.yml@OpenAstronomy
The template must be called as a job within a stage prior to
the stages you want to conditionally skip.

Applying conditions to pipeline stages
--------------------------------------

The job provided by this template creates a variable in the Azure Pipelines run.
This variable is accessed at
``dependencies.STAGE_NAME.outputs['check_skip.search.found']``
where ``STAGE_NAME`` should be replaced with the name of the stage the template
was used within.

It will have a string value of either ``'true'`` or ``'false'``. It will be
``'true'`` if a skip command was found in the commit message and ``'false'``
otherwise.

This variable can be used to apply a condition to subsequent stages of the
run, for example,
``and(succeeded(), ne(dependencies.Setup.outputs['check_skip.search.found'], 'true'))``.

Example
-------

The following code provides an example of how to configure the stages
section of your ``azure-pipelines.yml`` file using this template.
Note that you will also need to make sure you first load the templates as
described in :doc:`common`.

.. code:: yaml
stages:
- stage: StageOneTests
displayName: Basic Tests
jobs:
- template: check-skip.yml@OpenAstronomy
- template: run-tox-env.yml@OpenAstronomy
envs:
- linux: py39
- stage: StageTwoTests
displayName: Detailed Tests
condition: and(succeeded(), ne(dependencies.StageOneTests.outputs['check_skip.search.found'], 'true'))
jobs:
- template: run-tox-env.yml@OpenAstronomy
envs:
- macos: py39
- windows: py39
In this example, the *Basic Tests* stage will always run, however, if a skip
command is in the commit message the *Detailed Tests* stage will not run.
As this template is independent of the other OpenAstronomy templates,
the stages conditions are applied to do not need to run jobs defined
using ``run-tox-env.yml``.

.. _custom-skip-commands:

Custom skip commands
--------------------

By default, the list of recognised skip commands are taken from the `Azure Pipelines documentation
<https://docs.microsoft.com/en-us/azure/devops/pipelines/scripts/git-commands?view=azure-devops&tabs=yaml#how-do-i-avoid-triggering-a-ci-build-when-the-script-pushes>`__.
This list includes ``[skip ci]`` and ``[ci skip]`` among others.

This default list can be replaced as shown in the following code.

.. code:: yaml
stages:
- stage: StageOne
jobs:
- template: check-skip.yml@OpenAstronomy
commands: '"[skip ci]" "[ci skip]" noci'
This will configure the check to only recognise ``[skip ci]``, ``[ci skip]``
and ``noci`` as valid skip commands.
The value of ``commands`` must be a string of space separated skip commands,
with commands containing spaces inside double quotes.
Bash version 4.2 or above is required if specifying custom skip commands.
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
# -- Project information -----------------------------------------------------

project = 'OpenAstronomy Azure Pipeline Templates'
copyright = '2020, OpenAstronomy developers'
copyright = '2021, OpenAstronomy developers'
author = 'OpenAstronomy developers'


Expand Down
5 changes: 4 additions & 1 deletion docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@
OpenAstronomy Azure Pipeline Templates
======================================

This repository contains set of templates for `Azure Pipelines
This repository contains a set of templates for `Azure Pipelines
<https://azure.microsoft.com/en-gb/services/devops/pipelines/>`_ that helps
simplify configuration in individual packages. At this time, there are two main
templates available - one to make it easy to map `tox
<https://tox.readthedocs.org>`__ environments to builds on Azure Pipelines, and
one to automate the process of releasing Python packages.
An additional template is included for checking if a CI skip command is present
in the commit message.

.. toctree::
:maxdepth: 1
Expand All @@ -17,6 +19,7 @@ one to automate the process of releasing Python packages.
common
run_tox_env
publish
check_skip

The templates in this repository were inspired and adapted from `tox's
<https://github.com/tox-dev/azure-pipelines-template>`__ and `SunPy's templates
Expand Down

0 comments on commit 14d3f7d

Please sign in to comment.