mirror of
https://github.com/apple/swift-nio.git
synced 2026-05-20 20:30:36 +00:00
d36223d82c
### 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.
355 lines
18 KiB
Bash
Executable File
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
|