Skip to content
Merged
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
200 changes: 167 additions & 33 deletions .github/workflows/spc-download.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,12 @@ 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
Expand All @@ -18,18 +24,11 @@ jobs:
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true
PHP_MINORS: "8.2 8.3 8.4 8.5"
steps:
- name: Set architecture variables
- name: Install PHP and composer
run: |
if [[ "${{ matrix.arch }}" == "arm64" ]]; then
echo "RPM_ARCH=aarch64" >> $GITHUB_ENV
else
echo "RPM_ARCH=x86_64" >> $GITHUB_ENV
fi

- name: Install composer
run: |
sudo curl -L https://files.henderkes.com/${RPM_ARCH}-linux/php -o /usr/local/bin/php
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
Expand All @@ -42,41 +41,176 @@ jobs:
- 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 // "")) ]
')
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] // "")) ]
')
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: 2
retention-days: 14

- name: Trigger build-rpm-modular-packages workflow
if: success()
run: gh workflow run build-rpm-modular-packages.yml
- name: Trigger downstream workflows
if: steps.diff.outputs.any_change == 'true'
env:
GH_TOKEN: ${{ secrets.GH_PAT }} # use our own user as the triggering user

#- name: Trigger build-gcc-deb-packages workflow
# if: success()
# run: gh workflow run build-gcc-deb-packages.yml
# env:
# GH_TOKEN: ${{ secrets.GH_PAT }} # use our own user as the triggering user

- name: Trigger build-deb-forgejo workflow
if: success()
run: gh workflow run build-deb-forgejo.yml
env:
GH_TOKEN: ${{ secrets.GH_PAT }} # use our own user as the triggering user

- name: Trigger build-apk-forgejo workflow
if: success()
run: gh workflow run build-apk-forgejo.yml
env:
GH_TOKEN: ${{ secrets.GH_PAT }} # use our own user as the triggering user
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
Loading