Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
50 changes: 50 additions & 0 deletions .github/actions/install-cmake/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: 'Install CMake'
description: 'Download and install CMake'

inputs:
cmake-version:
description: "CMake version (e.g. '3.30.2')"
required: true
hash:
description: "Expected SHA-256 hash of the target archive (Required)."
required: true
install_dir:
description: "Location where to install CMake"
required: false
default: ""

outputs:
cmake-dir:
description: "Directory containing the CMake binaries"
value: ${{ steps.install.outputs.cmake-dir }}
cmake:
description: "Path to the installed CMake binary"
value: ${{ steps.install.outputs.cmake-path }}
ctest:
description: "Path to the installed CTest binary"
value: ${{ steps.install.outputs.ctest-path }}

runs:
using: "composite"
steps:
- name: Run CMake Installer
id: install
shell: bash
env:
CMAKE_VERSION: ${{ inputs.cmake-version }}
CMAKE_SHA256: ${{ inputs.hash }}
CMAKE_INSTALL_DIR: ${{ inputs.install_dir }}
RAW_ACTION_PATH: ${{ github.action_path }}
run: |
# Resolve path whether running on the host runner or inside a Docker container
SCRIPT_PATH="${RAW_ACTION_PATH}/install-cmake.sh"
if [ ! -f "${SCRIPT_PATH}" ]; then
CONTAINER_PATH="$(echo "${RAW_ACTION_PATH}" | sed 's|^/home/runner/work|/__w|')"
if [ -f "${CONTAINER_PATH}/install-cmake.sh" ]; then
SCRIPT_PATH="${CONTAINER_PATH}/install-cmake.sh"
elif [ -f "${GITHUB_WORKSPACE}/.github/actions/install-cmake/install-cmake.sh" ]; then
SCRIPT_PATH="${GITHUB_WORKSPACE}/.github/actions/install-cmake/install-cmake.sh"
fi
fi

bash "${SCRIPT_PATH}"
102 changes: 102 additions & 0 deletions .github/actions/install-cmake/install-cmake.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
#!/bin/bash
##===----------------------------------------------------------------------===##
##
## This source file is part of the Swift.org open source project
##
## Copyright (c) 2026 Apple Inc. and the Swift project authors
## Licensed under Apache License v2.0 with Runtime Library Exception
##
## See https://swift.org/LICENSE.txt for license information
## See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
##
##===----------------------------------------------------------------------===##
set -euo pipefail

VERSION="${CMAKE_VERSION:?CMAKE_VERSION is required}"
EXPECTED_SHA="${CMAKE_SHA256:?CMAKE_SHA256 is required}"
OS="${RUNNER_OS:?RUNNER_OS is required}"
ARCH="${RUNNER_ARCH:?RUNNER_ARCH is required}"
TEMP_DIR="${RUNNER_TEMP:?RUNNER_TEMP is required}"

# Normalize hash to lowercase and strip whitespace
EXPECTED_SHA=$(echo "${EXPECTED_SHA}" | tr '[:upper:]' '[:lower:]' | xargs)

# 1. Determine destination directory (fallback to $RUNNER_TEMP/cmake-<version> if unset/empty)
DEST_DIR="${CMAKE_INSTALL_DIR:-}"
if [ -z "${DEST_DIR}" ]; then
DEST_DIR="${TEMP_DIR}/cmake-${VERSION}"
fi

# 2. Determine archive filename, binary subpath, and binary extension
BIN_EXT=""
case "${OS}" in
Linux)
case "${ARCH}" in
X64) ARCH_NAME="x86_64" ;;
ARM64) ARCH_NAME="aarch64" ;;
*) echo "::error::Unsupported Linux architecture: ${ARCH}"; exit 1 ;;
esac
ARCHIVE="cmake-${VERSION}-linux-${ARCH_NAME}.tar.gz"
BIN_SUBPATH="bin"
;;
macOS)
ARCHIVE="cmake-${VERSION}-macos-universal.tar.gz"
BIN_SUBPATH="CMake.app/Contents/bin"
;;
Windows)
case "${ARCH}" in
X64) ARCH_NAME="x86_64" ;;
ARM64) ARCH_NAME="arm64" ;;
*) echo "::error::Unsupported Windows architecture: ${ARCH}"; exit 1 ;;
esac
ARCHIVE="cmake-${VERSION}-windows-${ARCH_NAME}.zip"
BIN_SUBPATH="bin"
BIN_EXT=".exe"
;;
*)
echo "::error::Unsupported OS: ${OS}"
exit 1
;;
esac

ARCHIVE_URL="https://github.com/Kitware/CMake/releases/download/v${VERSION}/${ARCHIVE}"
ARCHIVE_PATH="${TEMP_DIR}/${ARCHIVE}"

