Skip to content

spc-download

spc-download #267

Workflow file for this run

name: spc-download
on:
schedule:
- cron: '0 0 * * *'
workflow_dispatch:
inputs:
force_full:
description: "Force a full rebuild of all PHP versions regardless of detected changes"
type: boolean
required: false
default: false
permissions:
contents: read
actions: write
jobs:
download:
runs-on: ubuntu-24.04
permissions:
contents: read
actions: write
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
PHP_MINORS: "8.2 8.3 8.4 8.5"
steps:
- name: Install PHP and composer
run: |
sudo curl -L https://files.henderkes.com/x86_64-linux/php -o /usr/local/bin/php
sudo chmod +x /usr/local/bin/php
sudo curl -sS https://raw.githubusercontent.com/composer/getcomposer.org/f3108f64b4e1c1ce6eb462b159956461592b3e3e/web/installer | php -- --quiet
sudo mv composer.phar /usr/local/bin/composer
- name: Checkout code
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
with:
persist-credentials: false
- name: Composer install
run: composer install
- name: Restore previous state
uses: actions/cache/restore@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: .download-state
key: spc-downloads-lock
- name: Seed empty state if missing
run: |
set -euo pipefail
mkdir -p .download-state
[[ -f .download-state/lock.prev.json ]] || echo '{}' > .download-state/lock.prev.json
[[ -f .download-state/meta.prev.json ]] || echo '{}' > .download-state/meta.prev.json
- name: Download extensions
run: |
php vendor/bin/spc download --shallow-clone -e amqp,apcu,ast,bcmath,brotli,bz2,calendar,ctype,curl,dba,decimal,deepclone,dio,dom,ds,ev,event,excimer,exif,ffi,fileinfo,filter,ftp,gd,gettext,gmp,gmssl,grpc,iconv,igbinary,imagick,inotify,intl,ldap,libxml,lz4,maxminddb,mbregex,mbstring,memcache,memcached,mongodb,msgpack,mysqli,mysqlnd,mysqlnd_parsec,mysqlnd_ed25519,odbc,opcache,openssl,opentelemetry,parallel,password-argon2,pcov,pcntl,pdo,pdo_mysql,pdo_odbc,pdo_pgsql,pdo_sqlite,pdo_sqlsrv,pgsql,phar,posix,protobuf,rar,rdkafka,readline,redis,session,shmop,simdjson,simplexml,snappy,soap,sockets,sodium,spx,sqlite3,sqlsrv,ssh2,swoole,sysvmsg,sysvsem,sysvshm,tidy,tokenizer,trader,uuid,uv,xdebug,xhprof,xlswriter,xml,xmlreader,xmlwriter,xsl,xz,yac,yaml,zip,zlib,zstd
- name: Probe latest PHP versions
id: probe-php
run: |
set -euo pipefail
new='{}'
for v in $PHP_MINORS; do
latest=$(curl -fsSL "https://www.php.net/releases/index.php?json=&max=1&version=${v}" \
| jq -r 'keys[0] // empty')
if [[ -z "$latest" ]]; then
echo "::error::Failed to fetch latest PHP version for ${v}"
exit 1
fi
echo "PHP ${v} latest: ${latest}"
new=$(jq --arg v "$v" --arg l "$latest" '. + {($v): $l}' <<< "$new")
done
mkdir -p downloads
jq --argjson v "$new" -n '{php_versions: $v}' > downloads/.spc-meta.json
- name: Diff state and compute triggers
id: diff
env:
FORCE_FULL: ${{ inputs.force_full }}
run: |
set -euo pipefail
EXT_JSON=vendor/crazywhalecc/static-php-cli/config/ext.json
src_to_ext=$(jq -c '
to_entries
| map(select(.value.source != null))
| map({(.value.source): .key})
| add // {}
' "$EXT_JSON")
echo "Source->ext map size: $(jq 'length' <<< "$src_to_ext")"
changed_sources=$(jq -nc \
--slurpfile prev .download-state/lock.prev.json \
--slurpfile cur downloads/.lock.json '
($prev[0] // {}) as $p | ($cur[0] // {}) as $c |
[ ($c | keys[]) as $k
| select(($p[$k].hash // "") != ($c[$k].hash // ""))
| $k ]
')
echo "Changed source keys: $(jq -c . <<< "$changed_sources")"
changed_exts=$(jq -nc --argjson s "$changed_sources" --argjson m "$src_to_ext" '
$s | map($m[.]) | map(select(. != null)) | unique
')
pkgs=$(jq -r 'join(",")' <<< "$changed_exts")
echo "Changed packages: ${pkgs:-<none>}"
full_versions=$(jq -nrc \
--slurpfile prev .download-state/meta.prev.json \
--slurpfile cur downloads/.spc-meta.json '
(($prev[0].php_versions) // {}) as $p |
(($cur[0].php_versions) // {}) as $c |
[ ($c | keys[]) as $k
| select($c[$k] != ($p[$k] // ""))
| $k ]
')
echo "PHP minors with bumped php-src: $(jq -c . <<< "$full_versions")"
all_minors=$(jq -nc --arg s "$PHP_MINORS" '$s | split(" ")')
if [[ "$FORCE_FULL" == "true" ]]; then
echo "force_full input set -> rebuilding all PHP versions"
full_versions="$all_minors"
fi
full_csv=$(jq -r 'join(",")' <<< "$full_versions")
partial_csv=""
if [[ -n "$pkgs" ]]; then
partial_csv=$(jq -nrc \
--argjson all "$all_minors" \
--argjson full "$full_versions" '
$all - $full | join(",")
')
fi
any="false"
if [[ -n "$pkgs" || -n "$full_csv" ]]; then
any="true"
fi
echo "full_php=$full_csv" | tee -a $GITHUB_OUTPUT
echo "partial_php=$partial_csv" | tee -a $GITHUB_OUTPUT
echo "changed_packages=$pkgs" | tee -a $GITHUB_OUTPUT
echo "any_change=$any" | tee -a $GITHUB_OUTPUT
{
echo "## spc-download summary"
echo "- Full rebuild PHP versions: ${full_csv:-<none>}"
echo "- Partial rebuild PHP versions: ${partial_csv:-<none>}"
echo "- Changed packages: ${pkgs:-<none>}"
echo "- Any change: $any"
} >> "$GITHUB_STEP_SUMMARY"
- name: Persist current state for next run
if: hashFiles('downloads/.lock.json') != ''
run: |
set -euo pipefail
cp downloads/.lock.json .download-state/lock.prev.json
cp downloads/.spc-meta.json .download-state/meta.prev.json
- name: Delete previous state cache
if: hashFiles('downloads/.lock.json') != ''
continue-on-error: true
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
gh api -X DELETE \
"/repos/$GITHUB_REPOSITORY/actions/caches?key=spc-downloads-lock" \
2>/dev/null || true
- name: Save state cache
if: hashFiles('downloads/.lock.json') != ''
uses: actions/cache/save@0057852bfaa89a56745cba8c7296529d2fc39830 # v4
with:
path: .download-state
key: spc-downloads-lock
- name: Create tarball (keep permissions)
if: steps.diff.outputs.any_change == 'true'
run: |
tar -czf downloads.tar.gz -C downloads .
- name: Upload downloads directory
if: steps.diff.outputs.any_change == 'true'
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with:
name: downloads-tarball
path: downloads.tar.gz
retention-days: 14
- name: Trigger downstream workflows
if: steps.diff.outputs.any_change == 'true'
env:
GH_TOKEN: ${{ secrets.GH_PAT }}
FULL_PHP: ${{ steps.diff.outputs.full_php }}
PARTIAL_PHP: ${{ steps.diff.outputs.partial_php }}
CHANGED_PACKAGES: ${{ steps.diff.outputs.changed_packages }}
run: |
set -euo pipefail
workflows=(
build-rpm-modular-packages.yml
build-deb-forgejo.yml
build-apk-forgejo.yml
)
for wf in "${workflows[@]}"; do
if [[ -n "$FULL_PHP" ]]; then
echo ">> $wf : full rebuild for $FULL_PHP"
gh workflow run "$wf" -f php_versions="$FULL_PHP"
fi
if [[ -n "$CHANGED_PACKAGES" && -n "$PARTIAL_PHP" ]]; then
echo ">> $wf : partial rebuild for $PARTIAL_PHP, packages=$CHANGED_PACKAGES"
gh workflow run "$wf" \
-f php_versions="$PARTIAL_PHP" \
-f packages="$CHANGED_PACKAGES"
fi
done