All actions in this repository follow standardized patterns for consistency and maintainability. Key standardizations include:
All actions use consistent environment variables with safe fallback patterns (|| '' instead of false or null):
PULL_REQUEST: Pull request number or empty stringPULL_REQUEST_SHA: Pull request base SHA or empty stringDEFAULT_BRANCH: Repository default branch name
Actions use $GITHUB_EVENT_NAME environment variable for reliable pull request detection instead of legacy string comparison patterns.
The Bash scripts used by the GitHub actions are tested with the ShellSpec framework
Use mise to install the dependencies:
mise installkcov must be manually installed to generate code coverage reports.
sudo apt-get install kcov./run_shell_tests.shExample of output:
========================
Test ci-common-scripts
========================
Running: /usr/bin/bash [bash 5.1.16(1)-release]
............................
Finished in 1.87 seconds (user 0.95 seconds, sys 0.96 seconds)
28 examples, 0 failures
Code covered: 90.09%, Executed lines: 100, Instrumented lines: 111
The coverage report is available here: coverage/index.html
Note: you can also run directly a
.specfile in order to run only subset of the tests.
Use Dump in the spec file to print the variables and their values during the test execution.
Use ShellSpec options like -x, -X to run the tests in debug mode.
Use ShellSpec options like -q (--quick), -n (--next-failure)... to iterate on the tests and debug them.
# List all examples in the spec files, with IDs
shellspec --kcov --list examples
# List all examples in the spec files, with line numbers
shellspec --kcov --list examples:lineno├── .github
│ └── workflows
│ └── test-<action_name>.yml # Action test workflow
├── <action_name> # GitHub action directory
│ ├── action.yml
│ └── *.sh # Shell scripts to be tested
├── .shellspec # ShellSpec configuration file
├── run_shell_tests.sh
└── spec # ShellSpec unit tests
To add a new GitHub action, create a new directory under the root of the repository with the name of the action.
Inside this directory, create an action.yml file that defines the action and any necessary scripts.
Add a section in the README.md file to document the new action, including its usage and parameters.
Add the action folder to the .shellspec configuration file to include it in the tests.
Also add the action to the sonar-project.properties file to include its coverage in the SonarQube analysis.
Add a new spec file in the spec directory for the action. Use the existing tests as examples for writing your own tests.
Only create an Action test workflow when it completes the ShellSpec tests, and does not require complex setup or external dependencies. The actions are used in the dummy repositories, so they are tested for real in the CI/CD environment.
Focus on targeting 100% code coverage for the scripts in the action.
Do not test the underlying commands or tools used in the scripts, such as git, curl, etc. Instead, mock their outputs if necessary.
Do not test the missing parameters in the Shell scripts: this is handled by the Bash parameter expansion.
Additional tests will be added to cover specific scenarios or edge cases, when fixing bugs (test-driven development).
- name: Add a name to the step ONLY IF RELEVANT
uses: ...
if: ...
id: underscore_id_only_if_neededDo not name obvious steps, for instance: checkout, vault, etc. But name a step when it deserves a description.
Set an ID only if it is used.
When using local actions in an action, some fixes are necessary to ensure that the action works correctly both in the standard usage and in a container (see BUILD-9094).
Example of action build-xyz calling local action config-xyz:
runs:
using: composite
steps:
- name: Set local action paths
id: set-path
shell: bash
run: |
echo "::group::Fix for using local actions"
echo "GITHUB_ACTION_PATH=$GITHUB_ACTION_PATH" # For debugging purposes
echo "github.action_path=${{ github.action_path }}" # For debugging purposes
ACTION_PATH_BUILD_XYZ="${{ github.action_path }}" # For local usage instead of GITHUB_ACTION_PATH
echo "ACTION_PATH_BUILD_XYZ=$ACTION_PATH_BUILD_XYZ" # For debugging purposes
echo "ACTION_PATH_BUILD_XYZ=$ACTION_PATH_BUILD_XYZ" >> "$GITHUB_ENV" # For local usage instead of GITHUB_ACTION_PATH
host_actions_root="$(dirname "$ACTION_PATH_BUILD_XYZ")" # Effective path to the local actions checkout on the host
echo "host_actions_root=$host_actions_root" >> "$GITHUB_OUTPUT"
mkdir -p ".actions"
ln -sf "$host_actions_root/config-xyz" .actions/config-xyz # For local reference
ln -sf "$host_actions_root/shared" .actions/shared # For use in the Shell scripts
ls -la .actions/* # For debugging purposes
echo "::endgroup::"
- uses: ./.actions/config-xyz # Local action reference
with:
host-actions-root: ${{ steps.set-path.outputs.host_actions_root }} # Only needed if the child action will use local references
- shell: bash
run: $ACTION_PATH_BUILD_XYZ/build.sh # Use ACTION_PATH_BUILD_XYZ instead of GITHUB_ACTION_PATH#!/bin/bash
# Example build.sh loading the common functions
set -euo pipefail
# shellcheck source=SCRIPTDIR/../shared/common-functions.sh
source "$(dirname "${BASH_SOURCE[0]}")/../shared/common-functions.sh"In the case of a child action that also uses local references, host-actions-root input and similar fixes are necessary.
inputs:
host-actions-root:
description: Path to the actions folder on the host (used when called from another local action)
default: ''
runs:
using: composite
steps:
- name: Set local action paths
id: set-path
shell: bash
run: |
echo "::group::Fix for using local actions"
echo "GITHUB_ACTION_PATH=$GITHUB_ACTION_PATH"
echo "github.action_path=${{ github.action_path }}"
ACTION_PATH_CONFIG_XYZ="${{ github.action_path }}"
host_actions_root="${{ inputs.host-actions-root }}"
if [ -z "$host_actions_root" ]; then
host_actions_root="$(dirname "$ACTION_PATH_CONFIG_XYZ")"
else
ACTION_PATH_CONFIG_XYZ="$host_actions_root/config-xyz"
fi
echo "ACTION_PATH_CONFIG_XYZ=$ACTION_PATH_CONFIG_XYZ"
echo "ACTION_PATH_CONFIG_XYZ=$ACTION_PATH_CONFIG_XYZ" >> "$GITHUB_ENV"
echo "host_actions_root=$host_actions_root" >> "$GITHUB_OUTPUT"
mkdir -p ".actions"
ln -sf "$host_actions_root/another-action" .actions/another-action
ln -sf "$host_actions_root/shared" .actions/shared
ls -la .actions/*
echo "::endgroup::"
- uses: ./.actions/another-action
- shell: bash
run: $ACTION_PATH_CONFIG_XYZ/config.shThis repository includes a comprehensive migration guide at cirrus-github-migration.md that
documents the process of migrating Cirrus CI pipelines to GitHub Actions. This guide is accessible to everyone in the company
using Cursor through the @Doc command. The purpose of the document is to provide a context to Cursor and similar AI tools
to aid with migration
When working on this repository or migrating eng-xp repositories, follow these best practices to use AI and keep the doc up-to-date.
-
Multi-repository setup: If working on a different repository, add the
ci-github-actionsrepository to your workspace viaFile → Add Folder to Workspacein Cursor to access the documentation directly. -
Reference documentation: Directly attach the migration guide to your AI chat conversations rather than using the
@Docsyntax. -
Keep documentation current: After completing your work, ask the AI to review and update the migration guide based on any new patterns, edge cases, or improvements discovered during development. Include these documentation updates in your pull request to maintain accuracy for future migrations.