|
| 1 | +name: Composer patch compatibility |
| 2 | + |
| 3 | +# Exact Composer releases on ubuntu, windows and macOS through the real |
| 4 | +# vendored (`e2e_vendor_composer_build`) and hosted |
| 5 | +# (`e2e_redirect_composer_build`) capstones, each ending in the |
| 6 | +# manifest-less VEX step, plus the hermetic `composer::` VEX cells. |
| 7 | +# |
| 8 | +# Release boundaries: 1.10.28 = Composer 1 (inline-repository fixture, |
| 9 | +# packagist dropped Composer 1 metadata); 2.0.14 / 2.1.14 = the 2.x lines |
| 10 | +# whose path mirror still drops `.gitignore`d files; 2.2.30 = LTS; |
| 11 | +# 2.5.8 / 2.8.12 = mid 2.x; 2.9.8 = the last release whose dist failure |
| 12 | +# falls back to `source`, so the hosted redirect's source drop is load |
| 13 | +# bearing there; 2.10.3 = current, no fallback. |
| 14 | +# |
| 15 | +# PHP: Composer 1 never on 8.5 (broken there). No 7.4 cell: the psr/log |
| 16 | +# 3.0.2 capstones need PHP >= 8.0; the depscan harness covers 7.4. |
| 17 | +# |
| 18 | +# Each leg runs one checksum-pinned composer.phar through `php` (the |
| 19 | +# capstones' SOCKET_PATCH_COMPOSER_PHAR mode), which is also what makes |
| 20 | +# Windows work: `Command::new("composer")` cannot resolve `composer.bat`. |
| 21 | + |
| 22 | +on: |
| 23 | + pull_request: |
| 24 | + paths: |
| 25 | + - '.github/workflows/composer-compatibility.yml' |
| 26 | + - 'tests/docker/Dockerfile.composer' |
| 27 | + - 'Cargo.lock' |
| 28 | + - 'Cargo.toml' |
| 29 | + - 'crates/*/Cargo.toml' |
| 30 | + - 'crates/socket-patch-core/src/vendor/**' |
| 31 | + - 'crates/socket-patch-core/src/patch/**' |
| 32 | + - 'crates/socket-patch-core/src/formats/composer/**' |
| 33 | + - 'crates/socket-patch-core/src/formats/registry.rs' |
| 34 | + - 'crates/socket-patch-core/src/hosted/**' |
| 35 | + - 'crates/socket-patch-core/src/ledgers.rs' |
| 36 | + - 'crates/socket-patch-core/src/policy/**' |
| 37 | + - 'crates/socket-patch-core/src/rollout.rs' |
| 38 | + - 'crates/socket-patch-core/src/rollout/**' |
| 39 | + - 'crates/socket-patch-core/src/manifest/**' |
| 40 | + - 'crates/socket-patch-core/tests/fixtures/redirect/composer/**' |
| 41 | + - 'crates/socket-patch-core/tests/fixtures/composer-version-vectors.json' |
| 42 | + - 'crates/socket-patch-core/src/crawlers/composer_crawler.rs' |
| 43 | + - 'crates/socket-patch-core/src/crawlers/composer_crawler/**' |
| 44 | + - 'crates/socket-patch-core/src/utils/composer*.rs' |
| 45 | + - 'crates/socket-patch-core/src/utils/purl.rs' |
| 46 | + - 'crates/socket-patch-core/src/utils/group_commit.rs' |
| 47 | + - 'crates/socket-patch-core/src/utils/durability.rs' |
| 48 | + - 'crates/socket-patch-core/src/vex/**' |
| 49 | + - 'crates/socket-patch-cli/src/commands/vendor*' |
| 50 | + - 'crates/socket-patch-cli/src/commands/vendored_backend/**' |
| 51 | + - 'crates/socket-patch-cli/src/commands/get*.rs' |
| 52 | + - 'crates/socket-patch-cli/src/commands/apply.rs' |
| 53 | + - 'crates/socket-patch-cli/src/commands/rollback.rs' |
| 54 | + - 'crates/socket-patch-cli/src/commands/remove.rs' |
| 55 | + - 'crates/socket-patch-cli/src/commands/composer_hints.rs' |
| 56 | + - 'crates/socket-patch-cli/src/commands/scan/**' |
| 57 | + - 'crates/socket-patch-cli/src/commands/vex*.rs' |
| 58 | + - 'crates/socket-patch-cli/tests/e2e_*composer*.rs' |
| 59 | + - 'crates/socket-patch-cli/tests/docker_e2e_vendor_composer.rs' |
| 60 | + - 'crates/socket-patch-cli/tests/composer_e2e_common/**' |
| 61 | + - 'crates/socket-patch-cli/tests/docker_vendor_common/**' |
| 62 | + - 'crates/socket-patch-cli/tests/e2e_vex_lockfile/composer.rs' |
| 63 | + - 'crates/socket-patch-cli/tests/vex_e2e_common/**' |
| 64 | + push: |
| 65 | + branches: [main] |
| 66 | + workflow_dispatch: |
| 67 | + |
| 68 | +permissions: |
| 69 | + contents: read |
| 70 | + |
| 71 | +concurrency: |
| 72 | + group: composer-compat-${{ github.event.pull_request.number || github.ref }} |
| 73 | + cancel-in-progress: true |
| 74 | + |
| 75 | +env: |
| 76 | + SOCKET_NO_CONFIG: '1' |
| 77 | + SOCKET_NO_UPDATE_CHECK: '1' |
| 78 | + |
| 79 | +jobs: |
| 80 | + native: |
| 81 | + name: composer ${{ matrix.composer }} / php ${{ matrix.php }} / ${{ matrix.os }} |
| 82 | + strategy: |
| 83 | + fail-fast: false |
| 84 | + matrix: |
| 85 | + include: |
| 86 | + - {os: ubuntu-latest, composer: '1.10.28', php: '8.1', sha256: 0915af36eb01e3f0e16cd309adff7051832b9ef014e38371756804b20425cd5a} |
| 87 | + - {os: ubuntu-latest, composer: '2.0.14', php: '8.0', sha256: 29454b41558968ca634bf5e2d4d07ff2275d91b637a76d7a05e6747d36dd3473} |
| 88 | + - {os: ubuntu-latest, composer: '2.1.14', php: '8.1', sha256: d44a904520f9aaa766e8b4b05d2d9a766ad9a6f03fa1a48518224aad703061a4} |
| 89 | + - {os: ubuntu-latest, composer: '2.2.30', php: '8.1', sha256: 8c2b4478b64f8f7cdf1574838fdb0033b29049ca821dad452db7a3dcfcdbffc2} |
| 90 | + - {os: ubuntu-latest, composer: '2.2.30', php: '8.3', sha256: 8c2b4478b64f8f7cdf1574838fdb0033b29049ca821dad452db7a3dcfcdbffc2} |
| 91 | + - {os: ubuntu-latest, composer: '2.5.8', php: '8.2', sha256: f07934fad44f9048c0dc875a506cca31cc2794d6aebfc1867f3b1fbf48dce2c5} |
| 92 | + - {os: ubuntu-latest, composer: '2.8.12', php: '8.4', sha256: f446ea719708bb85fcbf4ef18def5d0515f1f9b4d703f6d820c9c1656e10a2f2} |
| 93 | + - {os: ubuntu-latest, composer: '2.9.8', php: '8.4', sha256: 59b2c50e10cafa0d8efc19ede9a326d782f096c674a26baf98cf042ce23de890} |
| 94 | + - {os: ubuntu-latest, composer: '2.10.3', php: '8.5', sha256: 7a2d379d5b8ffdaa028580ef26494c36d2feef4b178d3dd1473a4dbc5e17c8d6} |
| 95 | + - {os: windows-latest, composer: '1.10.28', php: '8.1', sha256: 0915af36eb01e3f0e16cd309adff7051832b9ef014e38371756804b20425cd5a} |
| 96 | + - {os: windows-latest, composer: '2.2.30', php: '8.3', sha256: 8c2b4478b64f8f7cdf1574838fdb0033b29049ca821dad452db7a3dcfcdbffc2} |
| 97 | + - {os: windows-latest, composer: '2.9.8', php: '8.4', sha256: 59b2c50e10cafa0d8efc19ede9a326d782f096c674a26baf98cf042ce23de890} |
| 98 | + - {os: windows-latest, composer: '2.10.3', php: '8.5', sha256: 7a2d379d5b8ffdaa028580ef26494c36d2feef4b178d3dd1473a4dbc5e17c8d6} |
| 99 | + - {os: macos-latest, composer: '1.10.28', php: '8.1', sha256: 0915af36eb01e3f0e16cd309adff7051832b9ef014e38371756804b20425cd5a} |
| 100 | + - {os: macos-latest, composer: '2.2.30', php: '8.3', sha256: 8c2b4478b64f8f7cdf1574838fdb0033b29049ca821dad452db7a3dcfcdbffc2} |
| 101 | + - {os: macos-latest, composer: '2.10.3', php: '8.5', sha256: 7a2d379d5b8ffdaa028580ef26494c36d2feef4b178d3dd1473a4dbc5e17c8d6} |
| 102 | + runs-on: ${{ matrix.os }} |
| 103 | + timeout-minutes: 60 |
| 104 | + steps: |
| 105 | + - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 |
| 106 | + with: |
| 107 | + persist-credentials: false |
| 108 | + - run: rustup show |
| 109 | + - uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 |
| 110 | + with: |
| 111 | + key: composer-compat |
| 112 | + save-if: ${{ github.ref == 'refs/heads/main' }} |
| 113 | + - uses: shivammathur/setup-php@f3e473d116dcccaddc5834248c87452386958240 # 2.37.2 |
| 114 | + with: |
| 115 | + php-version: ${{ matrix.php }} |
| 116 | + tools: none |
| 117 | + extensions: zip, mbstring, curl, openssl |
| 118 | + ini-values: memory_limit=-1 |
| 119 | + coverage: none |
| 120 | + - name: Download and verify composer ${{ matrix.composer }} |
| 121 | + shell: bash |
| 122 | + env: |
| 123 | + COMPOSER_RELEASE: ${{ matrix.composer }} |
| 124 | + COMPOSER_PHAR_SHA256: ${{ matrix.sha256 }} |
| 125 | + # The pinned digest is the contract; the published .sha256sum is a |
| 126 | + # second, independent check. php computes the digest so the step |
| 127 | + # needs no sha256sum/shasum on any runner image. |
| 128 | + run: | |
| 129 | + set -euo pipefail |
| 130 | + dir="$RUNNER_TEMP" |
| 131 | + if command -v cygpath >/dev/null 2>&1; then |
| 132 | + dir="$(cygpath -m "$RUNNER_TEMP")" |
| 133 | + fi |
| 134 | + phar="$dir/composer-$COMPOSER_RELEASE.phar" |
| 135 | + base="https://getcomposer.org/download/$COMPOSER_RELEASE/composer.phar" |
| 136 | + curl -fsSL --retry 3 -o "$phar" "$base" |
| 137 | + published="$(curl -fsSL --retry 3 "$base.sha256sum" | cut -d' ' -f1)" |
| 138 | + # shellcheck disable=SC2016 # $argv is PHP, not shell |
| 139 | + actual="$(php -r 'echo hash_file("sha256", $argv[1]);' "$phar")" |
| 140 | + if [ "$actual" != "$COMPOSER_PHAR_SHA256" ] || [ "$actual" != "$published" ]; then |
| 141 | + echo "::error::composer $COMPOSER_RELEASE phar sha256 $actual (pinned $COMPOSER_PHAR_SHA256, published $published)" |
| 142 | + exit 1 |
| 143 | + fi |
| 144 | + reported="$(php "$phar" --version --no-ansi)" |
| 145 | + echo "$reported" |
| 146 | + case "$reported" in |
| 147 | + *"Composer version $COMPOSER_RELEASE "*) ;; |
| 148 | + *) echo "::error::expected composer $COMPOSER_RELEASE"; exit 1 ;; |
| 149 | + esac |
| 150 | + echo "SOCKET_PATCH_COMPOSER_PHAR=$phar" >> "$GITHUB_ENV" |
| 151 | + - name: Real-composer vendored + hosted flows with manifest-less VEX |
| 152 | + shell: bash |
| 153 | + env: |
| 154 | + SOCKET_PATCH_COMPOSER_E2E_REQUIRED: '1' |
| 155 | + SOCKET_PATCH_COMPOSER_E2E_VERSION: ${{ matrix.composer }} |
| 156 | + # The build capstones are `#[ignore]`-gated (the unpinned `test` job |
| 157 | + # skips them). The hermetic `composer::` VEX cells are a module of |
| 158 | + # the shared `e2e_vex_lockfile` binary; their filter goes in a second |
| 159 | + # command so it does not also filter the build suites. |
| 160 | + run: | |
| 161 | + cargo test -p socket-patch-cli --no-fail-fast \ |
| 162 | + --test e2e_vendor_composer_build --test e2e_redirect_composer_build \ |
| 163 | + -- --ignored --nocapture |
| 164 | + cargo test -p socket-patch-cli --test e2e_vex_lockfile -- composer:: --nocapture |
| 165 | +
|
| 166 | + docker: |
| 167 | + # The vendored Docker capstone against an exact composer image. Its |
| 168 | + # fixture resolves psr/log from packagist, which no longer serves |
| 169 | + # Composer 1, so 1.10.28 is covered by the native legs only. |
| 170 | + name: docker composer ${{ matrix.composer }} |
| 171 | + runs-on: ubuntu-latest |
| 172 | + timeout-minutes: 35 |
| 173 | + strategy: |
| 174 | + fail-fast: false |
| 175 | + matrix: |
| 176 | + include: |
| 177 | + - {composer: '2.2.30', sha256: 8c2b4478b64f8f7cdf1574838fdb0033b29049ca821dad452db7a3dcfcdbffc2} |
| 178 | + - {composer: '2.10.3', sha256: 7a2d379d5b8ffdaa028580ef26494c36d2feef4b178d3dd1473a4dbc5e17c8d6} |
| 179 | + steps: |
| 180 | + - name: Checkout |
| 181 | + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 |
| 182 | + with: |
| 183 | + persist-credentials: false |
| 184 | + |
| 185 | + - name: Set up Docker Buildx |
| 186 | + # `driver: docker` so the composer image's |
| 187 | + # `FROM socket-patch-test-base:latest` resolves against the host |
| 188 | + # daemon (as in ci.yml's e2e-docker job). |
| 189 | + uses: docker/setup-buildx-action@4d04d5d9486b7bd6fa91e7baf45bbb4f8b9deedd # v4.0.0 |
| 190 | + with: |
| 191 | + driver: docker |
| 192 | + |
| 193 | + - name: Install Rust |
| 194 | + run: rustup show |
| 195 | + |
| 196 | + # No `actions/cache`: this job builds Docker images (zizmor |
| 197 | + # cache-poisoning audit), as in ci.yml's e2e-docker job. |
| 198 | + |
| 199 | + - name: Build base image |
| 200 | + uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 |
| 201 | + with: |
| 202 | + context: . |
| 203 | + file: tests/docker/Dockerfile.base |
| 204 | + tags: socket-patch-test-base:latest |
| 205 | + load: true |
| 206 | + |
| 207 | + - name: Build composer ${{ matrix.composer }} image |
| 208 | + uses: docker/build-push-action@f9f3042f7e2789586610d6e8b85c8f03e5195baf # v7.2.0 |
| 209 | + with: |
| 210 | + context: . |
| 211 | + file: tests/docker/Dockerfile.composer |
| 212 | + build-args: | |
| 213 | + COMPOSER_VERSION=${{ matrix.composer }} |
| 214 | + COMPOSER_SHA256=${{ matrix.sha256 }} |
| 215 | + tags: socket-patch-test-composer:latest |
| 216 | + load: true |
| 217 | + |
| 218 | + - name: Run the vendored composer Docker capstone |
| 219 | + env: |
| 220 | + SOCKET_PATCH_DOCKER_E2E_REQUIRED: '1' |
| 221 | + run: cargo test -p socket-patch-cli --features docker-e2e --test docker_e2e_vendor_composer |
0 commit comments