spc-download #267
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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 |