Files
swift-nio/scripts/generate_matrix.sh
Si Beaumont d36223d82c Add support for --security-opt docker flags in custom CI jobs (#3580)
### Motivation:

A recent PR (#3574) added support for customising the container
environment used for the matrix job for users of the reusable workflow.
It supported a custom Dockerfile and controlling `--cap-add` options to
the `docker run` command. Users who require this fine-grained control
with `--cap-add` also will benefit from being able to control the
`--security-opt` of the `docker run` command.

### Modifications:

- Add support for `--security-opt` docker flags in custom CI jobs

### Result:

Users of the reusable matrix CI workflow can control the
`--security-opt` for the container.
2026-04-21 15:38:44 +02:00

355 lines
18 KiB
Bash
Executable File

#!/bin/bash
##===----------------------------------------------------------------------===##
##
## This source file is part of the SwiftNIO open source project
##
## Copyright (c) 2024 Apple Inc. and the SwiftNIO project authors
## Licensed under Apache License v2.0
##
## See LICENSE.txt for license information
## See CONTRIBUTORS.txt for the list of SwiftNIO project authors
##
## SPDX-License-Identifier: Apache-2.0
##
##===----------------------------------------------------------------------===##
set -uo pipefail
log() { printf -- "** %s\n" "$*" >&2; }
error() { printf -- "** ERROR: %s\n" "$*" >&2; }
fatal() { error "$@"; exit 1; }
# Binary dependencies
JQ_BIN="${JQ_BIN:-$(which jq 2> /dev/null)}"; test -n "$JQ_BIN" || fatal "JQ_BIN unset and no jq on PATH"
SED_BIN="${SED_BIN:-$(which sed 2> /dev/null)}"; test -n "$SED_BIN" || fatal "SED_BIN unset and no sed on PATH"
HEAD_BIN="${HEAD_BIN:-$(which head 2> /dev/null)}"; test -n "$HEAD_BIN" || fatal "HEAD_BIN unset and no head on PATH"
# Parameters
find_subdirectory_manifests_enabled="${FIND_SUBDIRECTORY_MANIFESTS_ENABLED:=false}"
linux_command="${MATRIX_LINUX_COMMAND:-}" # required if any Linux pipeline is enabled
linux_setup_command="${MATRIX_LINUX_SETUP_COMMAND:-}"
linux_dockerfile="${MATRIX_LINUX_DOCKERFILE:-}"
linux_docker_capabilities_json="${MATRIX_LINUX_DOCKER_CAPABILITIES_JSON:-"[]"}"
linux_docker_security_opts_json="${MATRIX_LINUX_DOCKER_SECURITY_OPTS_JSON:-"[]"}"
linux_5_9_enabled="${MATRIX_LINUX_5_9_ENABLED:=false}"
linux_5_9_command_arguments="${MATRIX_LINUX_5_9_COMMAND_ARGUMENTS:-}"
linux_5_10_enabled="${MATRIX_LINUX_5_10_ENABLED:=false}"
linux_5_10_command_arguments="${MATRIX_LINUX_5_10_COMMAND_ARGUMENTS:-}"
linux_6_0_enabled="${MATRIX_LINUX_6_0_ENABLED:=true}"
linux_6_1_enabled="${MATRIX_LINUX_6_1_ENABLED:=true}"
linux_6_2_enabled="${MATRIX_LINUX_6_2_ENABLED:=true}"
linux_6_3_enabled="${MATRIX_LINUX_6_3_ENABLED:=true}"
linux_6_0_command_arguments="${MATRIX_LINUX_6_0_COMMAND_ARGUMENTS:-}"
linux_6_1_command_arguments="${MATRIX_LINUX_6_1_COMMAND_ARGUMENTS:-}"
linux_6_2_command_arguments="${MATRIX_LINUX_6_2_COMMAND_ARGUMENTS:-}"
linux_6_3_command_arguments="${MATRIX_LINUX_6_3_COMMAND_ARGUMENTS:-}"
linux_nightly_next_enabled="${MATRIX_LINUX_NIGHTLY_NEXT_ENABLED:=true}"
linux_nightly_next_command_arguments="${MATRIX_LINUX_NIGHTLY_NEXT_COMMAND_ARGUMENTS:-}"
linux_nightly_main_enabled="${MATRIX_LINUX_NIGHTLY_MAIN_ENABLED:=true}"
linux_nightly_main_command_arguments="${MATRIX_LINUX_NIGHTLY_MAIN_COMMAND_ARGUMENTS:-}"
windows_command="${MATRIX_WINDOWS_COMMAND:-}" # required if any Windows pipeline is enabled
windows_setup_command="${MATRIX_WINDOWS_SETUP_COMMAND:-}"
windows_6_0_enabled="${MATRIX_WINDOWS_6_0_ENABLED:=false}"
windows_6_1_enabled="${MATRIX_WINDOWS_6_1_ENABLED:=false}"
windows_6_2_enabled="${MATRIX_WINDOWS_6_2_ENABLED:=false}"
windows_6_3_enabled="${MATRIX_WINDOWS_6_3_ENABLED:=false}"
windows_6_0_command_arguments="${MATRIX_WINDOWS_6_0_COMMAND_ARGUMENTS:-}"
windows_6_1_command_arguments="${MATRIX_WINDOWS_6_1_COMMAND_ARGUMENTS:-}"
windows_6_2_command_arguments="${MATRIX_WINDOWS_6_2_COMMAND_ARGUMENTS:-}"
windows_6_3_command_arguments="${MATRIX_WINDOWS_6_3_COMMAND_ARGUMENTS:-}"
windows_nightly_next_enabled="${MATRIX_WINDOWS_NIGHTLY_NEXT_ENABLED:=false}"
windows_nightly_next_command_arguments="${MATRIX_WINDOWS_NIGHTLY_NEXT_COMMAND_ARGUMENTS:-}"
windows_nightly_main_enabled="${MATRIX_WINDOWS_NIGHTLY_MAIN_ENABLED:=false}"
windows_nightly_main_command_arguments="${MATRIX_WINDOWS_NIGHTLY_MAIN_COMMAND_ARGUMENTS:-}"
# Get pre-parsed environment variables JSON
linux_env_vars_json="${MATRIX_LINUX_ENV_VARS_JSON:-"{}"}"
windows_env_vars_json="${MATRIX_WINDOWS_ENV_VARS_JSON:-"{}"}"
# Defaults
linux_runner="ubuntu-latest"
linux_5_9_container_image="swift:5.9-jammy"
linux_5_10_container_image="swift:5.10-jammy"
linux_6_0_container_image="swift:6.0-jammy"
linux_6_1_container_image="swift:6.1-jammy"
linux_6_2_container_image="swift:6.2-noble"
linux_6_3_container_image="swift:6.3-noble"
linux_nightly_next_container_image="swiftlang/swift:nightly-6.3-noble"
linux_nightly_main_container_image="swiftlang/swift:nightly-main-noble"
windows_6_0_runner="windows-2022"
windows_6_0_container_image="swift:6.0-windowsservercore-ltsc2022"
windows_6_1_runner="windows-2022"
windows_6_1_container_image="swift:6.1-windowsservercore-ltsc2022"
windows_6_2_runner="windows-2022"
windows_6_2_container_image="swift:6.2-windowsservercore-ltsc2022"
windows_6_3_runner="windows-2022"
windows_6_3_container_image="swift:6.3-windowsservercore-ltsc2022"
windows_nightly_next_runner="windows-2022"
windows_nightly_next_container_image="swiftlang/swift:nightly-6.3-windowsservercore-ltsc2022"
windows_nightly_main_runner="windows-2022"
windows_nightly_main_container_image="swiftlang/swift:nightly-main-windowsservercore-ltsc2022"
# Extract swift-tools-version from a manifest file
get_tools_version() {
local manifest="$1"
if [[ ! -f "$manifest" ]]; then
echo ""
return
fi
# Parse the first line: // swift-tools-version:X.Y or // swift-tools-version: X.Y
local tools_version
tools_version=$("$HEAD_BIN" -n 1 "$manifest" | "$SED_BIN" -n 's#^// *swift-tools-version: *\([0-9.]*\).*#\1#p')
echo "$tools_version"
}
# Compare versions using SemVer: returns 0 if v1 >= v2, 1 otherwise
version_gte() {
local v1="$1"
local v2="$2"
# Parse v1 as major.minor.patch
IFS='.' read -r v1_major v1_minor v1_patch <<< "$v1"
v1_minor=${v1_minor:-0}
v1_patch=${v1_patch:-0}
# Parse v2 as major.minor.patch
IFS='.' read -r v2_major v2_minor v2_patch <<< "$v2"
v2_minor=${v2_minor:-0}
v2_patch=${v2_patch:-0}
# Compare major
if [[ $v1_major -gt $v2_major ]]; then return 0; fi
if [[ $v1_major -lt $v2_major ]]; then return 1; fi
# Major equal, compare minor
if [[ $v1_minor -gt $v2_minor ]]; then return 0; fi
if [[ $v1_minor -lt $v2_minor ]]; then return 1; fi
# Major and minor equal, compare patch
if [[ $v1_patch -ge $v2_patch ]]; then return 0; fi
return 1
}
# Find minimum Swift version across all Package manifests
# Checks Package.swift and all Package@swift-*.swift files in current directory and subdirectories
# Returns the minimum tools version found across all manifests
find_minimum_swift_version() {
local min_version=""
# Check default Package.swift in current directory
local default_version
default_version=$(get_tools_version "Package.swift")
if [[ -n "$default_version" ]]; then
min_version="$default_version"
log "Found Package.swift with tools-version: $default_version"
fi
# Check all Package.swift files in subdirectories (multi-package repository support)
if [[ "$find_subdirectory_manifests_enabled" == "true" ]]; then
while read -r manifest; do
if [[ -f "$manifest" ]]; then
local version
version=$(get_tools_version "$manifest")
if [[ -n "$version" ]]; then
log "Found $manifest with tools-version: $version"
# If this version is less than current minimum, update minimum
if [[ -z "$min_version" ]] || version_gte "$min_version" "$version"; then
min_version="$version"
fi
fi
fi
done < <(ls -1 ./*/Package.swift 2>/dev/null || true)
fi
# Check all version-specific manifests in current directory
for manifest in Package@swift-*.swift; do
if [[ ! -f "$manifest" ]]; then
continue
fi
local version
version=$(get_tools_version "$manifest")
if [[ -z "$version" ]]; then
continue
fi
log "Found $manifest with tools-version: $version"
# If this version is less than current minimum, update minimum
if [[ -z "$min_version" ]] || version_gte "$min_version" "$version"; then
min_version="$version"
fi
done
# Check all version-specific manifests in subdirectories
if [[ "$find_subdirectory_manifests_enabled" == "true" ]]; then
while read -r manifest; do
if [[ -f "$manifest" ]]; then
local version
version=$(get_tools_version "$manifest")
if [[ -n "$version" ]]; then
log "Found $manifest with tools-version: $version"
# If this version is less than current minimum, update minimum
if [[ -z "$min_version" ]] || version_gte "$min_version" "$version"; then
min_version="$version"
fi
fi
fi
done < <(ls -1 ./*/Package@swift-*.swift 2>/dev/null || true)
fi
if [[ -n "$min_version" ]]; then
echo "$min_version"
else
log "Warning: Could not find any Swift tools version in Package manifests"
echo ""
fi
}
# Check if a Swift version should be included in the matrix
# Nightlies are always included
# If MATRIX_MIN_SWIFT_VERSION is "none", all versions are included
# If MATRIX_MIN_SWIFT_VERSION is not set, it's auto-detected from Package manifests
# If MATRIX_MIN_SWIFT_VERSION is set to a version, that version is used as the minimum
should_include_version() {
local version="$1"
# If minimum version check is disabled, include everything
if [[ "${MATRIX_MIN_SWIFT_VERSION:-}" == "none" ]]; then
return 0
fi
# If no minimum version specified, include everything
if [[ -z "${MATRIX_MIN_SWIFT_VERSION:-}" ]]; then
return 0
fi
# Nightly builds always included
if [[ "$version" =~ ^nightly- ]]; then
return 0
fi
# Check if version >= minimum
if version_gte "$version" "$MATRIX_MIN_SWIFT_VERSION"; then
return 0
else
log "Skipping Swift $version (< minimum $MATRIX_MIN_SWIFT_VERSION)"
return 1
fi
}
# Add a matrix entry for a specific Swift version and platform
# Parameters:
# $1: platform ("Linux" or "Windows")
# $2: version name (e.g., "5.9", "6.0", "nightly-main")
# $3: enabled flag ("true" or "false")
# $4: setup_command
# $5: command
# $6: command_arguments
# $7: container_image
# $8: runner
# $9: env_vars_json
# ${10}: dockerfile (optional, path relative to repo root)
# ${11}: docker_capabilities_json (optional, JSON array e.g. '["CAP_BPF"]')
# ${12}: docker_security_opts_json (optional, JSON array e.g. '["seccomp=unconfined"]')
add_matrix_entry() {
local platform="$1"
local version="$2"
local enabled="$3"
local setup_command="$4"
local command="$5"
local command_arguments="$6"
local container_image="$7"
local runner="$8"
local env_vars_json="$9"
local dockerfile="${10:-}"
local docker_capabilities_json="${11:-"[]"}"
local docker_security_opts_json="${12:-"[]"}"
if [[ "$enabled" == "true" ]] && should_include_version "$version"; then
# shellcheck disable=SC2016 # Our use of JQ_BIN means that shellcheck can't tell this is a `jq` invocation
matrix=$(echo "$matrix" | "$JQ_BIN" -c \
--arg setup_command "$setup_command" \
--arg command "$command" \
--arg command_arguments "$command_arguments" \
--arg container_image "$container_image" \
--arg runner "$runner" \
--arg platform "$platform" \
--arg version "$version" \
--argjson env_vars "$env_vars_json" \
--arg dockerfile "$dockerfile" \
--argjson docker_capabilities "$docker_capabilities_json" \
--argjson docker_security_opts "$docker_security_opts_json" \
'.config[.config| length] |= . + { "name": $version, "image": $container_image, "swift_version": $version, "platform": $platform, "command": $command, "command_arguments": $command_arguments, "setup_command": $setup_command, "runner": $runner, "env": $env_vars, "dockerfile": $dockerfile, "docker_capabilities": $docker_capabilities, "docker_security_opts": $docker_security_opts}')
fi
}
# Create matrix from inputs
matrix='{"config": []}'
# Auto-detect minimum Swift version if not explicitly set
if [[ -z "${MATRIX_MIN_SWIFT_VERSION:-}" ]]; then
MATRIX_MIN_SWIFT_VERSION=$(find_minimum_swift_version)
if [[ -n "$MATRIX_MIN_SWIFT_VERSION" ]]; then
log "Minimum Swift tools version: $MATRIX_MIN_SWIFT_VERSION"
fi
fi
## Linux
if [[ \
"$linux_5_9_enabled" == "true" || \
"$linux_5_10_enabled" == "true" || \
"$linux_6_0_enabled" == "true" || \
"$linux_6_1_enabled" == "true" || \
"$linux_6_2_enabled" == "true" || \
"$linux_6_3_enabled" == "true" || \
"$linux_nightly_next_enabled" == "true" || \
"$linux_nightly_main_enabled" == "true" \
]]; then
if [[ -z "$linux_command" ]]; then
fatal "No linux command defined"
fi
fi
# Platform Version Enabled Setup Command Arguments Image Runner Env Dockerfile Capabilities Security opts
add_matrix_entry "Linux" "5.9" "$linux_5_9_enabled" "$linux_setup_command" "$linux_command" "$linux_5_9_command_arguments" "$linux_5_9_container_image" "$linux_runner" "$linux_env_vars_json" "$linux_dockerfile" "$linux_docker_capabilities_json" "$linux_docker_security_opts_json"
add_matrix_entry "Linux" "5.10" "$linux_5_10_enabled" "$linux_setup_command" "$linux_command" "$linux_5_10_command_arguments" "$linux_5_10_container_image" "$linux_runner" "$linux_env_vars_json" "$linux_dockerfile" "$linux_docker_capabilities_json" "$linux_docker_security_opts_json"
add_matrix_entry "Linux" "6.0" "$linux_6_0_enabled" "$linux_setup_command" "$linux_command" "$linux_6_0_command_arguments" "$linux_6_0_container_image" "$linux_runner" "$linux_env_vars_json" "$linux_dockerfile" "$linux_docker_capabilities_json" "$linux_docker_security_opts_json"
add_matrix_entry "Linux" "6.1" "$linux_6_1_enabled" "$linux_setup_command" "$linux_command" "$linux_6_1_command_arguments" "$linux_6_1_container_image" "$linux_runner" "$linux_env_vars_json" "$linux_dockerfile" "$linux_docker_capabilities_json" "$linux_docker_security_opts_json"
add_matrix_entry "Linux" "6.2" "$linux_6_2_enabled" "$linux_setup_command" "$linux_command" "$linux_6_2_command_arguments" "$linux_6_2_container_image" "$linux_runner" "$linux_env_vars_json" "$linux_dockerfile" "$linux_docker_capabilities_json" "$linux_docker_security_opts_json"
add_matrix_entry "Linux" "6.3" "$linux_6_3_enabled" "$linux_setup_command" "$linux_command" "$linux_6_3_command_arguments" "$linux_6_3_container_image" "$linux_runner" "$linux_env_vars_json" "$linux_dockerfile" "$linux_docker_capabilities_json" "$linux_docker_security_opts_json"
add_matrix_entry "Linux" "nightly-next" "$linux_nightly_next_enabled" "$linux_setup_command" "$linux_command" "$linux_nightly_next_command_arguments" "$linux_nightly_next_container_image" "$linux_runner" "$linux_env_vars_json" "$linux_dockerfile" "$linux_docker_capabilities_json" "$linux_docker_security_opts_json"
add_matrix_entry "Linux" "nightly-main" "$linux_nightly_main_enabled" "$linux_setup_command" "$linux_command" "$linux_nightly_main_command_arguments" "$linux_nightly_main_container_image" "$linux_runner" "$linux_env_vars_json" "$linux_dockerfile" "$linux_docker_capabilities_json" "$linux_docker_security_opts_json"
## Windows
if [[ \
"$windows_6_0_enabled" == "true" || \
"$windows_6_1_enabled" == "true" || \
"$windows_6_2_enabled" == "true" || \
"$windows_6_3_enabled" == "true" || \
"$windows_nightly_next_enabled" == "true" || \
"$windows_nightly_main_enabled" == "true" \
]]; then
if [[ -z "$windows_command" ]]; then
fatal "No windows command defined"
fi
fi
# Platform Version Enabled Setup Command Arguments Image Runner Env
add_matrix_entry "Windows" "6.0" "$windows_6_0_enabled" "$windows_setup_command" "$windows_command" "$windows_6_0_command_arguments" "$windows_6_0_container_image" "$windows_6_0_runner" "$windows_env_vars_json"
add_matrix_entry "Windows" "6.1" "$windows_6_1_enabled" "$windows_setup_command" "$windows_command" "$windows_6_1_command_arguments" "$windows_6_1_container_image" "$windows_6_1_runner" "$windows_env_vars_json"
add_matrix_entry "Windows" "6.2" "$windows_6_2_enabled" "$windows_setup_command" "$windows_command" "$windows_6_2_command_arguments" "$windows_6_2_container_image" "$windows_6_2_runner" "$windows_env_vars_json"
add_matrix_entry "Windows" "6.3" "$windows_6_3_enabled" "$windows_setup_command" "$windows_command" "$windows_6_3_command_arguments" "$windows_6_3_container_image" "$windows_6_3_runner" "$windows_env_vars_json"
add_matrix_entry "Windows" "nightly-next" "$windows_nightly_next_enabled" "$windows_setup_command" "$windows_command" "$windows_nightly_next_command_arguments" "$windows_nightly_next_container_image" "$windows_nightly_next_runner" "$windows_env_vars_json"
add_matrix_entry "Windows" "nightly-main" "$windows_nightly_main_enabled" "$windows_setup_command" "$windows_command" "$windows_nightly_main_command_arguments" "$windows_nightly_main_container_image" "$windows_nightly_main_runner" "$windows_env_vars_json"
echo "$matrix" | "$JQ_BIN" -c