Compare commits

..

1 Commits

Author SHA1 Message Date
Shivam Mathur 20f54cb8d0 Use php-darwin caches on macOS 2026-08-29 00:24:48 +05:30
16 changed files with 7 additions and 638 deletions
-21
View File
@@ -254,27 +254,6 @@ describe('Utils tests', () => {
); );
}); });
it('builds the run-script path from only the script filename', () => {
const scriptPath = path.join(
'/tmp',
'feature',
'php-darwin-cache',
'src',
'scripts',
'darwin.sh'
);
expect(utils.scriptRunPath(scriptPath)).toBe(
path.join(
'/tmp',
'feature',
'php-darwin-cache',
'src',
'scripts',
'run.sh'
)
);
});
it('checking scriptTool', async () => { it('checking scriptTool', async () => {
expect(await utils.scriptTool('linux')).toBe('bash '); expect(await utils.scriptTool('linux')).toBe('bash ');
expect(await utils.scriptTool('darwin')).toBe('bash '); expect(await utils.scriptTool('darwin')).toBe('bash ');
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
@@ -17,7 +17,7 @@ export async function getScript(os: string): Promise<string> {
const url = 'https://setup-php.com/sponsor'; const url = 'https://setup-php.com/sponsor';
const filename = os + (await utils.scriptExtension(os)); const filename = os + (await utils.scriptExtension(os));
const script_path = path.join(__dirname, '../src/scripts', filename); const script_path = path.join(__dirname, '../src/scripts', filename);
const run_path = utils.scriptRunPath(script_path); const run_path = script_path.replace(os, 'run');
const extension_csv: string = utils.sanitizeShellInput( const extension_csv: string = utils.sanitizeShellInput(
await utils.getInput('extensions', false), await utils.getInput('extensions', false),
true true
+5 -7
View File
@@ -174,11 +174,9 @@ get_brewed_php() {
fi fi
} }
# Install PHP from the architecture-specific php-darwin cache. # Function to setup PHP from the cached builds.
add_php_from_cache() { setup_cached_versions() {
cache_root="${src:?}/scripts/tools/php-darwin" run_script "php-darwin" "$version" "${debug:?}" "${ts:?}"
PHP_DARWIN_ROOT="$cache_root" bash "$cache_root/scripts/install.sh" \
"$version" "${debug:?}" "${ts:?}"
} }
# Function to setup PHP 5.6 and newer using Homebrew. # Function to setup PHP 5.6 and newer using Homebrew.
@@ -189,7 +187,7 @@ add_php() {
php_keg="php@$version$suffix" php_keg="php@$version$suffix"
php_formula="shivammathur/php/$php_keg" php_formula="shivammathur/php/$php_keg"
if [[ "$existing_version" = "false" || -n "$suffix" || "$action" = "upgrade" ]]; then if [[ "$existing_version" = "false" || -n "$suffix" || "$action" = "upgrade" ]]; then
if add_php_from_cache; then if [ "${runner:?}" != "self-hosted" ] && [ "${use_package_cache:-true}" != "false" ] && setup_cached_versions; then
return 0 return 0
fi fi
update_dependencies update_dependencies
@@ -262,7 +260,7 @@ setup_php() {
if [[ "$version" =~ ${old_versions:?} ]]; then if [[ "$version" =~ ${old_versions:?} ]]; then
run_script "php5-darwin" "${version/./}" >/dev/null 2>&1 run_script "php5-darwin" "${version/./}" >/dev/null 2>&1
status="Installed" status="Installed"
elif [ "${existing_version:0:3}" != "$version" ]; then elif [[ "${existing_version:0:3}" != "$version" || -n "$(get_php_formula_suffix)" ]]; then
add_php "install" "$existing_version" >/dev/null 2>&1 add_php "install" "$existing_version" >/dev/null 2>&1
status="Installed" status="Installed"
elif [[ "${existing_version:0:3}" = "$version" && "${update:?}" = "true" ]]; then elif [[ "${existing_version:0:3}" = "$version" && "${update:?}" = "true" ]]; then
@@ -1,5 +0,0 @@
# Homebrew prefix roots permitted in an archive and merged during installation.
Cellar
etc
opt
var
@@ -1,12 +0,0 @@
{
"archive_media_type": "application/vnd.shivammathur.php-darwin.cache.layer.v1.tar+zstd",
"artifact_type": "application/vnd.shivammathur.php-darwin.cache.v1",
"compression_level": 19,
"current_version": "8.5",
"image": "ghcr.io/shivammathur/php-darwin",
"max_install_seconds": 60,
"registry_manifest_media_type": "application/vnd.oci.image.manifest.v1+json",
"tap": "shivammathur/php",
"tap_branch": "main",
"tap_repository": "https://github.com/shivammathur/homebrew-php"
}
@@ -1,16 +0,0 @@
{
"arm64": {
"build_runner": "macos-15",
"brew_prefix": "/opt/homebrew",
"minimum_macos": 15,
"platform_key": "arm64_sequoia",
"test_runners": ["macos-15", "macos-26"]
},
"x86_64": {
"build_runner": "macos-15-intel",
"brew_prefix": "/usr/local",
"minimum_macos": 15,
"platform_key": "sequoia",
"test_runners": ["macos-15-intel", "macos-26-intel"]
}
}
@@ -1,14 +0,0 @@
# channel version
stable 5.6
stable 7.0
stable 7.1
stable 7.2
stable 7.3
stable 7.4
stable 8.0
stable 8.1
stable 8.2
stable 8.3
stable 8.4
stable 8.5
nightly 8.6
@@ -1,52 +0,0 @@
#!/usr/bin/env bash
prefix=${1:?}
output=${2:?}
roots_file=${3:?}
path_list="$output.paths.$$"
trap 'rm -f "$path_list"' EXIT
[ -d "$prefix" ] || {
printf 'Missing Homebrew prefix: %s\n' "$prefix" >&2
exit 1
}
[ -f "$roots_file" ] || {
printf 'Missing archive roots: %s\n' "$roots_file" >&2
exit 1
}
: > "$output" || exit 1
while IFS= read -r managed_dir extra; do
[ -n "$managed_dir" ] || continue
case "$managed_dir" in \#*) continue ;; esac
[ -z "$extra" ] || {
printf 'Invalid archive root: %s %s\n' "$managed_dir" "$extra" >&2
exit 1
}
case "$managed_dir" in Cellar|etc|opt|var) ;; *)
printf 'Unsafe archive root: %s\n' "$managed_dir" >&2
exit 1
;;
esac
if [ "$managed_dir" = Cellar ]; then
# One exclusion per existing keg protects its complete subtree and keeps
# the pattern file small even on runner images with hundreds of formulae.
find "$prefix/Cellar" -mindepth 2 -maxdepth 2 -print0 > "$path_list" || exit 1
else
find "$prefix/$managed_dir" ! -type d -print0 > "$path_list" || exit 1
fi
while IFS= read -r -d '' existing_path; do
relative_path=${existing_path#"$prefix"/}
case "$relative_path" in *$'\n'*|*$'\r'*)
printf 'Unsupported Homebrew path: %s\n' "$relative_path" >&2
exit 1
;;
esac
tar_pattern=${relative_path//\\/\\\\}
tar_pattern=${tar_pattern//\[/\\[}
tar_pattern=${tar_pattern//\]/\\]}
tar_pattern=${tar_pattern//\*/\\*}
tar_pattern=${tar_pattern//\?/\\?}
printf '%s\n' "$tar_pattern" >> "$output" || exit 1
done < "$path_list"
done < "$roots_file"
@@ -1,22 +0,0 @@
#!/usr/bin/env bash
archive=${1:?}
prefix=${2:?}
exclude_file=${3:?}
[ -f "$archive" ] || {
printf 'Archive not found: %s\n' "$archive" >&2
exit 1
}
[ -d "$prefix" ] || {
printf 'Extraction prefix not found: %s\n' "$prefix" >&2
exit 1
}
[ -f "$exclude_file" ] || {
printf 'Extraction exclusion list not found: %s\n' "$exclude_file" >&2
exit 1
}
zstd -dc "$archive" | tar -xmpf - --no-same-owner -X "$exclude_file" -C "$prefix"
pipeline_status=("${PIPESTATUS[@]}")
[ "${pipeline_status[0]}" -eq 0 ] && [ "${pipeline_status[1]}" -eq 0 ]
@@ -1,149 +0,0 @@
#!/usr/bin/env bash
script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
# shellcheck source=scripts/lib.sh
. "$script_dir/lib.sh"
version=${1:-8.5}
build=${2:-release}
ts=${3:-nts}
local_archive=${4:-}
arch=$(php_darwin_normalize_arch "$(uname -m)")
formula=$(php_darwin_formula "$version" "$build" "$ts")
requested_formula=$(php_darwin_requested_formula "$version" "$build" "$ts")
asset=$(php_darwin_asset "$version" "$build" "$ts" "$arch")
expected_prefix=$(php_darwin_expected_prefix "$arch")
tap=$(php_darwin_package_config tap)
archive_roots="$script_dir/../conf/archive-paths"
[ "$(uname -s)" = Darwin ] || php_darwin_die 'the cache installer only supports macOS'
command -v brew >/dev/null 2>&1 || php_darwin_die 'Homebrew is required'
brew_prefix=$(brew --prefix)
[ "$brew_prefix" = "$expected_prefix" ] || php_darwin_die "architecture $arch requires Homebrew at $expected_prefix, found $brew_prefix"
command -v zstd >/dev/null 2>&1 || php_darwin_die 'zstd is required to extract the cache'
macos_version=$(sw_vers -productVersion) || php_darwin_die 'could not determine the macOS version'
macos_major=${macos_version%%.*}
while IFS= read -r managed_dir extra; do
[ -n "$managed_dir" ] || continue
case "$managed_dir" in \#*) continue ;; esac
[ -z "$extra" ] || php_darwin_die "invalid archive root: $managed_dir $extra"
case "$managed_dir" in Cellar|etc|opt|var) ;; *) php_darwin_die "unsafe archive root: $managed_dir" ;; esac
[ -d "$brew_prefix/$managed_dir" ] || mkdir -p "$brew_prefix/$managed_dir" || \
php_darwin_die "could not create Homebrew directory: $brew_prefix/$managed_dir"
[ -w "$brew_prefix/$managed_dir" ] || \
php_darwin_die "Homebrew directory is not writable: $brew_prefix/$managed_dir"
done < "$archive_roots"
export HOMEBREW_NO_AUTO_UPDATE=1
export HOMEBREW_NO_ENV_HINTS=1
export HOMEBREW_NO_INSTALL_CLEANUP=1
export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1
export HOMEBREW_NO_INSTALL_FROM_API=1
# Use Homebrew to establish the formula source and trust state. In setup-php the
# tap is already present, so this is a local no-op and adds no install request.
brew tap "$tap" || php_darwin_die "could not tap $tap"
brew trust "$tap" || php_darwin_die "could not trust $tap"
brew formula "$tap/$requested_formula" >/dev/null || php_darwin_die "could not resolve $tap/$requested_formula"
tmp_dir=$(mktemp -d "${RUNNER_TEMP:-/tmp}/php-darwin-install.XXXXXX")
trap 'rm -rf "$tmp_dir"' EXIT
archive="$tmp_dir/$asset"
external_metadata=
if [ -n "$local_archive" ]; then
archive=$local_archive
checksum="$local_archive.sha256"
external_metadata="$(dirname "$local_archive")/${asset%.tar.zst}.json"
[ -f "$archive" ] || php_darwin_die "archive not found: $archive"
[ -f "$checksum" ] || php_darwin_die "checksum not found: $checksum"
[ -f "$external_metadata" ] || php_darwin_die "metadata not found: $external_metadata"
expected_hash=$(awk -v name="$asset" '$2 == name { print $1; found=1; exit } END { exit !found }' "$checksum") || \
php_darwin_die "checksum file does not contain $asset"
[[ "$expected_hash" =~ ^[0-9a-f]{64}$ ]] || php_darwin_die "checksum is invalid for $asset"
actual_hash=$(php_darwin_sha256 "$archive") || php_darwin_die "could not hash $asset"
[ "$actual_hash" = "$expected_hash" ] || php_darwin_die "checksum mismatch for $asset"
else
image=${PHP_DARWIN_IMAGE:-$(php_darwin_package_config image)}
tag="php-$version-$ts-$build"
descriptor="$tmp_dir/descriptor.json"
bash "$script_dir/registry.sh" pull "$image" "$tag" "$asset" "$archive" "$descriptor" || \
php_darwin_die "could not pull $image:$tag"
if ! jq -e --arg version "$version" --arg build "$build" --arg ts "$ts" \
--arg artifact_type "$(php_darwin_package_config artifact_type)" \
--arg manifest_media_type "$(php_darwin_package_config registry_manifest_media_type)" \
--arg media_type "$(php_darwin_package_config archive_media_type)" \
--argjson macos_major "$macos_major" \
'.annotations["com.setup-php.php-darwin.php-version"] == $version and
.annotations["com.setup-php.php-darwin.build"] == $build and
.annotations["com.setup-php.php-darwin.thread-safety"] == $ts and
(.annotations["com.setup-php.php-darwin.minimum-macos"] | tonumber) <= $macos_major and
(.annotations["com.setup-php.php-darwin.homebrew-php-commit"] |
type == "string" and test("^[0-9a-f]{40}$")) and
(.annotations["com.setup-php.php-darwin.source-hash"] |
type == "string" and test("^[0-9a-f]{64}$")) and
(.annotations["org.opencontainers.image.version"] |
type == "string" and startswith($version + ".") and test("^[0-9]+\\.[0-9]+\\.[0-9]+$")) and
.schema_version == 2 and .artifact_type == $artifact_type and
.manifest_media_type == $manifest_media_type and
.layer.mediaType == $media_type' "$descriptor" >/dev/null; then
php_darwin_die 'OCI manifest annotations did not match the request'
fi
tap_path=$(brew --repository "$tap") || php_darwin_die "could not resolve the $tap repository"
current_source_hash=$(HOMEBREW_PHP_PATH="$tap_path" bash "$script_dir/source-hash.sh" "$version") || \
php_darwin_die "could not compute the installed $tap source hash"
cached_source_hash=$(jq -er '.annotations["com.setup-php.php-darwin.source-hash"]' "$descriptor") || \
php_darwin_die 'OCI manifest source hash is missing'
[ "$cached_source_hash" = "$current_source_hash" ] || \
php_darwin_die "cache is stale for the installed $tap formulae"
fi
if [ -n "$external_metadata" ]; then
if ! jq -e --arg version "$version" --arg build "$build" --arg ts "$ts" \
--arg arch "$arch" --arg brew_prefix "$brew_prefix" --arg asset "$asset" --arg formula "$formula" \
--arg expected_commit "${HOMEBREW_PHP_COMMIT:-}" --arg requested_formula "$requested_formula" \
--argjson macos_major "$macos_major" \
'.schema == 1 and .php_version == $version and .build == $build and
.thread_safety == $ts and .architecture == $arch and .brew_prefix == $brew_prefix and
.archive == $asset and .formula == $formula and .requested_formula == $requested_formula and
.minimum_macos <= $macos_major and
(.homebrew_php_commit | type == "string" and test("^[0-9a-f]{40}$")) and
($expected_commit == "" or .homebrew_php_commit == $expected_commit) and
(.formula_sha256 | type == "string" and test("^[0-9a-f]{64}$")) and
(.php_semver | type == "string" and startswith($version + ".") and test("^[0-9]+\\.[0-9]+\\.[0-9]+$")) and
(.packages | type == "array" and length > 0) and any(.packages[]; .name == $formula)' \
"$external_metadata" >/dev/null; then
php_darwin_die 'archive metadata did not match the runner or request'
fi
fi
# Let Homebrew remove only the requested PHP formula, then build a literal
# exclusion list for every remaining file and symlink. The archive has no
# directory entries, so tar can stream it straight into the prefix without
# reading, copying, chmodding, or replacing any existing Homebrew path.
if brew list --versions "$formula" >/dev/null 2>&1; then
brew uninstall --force --ignore-dependencies "$formula" >/dev/null || \
php_darwin_die "could not remove the existing $formula keg"
fi
exclude_file="$tmp_dir/existing-paths.txt"
bash "$script_dir/existing-paths.sh" "$brew_prefix" "$exclude_file" "$archive_roots" || \
php_darwin_die 'could not record existing Homebrew paths'
bash "$script_dir/extract.sh" "$archive" "$brew_prefix" "$exclude_file" || \
php_darwin_die "could not extract $asset into Homebrew"
brew link --overwrite --force "$formula" >/dev/null || php_darwin_die "could not link $formula"
missing=$(brew missing "$formula" 2>&1)
missing_status=$?
[ "$missing_status" -eq 0 ] || [ -n "$missing" ] || php_darwin_die 'Homebrew dependency validation failed without diagnostics'
[ -z "$missing" ] || php_darwin_die "cache has missing Homebrew dependencies: $missing"
php_bin="$brew_prefix/opt/$formula/bin/php"
[ -x "$php_bin" ] || php_darwin_die "PHP binary missing after cache extraction: $php_bin"
installed_semver=$($php_bin -r 'echo PHP_VERSION;') || php_darwin_die 'cached PHP could not report its version'
[ "${installed_semver%.*}" = "$version" ] || php_darwin_die "cache installed PHP $installed_semver for requested $version"
if [ -n "${GITHUB_PATH:-}" ]; then
printf '%s\n%s\n' "$brew_prefix/opt/$formula/bin" "$brew_prefix/opt/$formula/sbin" >> "$GITHUB_PATH"
fi
printf 'Installed PHP %s (%s, %s, %s) from %s\n' "$installed_semver" "$build" "$ts" "$arch" "$asset"
-124
View File
@@ -1,124 +0,0 @@
#!/usr/bin/env bash
php_darwin_root=${PHP_DARWIN_ROOT:-$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)}
php_darwin_die() {
printf 'php-darwin: %s\n' "$*" >&2
exit 1
}
php_darwin_validate_version() {
awk '!/^#/ && $2 == version { found=1 } END { exit !found }' version="${1:-}" "$php_darwin_root/conf/versions" || \
php_darwin_die "unsupported PHP version: ${1:-<empty>}"
}
php_darwin_version_channel() {
awk '!/^#/ && $2 == version { print $1; found=1; exit } END { exit !found }' \
version="${1:-}" "$php_darwin_root/conf/versions" || \
php_darwin_die "unsupported PHP version: ${1:-<empty>}"
}
php_darwin_validate_channel() {
local version=${1:-}
local expected=${2:-}
local actual
case "$expected" in stable|nightly) ;; *) php_darwin_die "unsupported release channel: ${expected:-<empty>}" ;; esac
actual=$(php_darwin_version_channel "$version")
[ "$actual" = "$expected" ] || php_darwin_die "PHP $version is $actual, not $expected"
}
php_darwin_validate_build() {
case "${1:-}" in
release|debug) ;;
*) php_darwin_die "build must be release or debug: ${1:-<empty>}" ;;
esac
}
php_darwin_validate_ts() {
case "${1:-}" in
nts|zts) ;;
*) php_darwin_die "thread safety must be nts or zts: ${1:-<empty>}" ;;
esac
}
php_darwin_normalize_arch() {
case "${1:-$(uname -m)}" in
arm64|aarch64) printf 'arm64\n' ;;
x86_64|amd64) printf 'x86_64\n' ;;
*) php_darwin_die "unsupported architecture: ${1:-<empty>}" ;;
esac
}
php_darwin_expected_prefix() {
jq -er --arg arch "$(php_darwin_normalize_arch "${1:-}")" '.[$arch].brew_prefix' \
"$php_darwin_root/conf/platforms.json" || php_darwin_die 'Homebrew prefix is not configured'
}
php_darwin_package_config() {
jq -er --arg key "$1" '.[$key]' "$php_darwin_root/conf/package.json" || \
php_darwin_die "package configuration is missing: $1"
}
php_darwin_formula_suffix() {
local build=${1:-release}
local ts=${2:-nts}
local suffix=
php_darwin_validate_build "$build"
php_darwin_validate_ts "$ts"
[ "$build" = debug ] && suffix=-debug
[ "$ts" = zts ] && suffix="$suffix-zts"
printf '%s\n' "$suffix"
}
php_darwin_formula() {
local version=$1
local current_version
local suffix
php_darwin_validate_version "$version"
suffix=$(php_darwin_formula_suffix "${2:-release}" "${3:-nts}")
current_version=$(php_darwin_package_config current_version)
if [ "$version" = "$current_version" ]; then
printf 'php%s\n' "$suffix"
else
printf 'php@%s%s\n' "$version" "$suffix"
fi
}
php_darwin_requested_formula() {
local version=$1
local suffix
php_darwin_validate_version "$version"
suffix=$(php_darwin_formula_suffix "${2:-release}" "${3:-nts}")
printf 'php@%s%s\n' "$version" "$suffix"
}
php_darwin_asset() {
local version=$1
local version_major
local version_minor
local build=${2:-release}
local ts=${3:-nts}
local arch
IFS=. read -r version_major version_minor _ <<< "$version"
php_darwin_validate_version "$version_major.$version_minor"
php_darwin_validate_build "$build"
php_darwin_validate_ts "$ts"
arch=$(php_darwin_normalize_arch "${4:-}")
printf 'php_%s-%s-%s+darwin_%s.tar.zst\n' "$version" "$ts" "$build" "$arch"
}
php_darwin_sha256() {
local hash_output
if command -v sha256sum >/dev/null 2>&1; then
hash_output=$(sha256sum "$1") || return 1
else
hash_output=$(shasum -a 256 "$1") || return 1
fi
printf '%s\n' "${hash_output%% *}"
}
@@ -1,164 +0,0 @@
#!/usr/bin/env bash
script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
# shellcheck source=scripts/lib.sh
. "$script_dir/lib.sh"
manifest_media_type=$(php_darwin_package_config registry_manifest_media_type)
artifact_type=$(php_darwin_package_config artifact_type)
archive_media_type=$(php_darwin_package_config archive_media_type)
configured_layer_count=$(jq 'keys | length' "$script_dir/../conf/platforms.json") || \
php_darwin_die 'could not read the configured platform count'
registry_auth() {
registry_image=$1
[[ "$registry_image" =~ ^[A-Za-z0-9.-]+/[A-Za-z0-9._/-]+$ ]] || \
php_darwin_die "invalid registry image: $registry_image"
case "$registry_image" in *..*) php_darwin_die "invalid registry image: $registry_image" ;; esac
registry_host=${registry_image%%/*}
registry_repository=${registry_image#*/}
registry_token_url="https://$registry_host/token"
registry_token_args=(--retry 3 --retry-all-errors -fsSLG)
registry_credential=${GHCR_TOKEN:-${GITHUB_TOKEN:-${GH_TOKEN:-}}}
[ -z "$registry_credential" ] || registry_token_args+=(-u "${GITHUB_ACTOR:-token}:$registry_credential")
registry_token_response=$(curl "${registry_token_args[@]}" "$registry_token_url" \
--data-urlencode "service=$registry_host" \
--data-urlencode "scope=repository:$registry_repository:pull") || \
php_darwin_die "could not get a registry token for $registry_image"
registry_token=$(jq -er '.token // .access_token' <<< "$registry_token_response") || \
php_darwin_die "registry token response was invalid for $registry_image"
}
registry_manifest() {
manifest_tag=$1
manifest_output=$2
manifest_headers=$3
[[ "$manifest_tag" =~ ^[A-Za-z0-9._-]+$ ]] || php_darwin_die "invalid OCI tag: $manifest_tag"
curl --retry 3 --retry-all-errors -fsSL \
-H "Authorization: Bearer $registry_token" \
-H "Accept: $manifest_media_type" \
-D "$manifest_headers" \
"https://$registry_host/v2/$registry_repository/manifests/$manifest_tag" \
-o "$manifest_output" || return 1
jq -e --arg archive_media_type "$archive_media_type" --arg artifact_type "$artifact_type" \
--arg media_type "$manifest_media_type" --argjson layer_count "$configured_layer_count" \
'.schemaVersion == 2 and .mediaType == $media_type and .artifactType == $artifact_type and
(.layers | type == "array" and length == $layer_count) and
all(.layers[];
.mediaType == $archive_media_type and
(.digest | type == "string" and test("^sha256:[0-9a-f]{64}$")) and
(.size | type == "number" and . >= 0 and . == floor) and
(.annotations["org.opencontainers.image.title"] | type == "string" and
test("^php_[0-9]+\\.[0-9]+-(nts|zts)-(debug|release)\\+darwin_(arm64|x86_64)\\.tar\\.zst$"))) and
([.layers[].annotations["org.opencontainers.image.title"]] | unique | length) == $layer_count and
any(.layers[]; .annotations["org.opencontainers.image.title"] | endswith("_arm64.tar.zst")) and
any(.layers[]; .annotations["org.opencontainers.image.title"] | endswith("_x86_64.tar.zst"))' \
"$manifest_output" >/dev/null || \
php_darwin_die 'registry returned an invalid php-darwin OCI manifest'
header_digest=$(awk 'tolower($1) == "docker-content-digest:" {gsub("\\r", "", $2); digest=$2} END {print digest}' "$manifest_headers") || \
php_darwin_die 'could not read the registry manifest digest header'
manifest_hash=$(php_darwin_sha256 "$manifest_output") || php_darwin_die 'could not hash the OCI manifest'
manifest_digest="sha256:$manifest_hash"
[ -z "$header_digest" ] || [[ "$header_digest" =~ ^sha256:[0-9a-f]{64}$ ]] || \
php_darwin_die 'registry returned an invalid manifest digest header'
[ -z "$header_digest" ] || [ "$header_digest" = "$manifest_digest" ] || \
php_darwin_die "registry manifest digest mismatch: expected $header_digest, got $manifest_digest"
}
registry_inspect() {
inspect_image=$1
inspect_tag=$2
inspect_dir=$(mktemp -d "${RUNNER_TEMP:-/tmp}/php-darwin-registry.XXXXXX")
trap 'rm -rf "$inspect_dir"' EXIT
registry_auth "$inspect_image"
registry_manifest "$inspect_tag" "$inspect_dir/manifest.json" "$inspect_dir/headers" || return 1
jq -n --arg digest "$manifest_digest" --slurpfile manifest "$inspect_dir/manifest.json" \
'{digest:$digest,manifest:$manifest[0]}'
}
registry_inspect_many() {
inspect_image=$1
shift
inspect_dir=$(mktemp -d "${RUNNER_TEMP:-/tmp}/php-darwin-registry.XXXXXX")
trap 'rm -rf "$inspect_dir"' EXIT
inspect_jsonl="$inspect_dir/manifests.jsonl"
: > "$inspect_jsonl"
registry_auth "$inspect_image"
inspect_index=0
for inspect_tag in "$@"; do
[[ "$inspect_tag" =~ ^[A-Za-z0-9._-]+$ ]] || php_darwin_die "invalid OCI tag: $inspect_tag"
inspect_index=$((inspect_index + 1))
registry_manifest "$inspect_tag" "$inspect_dir/manifest-$inspect_index.json" "$inspect_dir/headers-$inspect_index" || return 1
jq -cn --arg tag "$inspect_tag" --arg digest "$manifest_digest" \
--slurpfile manifest "$inspect_dir/manifest-$inspect_index.json" \
'{key:$tag,value:{digest:$digest,manifest:$manifest[0]}}' >> "$inspect_jsonl" || \
php_darwin_die "could not record OCI manifest $inspect_tag"
done
jq -s 'from_entries' "$inspect_jsonl" || php_darwin_die 'could not combine OCI manifests'
}
registry_pull() {
pull_image=$1
pull_tag=$2
pull_asset=$3
pull_output=$4
pull_descriptor=$5
pull_dir=$(mktemp -d "${RUNNER_TEMP:-/tmp}/php-darwin-registry.XXXXXX")
trap 'rm -rf "$pull_dir"' EXIT
registry_auth "$pull_image"
registry_manifest "$pull_tag" "$pull_dir/manifest.json" "$pull_dir/headers" || \
php_darwin_die "could not fetch $pull_image:$pull_tag"
jq -c --arg asset "$pull_asset" \
'[.layers[] | select(.annotations["org.opencontainers.image.title"] == $asset)]' \
"$pull_dir/manifest.json" > "$pull_dir/layers.json" || php_darwin_die 'could not parse OCI layers'
[ "$(jq 'length' "$pull_dir/layers.json")" -eq 1 ] || \
php_darwin_die "expected one $pull_asset layer in $pull_tag"
layer_digest=$(jq -er '.[0].digest' "$pull_dir/layers.json") || php_darwin_die 'OCI layer digest is missing'
layer_size=$(jq -er '.[0].size' "$pull_dir/layers.json") || php_darwin_die 'OCI layer size is missing'
[[ "$layer_digest" =~ ^sha256:[0-9a-f]{64}$ ]] || php_darwin_die 'OCI layer digest is invalid'
[[ "$layer_size" =~ ^[0-9]+$ ]] || php_darwin_die 'OCI layer size is invalid'
curl --retry 3 --retry-all-errors -fsSL \
-H "Authorization: Bearer $registry_token" \
"https://$registry_host/v2/$registry_repository/blobs/$layer_digest" \
-o "$pull_output" || php_darwin_die "could not download $pull_asset"
actual_hash=$(php_darwin_sha256 "$pull_output") || php_darwin_die 'could not hash the OCI archive layer'
actual_digest="sha256:$actual_hash"
[ "$actual_digest" = "$layer_digest" ] || \
php_darwin_die "registry blob digest mismatch: expected $layer_digest, got $actual_digest"
actual_size=$(wc -c < "$pull_output")
actual_size=${actual_size//[[:space:]]/}
[ "$actual_size" = "$layer_size" ] || \
php_darwin_die "registry blob size mismatch: expected $layer_size, got $actual_size"
jq --arg manifest_digest "$manifest_digest" \
--slurpfile manifest "$pull_dir/manifest.json" \
--slurpfile layers "$pull_dir/layers.json" \
'.manifest_digest=$manifest_digest |
.schema_version=$manifest[0].schemaVersion |
.manifest_media_type=$manifest[0].mediaType |
.artifact_type=$manifest[0].artifactType |
.annotations=($manifest[0].annotations // {}) |
.layer=$layers[0][0]' \
"$script_dir/../templates/oci-descriptor.json" \
> "$pull_descriptor" || php_darwin_die 'could not write the OCI descriptor'
}
case "${1:-}" in
inspect)
[ "$#" -eq 3 ] || php_darwin_die 'usage: registry.sh inspect IMAGE TAG'
registry_inspect "$2" "$3"
;;
inspect-many)
[ "$#" -ge 4 ] || php_darwin_die 'usage: registry.sh inspect-many IMAGE TAG TAG [...]'
registry_inspect_many "$2" "${@:3}"
;;
pull)
[ "$#" -eq 6 ] || php_darwin_die 'usage: registry.sh pull IMAGE TAG ASSET OUTPUT DESCRIPTOR'
registry_pull "$2" "$3" "$4" "$5" "$6"
;;
*) php_darwin_die 'usage: registry.sh inspect|inspect-many|pull ...' ;;
esac
@@ -1,33 +0,0 @@
#!/usr/bin/env bash
script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)
# shellcheck source=scripts/lib.sh
. "$script_dir/lib.sh"
version=${1:?}
tap_path=${HOMEBREW_PHP_PATH:-}
php_darwin_validate_version "$version"
repository=$(php_darwin_package_config tap_repository)
branch=$(php_darwin_package_config tap_branch)
tmp_dir=$(mktemp -d "${RUNNER_TEMP:-/tmp}/php-darwin-source.XXXXXX")
trap 'rm -rf "$tmp_dir"' EXIT
hashes="$tmp_dir/formulae.tsv"
for build in release debug; do
for ts in nts zts; do
formula=$(php_darwin_formula "$version" "$build" "$ts")
formula_file="$tmp_dir/$formula.rb"
if [ -n "$tap_path" ]; then
cp "$tap_path/Formula/$formula.rb" "$formula_file" || php_darwin_die "could not read $formula from the local tap"
else
curl --retry 3 --retry-all-errors -fsSL \
"${repository/github.com/raw.githubusercontent.com}/$branch/Formula/$formula.rb" \
-o "$formula_file" || php_darwin_die "could not download $formula"
fi
formula_hash=$(php_darwin_sha256 "$formula_file") || php_darwin_die "could not hash $formula"
printf '%s\t%s\n' "$formula" "$formula_hash" >> "$hashes" || php_darwin_die 'could not record a formula hash'
done
done
LC_ALL=C sort -u "$hashes" -o "$hashes" || php_darwin_die 'could not sort formula hashes'
php_darwin_sha256 "$hashes" || php_darwin_die 'could not hash formula metadata'
@@ -1,8 +0,0 @@
{
"annotations": {},
"artifact_type": "",
"layer": {},
"manifest_digest": "",
"manifest_media_type": "",
"schema_version": 0
}
-9
View File
@@ -407,15 +407,6 @@ export async function scriptExtension(os: string): Promise<string> {
} }
} }
/**
* Get the generated run-script path without rewriting parent directories.
*
* @param scriptPath
*/
export function scriptRunPath(scriptPath: string): string {
return path.join(path.dirname(scriptPath), `run${path.extname(scriptPath)}`);
}
/** /**
* Function to get script tool * Function to get script tool
* *