# 3. Download
echo "==> Downloading ${ARCHIVE_URL}..."
curl -fsSL --retry 3 -o "${ARCHIVE_PATH}" "${ARCHIVE_URL}"

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the future and digging through the action marketplace, migrating this script to Javascript should let us drop all of the dependencies since nodejs is supposed to always be available.


# 4. Verify SHA-256 directly in one step
echo "==> Verifying SHA-256..."
CHECK_CMD=$(command -v sha256sum || echo "shasum -a 256")
echo "${EXPECTED_SHA} ${ARCHIVE_PATH}" | ${CHECK_CMD} -c -

# 5. Extract into destination directory
echo "==> Extracting into ${DEST_DIR}..."
mkdir -p "${DEST_DIR}"

if [[ "${ARCHIVE}" == *.tar.gz ]]; then
tar -xzf "${ARCHIVE_PATH}" -C "${DEST_DIR}" --strip-components=1
elif [[ "${ARCHIVE}" == *.zip ]]; then
UNZIP_TEMP="${TEMP_DIR}/unzip_tmp"
mkdir -p "${UNZIP_TEMP}"
unzip -q -o "${ARCHIVE_PATH}" -d "${UNZIP_TEMP}"
TOP_DIR=$(find "${UNZIP_TEMP}" -mindepth 1 -maxdepth 1 -type d)
mv "${TOP_DIR}"/* "${DEST_DIR}/"
rm -rf "${UNZIP_TEMP}"
fi
rm -f "${ARCHIVE_PATH}"

# 6. Set PATH and GITHUB_OUTPUT
CMAKE_BIN_DIR="${DEST_DIR}/${BIN_SUBPATH}"
echo "${CMAKE_BIN_DIR}" >> "${GITHUB_PATH}"

if [ -n "${GITHUB_OUTPUT:-}" ]; then
{
echo "cmake-dir=${CMAKE_BIN_DIR}"
echo "cmake-path=${CMAKE_BIN_DIR}/cmake${BIN_EXT}"
echo "ctest-path=${CMAKE_BIN_DIR}/ctest${BIN_EXT}"
} >> "${GITHUB_OUTPUT}"
fi

echo "==> CMake ${VERSION} successfully installed at ${CMAKE_BIN_DIR}"
47 changes: 47 additions & 0 deletions .github/actions/install-cmake/readme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
# Install CMake

Install an arbitrary version of CMake.

Inputs:
- cmake-version: Version of CMake to install (e.g. 3.30.2)
- hash: sha256 of the downloaded artifact (.zip or .tar.gz)
- install_dir: (optional) location where to install CMake

Outputs:
- cmake-dir: Directory containing the CMake binaries
- cmake: Path to the installed CMake binary
- ctest: Path to the installed CTest binary


## Usage

```yaml
...

steps:

...

- name: Install CMake
id: install-cmake
uses: swiftlang/github-workflows/.github/actions/install-cmake@<version>
with:
cmake-version: 4.0.3
hash: 585ae9e013107bc8e7c7c9ce872cbdcbdff569e675b07ef57aacfb88c886faac

- name: Run Build
shell: bash
env:
CMAKE: ${{ steps.install-cmake.outputs.cmake }}
run: |
"$CMAKE" -G Ninja -B build -S <sources> -DCMAKE_BUILD_TYPE=Release

- name: Run Tests
shell: bash
env:
CTEST: ${{ steps.install-cmake.outputs.ctest }}
run: |
"$CTEST" --output-on-failure --test-timeout 150 --test-dir build -j

...
```
178 changes: 178 additions & 0 deletions .github/workflows/install_cmake_test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
name: Install CMake Test

permissions:
contents: read

on:
workflow_call:
inputs:
cmake_version:
type: string
description: "CMake version to install and verify"
default: "4.0.3"
linux_x86_64_hash:
type: string
description: "SHA-256 of cmake-<version>-linux-x86_64.tar.gz"
default: "585ae9e013107bc8e7c7c9ce872cbdcbdff569e675b07ef57aacfb88c886faac"
linux_aarch64_hash:
type: string
description: "SHA-256 of cmake-<version>-linux-aarch64.tar.gz"
default: "391da1544ef50ac31300841caaf11db4de3976cdc4468643272e44b3f4644713"
macos_hash:
type: string
description: "SHA-256 of cmake-<version>-macos-universal.tar.gz"
default: "4e85de4daf1c3e82d7dc6b8ba5683972944b466343aeb9c327a742437bb3ce9a"
windows_x86_64_hash:
type: string
description: "SHA-256 of cmake-<version>-windows-x86_64.zip"
default: "b59a31dfbfa376a4aaea9ff560ff2b29f78ee5f9fb15447fc71ae7bf9fea9379"
windows_arm64_hash:
type: string
description: "SHA-256 of cmake-<version>-windows-arm64.zip"
default: "86ccd6485bbd4bb41a1a858db397be5bca5e0de96858bf8dbba7a64407bd6c00"
workflow_dispatch:

jobs:
linux:
name: "Linux (${{ matrix.arch }})"
strategy:
fail-fast: false
matrix:
include:
- arch: X64
runner: ubuntu-24.04
hash: ${{ inputs.linux_x86_64_hash }}
- arch: ARM64
runner: ubuntu-24.04-arm
hash: ${{ inputs.linux_aarch64_hash }}
runs-on: ${{ matrix.runner }}
container:
image: swift:6.2-jammy
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Install prerequisites
shell: bash
run: |
apt-get update
apt-get install -y curl
- name: Install CMake
id: install
uses: ./.github/actions/install-cmake
with:
cmake-version: ${{ inputs.cmake_version }}
hash: ${{ matrix.hash }}
- name: Verify CMake installation
shell: bash
env:
EXPECTED_VERSION: ${{ inputs.cmake_version }}
CMAKE: ${{ steps.install.outputs.cmake }}
CTEST: ${{ steps.install.outputs.ctest }}
CMAKE_DIR: ${{ steps.install.outputs.cmake-dir }}
run: |
set -euo pipefail
# Outputs point at real, executable files
test -x "${CMAKE}"
test -x "${CTEST}"
test -d "${CMAKE_DIR}"
# Explicit output paths run and report the expected version
"${CMAKE}" --version | grep -q "${EXPECTED_VERSION}"
"${CTEST}" --version
# GITHUB_PATH was updated: bare `cmake` resolves in this later step
cmake --version | grep -q "${EXPECTED_VERSION}"

macos:
name: "macOS (ARM64)"
runs-on: [self-hosted, macos, tahoe, ARM64]
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Install CMake
id: install
uses: ./.github/actions/install-cmake
with:
cmake-version: ${{ inputs.cmake_version }}
hash: ${{ inputs.macos_hash }}
- name: Verify CMake installation
shell: bash
env:
EXPECTED_VERSION: ${{ inputs.cmake_version }}
CMAKE: ${{ steps.install.outputs.cmake }}
CTEST: ${{ steps.install.outputs.ctest }}
CMAKE_DIR: ${{ steps.install.outputs.cmake-dir }}
run: |
set -euo pipefail
test -x "${CMAKE}"
test -x "${CTEST}"
test -d "${CMAKE_DIR}"
"${CMAKE}" --version | grep -q "${EXPECTED_VERSION}"
"${CTEST}" --version
cmake --version | grep -q "${EXPECTED_VERSION}"

windows:
name: "Windows (${{ matrix.runner }})"
strategy:
fail-fast: false
matrix:
include:
- runner: windows-2022
hash: ${{ inputs.windows_x86_64_hash }}
- runner: windows-11-arm
hash: ${{ inputs.windows_arm64_hash }}
runs-on: ${{ matrix.runner }}
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Install CMake
id: install
uses: ./.github/actions/install-cmake
with:
cmake-version: ${{ inputs.cmake_version }}
hash: ${{ matrix.hash }}
- name: Verify CMake installation
shell: bash
env:
EXPECTED_VERSION: ${{ inputs.cmake_version }}
CMAKE: ${{ steps.install.outputs.cmake }}
CTEST: ${{ steps.install.outputs.ctest }}
CMAKE_DIR: ${{ steps.install.outputs.cmake-dir }}
run: |
set -euo pipefail
test -x "${CMAKE}"
test -x "${CTEST}"
test -d "${CMAKE_DIR}"
"${CMAKE}" --version | grep -q "${EXPECTED_VERSION}"
"${CTEST}" --version
cmake --version | grep -q "${EXPECTED_VERSION}"

bad_hash:
name: "Negative test (wrong hash)"
runs-on: ubuntu-24.04
container:
image: swift:6.2-jammy
steps:
- name: Checkout
uses: actions/checkout@v7
- name: Install prerequisites
shell: bash
run: |
apt-get update
apt-get install -y curl
- name: Install CMake with a wrong hash (expected to fail)
id: install
continue-on-error: true
uses: ./.github/actions/install-cmake
with:
cmake-version: ${{ inputs.cmake_version }}
hash: "0000000000000000000000000000000000000000000000000000000000000000"
- name: Assert the install failed
shell: bash
env:
OUTCOME: ${{ steps.install.outcome }}
run: |
set -euo pipefail
if [ "${OUTCOME}" != "failure" ]; then
echo "::error::Expected install to fail on hash mismatch, but outcome was '${OUTCOME}'"
exit 1
fi
echo "Install correctly failed on SHA-256 mismatch."
4 changes: 4 additions & 0 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,10 @@ jobs:
project: "swift"
workflow_test_enabled: true

install_cmake:
name: Install CMake
uses: ./.github/workflows/install_cmake_test.yml

soundness-docs:
name: "Soundness - Docs (override_target_name: ${{ matrix.override_target_name }} ; post command: ${{ matrix.post_command }})"
strategy:
Expand Down
Loading