Compare commits

...

68 Commits

Author SHA1 Message Date
Shivam Mathur
6969e44b03 Release v2-verbose 2026-05-14 11:54:49 +05:30
Shivam Mathur
7c071dfe9d Bump version to 2.37.1
Update Node.js dependencies
2026-05-14 08:58:36 +05:30
Shivam Mathur
eeef37e059 GHSA-pqwm-q9pv-ph8r - Fix CWE-78 [skip ci] 2026-05-14 08:58:36 +05:30
Shivam Mathur
0dc33069a3 Fix phalcon5 support on Windows 2026-05-14 03:00:30 +05:30
Shivam Mathur
680a983990 Fix phalcon version for PHP 8.0 [skip ci] 2026-05-14 03:00:29 +05:30
Shivam Mathur
694649a4a3 Fix mutable tool cache restore 2026-05-13 19:43:21 +05:30
Shivam Mathur
46a991b6aa Merge pull request #1081 from Pyker/patch-1
Fix composer v2 version in README
2026-05-13 16:22:14 +05:30
Shivam Mathur
7748c24380 GHSA-f9f8-rm49-7jv2: Fix GitHub auth handling for composer in affected versions 2026-05-13 16:19:11 +05:30
Pedro Cunha
ac9c953234 Fix composer v2 version in README 2026-05-13 10:26:15 +01:00
Shivam Mathur
7729e411ec Improve enabling gearman [skip ci] 2026-04-29 01:00:16 +05:30
Shivam Mathur
af2322b95c Fix fallback in Install-PSPackage on Windows 2026-04-26 23:49:26 +05:30
Shivam Mathur
45e37a5311 Update os_releases.csv [skip ci] 2026-04-26 15:12:30 +05:30
Shivam Mathur
690cb7c9d8 Merge pull request #1074 from shivammathur/dependabot/github_actions/develop/codecov/codecov-action-6
Bump codecov/codecov-action from 5 to 6
2026-03-30 17:04:06 +05:30
dependabot[bot]
b2cf73f5fa Bump codecov/codecov-action from 5 to 6
Bumps [codecov/codecov-action](https://github.com/codecov/codecov-action) from 5 to 6.
- [Release notes](https://github.com/codecov/codecov-action/releases)
- [Changelog](https://github.com/codecov/codecov-action/blob/main/CHANGELOG.md)
- [Commits](https://github.com/codecov/codecov-action/compare/v5...v6)

---
updated-dependencies:
- dependency-name: codecov/codecov-action
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-30 11:19:53 +00:00
Shivam Mathur
64e2975255 Update dependencies 2026-03-29 20:51:51 +05:30
Shivam Mathur
106fd0866c Strip backslash line continuation from extension inputs 2026-03-29 20:46:19 +05:30
Shivam Mathur
44724c9282 Merge pull request #1072 from shivammathur/dependabot/npm_and_yarn/npm_and_yarn-66413a1f6e
Bump picomatch from 2.3.1 to 2.3.2 in the npm_and_yarn group across 1 directory
2026-03-26 07:42:11 +05:30
dependabot[bot]
2fa35d2e4e Bump picomatch in the npm_and_yarn group across 1 directory
Bumps the npm_and_yarn group with 1 update in the / directory: [picomatch](https://github.com/micromatch/picomatch).


Updates `picomatch` from 2.3.1 to 2.3.2
- [Release notes](https://github.com/micromatch/picomatch/releases)
- [Changelog](https://github.com/micromatch/picomatch/blob/master/CHANGELOG.md)
- [Commits](https://github.com/micromatch/picomatch/compare/2.3.1...2.3.2)

---
updated-dependencies:
- dependency-name: picomatch
  dependency-version: 2.3.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-26 00:21:48 +00:00
Shivam Mathur
29e04e0a1d Add workflow examples for Drupal and WordPress plugins 2026-03-23 14:33:38 +05:30
Shivam Mathur
ea13793c13 Merge pull request #1071 from shivammathur/dependabot/npm_and_yarn/npm_and_yarn-e5a595f223
Bump flatted from 3.4.1 to 3.4.2 in the npm_and_yarn group across 1 directory
2026-03-22 02:27:26 +05:30
dependabot[bot]
fd580061e0 Bump flatted in the npm_and_yarn group across 1 directory
Bumps the npm_and_yarn group with 1 update in the / directory: [flatted](https://github.com/WebReflection/flatted).


Updates `flatted` from 3.4.1 to 3.4.2
- [Commits](https://github.com/WebReflection/flatted/compare/v3.4.1...v3.4.2)

---
updated-dependencies:
- dependency-name: flatted
  dependency-version: 3.4.2
  dependency-type: indirect
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-21 20:14:26 +00:00
Shivam Mathur
90d81e2adc Update examples 2026-03-21 21:47:58 +05:30
Shivam Mathur
93cb3149d2 Fix permission for OIDC tokens in publish.yml for trusted publishing [skip ci] 2026-03-15 22:01:57 +05:30
Shivam Mathur
accd6127cb Update dependencies 2026-03-15 21:44:02 +05:30
Shivam Mathur
a5c2146f3f Improve tool cache to not cache persistent urls 2026-03-15 11:36:39 +05:30
Shivam Mathur
6a1d559c57 Fix regression in pecl_http support [skip ci] 2026-03-15 11:18:29 +05:30
Shivam Mathur
076a5e3f0d Update actions in examples [skip ci] 2026-03-15 11:00:48 +05:30
Shivam Mathur
00c8f84a71 Add support for ionCube for PHP 8.5 [skip ci] 2026-03-13 22:21:20 +05:30
Shivam Mathur
c033e31e7c Add support for zephir parser for PHP 8.5 [skip ci] 2026-03-13 22:10:53 +05:30
Shivam Mathur
bc6f40a11a Switch to downloads.php.net in Get-ICUUrl [skip ci] 2026-03-13 21:33:26 +05:30
Shivam Mathur
fe26b509d7 Fix support for pecl_http on Windows [skip ci] 2026-03-13 20:50:10 +05:30
Shivam Mathur
e67ff94e49 Fix caching couchbase on macOS [skip ci] 2026-03-13 20:15:35 +05:30
Shivam Mathur
4e89813b4b Improve switch_version on Linux 2026-03-12 19:35:06 +05:30
Shivam Mathur
a33066c001 Merge pull request #1065 from shivammathur/dependabot/github_actions/develop/actions/download-artifact-8
Bump actions/download-artifact from 7 to 8
2026-03-09 17:10:54 +05:30
Shivam Mathur
f1643fd598 Merge pull request #1066 from shivammathur/dependabot/github_actions/develop/actions/upload-artifact-7
Bump actions/upload-artifact from 6 to 7
2026-03-09 17:10:45 +05:30
dependabot[bot]
e167f5c259 Bump actions/upload-artifact from 6 to 7
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 6 to 7.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-09 11:18:10 +00:00
dependabot[bot]
45158d762a Bump actions/download-artifact from 7 to 8
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 7 to 8.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v7...v8)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '8'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2026-03-09 11:18:07 +00:00
Shivam Mathur
8724c1dcc1 Add macos-26-intel to the README [skip ci] 2026-03-09 01:03:00 +05:30
Shivam Mathur
a1e0f566a8 Revert to old brew linking behaviour 2026-03-08 17:23:40 +05:30
Shivam Mathur
650d05dc41 Merge pull request #1064 from theluckystrike/main
fix: use latest in URL when version is empty
2026-03-04 21:05:48 +05:30
theluckystrike
746e1a46d1 fix: use latest in URL when version is empty
When version is not specified (empty string), the getUrl function
was generating invalid URLs like /releases/download/cs2pr without
any version, which returned 404.

This fix ensures that when version is empty or 'latest', the URL
includes '/latest/' which GitHub handles correctly with a redirect
to the actual latest release.

Fixes #1063
2026-03-04 22:03:38 +07:00
Shivam Mathur
a8ca9e3783 Fix npm audit 2026-02-28 11:55:28 +05:30
Shivam Mathur
769a4a81fd Update sqlsrv and pdo_sqlsrv versions [skip ci] 2026-02-28 02:22:42 +05:30
Shivam Mathur
d042aafd13 Add retry and timeout for stuck brew calls 2026-02-23 15:42:41 +05:30
Shivam Mathur
f97ca00780 Bump dependencies 2026-02-23 15:42:41 +05:30
Shivam Mathur
3aeeb03660 Update PHP version and OS support in README [skip ci] 2026-02-17 03:59:36 +05:30
Shivam Mathur
57e8183dae Switch to brew formula for pdo_firebird on macos [skip ci] 2026-02-08 11:29:04 +05:30
Shivam Mathur
5e98c022f7 Update dependencies 2026-02-08 09:13:03 +05:30
Shivam Mathur
f0b3fd9afe Update brew extensions 2026-02-08 07:56:22 +05:30
Shivam Mathur
1eee54fe48 Fix paths for bin tools and scoped tools [skip ci] 2026-01-23 01:04:38 +05:30
Shivam Mathur
341bc9e176 Improve cache for tools 2026-01-21 17:02:28 +05:30
Shivam Mathur
185f9de395 Fix linking tools 2026-01-21 15:54:25 +05:30
Shivam Mathur
46cb5030ab Replace generic record interfaces with specific ones 2026-01-21 02:46:20 +05:30
Shivam Mathur
6300a313a9 Remove unused @actions/io 2026-01-20 08:15:36 +05:30
Shivam Mathur
b7741bd785 Fix eslint config for imports 2026-01-20 07:47:37 +05:30
Shivam Mathur
109ae4d1c0 Reduce bundle size
Replace @actions/core with local functions

@actions/core is unmaintained and poorly designed for projects that just need basic functions
2026-01-20 07:47:23 +05:30
Shivam Mathur
871ff01b2b Refactor to use ES2024+ features for Node 24
Use Set for O(1) redirect status code lookup in fetch.ts

Use at(-1) and Object.hasOwn() in tools.ts

Use for...of with entries() in utils.ts
2026-01-20 06:46:28 +05:30
Shivam Mathur
f0e37f9e90 Update Node version in node workflow 2026-01-20 06:30:53 +05:30
Shivam Mathur
46ae35f333 Update to Node 24
Update fetch.ts to use native fetch() API

Use immutable sort in tools.ts
2026-01-20 06:29:00 +05:30
Shivam Mathur
f89a301251 Override undici version 2026-01-17 03:15:23 +05:30
Shivam Mathur
5efa2a774e Fix support to parse master in php-version input 2025-12-18 21:36:10 +05:30
Shivam Mathur
f72fc99524 Merge pull request #1045 from shivammathur/dependabot/github_actions/develop/actions/download-artifact-7
Bump actions/download-artifact from 6 to 7
2025-12-15 15:54:23 +05:30
Shivam Mathur
c84edb415f Merge pull request #1047 from shivammathur/dependabot/github_actions/develop/actions/upload-artifact-6
Bump actions/upload-artifact from 5 to 6
2025-12-15 15:54:03 +05:30
Shivam Mathur
4dda6da925 Merge pull request #1046 from shivammathur/dependabot/github_actions/develop/actions/cache-5
Bump actions/cache from 4 to 5
2025-12-15 15:53:27 +05:30
dependabot[bot]
c14319add5 Bump actions/upload-artifact from 5 to 6
Bumps [actions/upload-artifact](https://github.com/actions/upload-artifact) from 5 to 6.
- [Release notes](https://github.com/actions/upload-artifact/releases)
- [Commits](https://github.com/actions/upload-artifact/compare/v5...v6)

---
updated-dependencies:
- dependency-name: actions/upload-artifact
  dependency-version: '6'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-15 10:02:24 +00:00
dependabot[bot]
5ba12107fc Bump actions/cache from 4 to 5
Bumps [actions/cache](https://github.com/actions/cache) from 4 to 5.
- [Release notes](https://github.com/actions/cache/releases)
- [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md)
- [Commits](https://github.com/actions/cache/compare/v4...v5)

---
updated-dependencies:
- dependency-name: actions/cache
  dependency-version: '5'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-15 10:02:21 +00:00
dependabot[bot]
8bd624e171 Bump actions/download-artifact from 6 to 7
Bumps [actions/download-artifact](https://github.com/actions/download-artifact) from 6 to 7.
- [Release notes](https://github.com/actions/download-artifact/releases)
- [Commits](https://github.com/actions/download-artifact/compare/v6...v7)

---
updated-dependencies:
- dependency-name: actions/download-artifact
  dependency-version: '7'
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-12-15 10:02:16 +00:00
Shivam Mathur
3dfaca4ee1 Rename nightly_versions -> php_builder_versions 2025-11-26 22:22:06 +05:30
96 changed files with 3272 additions and 4620 deletions

View File

@@ -88,7 +88,7 @@ jobs:
Remove-Item "$env:file.all" -Force
Remove-Item "$env:file.builtin" -Force
- name: Upload artifacts
uses: actions/upload-artifact@v5
uses: actions/upload-artifact@v7
with:
name: lists-php${{ matrix.php-versions }}-${{ matrix.operating-system }}.md
path: php${{ matrix.php-versions }}-${{ matrix.operating-system }}.md
@@ -105,7 +105,7 @@ jobs:
with:
repository: ${{ github.repository }}.wiki
- name: Download artifacts
uses: actions/download-artifact@v6
uses: actions/download-artifact@v8
with:
path: ${{ github.workspace }}/lists
pattern: lists-*

View File

@@ -33,10 +33,10 @@ jobs:
with:
fetch-depth: 2
- name: Setup Node.js 20.x
- name: Setup Node.js 24.x
uses: actions/setup-node@v6
with:
node-version: 20.x
node-version: 24.x
- name: Install dependencies
run: npm install
@@ -54,7 +54,7 @@ jobs:
run: npm audit
- name: Send Coverage
uses: codecov/codecov-action@v5
uses: codecov/codecov-action@v6
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: coverage/lcov.info

View File

@@ -50,7 +50,7 @@ jobs:
key: ${{ env.key }}
- name: Cache extensions
uses: actions/cache@v4
uses: actions/cache@v5
with:
path: ${{ steps.cache-env.outputs.dir }}
key: ${{ steps.cache-env.outputs.key }}

View File

@@ -16,6 +16,7 @@ jobs:
permissions:
contents: read
packages: write
id-token: write
steps:
- name: Checkout release
if: github.event_name != 'workflow_dispatch'
@@ -30,7 +31,7 @@ jobs:
- name: Setup Node.js
uses: actions/setup-node@v6
with:
node-version: '20.x'
node-version: '24.x'
registry-url: https://registry.npmjs.org
- name: Install dependencies and add lib
@@ -42,8 +43,6 @@ jobs:
- name: Publish to NPM
if: "!contains(github.event.inputs.skip, 'skip-npm')"
run: npm publish --access public
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Change to GitHub Packages registry
uses: actions/setup-node@v6

View File

@@ -74,13 +74,13 @@ Both `GitHub-hosted` and `self-hosted` runners are supported by `setup-php` on t
| Ubuntu 22.04 | x86_64 | `ubuntu-22.04` | `PHP 8.1` |
| Ubuntu 24.04 | aarch64 | `ubuntu-24.04-arm` | `PHP 8.3` |
| Ubuntu 22.04 | aarch64 | `ubuntu-22.04-arm` | `PHP 8.1` |
| Windows Server 2025 | x64 | `windows-2025` | `PHP 8.3` |
| Windows Server 2022 | x64 | `windows-latest` or `windows-2022` | `PHP 8.3` |
| Windows Server 2025 | x64 | `windows-2025` | `PHP 8.5` |
| Windows Server 2022 | x64 | `windows-latest` or `windows-2022` | `PHP 8.5` |
| macOS Tahoe 26.x | arm64 | `macos-26` | - |
| macOS Sequoia 15.x | arm64 | `macos-latest` or `macos-15` | - |
| macOS Sonoma 14.x | arm64 | `macos-14` | - |
| macOS Sequoia 15.x | x86_64 | `macos-15-intel` | `PHP 8.3` |
| macOS Ventura 13.x | x86_64 | `macos-13` | `PHP 8.3` |
| macOS Tahoe 26.x | x86_64 | `macos-26-intel` | `PHP 8.5` |
| macOS Sequoia 15.x | x86_64 | `macos-15-intel` | `PHP 8.5` |
### Self-Hosted Runners
@@ -121,9 +121,9 @@ On all supported OS/Platforms, the following PHP versions can be set up as per t
| `7.3` | `Stable` | `End of life` | `GitHub-hosted`, `self-hosted` |
| `7.4` | `Stable` | `End of life` | `GitHub-hosted`, `self-hosted` |
| `8.0` | `Stable` | `End of life` | `GitHub-hosted`, `self-hosted` |
| `8.1` | `Stable` | `Security fixes only` | `GitHub-hosted`, `self-hosted` |
| `8.1` | `Stable` | `End of life` | `GitHub-hosted`, `self-hosted` |
| `8.2` | `Stable` | `Security fixes only` | `GitHub-hosted`, `self-hosted` |
| `8.3` | `Stable` | `Active` | `GitHub-hosted`, `self-hosted` |
| `8.3` | `Stable` | `Security fixes only` | `GitHub-hosted`, `self-hosted` |
| `8.4` | `Stable` | `Active` | `GitHub-hosted`, `self-hosted` |
| `8.5` | `Stable` | `Active` | `GitHub-hosted`, `self-hosted` |
| `8.6` | `Nightly` | `In development` | `GitHub-hosted`, `self-hosted` |
@@ -268,7 +268,7 @@ These tools can be set up globally using the `tools` input. It accepts a string
uses: shivammathur/setup-php@v2
with:
php-version: '8.5'
tools: composer:v1
tools: composer:v2
```
- If you do not use composer in your workflow, you can specify `tools: none` to skip it.
@@ -950,11 +950,12 @@ Examples of using `setup-php` with various PHP frameworks and packages.
| Framework/Package | Runs on | Workflow |
|----------------------------------------|---------------------------------|---------------------------------------------------------------------------------------------------------------|
| Blackfire | `macOS`, `ubuntu` and `windows` | [blackfire.yml](./examples/blackfire.yml "GitHub Action using Blackfire") |
| Blackfire Player | `macOS`, `ubuntu` and `windows` | [blackfire-player.yml](./examples/blackfire-player.yml "GitHub Action using Blackfire Player") |
| Blackfire Player | `macOS` and `ubuntu` | [blackfire-player.yml](./examples/blackfire-player.yml "GitHub Action using Blackfire Player") |
| CakePHP with `MySQL` and `Redis` | `ubuntu` | [cakephp-mysql.yml](./examples/cakephp-mysql.yml "GitHub Action for CakePHP with MySQL and Redis") |
| CakePHP with `PostgreSQL` and `Redis` | `ubuntu` | [cakephp-postgres.yml](./examples/cakephp-postgres.yml "GitHub Action for CakePHP with Postgres and Redis") |
| CakePHP without services | `macOS`, `ubuntu` and `windows` | [cakephp.yml](./examples/cakephp.yml "GitHub Action for CakePHP without services") |
| CodeIgniter | `macOS`, `ubuntu` and `windows` | [codeigniter.yml](./examples/codeigniter.yml "GitHub Action for CodeIgniter") |
| Drupal 11 (Composer-managed) | `ubuntu` | [drupal.yml](./examples/drupal.yml "GitHub Action for Drupal 11 composer-managed projects") |
| Laminas MVC | `macOS`, `ubuntu` and `windows` | [laminas-mvc.yml](./examples/laminas-mvc.yml "GitHub Action for Laminas Framework MVC Projects") |
| Laravel with `MySQL` and `Redis` | `ubuntu` | [laravel-mysql.yml](./examples/laravel-mysql.yml "GitHub Action for Laravel with MySQL and Redis") |
| Laravel with `PostgreSQL` and `Redis` | `ubuntu` | [laravel-postgres.yml](./examples/laravel-postgres.yml "GitHub Action for Laravel with PostgreSQL and Redis") |
@@ -964,14 +965,16 @@ Examples of using `setup-php` with various PHP frameworks and packages.
| Lumen without services | `macOS`, `ubuntu` and `windows` | [lumen.yml](./examples/lumen.yml "GitHub Action for Lumen without services") |
| Phalcon with `MySQL` | `ubuntu` | [phalcon-mysql.yml](./examples/phalcon-mysql.yml "GitHub Action for Phalcon with MySQL") |
| Phalcon with `PostgreSQL` | `ubuntu` | [phalcon-postgres.yml](./examples/phalcon-postgres.yml "GitHub Action for Phalcon with PostgreSQL") |
| Roots/bedrock | `ubuntu` | [bedrock.yml](./examples/bedrock.yml "GitHub Action for Wordpress Development using @roots/bedrock") |
| Roots/sage | `ubuntu` | [sage.yml](./examples/sage.yml "GitHub Action for Wordpress Development using @roots/sage") |
| Slim Framework | `macOS`, `ubuntu` and `windows` | [slim-framework.yml](./examples/slim-framework.yml "GitHub Action for Slim Framework") |
| Symfony with `MySQL` | `ubuntu` | [symfony-mysql.yml](./examples/symfony-mysql.yml "GitHub Action for Symfony with MySQL") |
| Symfony with `PostgreSQL` | `ubuntu` | [symfony-postgres.yml](./examples/symfony-postgres.yml "GitHub Action for Symfony with PostgreSQL") |
| Symfony without services | `macOS`, `ubuntu` and `windows` | [symfony.yml](./examples/symfony.yml "GitHub Action for Symfony without services") |
| Yii2 Starter Kit with `MySQL` | `ubuntu` | [yii2-mysql.yml](./examples/yii2-mysql.yml "GitHub Action for Yii2 Starter Kit with MySQL") |
| Yii2 Starter Kit with `PostgreSQL` | `ubuntu` | [yii2-postgres.yml](./examples/yii2-postgres.yml "GitHub Action for Yii2 Starter Kit with PostgreSQL") |
| WordPress plugin | `ubuntu` | [wordpress.yml](./examples/wordpress.yml "GitHub Action for WordPress plugins") |
| WordPress with Roots/Bedrock | `ubuntu` | [bedrock.yml](./examples/bedrock.yml "GitHub Action for WordPress development using @roots/bedrock") |
| WordPress with Roots/Sage | `ubuntu` | [sage.yml](./examples/sage.yml "GitHub Action for WordPress development using @roots/sage") |
| Yii3 web application with `MySQL` | `ubuntu` | [yii3-mysql.yml](./examples/yii3-mysql.yml "GitHub Action for Yii3 web application with MySQL") |
| Yii3 web application with `PostgreSQL` | `ubuntu` | [yii3-postgres.yml](./examples/yii3-postgres.yml "GitHub Action for Yii3 web application with PostgreSQL") |
| Yii3 web application | `ubuntu` and `windows` | [yii3.yml](./examples/yii3.yml "GitHub Action for Yii3 web application") |
## :bookmark: Versioning

View File

@@ -9,6 +9,10 @@ describe('Config tests', () => {
${'a=b & ~c'} | ${'win32'} | ${'Add-Content "$php_dir\\php.ini" "a=\'b & ~c\'"'}
${'a="~(b)"'} | ${'win32'} | ${'Add-Content "$php_dir\\php.ini" "a=\'~(b)\'"'}
${'a="b, c"'} | ${'win32'} | ${'Add-Content "$php_dir\\php.ini" "a=b, c"'}
${'disable_functions="exec,system"'} | ${'linux'} | ${'echo "disable_functions=exec,system" | sudo tee -a'}
${'disable_functions="exec,system"'} | ${'win32'} | ${'Add-Content "$php_dir\\php.ini" "disable_functions=exec,system"'}
${'a=$(id)'} | ${'linux'} | ${'echo "a=\'\\$(id)\'"'}
${'a=$(id)'} | ${'win32'} | ${'Add-Content "$php_dir\\php.ini" "a=\'`$(id)\'"'}
${'a=b, c=d'} | ${'openbsd'} | ${'Platform openbsd is not supported'}
`('checking addINIValues on $os', async ({ini_values, os, output}) => {
expect(await config.addINIValues(ini_values, os)).toContain(output);

106
__tests__/core.test.ts Normal file
View File

@@ -0,0 +1,106 @@
import * as core from '../src/core';
describe('Core tests', () => {
const originalEnv = process.env;
const originalExitCode = process.exitCode;
let stdoutOutput: string;
const originalWrite = process.stdout.write;
beforeEach(() => {
process.env = {...originalEnv};
process.exitCode = undefined;
stdoutOutput = '';
process.stdout.write = jest.fn((chunk: string | Uint8Array): boolean => {
stdoutOutput += chunk.toString();
return true;
}) as unknown as typeof process.stdout.write;
});
afterEach(() => {
process.env = originalEnv;
process.exitCode = originalExitCode;
process.stdout.write = originalWrite;
});
it('checking issueCommand with no properties', () => {
core.issueCommand('warning', {}, 'test message');
expect(stdoutOutput).toContain('::warning::test message');
});
it('checking issueCommand with properties', () => {
core.issueCommand('error', {file: 'test.ts', line: '10'}, 'error message');
expect(stdoutOutput).toContain(
'::error file=test.ts,line=10::error message'
);
});
it('checking issueCommand escapes special characters in message', () => {
core.issueCommand('warning', {}, 'line1\nline2\rline3%percent');
expect(stdoutOutput).toContain(
'::warning::line1%0Aline2%0Dline3%25percent'
);
});
it('checking issueCommand escapes special characters in properties', () => {
core.issueCommand('error', {file: 'path:to,file'}, 'message');
expect(stdoutOutput).toContain('::error file=path%3Ato%2Cfile::message');
});
it('checking issueCommand with Error object', () => {
const error = new Error('test error');
core.issueCommand('error', {}, error);
expect(stdoutOutput).toContain('::error::Error: test error');
});
it('checking issueCommand filters empty properties', () => {
core.issueCommand('warning', {file: 'test.ts', line: ''}, 'message');
expect(stdoutOutput).toContain('::warning file=test.ts::message');
});
it('checking error', () => {
core.error('error message');
expect(stdoutOutput).toContain('::error::error message');
});
it('checking error with Error object', () => {
core.error(new Error('error instance'));
expect(stdoutOutput).toContain('::error::Error: error instance');
});
it('checking setFailed', () => {
core.setFailed('failure message');
expect(process.exitCode).toBe(1);
expect(stdoutOutput).toContain('::error::failure message');
});
it('checking setFailed with Error object', () => {
core.setFailed(new Error('failure error'));
expect(process.exitCode).toBe(1);
expect(stdoutOutput).toContain('::error::Error: failure error');
});
it('checking getInput returns value', () => {
process.env['INPUT_TEST-INPUT'] = 'test value';
expect(core.getInput('test-input')).toBe('test value');
});
it('checking getInput trims value', () => {
process.env['INPUT_TEST-INPUT'] = ' trimmed ';
expect(core.getInput('test-input')).toBe('trimmed');
});
it('checking getInput returns empty string for missing input', () => {
expect(core.getInput('missing-input')).toBe('');
});
it('checking getInput throws for required missing input', () => {
expect(() => core.getInput('missing-input', true)).toThrow(
'Input required and not supplied: missing-input'
);
});
it('checking getInput handles spaces in name', () => {
process.env['INPUT_INPUT_WITH_SPACES'] = 'spaced value';
expect(core.getInput('input with spaces')).toBe('spaced value');
});
});

View File

@@ -21,6 +21,7 @@ describe('Extension tests', () => {
${'ibm_db2'} | ${'7.4'} | ${'Add-Ibm ibm_db2'}
${'pdo_ibm'} | ${'7.4'} | ${'Add-Ibm pdo_ibm'}
${'pecl_http'} | ${'7.4'} | ${'Add-Http'}
${'http'} | ${'8.5'} | ${'Add-Http'}
${'pdo_sqlsrv'} | ${'7.4'} | ${'Add-Sqlsrv pdo_sqlsrv'}
${'phalcon3'} | ${'7.2'} | ${'Add-Phalcon phalcon3'}
${'phalcon4'} | ${'7.4'} | ${'Add-Phalcon phalcon4'}
@@ -131,7 +132,11 @@ describe('Extension tests', () => {
)
? `add_${ext_name}`
: `add_brew_extension ${formula} ${prefix}`;
return [formula, formula === 'phalcon3' ? '7.3' : '7.4', output];
return [
formula,
formula.match(/phalcon3|lua|propro/) ? '7.3' : '8.1',
output
];
});
it.each(data)(

View File

@@ -19,7 +19,9 @@ it('checking fetch', async () => {
Location: host_url + '/pong'
})
.get('/pong')
.reply(200, 'pong');
.reply(200, 'pong')
.get('/error')
.replyWithError('Network failure');
let response: Record<string, string> = await fetch.fetch(manifest_url);
expect(response.error).toBe(undefined);
@@ -36,4 +38,8 @@ it('checking fetch', async () => {
response = await fetch.fetch(manifest_url, 'invalid_token');
expect(response.error).not.toBe(undefined);
expect(response.data).toBe(undefined);
response = await fetch.fetch(host_url + '/error');
expect(response.error).toContain('Fetch error:');
expect(response.data).toBe(undefined);
});

View File

@@ -1,43 +1,42 @@
import * as fs from 'fs';
import * as tools from '../src/tools';
import {ToolData, ToolInput} from '../src/tools';
interface IData {
tool: string;
version?: string;
domain?: string;
extension?: string;
os?: string;
php_version?: string;
release?: string;
repository?: string;
scope?: string;
type?: string;
fetch_latest?: string;
version_parameter?: string;
version_prefix?: string;
}
function getData(data: IData): Record<string, string> {
function getData(data: Partial<ToolData>): ToolData {
const tool = data.tool || 'tool';
const version = data.version || '';
return {
tool: data.tool,
version: data.version || '',
tool,
version,
url: data.url || '',
domain: data.domain || 'https://example.com',
extension: data.extension || '.phar',
os: data.os || 'linux',
php_version: data.php_version || '7.4',
release: data.release || [data.tool, data.version].join(':'),
release: data.release || [tool, version].join(':'),
repository: data.repository || '',
scope: data.scope || 'global',
type: data.type || 'phar',
fetch_latest: data.fetch_latest || 'false',
version_parameter: data.version_parameter || '-V',
version_prefix: data.version_prefix || '',
github: 'https://github.com',
prefix: 'releases',
verb: 'download'
github: data.github || 'https://github.com',
prefix: data.prefix || 'releases',
verb: data.verb || 'download',
packagist: data.packagist || data.repository || '',
function: data.function,
alias: data.alias,
uri: data.uri,
error: data.error
};
}
function unsetComposerAuthEnv(): void {
delete process.env['GITHUB_TOKEN'];
delete process.env['COMPOSER_TOKEN'];
delete process.env['COMPOSER_AUTH_JSON'];
}
/**
* Mock fetch.ts
*/
@@ -161,6 +160,18 @@ describe('Tools tests', () => {
}
);
it('checking getLatestVersion with fetch_latest=true but no repository', async () => {
expect(
await tools.getLatestVersion(
getData({
tool: 'tool',
repository: '',
fetch_latest: 'true'
})
)
).toBe('latest');
});
it.each`
version | tool | type | expected
${'latest'} | ${'tool'} | ${'phar'} | ${'latest'}
@@ -176,6 +187,7 @@ describe('Tools tests', () => {
${'1.2.3-dev'} | ${'tool'} | ${'phar'} | ${'1.2.3-dev'}
${'1.2.3-alpha1'} | ${'tool'} | ${'phar'} | ${'1.2.3-alpha1'}
${'1.2.3-alpha.1'} | ${'tool'} | ${'phar'} | ${'1.2.3-alpha.1'}
${'1.>=0'} | ${'tool'} | ${'phar'} | ${'1.0'}
`(
'checking getVersion: $version, $tool, $type',
async ({version, tool, type, expected}) => {
@@ -245,15 +257,17 @@ describe('Tools tests', () => {
);
it('checking getUrl handles undefined version without double slash', async () => {
const data = getData({
const data: ToolInput = {
...getData({
tool: 'cs2pr',
repository: 'staabm/annotate-pull-request-from-checkstyle',
domain: 'https://github.com'
});
data['extension'] = '';
delete data['version'];
}),
version: undefined
};
data.extension = '';
expect(await tools.getUrl(data)).toBe(
'https://github.com/staabm/annotate-pull-request-from-checkstyle/releases/download/cs2pr'
'https://github.com/staabm/annotate-pull-request-from-checkstyle/releases/latest/download/cs2pr'
);
});
@@ -284,29 +298,37 @@ describe('Tools tests', () => {
tool: 'tool',
version: 'latest',
version_parameter: JSON.stringify('-v'),
os: os
os: os,
url: 'https://example.com/tool.phar'
});
data['url'] = 'https://example.com/tool.phar';
expect(await tools.addArchive(data)).toContain(script);
});
it.each`
os | script | scope
${'linux'} | ${'add_composer_tool tool tool:1.2.3 user/ global'} | ${'global'}
${'darwin'} | ${'add_composer_tool tool tool:1.2.3 user/ scoped'} | ${'scoped'}
${'win32'} | ${'Add-ComposerTool tool tool:1.2.3 user/ scoped'} | ${'scoped'}
${'openbsd'} | ${'Platform openbsd is not supported'} | ${'global'}
`('checking addPackage: $os, $scope', async ({os, script, scope}) => {
os | release | scope | script
${'linux'} | ${'tool:1.2.3'} | ${'global'} | ${'add_composer_tool tool tool:1.2.3 user/ global'}
${'darwin'} | ${'tool:1.2.3'} | ${'scoped'} | ${'add_composer_tool tool tool:1.2.3 user/ scoped'}
${'win32'} | ${'tool:1.2.3'} | ${'scoped'} | ${'Add-ComposerTool tool tool:1.2.3 user/ scoped'}
${'linux'} | ${'tool:>=1.2'} | ${'global'} | ${'add_composer_tool tool "tool:>=1.2" user/ global'}
${'win32'} | ${'tool:>=1.2'} | ${'global'} | ${'Add-ComposerTool tool "tool:>=1.2" user/ global'}
${'linux'} | ${'tool:1.*'} | ${'global'} | ${'add_composer_tool tool "tool:1.*" user/ global'}
${'linux'} | ${'psalm:^5||^6'} | ${'global'} | ${'add_composer_tool tool "psalm:^5||^6" user/ global'}
${'linux'} | ${'psalm:>=5,<6'} | ${'global'} | ${'add_composer_tool tool "psalm:>=5,<6" user/ global'}
${'openbsd'} | ${'tool:1.2.3'} | ${'global'} | ${'Platform openbsd is not supported'}
`(
'checking addPackage: $os, $release',
async ({os, release, scope, script}) => {
const data = getData({
tool: 'tool',
version: '1.2.3',
repository: 'user/tool',
os: os,
scope: scope
os,
scope
});
data['release'] = [data['tool'], data['version']].join(':');
data['release'] = release;
expect(await tools.addPackage(data)).toContain(script);
});
}
);
it.each`
version | php_version | os | script
@@ -416,6 +438,118 @@ describe('Tools tests', () => {
}
);
it.each`
version | affected
${'1'} | ${false}
${'1.0.0-alpha1'} | ${true}
${'1.0.0-alpha2'} | ${true}
${'1.0.0-alpha3'} | ${true}
${'1.0.0-alpha4'} | ${true}
${'1.0.0-alpha5'} | ${true}
${'1.0.0-alpha6'} | ${true}
${'1.0.0-alpha7'} | ${true}
${'1.0.0-alpha8'} | ${true}
${'1.0.0-alpha9'} | ${true}
${'1.0.0-alpha10'} | ${true}
${'1.0.0-alpha11'} | ${true}
${'1.0.0-beta1'} | ${true}
${'1.0.0-beta2'} | ${true}
${'1.0.0'} | ${true}
${'1.10.27'} | ${true}
${'1.10.28'} | ${false}
${'2.0.0-alpha1'} | ${true}
${'2.0.0-alpha2'} | ${true}
${'2.0.0-alpha3'} | ${true}
${'2.0.0-RC1'} | ${true}
${'2.0.0-RC2'} | ${true}
${'2.2.27'} | ${true}
${'2.2.28'} | ${false}
${'2.3.0-RC1'} | ${true}
${'2.3.0-RC2'} | ${true}
${'2.9.7'} | ${true}
${'2.9.7-RC1'} | ${true}
${'2.9.8'} | ${false}
${'2.9.0RC1'} | ${false}
${'2.9.x-dev'} | ${false}
`('checking affected composer version: $version', ({version, affected}) => {
expect(tools.skipGitHubAuthForComposerVersion(version)).toBe(affected);
});
it('checking affected composer version with CRLF ranges', async () => {
let affected = false;
let fixed = true;
await jest.isolateModulesAsync(async () => {
jest.doMock('fs', () => ({
...jest.requireActual('fs'),
readFileSync: (
filePath: fs.PathOrFileDescriptor,
options?: unknown
) => {
if (String(filePath).includes('composer-gh-auth-no-op')) {
return '1.0.0-0 1.10.28\r\n2.0.0-0 2.2.28\r\n2.3.0-0 2.9.8';
}
return (jest.requireActual('fs') as typeof fs).readFileSync(
filePath,
options as fs.ObjectEncodingOptions & {flag?: string}
);
}
}));
const isolatedTools = await import('../src/tools');
affected = isolatedTools.skipGitHubAuthForComposerVersion('2.9.7');
fixed = isolatedTools.skipGitHubAuthForComposerVersion('2.9.8');
});
expect(affected).toBe(true);
expect(fixed).toBe(false);
});
it.each`
auth_json | expected
${'{"github-oauth":{"github.com":"ghs_new-token"},"http-basic":{"repo.example":{"username":"u","password":"p"}}}'} | ${'{"http-basic":{"repo.example":{"username":"u","password":"p"}}}'}
${'{"github-oauth":{"github.com":"ghs_new-token"}}'} | ${undefined}
${'{"http-basic":{"repo.example":{"username":"u","password":"p"}}}'} | ${'{"http-basic":{"repo.example":{"username":"u","password":"p"}}}'}
${'{"nested":{"github-oauth":{"github.com":"ghs_new-token"}}}'} | ${'{"nested":{"github-oauth":{"github.com":"ghs_new-token"}}}'}
${'{"github-oauth":'} | ${'{"github-oauth":'}
`('cleaning composer auth json', ({auth_json, expected}) => {
unsetComposerAuthEnv();
process.env['COMPOSER_AUTH_JSON'] = auth_json;
tools.cleanComposerAuthJson();
expect(process.env['COMPOSER_AUTH_JSON']).toBe(expected);
unsetComposerAuthEnv();
});
it.each`
version | os | envs | skip_github_auth
${'latest'} | ${'linux'} | ${{GITHUB_TOKEN: 'ghs_token'}} | ${false}
${'1'} | ${'linux'} | ${{GITHUB_TOKEN: 'ghs_token'}} | ${false}
${'2'} | ${'linux'} | ${{GITHUB_TOKEN: 'ghs_token'}} | ${false}
${'2.9.7'} | ${'linux'} | ${{}} | ${true}
${'2.9.7'} | ${'linux'} | ${{GITHUB_TOKEN: 'ghs_token'}} | ${true}
${'2.9.7'} | ${'linux'} | ${{COMPOSER_TOKEN: 'ghs_token'}} | ${true}
${'2.9.7'} | ${'linux'} | ${{COMPOSER_AUTH_JSON: '{"github-oauth":{"github.com":"ghs_new-token"}}'}} | ${true}
${'2.9.7'} | ${'linux'} | ${{COMPOSER_AUTH_JSON: '{"http-basic":{"repo.example":{"username":"u","password":"p"}}}'}} | ${true}
${'2.9.8'} | ${'linux'} | ${{GITHUB_TOKEN: 'ghs_token'}} | ${false}
${'2.9.7'} | ${'win32'} | ${{GITHUB_TOKEN: 'ghs_token'}} | ${true}
`(
'checking composer github auth skip flag: $version, $os',
async ({version, os, envs, skip_github_auth}) => {
unsetComposerAuthEnv();
Object.assign(process.env, envs);
const data = getData({
tool: 'composer',
os: os,
php_version: '7.4',
domain: 'https://getcomposer.org',
repository: 'composer/composer',
version: version
});
const script = await tools.addComposer(data);
expect(script).toContain(
`composer ${version}${skip_github_auth ? ' true' : ''}`
);
unsetComposerAuthEnv();
}
);
it.each`
version | uri
${'latest'} | ${'wp-cli/builds/blob/gh-pages/phar/wp-cli.phar?raw=true'}
@@ -526,7 +660,7 @@ describe('Tools tests', () => {
'add_devtools phpize',
'add_tool https://github.com/phpmd/phpmd/releases/latest/download/phpmd.phar phpmd "--version"',
'add_tool https://github.com/phpspec/phpspec/releases/latest/download/phpspec.phar phpspec "-V"',
'add_composer_tool phpunit-bridge phpunit-bridge:5.6.* symfony/ global',
'add_composer_tool phpunit-bridge "phpunit-bridge:5.6.*" symfony/ global',
'add_composer_tool phpunit-polyfills phpunit-polyfills:1.0.1 yoast/ global',
'add_protoc 1.2.3',
'add_tool https://github.com/vimeo/psalm/releases/latest/download/psalm.phar psalm "-v"',
@@ -586,7 +720,7 @@ describe('Tools tests', () => {
'Add-ComposerTool codeception codeception codeception/ global',
'Add-ComposerTool prestissimo prestissimo hirak/ global',
'Add-ComposerTool automatic-composer-prefetcher automatic-composer-prefetcher narrowspark/ global',
'Add-ComposerTool phinx phinx:1.2.* robmorgan/ scoped',
'Add-ComposerTool phinx "phinx:1.2.*" robmorgan/ scoped',
'Add-ComposerTool phinx phinx:^1.2 robmorgan/ global',
'Add-ComposerTool tool tool:1.2.3 user/ global',
'Add-ComposerTool tool tool:~1.2 user/ global'
@@ -635,6 +769,7 @@ describe('Tools tests', () => {
${'composer:preview'} | ${'add_tool https://github.com/shivammathur/composer-cache/releases/latest/download/composer-7.4-preview.phar,https://artifacts.setup-php.com/composer/composer-7.4-preview.phar,https://dl.cloudsmith.io/public/shivammathur/composer-cache/raw/files/composer-7.4-preview.phar,https://getcomposer.org/composer-preview.phar composer preview'}
${'composer, composer:v1'} | ${'add_tool https://github.com/shivammathur/composer-cache/releases/latest/download/composer-7.4-1.phar,https://artifacts.setup-php.com/composer/composer-7.4-1.phar,https://dl.cloudsmith.io/public/shivammathur/composer-cache/raw/files/composer-7.4-1.phar,https://getcomposer.org/composer-1.phar composer'}
${'composer:v1, composer:preview, composer:snapshot'} | ${'add_tool https://github.com/shivammathur/composer-cache/releases/latest/download/composer-7.4-snapshot.phar,https://artifacts.setup-php.com/composer/composer-7.4-snapshot.phar,https://dl.cloudsmith.io/public/shivammathur/composer-cache/raw/files/composer-7.4-snapshot.phar,https://getcomposer.org/composer.phar composer snapshot'}
${'composer:2.9.7'} | ${'add_tool https://github.com/composer/composer/releases/download/2.9.7/composer.phar,https://getcomposer.org/download/2.9.7/composer.phar composer 2.9.7 true'}
`('checking composer setup: $tools_csv', async ({tools_csv, script}) => {
expect(await tools.addTools(tools_csv, '7.4', 'linux')).toContain(script);
});
@@ -649,14 +784,43 @@ describe('Tools tests', () => {
expect(await tools.addTools(tools_csv, '7.4', 'linux')).toContain(script);
});
it.each`
tools_csv | token | script
${'cs2pr:1.2'} | ${'invalid_token'} | ${'add_log "$cross" "cs2pr" "Invalid token"'}
${'phpunit:1.2'} | ${'invalid_token'} | ${'add_log "$cross" "phpunit" "Invalid token"'}
${'phpunit:0.1'} | ${'no_data'} | ${'add_log "$cross" "phpunit" "No version found with prefix 0.1."'}
`('checking error: $tools_csv', async ({tools_csv, token, script}) => {
process.env['GITHUB_TOKEN'] = token;
expect(await tools.addTools(tools_csv, '7.4', 'linux')).toContain(script);
it('checking error when custom-function tool is missing function field', async () => {
const brokenToolsJson = JSON.stringify({
composer: {
type: 'custom-function',
domain: 'https://getcomposer.org',
repository: 'composer/composer',
function: 'composer'
},
'broken-tool': {
type: 'custom-function'
}
});
let result: string = '';
await jest.isolateModulesAsync(async () => {
jest.doMock('fs', () => ({
...jest.requireActual('fs'),
readFileSync: (
filePath: fs.PathOrFileDescriptor,
options?: unknown
) => {
if (String(filePath).includes('tools.json')) {
return brokenToolsJson;
}
return (jest.requireActual('fs') as typeof fs).readFileSync(
filePath,
options as fs.ObjectEncodingOptions & {flag?: string}
);
}
}));
const isolatedTools = await import('../src/tools');
result = await isolatedTools.addTools('broken-tool', '7.4', 'linux');
});
expect(result).toContain(
'add_log "$cross" "broken-tool" "broken-tool has no function defined. Please report this issue."'
);
});
it.each`

View File

@@ -3,16 +3,6 @@ import * as path from 'path';
import * as utils from '../src/utils';
import * as fetchModule from '../src/fetch';
/**
* Mock @actions/core
*/
jest.mock('@actions/core', () => ({
getInput: jest.fn().mockImplementation(key => {
return ['setup-php'].indexOf(key) !== -1 ? key : '';
}),
info: jest.fn()
}));
describe('Utils tests', () => {
it('checking readEnv', async () => {
process.env['test'] = 'setup-php';
@@ -26,12 +16,14 @@ describe('Utils tests', () => {
it('checking getInput', async () => {
process.env['test'] = 'setup-php';
process.env['INPUT_SETUP-PHP'] = 'setup-php';
expect(await utils.getInput('test', false)).toBe('setup-php');
expect(await utils.getInput('setup-php', false)).toBe('setup-php');
expect(await utils.getInput('DoesNotExist', false)).toBe('');
await expect(async () => {
await utils.getInput('DoesNotExist', true);
}).rejects.toThrow('Input required and not supplied: DoesNotExist');
delete process.env['INPUT_SETUP-PHP'];
});
it('checking getManifestURL', async () => {
@@ -48,7 +40,19 @@ describe('Utils tests', () => {
expect(await utils.parseVersion('7')).toBe('7.0');
expect(await utils.parseVersion('7.4')).toBe('7.4');
expect(await utils.parseVersion('5.x')).toBe('5.6');
expect(await utils.parseVersion('4.x')).toBe(undefined);
expect(await utils.parseVersion('pre')).toBe('pre');
expect(await utils.parseVersion('pre-installed')).toBe('pre');
await expect(utils.parseVersion('4.x')).rejects.toThrow(
'Invalid PHP version: 4.x'
);
await expect(utils.parseVersion('foo')).rejects.toThrow(
'Invalid PHP version:'
);
fetchSpy.mockResolvedValue({data: '{ "latest": "8.1.0" }'});
await expect(utils.parseVersion('latest')).rejects.toThrow(
'Invalid PHP version in manifest:'
);
fetchSpy.mockReset();
fetchSpy.mockResolvedValueOnce({}).mockResolvedValueOnce({});
@@ -64,6 +68,12 @@ describe('Utils tests', () => {
expect(await utils.parseIniFile('none')).toBe('none');
expect(await utils.parseIniFile('php.ini-production')).toBe('production');
expect(await utils.parseIniFile('php.ini-development')).toBe('development');
expect(await utils.parseIniFile('/etc/php.ini-production')).toBe(
'production'
);
expect(await utils.parseIniFile('/a-b/php.ini-development')).toBe(
'development'
);
expect(await utils.parseIniFile('invalid')).toBe('production');
});
@@ -93,6 +103,26 @@ describe('Utils tests', () => {
expect(await utils.extensionArray('')).toEqual([]);
expect(await utils.extensionArray(' ')).toEqual([]);
expect(
await utils.extensionArray('apcu, mbstring, \\ pdo_pgsql, posix, session')
).toEqual(['apcu', 'mbstring', 'pdo_pgsql', 'posix', 'session']);
});
it('checking shell helpers', () => {
expect(utils.escapeForShell('a$b`c\\d"e', 'linux')).toBe(
'a\\$b\\`c\\\\d\\"e'
);
expect(utils.escapeForShell('a$b`c"d', 'win32')).toBe('a`$b``c`"d');
expect(utils.safeArg('vendor-pkg/repo@v1.0.0', 'linux')).toBe(
'vendor-pkg/repo@v1.0.0'
);
expect(utils.safeArg('phpcs:>=3.0', 'linux')).toBe('"phpcs:>=3.0"');
expect(utils.safeArg('foo$bar', 'win32')).toBe('"foo`$bar"');
expect(utils.sanitizeShellInput('foo;$(`ls`)bar')).toBe('foolsbar');
expect(utils.sanitizeShellInput('vendor/foo:1.*', true)).toBe(
'vendor/foo:1.'
);
});
it('checking INIArray', async () => {
@@ -181,9 +211,9 @@ describe('Utils tests', () => {
});
it('checking suppressOutput', async () => {
expect(await utils.suppressOutput('win32')).toEqual(' >$null 2>&1');
expect(await utils.suppressOutput('linux')).toEqual(' >/dev/null 2>&1');
expect(await utils.suppressOutput('darwin')).toEqual(' >/dev/null 2>&1');
expect(await utils.suppressOutput('win32')).toEqual(' ');
expect(await utils.suppressOutput('linux')).toEqual(' ');
expect(await utils.suppressOutput('darwin')).toEqual(' ');
expect(await utils.suppressOutput('openbsd')).toContain(
'Platform openbsd is not supported'
);
@@ -286,6 +316,9 @@ describe('Utils tests', () => {
process.env['php-version'] = '8.2';
expect(await utils.readPHPVersion()).toBe('8.2');
process.env['php-version'] = 'pre-installed';
expect(await utils.readPHPVersion()).toBe('pre-installed');
delete process.env['php-version-file'];
delete process.env['php-version'];
@@ -295,7 +328,7 @@ describe('Utils tests', () => {
existsSync.mockReturnValue(true);
readFileSync.mockReturnValue('setup-php');
expect(await utils.readPHPVersion()).toBe('setup-php');
await expect(utils.readPHPVersion()).rejects.toThrow('Invalid PHP version');
existsSync.mockReturnValueOnce(false).mockReturnValueOnce(true);
readFileSync.mockReturnValue(
@@ -316,6 +349,37 @@ describe('Utils tests', () => {
readFileSync.mockClear();
});
it('readPHPVersion rejects unsupported values from each source', async () => {
const existsSync = jest.spyOn(fs, 'existsSync').mockImplementation();
const readFileSync = jest.spyOn(fs, 'readFileSync').mockImplementation();
process.env['php-version'] = 'bogus';
await expect(utils.readPHPVersion()).rejects.toThrow('php-version input');
delete process.env['php-version'];
existsSync.mockReturnValue(true);
readFileSync.mockReturnValue('bogus');
await expect(utils.readPHPVersion()).rejects.toThrow('.php-version');
existsSync.mockReturnValueOnce(false).mockReturnValueOnce(true);
readFileSync.mockReturnValue('{"platform-overrides":{"php":"bogus"}}');
await expect(utils.readPHPVersion()).rejects.toThrow(
'composer.lock platform-overrides.php'
);
existsSync
.mockReturnValueOnce(false)
.mockReturnValueOnce(false)
.mockReturnValueOnce(true);
readFileSync.mockReturnValue('{"config":{"platform":{"php":"bogus"}}}');
await expect(utils.readPHPVersion()).rejects.toThrow(
'composer.json config.platform.php'
);
existsSync.mockClear();
readFileSync.mockClear();
});
it('checking setVariable', async () => {
let script: string = await utils.setVariable('var', 'command', 'linux');
expect(script).toEqual('\nvar="$(command)"\n');

View File

@@ -34,5 +34,5 @@ outputs:
php-version:
description: 'PHP version in semver format'
runs:
using: 'node20'
using: 'node24'
main: 'dist/index.js'

4
dist/index.js vendored

File diff suppressed because one or more lines are too long

View File

@@ -1,39 +1,22 @@
import {fixupConfigRules, fixupPluginRules} from '@eslint/compat';
// eslint-disable-next-line import/no-unresolved
import typescriptEslint from '@typescript-eslint/eslint-plugin';
import {importX} from 'eslint-plugin-import-x';
import jest from 'eslint-plugin-jest';
import prettierRecommended from 'eslint-plugin-prettier/recommended';
import eslintConfigPrettier from 'eslint-config-prettier';
import globals from 'globals';
// eslint-disable-next-line import/no-unresolved
import tsParser from '@typescript-eslint/parser';
import path from 'node:path';
import {fileURLToPath} from 'node:url';
import js from '@eslint/js';
import {FlatCompat} from '@eslint/eslintrc';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
const compat = new FlatCompat({
baseDirectory: __dirname,
recommendedConfig: js.configs.recommended,
allConfig: js.configs.all
});
export default [
...fixupConfigRules(
compat.extends(
'eslint:recommended',
'plugin:@typescript-eslint/eslint-recommended',
'plugin:@typescript-eslint/recommended',
'plugin:import/errors',
'plugin:import/warnings',
'plugin:import/typescript',
'plugin:prettier/recommended',
'prettier'
)
),
js.configs.recommended,
...typescriptEslint.configs['flat/recommended'],
importX.flatConfigs.errors,
importX.flatConfigs.warnings,
importX.flatConfigs.typescript,
prettierRecommended,
eslintConfigPrettier,
{
plugins: {
'@typescript-eslint': fixupPluginRules(typescriptEslint),
jest
},
@@ -46,6 +29,21 @@ export default [
parser: tsParser,
ecmaVersion: 2021,
sourceType: 'module'
},
settings: {
'import-x/resolver': {
typescript: {
alwaysTryTypes: true,
project: './tsconfig.json'
},
node: {
extensions: ['.js', '.ts']
}
},
'import-x/parsers': {
'@typescript-eslint/parser': ['.ts']
}
}
}
];

View File

@@ -8,7 +8,7 @@ jobs:
strategy:
fail-fast: false
matrix:
php-versions: ['7.4', '8.0', '8.1']
php-versions: ['8.3', '8.4', '8.5']
steps:
- name: Checkout
uses: actions/checkout@v6
@@ -23,7 +23,7 @@ jobs:
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
- uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.

View File

@@ -4,6 +4,9 @@ on: [push, pull_request]
jobs:
blackfire-player:
name: Blackfire (PHP ${{ matrix.php-versions }})
defaults:
run:
shell: bash
# Add your Blackfire credentials securely using GitHub Secrets
env:
BLACKFIRE_SERVER_ID: ${{ secrets.BLACKFIRE_SERVER_ID }}
@@ -14,9 +17,9 @@ jobs:
strategy:
fail-fast: false
matrix:
operating-system: [ubuntu-latest, windows-latest, macos-latest]
php-versions: ['7.4', '8.0', '8.1']
# blackfire-player supports PHP >= 5.5
operating-system: [ubuntu-latest, macos-latest]
php-versions: ['8.3', '8.4', '8.5']
# Blackfire Player supports PHP 8.5 and is available on Ubuntu and macOS.
steps:
- name: Checkout
uses: actions/checkout@v6
@@ -31,6 +34,14 @@ jobs:
tools: blackfire, blackfire-player
coverage: none
# Refer to https://blackfire.io/docs/player/index#usage
- name: Start local endpoint
run: |
php -S 127.0.0.1:8080 > "$RUNNER_TEMP/blackfire-player.log" 2>&1 &
sleep 5
- name: Validate scenario
run: blackfire-player validate scenario.bkf
# Refer to https://docs.blackfire.io/builds-cookbooks/player
- name: Play the scenario
run: blackfire-player run scenario.bkf
run: blackfire-player run scenario.bkf --endpoint=http://127.0.0.1:8080

View File

@@ -15,8 +15,8 @@ jobs:
fail-fast: false
matrix:
operating-system: [ubuntu-latest, windows-latest, macos-latest]
php-versions: ['7.4', '8.0', '8.1']
# Blackfire supports PHP >= 5.3 on Ubuntu and macOS, and PHP >= 5.4 on Windows
php-versions: ['8.3', '8.4', '8.5']
# Blackfire supports the current PHP releases on Ubuntu, macOS, and Windows.
steps:
- name: Checkout
uses: actions/checkout@v6
@@ -26,12 +26,24 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
# Setup Blackfire extension and CLI
extensions: blackfire
# Setup Blackfire extension and CLI.
extensions: blackfire, :xdebug
tools: blackfire
# Disable Xdebug and PCOV coverage drivers
coverage: none
# Refer to https://blackfire.io/docs/cookbooks/profiling-cli
- name: Profile
run: blackfire run php my-script.php
shell: bash
run: |
set +e
output=$(blackfire run php my-script.php 2>&1)
exit_code=$?
printf '%s\n' "$output"
if [ "$exit_code" -ne 0 ]; then
if printf '%s' "$output" | grep -q "upgrade your subscription"; then
echo "Blackfire profiling reached the repository quota limit; treating this as a known non-fatal condition."
exit 0
fi
exit "$exit_code"
fi

View File

@@ -6,7 +6,8 @@ jobs:
tests:
strategy:
matrix:
php-versions: ['7.4', '8.0', '8.1']
php-versions: ['8.4', '8.5']
# The latest cakephp/app release resolves dev dependencies that require PHP 8.4+.
runs-on: ubuntu-latest
# Docs: https://docs.github.com/en/actions/using-containerized-services
@@ -37,7 +38,7 @@ jobs:
php-version: ${{ matrix.php-versions }}
# You can also use ext-apcu or ext-memcached instead of ext-redis
# Install memcached if using ext-memcached
extensions: mbstring, intl, redis, pdo_mysql
extensions: mbstring, intl, redis, apcu, pdo_mysql
coverage: pcov
# Local MySQL service in GitHub hosted environments is disabled by default.
@@ -50,7 +51,7 @@ jobs:
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v3
uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.
@@ -61,14 +62,15 @@ jobs:
- name: Install dependencies
run: |
composer install --no-progress --prefer-dist --optimize-autoloader
composer run-script post-install-cmd
composer run-script post-install-cmd --no-interaction
# Add a step to run migrations if required
- name: Test with phpunit
run: vendor/bin/phpunit --coverage-text
env:
REDIS_PORT: ${{ job.services.redis.ports['6379'] }}
DB_DSN: "mysql://root:password@127.0.0.1:${{ job.services.mysql.ports['3306'] }}/cakephp?init[]=SET sql_mode = \"STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION\""
DATABASE_URL: "mysql://root:password@127.0.0.1:${{ job.services.mysql.ports['3306'] }}/cakephp?init[]=SET sql_mode = \"STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION\""
DATABASE_TEST_URL: "mysql://root:password@127.0.0.1:${{ job.services.mysql.ports['3306'] }}/cakephp?init[]=SET sql_mode = \"STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_ENGINE_SUBSTITUTION\""
coding-standard:
name: Coding Standard
@@ -81,7 +83,7 @@ jobs:
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.1'
php-version: '8.5'
extensions: mbstring, intl
- name: Get composer cache directory
@@ -89,7 +91,7 @@ jobs:
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v3
uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.
@@ -114,7 +116,7 @@ jobs:
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.1'
php-version: '8.5'
extensions: mbstring, intl
tools: phpstan
@@ -123,7 +125,7 @@ jobs:
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v3
uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.
@@ -132,7 +134,9 @@ jobs:
restore-keys: ${{ runner.os }}-composer-
- name: Install dependencies
run: composer install --no-progress --prefer-dist --optimize-autoloader
run: |
composer install --no-progress --prefer-dist --optimize-autoloader
composer run-script post-install-cmd --no-interaction
- name: Static Analysis using PHPStan
run: phpstan analyse --no-progress src/

View File

@@ -5,8 +5,10 @@ on: [push, pull_request]
jobs:
tests:
strategy:
fail-fast: false
matrix:
php-versions: ['7.4', '8.0', '8.1']
php-versions: ['8.4', '8.5']
# The latest cakephp/app release resolves dev dependencies that require PHP 8.4+.
runs-on: ubuntu-latest
# Docs: https://docs.github.com/en/actions/using-containerized-services
@@ -37,7 +39,7 @@ jobs:
php-version: ${{ matrix.php-versions }}
# You can also use ext-apcu or ext-memcached instead of ext-redis
# Install memcached if using ext-memcached
extensions: mbstring, intl, redis, pdo_pgsql
extensions: mbstring, intl, redis, apcu, pdo_pgsql
coverage: pcov
# Local PostgreSQL service in GitHub hosted environments is disabled by default.
@@ -50,7 +52,7 @@ jobs:
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v3
uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.
@@ -61,14 +63,15 @@ jobs:
- name: Install dependencies
run: |
composer install --no-progress --prefer-dist --optimize-autoloader
composer run-script post-install-cmd
composer run-script post-install-cmd --no-interaction
# Add a step to run migrations if required
- name: Test with phpunit
run: vendor/bin/phpunit --coverage-text
env:
REDIS_PORT: ${{ job.services.redis.ports['6379'] }}
DB_DSN: postgres://postgres@127.0.0.1:${{ job.services.postgres.ports['5432'] }}/postgres
DATABASE_URL: postgres://postgres:postgres@127.0.0.1:${{ job.services.postgres.ports['5432'] }}/postgres?encoding=UTF8
DATABASE_TEST_URL: postgres://postgres:postgres@127.0.0.1:${{ job.services.postgres.ports['5432'] }}/postgres?encoding=UTF8
coding-standard:
name: Coding Standard
@@ -81,15 +84,15 @@ jobs:
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.1'
extensions: mbstring, intl
php-version: '8.5'
extensions: mbstring, intl, apcu
- name: Get composer cache directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v3
uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.
@@ -114,8 +117,8 @@ jobs:
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.1'
extensions: mbstring, intl
php-version: '8.5'
extensions: mbstring, intl, apcu
tools: phpstan
- name: Get composer cache directory
@@ -123,7 +126,7 @@ jobs:
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v3
uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.
@@ -132,7 +135,9 @@ jobs:
restore-keys: ${{ runner.os }}-composer-
- name: Install dependencies
run: composer install --no-progress --prefer-dist --optimize-autoloader
run: |
composer install --no-progress --prefer-dist --optimize-autoloader
composer run-script post-install-cmd --no-interaction
- name: Static Analysis using PHPStan
run: phpstan analyse --no-progress src/

View File

@@ -7,7 +7,8 @@ jobs:
strategy:
matrix:
operating-system: [ubuntu-latest, windows-latest, macos-latest]
php-versions: ['7.4', '8.0', '8.1']
php-versions: ['8.4', '8.5']
# The latest cakephp/app release resolves dev dependencies that require PHP 8.4+.
runs-on: ${{ matrix.operating-system }}
steps:
- name: Checkout
@@ -23,10 +24,11 @@ jobs:
- name: Get composer cache directory
id: composer-cache
shell: bash
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v3
uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.
@@ -37,7 +39,7 @@ jobs:
- name: Install dependencies
run: |
composer install --no-progress --prefer-dist --optimize-autoloader
composer run-script post-install-cmd
composer run-script post-install-cmd --no-interaction
- name: Test with phpunit
run: vendor/bin/phpunit --coverage-text
@@ -53,14 +55,15 @@ jobs:
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.1'
php-version: '8.5'
extensions: mbstring, intl
- name: Get composer cache directory
id: composer-cache
shell: bash
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v3
uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.
@@ -85,16 +88,17 @@ jobs:
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.1'
php-version: '8.5'
extensions: mbstring, intl
tools: phpstan
- name: Get composer cache directory
id: composer-cache
shell: bash
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v3
uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.
@@ -103,7 +107,9 @@ jobs:
restore-keys: ${{ runner.os }}-composer-
- name: Install dependencies
run: composer install --no-progress --prefer-dist --optimize-autoloader
run: |
composer install --no-progress --prefer-dist --optimize-autoloader
composer run-script post-install-cmd --no-interaction
- name: Static Analysis using PHPStan
run: phpstan analyse --no-progress src/

View File

@@ -6,7 +6,7 @@ jobs:
strategy:
matrix:
operating-system: [ubuntu-latest, windows-latest, macos-latest]
php-versions: ['7.4', '8.0', '8.1']
php-versions: ['8.3', '8.4', '8.5']
runs-on: ${{ matrix.operating-system }}
steps:
- name: Checkout
@@ -17,15 +17,16 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
extensions: mbstring, intl, curl, dom
extensions: mbstring, intl, curl, dom, sqlite3, pdo_sqlite
coverage: xdebug
- name: Get composer cache directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
shell: bash
run: echo "dir=$(composer config cache-files-dir)" >> "$GITHUB_OUTPUT"
- name: Cache composer dependencies
uses: actions/cache@v3
uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.

59
examples/drupal.yml Normal file
View File

@@ -0,0 +1,59 @@
# GitHub Action for Drupal 11 composer-managed projects
# Requires drupal/core-dev in require-dev for vendor/bin/phpunit
name: Testing Drupal
on: [push, pull_request]
jobs:
drupal:
name: Drupal (PHP ${{ matrix.php-versions }})
runs-on: ubuntu-latest
env:
SIMPLETEST_BASE_URL: http://127.0.0.1:8080
SIMPLETEST_DB: sqlite://localhost/sites/default/files/.ht.sqlite
BROWSERTEST_OUTPUT_DIRECTORY: /tmp/browser_output
strategy:
fail-fast: false
matrix:
php-versions: ['8.3', '8.4', '8.5']
steps:
- name: Checkout
uses: actions/checkout@v6
# Docs: https://github.com/shivammathur/setup-php
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
extensions: apcu, ctype, curl, dom, gd, iconv, intl, mbstring, pdo_sqlite, simplexml, xml, zip
coverage: none
- name: Get composer cache directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.
# key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: Install Composer dependencies
run: |
if [ "${{ matrix.php-versions }}" = "8.3" ]; then
composer require --dev drupal/core-dev:11.2.10 doctrine/instantiator:^2.0.0 --no-interaction --no-update
composer update drupal/core-dev doctrine/instantiator --with-all-dependencies --no-interaction --no-progress --prefer-dist --optimize-autoloader
else
composer install --no-interaction --no-progress --prefer-dist --optimize-autoloader
fi
- name: Prepare Drupal test directories
run: mkdir -p web/sites/default/files "$BROWSERTEST_OUTPUT_DIRECTORY"
- name: Start Drupal web server
run: php -S 127.0.0.1:8080 -t web >/tmp/php-server.log 2>&1 &
- name: Test with phpunit
# Adjust the test path to match your custom modules or themes.
run: vendor/bin/phpunit -c web/core web/modules/custom

View File

@@ -1,12 +1,12 @@
# GitHub Action for Laminas framework MVC projects
name: Testing Zend Framework
name: Testing Laminas MVC
on: [push, pull_request]
jobs:
build:
strategy:
matrix:
operating-system: [ubuntu-latest, windows-latest, macos-latest]
php-versions: ['7.4', '8.0', '8.1']
php-versions: ['8.1', '8.2', '8.3']
runs-on: ${{ matrix.operating-system }}
steps:
- name: Checkout
@@ -21,10 +21,11 @@ jobs:
- name: Get composer cache directory
id: composer-cache
shell: bash
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v3
uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.

View File

@@ -6,11 +6,13 @@ jobs:
name: Laravel (PHP ${{ matrix.php-versions }})
runs-on: ubuntu-latest
env:
DB_CONNECTION: mysql
DB_HOST: 127.0.0.1
DB_DATABASE: laravel
DB_USERNAME: root
DB_PASSWORD: password
BROADCAST_DRIVER: log
CACHE_DRIVER: redis
BROADCAST_CONNECTION: log
CACHE_STORE: redis
QUEUE_CONNECTION: redis
SESSION_DRIVER: redis
@@ -34,7 +36,7 @@ jobs:
strategy:
fail-fast: false
matrix:
php-versions: ['7.4', '8.0', '8.1']
php-versions: ['8.3', '8.4', '8.5']
steps:
- name: Checkout
uses: actions/checkout@v6
@@ -57,7 +59,7 @@ jobs:
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v3
uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.

View File

@@ -11,7 +11,7 @@ jobs:
QUEUE_CONNECTION: redis
SESSION_DRIVER: redis
DB_CONNECTION: pgsql
DB_HOST: localhost
DB_HOST: 127.0.0.1
DB_PASSWORD: postgres
DB_USERNAME: postgres
DB_DATABASE: postgres
@@ -36,7 +36,7 @@ jobs:
strategy:
fail-fast: false
matrix:
php-versions: ['7.4', '8.0', '8.1']
php-versions: ['8.3', '8.4', '8.5']
steps:
- name: Checkout
uses: actions/checkout@v6
@@ -59,7 +59,7 @@ jobs:
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v3
uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.
@@ -81,11 +81,11 @@ jobs:
- name: Run Migration
run: php artisan migrate -v
env:
DB_PORT: ${{ job.services.postgres.ports[5432] }}
DB_PORT: ${{ job.services.postgres.ports['5432'] }}
REDIS_PORT: ${{ job.services.redis.ports['6379'] }}
- name: Test with phpunit
run: vendor/bin/phpunit --coverage-text
env:
DB_PORT: ${{ job.services.postgres.ports[5432] }}
DB_PORT: ${{ job.services.postgres.ports['5432'] }}
REDIS_PORT: ${{ job.services.redis.ports['6379'] }}

View File

@@ -9,7 +9,7 @@ jobs:
fail-fast: false
matrix:
operating-system: [ubuntu-latest, windows-latest, macos-latest]
php-versions: ['7.4', '8.0', '8.1']
php-versions: ['8.3', '8.4', '8.5']
steps:
- name: Checkout
uses: actions/checkout@v6
@@ -24,10 +24,11 @@ jobs:
- name: Get composer cache directory
id: composer-cache
shell: bash
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v3
uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.

View File

@@ -34,7 +34,7 @@ jobs:
strategy:
fail-fast: false
matrix:
php-versions: ['7.4', '8.0', '8.1']
php-versions: ['8.3', '8.4', '8.5']
steps:
- name: Checkout
uses: actions/checkout@v6
@@ -57,7 +57,7 @@ jobs:
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v3
uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.
@@ -83,7 +83,7 @@ jobs:
REDIS_PORT: ${{ job.services.redis.ports['6379'] }}
- name: Test with phpunit
run: vendor/bin/phpunit --coverage-text
run: vendor/bin/phpunit --coverage-text --coverage-filter app
env:
DB_PORT: ${{ job.services.mysql.ports['3306'] }}
REDIS_PORT: ${{ job.services.redis.ports['6379'] }}

View File

@@ -11,7 +11,7 @@ jobs:
QUEUE_CONNECTION: redis
SESSION_DRIVER: redis
DB_CONNECTION: pgsql
DB_HOST: localhost
DB_HOST: 127.0.0.1
DB_PASSWORD: postgres
DB_USERNAME: postgres
DB_DATABASE: postgres
@@ -36,7 +36,7 @@ jobs:
strategy:
fail-fast: false
matrix:
php-versions: ['7.4', '8.0', '8.1']
php-versions: ['8.3', '8.4', '8.5']
steps:
- name: Checkout
uses: actions/checkout@v6
@@ -47,7 +47,7 @@ jobs:
with:
php-version: ${{ matrix.php-versions }}
extensions: mbstring, dom, fileinfo, pgsql
coverage: xdebug
coverage: none
# Local PostgreSQL service in GitHub hosted environments is disabled by default.
# If you are using it instead of service containers, make sure you start it.
@@ -59,7 +59,7 @@ jobs:
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v3
uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.
@@ -81,11 +81,11 @@ jobs:
- name: Run Migration
run: php artisan migrate -v
env:
DB_PORT: ${{ job.services.postgres.ports[5432] }}
DB_PORT: ${{ job.services.postgres.ports['5432'] }}
REDIS_PORT: ${{ job.services.redis.ports['6379'] }}
- name: Test with phpunit
run: vendor/bin/phpunit --coverage-text
run: vendor/bin/phpunit
env:
DB_PORT: ${{ job.services.postgres.ports[5432] }}
DB_PORT: ${{ job.services.postgres.ports['5432'] }}
REDIS_PORT: ${{ job.services.redis.ports['6379'] }}

View File

@@ -9,7 +9,7 @@ jobs:
fail-fast: false
matrix:
operating-system: [ubuntu-latest, windows-latest, macos-latest]
php-versions: ['7.4', '8.0', '8.1']
php-versions: ['8.3', '8.4', '8.5']
steps:
- name: Checkout
uses: actions/checkout@v6
@@ -24,10 +24,11 @@ jobs:
- name: Get composer cache directory
id: composer-cache
shell: bash
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v3
uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.
@@ -42,4 +43,4 @@ jobs:
run: php -r "file_exists('.env') || copy('.env.example', '.env');"
- name: Test with phpunit
run: vendor/bin/phpunit --coverage-text
run: vendor/bin/phpunit --coverage-text --coverage-filter app

View File

@@ -11,7 +11,7 @@ jobs:
env:
DB_ADAPTER: mysql
DB_HOST: 127.0.0.1
DB_NAME: phalcon
DB_NAME: vokuro
DB_USERNAME: root
DB_PASSWORD: password
CODECEPTION_URL: 127.0.0.1
@@ -20,11 +20,11 @@ jobs:
# Docs: https://docs.github.com/en/actions/using-containerized-services
services:
mysql:
image: mysql:latest
image: mysql:5.7
env:
MYSQL_ALLOW_EMPTY_PASSWORD: false
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: phalcon
MYSQL_DATABASE: vokuro
ports:
- 3306/tcp
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
@@ -32,8 +32,6 @@ jobs:
fail-fast: false
matrix:
php-versions: ['7.2', '7.3', '7.4']
# For phalcon 3.x, use
# php-versions: ['7.0', '7.1', '7.2', '7.3']
steps:
- name: Checkout
uses: actions/checkout@v6
@@ -43,7 +41,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
# Use phalcon3 for the phalcon 3.x.
# Use phalcon4 for the latest compatible Vokuro sample release.
extensions: mbstring, dom, zip, phalcon4, mysql
coverage: xdebug
@@ -57,7 +55,7 @@ jobs:
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v3
uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.
@@ -73,7 +71,6 @@ jobs:
- name: Run Migration
run: |
if [ ! -e phinx.yml ]; then vendor/bin/phinx init; fi
vendor/bin/phinx migrate
vendor/bin/phinx seed:run
env:

View File

@@ -11,21 +11,18 @@ jobs:
env:
DB_ADAPTER: pgsql
DB_HOST: 127.0.0.1
DB_NAME: postgres
DB_NAME: vokuro
DB_USERNAME: postgres
DB_PASSWORD: postgres
CODECEPTION_URL: 127.0.0.1
CODECEPTION_PORT: 8888
DB_CONNECTION: pgsql
# Docs: https://docs.github.com/en/actions/using-containerized-services
services:
postgres:
image: postgres:latest
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres
POSTGRES_DB: vokuro
ports:
- 5432/tcp
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 3
@@ -33,8 +30,6 @@ jobs:
fail-fast: false
matrix:
php-versions: ['7.2', '7.3', '7.4']
# For phalcon 3.x, use
# php-versions: ['7.0', '7.1', '7.2', '7.3']
steps:
- name: Checkout
uses: actions/checkout@v6
@@ -44,40 +39,39 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
# Use phalcon3 for the phalcon 3.x
extensions: mbstring, dom, zip, phalcon4, pgsql
coverage: xdebug
# Local PostgreSQL service in GitHub hosted environments is disabled by default.
# If you are using it instead of service containers, make sure you start it.
# - name: Start postgresql service
# run: sudo systemctl start postgresql.service
- name: Get composer cache directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v3
uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.
# key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: Install Composer dependencies
run: composer install --no-progress --prefer-dist --optimize-autoloader
run: composer install --no-progress --prefer-dist --optimize-autoloader --no-security-blocking
- name: Prepare the application
run: php -r "file_exists('.env') || copy('.env.example', '.env');"
run: |
php -r "file_exists('.env') || copy('.env.example', '.env');"
mkdir -p var/cache/acl var/cache/metaData var/cache/session var/cache/volt var/logs
chmod -R 777 var/cache var/logs
- name: Run Migration
run: |
if [ ! -e phinx.yml ]; then vendor/bin/phinx init; fi
vendor/bin/phinx migrate
vendor/bin/phinx seed:run
env:
DB_PORT: ${{ job.services.postgres.ports['5432'] }}
- name: Run Tests
run: |
(cd public && nohup php -S $CODECEPTION_URL:$CODECEPTION_PORT > phalcon.log 2>&1 &)
(cd public && nohup php -S $CODECEPTION_URL:$CODECEPTION_PORT > vokuro.log 2>&1 &)
vendor/bin/codecept build
vendor/bin/codecept run
env:

View File

@@ -8,8 +8,8 @@ jobs:
strategy:
fail-fast: false
matrix:
php-versions: ['7.4', '8.0', '8.1']
node-versions: ['16']
php-versions: ['8.3', '8.4', '8.5']
node-versions: ['20']
steps:
- name: Checkout
uses: actions/checkout@v6
@@ -33,7 +33,7 @@ jobs:
id: yarn-cache
run: echo "dir=$(yarn cache dir)" >> $GITHUB_OUTPUT
- uses: actions/cache@v3
- uses: actions/cache@v5
with:
path: ${{ steps.yarn-cache.outputs.dir }}
key: ${{ runner.os }}-yarn-${{ hashFiles('**/yarn.lock') }}
@@ -44,7 +44,7 @@ jobs:
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v3
uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.
@@ -53,17 +53,10 @@ jobs:
restore-keys: ${{ runner.os }}-composer-
- name: Install yarn dependencies
run: yarn -V
run: yarn install --frozen-lockfile --non-interactive
- name: Install Composer dependencies
run: composer install --no-progress --prefer-dist --optimize-autoloader
- name: Yarn test and build
run: |
yarn run test
yarn run build
yarn run rmdist
yarn run "build:production"
- name: PHP test
run: composer test
- name: Yarn build
run: yarn build

View File

@@ -6,7 +6,7 @@ jobs:
strategy:
matrix:
operating-system: [ubuntu-latest, windows-latest, macos-latest]
php-versions: ['7.4', '8.0', '8.1']
php-versions: ['8.3', '8.4', '8.5']
runs-on: ${{ matrix.operating-system }}
steps:
- name: Checkout
@@ -22,10 +22,11 @@ jobs:
- name: Get composer cache directory
id: composer-cache
shell: bash
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v3
uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.

View File

@@ -5,11 +5,13 @@ jobs:
symfony:
name: Symfony (PHP ${{ matrix.php-versions }})
runs-on: ubuntu-latest
env:
APP_ENV: test
# Docs: https://docs.github.com/en/actions/using-containerized-services
services:
mysql:
image: mysql:latest
image: mysql:8.4
env:
MYSQL_ALLOW_EMPTY_PASSWORD: false
MYSQL_ROOT_PASSWORD: symfony
@@ -20,7 +22,7 @@ jobs:
strategy:
fail-fast: false
matrix:
php-versions: ['7.4', '8.0', '8.1']
php-versions: ['8.4', '8.5', '8.6']
steps:
- name: Checkout
uses: actions/checkout@v6
@@ -30,7 +32,6 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
tools: phpunit-bridge
extensions: mbstring, xml, ctype, iconv, intl, pdo_sqlite, mysql
coverage: xdebug
@@ -44,7 +45,7 @@ jobs:
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v3
uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.
@@ -53,18 +54,17 @@ jobs:
restore-keys: ${{ runner.os }}-composer-
- name: Install Composer dependencies
run: composer install --no-progress --prefer-dist --optimize-autoloader
run: composer install --no-interaction --no-progress --prefer-dist --optimize-autoloader
- name: Run Migration
- name: Prepare database
run: |
composer require --dev symfony/orm-pack
php bin/console doctrine:schema:update --force || echo "No migrations found or schema update failed"
php bin/console doctrine:migrations:migrate || echo "No migrations found or migration failed"
php bin/console doctrine:database:create --if-not-exists --no-interaction
php bin/console doctrine:schema:create --no-interaction
php bin/console doctrine:fixtures:load --no-interaction
env:
DATABASE_URL: mysql://root:symfony@127.0.0.1:${{ job.services.mysql.ports['3306'] }}/symfony
- name: Install PHPUnit
run: simple-phpunit install
DATABASE_URL: mysql://root:symfony@127.0.0.1:${{ job.services.mysql.ports['3306'] }}/symfony?serverVersion=8.4&charset=utf8mb4
- name: Run tests
run: simple-phpunit --coverage-text
run: php bin/phpunit --coverage-text
env:
DATABASE_URL: mysql://root:symfony@127.0.0.1:${{ job.services.mysql.ports['3306'] }}/symfony?serverVersion=8.4&charset=utf8mb4

View File

@@ -5,11 +5,13 @@ jobs:
symfony:
name: Symfony (PHP ${{ matrix.php-versions }})
runs-on: ubuntu-latest
env:
APP_ENV: test
# Docs: https://docs.github.com/en/actions/using-containerized-services
services:
postgres:
image: postgres:latest
image: postgres:16
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
@@ -20,7 +22,7 @@ jobs:
strategy:
fail-fast: false
matrix:
php-versions: ['7.4', '8.0', '8.1']
php-versions: ['8.4', '8.5', '8.6']
steps:
- name: Checkout
uses: actions/checkout@v6
@@ -30,8 +32,7 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
tools: phpunit-bridge
extensions: mbstring, xml, ctype, iconv, intl, pdo_sqlite, pgsql
extensions: mbstring, xml, ctype, iconv, intl, pdo_pgsql, pgsql
coverage: xdebug
# Local PostgreSQL service in GitHub hosted environments is disabled by default.
@@ -44,7 +45,7 @@ jobs:
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v3
uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.
@@ -55,16 +56,15 @@ jobs:
- name: Install Composer dependencies
run: composer install --no-progress --prefer-dist --optimize-autoloader
- name: Run Migration
- name: Prepare database
run: |
composer require --dev symfony/orm-pack
php bin/console doctrine:schema:update --force || echo "No migrations found or schema update failed"
php bin/console doctrine:migrations:migrate || echo "No migrations found or migration failed"
php bin/console doctrine:database:create --if-not-exists --no-interaction
php bin/console doctrine:schema:create --no-interaction
php bin/console doctrine:fixtures:load --no-interaction
env:
DATABASE_URL: postgres://postgres:postgres@127.0.0.1:${{ job.services.postgres.ports[5432] }}/postgres?charset=UTF-8
- name: Install PHPUnit
run: simple-phpunit install
DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:${{ job.services.postgres.ports[5432] }}/postgres?serverVersion=16.0.0&charset=utf8
- name: Run tests
run: simple-phpunit --coverage-text
run: php bin/phpunit --coverage-text
env:
DATABASE_URL: postgresql://postgres:postgres@127.0.0.1:${{ job.services.postgres.ports[5432] }}/postgres?serverVersion=16.0.0&charset=utf8

View File

@@ -9,7 +9,7 @@ jobs:
fail-fast: false
matrix:
operating-system: [ubuntu-latest, windows-latest, macos-latest]
php-versions: ['7.4', '8.0', '8.1']
php-versions: ['8.4', '8.5', '8.6']
steps:
- name: Checkout
uses: actions/checkout@v6
@@ -19,16 +19,16 @@ jobs:
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
tools: phpunit-bridge
extensions: mbstring, xml, ctype, iconv, intl, pdo_sqlite
extensions: mbstring, xml, ctype, iconv, intl, pdo_sqlite, zip
coverage: xdebug
- name: Get composer cache directory
id: composer-cache
shell: bash
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v3
uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.
@@ -39,8 +39,5 @@ jobs:
- name: Install Composer dependencies
run: composer install --no-progress --prefer-dist --optimize-autoloader
- name: Install PHPUnit
run: simple-phpunit install
- name: Run tests
run: simple-phpunit --coverage-text
run: ./bin/phpunit --coverage-text

68
examples/wordpress.yml Normal file
View File

@@ -0,0 +1,68 @@
# GitHub Action for WordPress plugins
# Tested with files scaffolded by wp scaffold plugin-tests --ci=github
# Requires phpunit/phpunit and yoast/phpunit-polyfills in require-dev for vendor/bin/phpunit
name: Testing WordPress
on: [push, pull_request]
jobs:
wordpress:
name: WordPress (PHP ${{ matrix.php-versions }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
php-versions: ['8.3', '8.4', '8.5']
# Docs: https://docs.github.com/en/actions/using-containerized-services
services:
mysql:
image: mysql:latest
env:
MYSQL_ALLOW_EMPTY_PASSWORD: false
MYSQL_ROOT_PASSWORD: root
MYSQL_DATABASE: wordpress_test
ports:
- 3306/tcp
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
steps:
- name: Checkout
uses: actions/checkout@v6
# Docs: https://github.com/shivammathur/setup-php
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
extensions: mbstring, xml, zip, intl, mysql
coverage: none
- name: Get composer cache directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.
# key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: Install system dependencies
run: sudo apt-get update && sudo apt-get install -y subversion default-mysql-client
- name: Install Composer dependencies
run: |
if [ "${{ matrix.php-versions }}" = "8.3" ]; then
composer require --dev doctrine/instantiator:^2.0.0 --no-interaction --no-update
composer update doctrine/instantiator --with-all-dependencies --no-interaction --no-progress --prefer-dist --optimize-autoloader
else
composer install --no-interaction --no-progress --prefer-dist --optimize-autoloader
fi
- name: Install WordPress test environment
run: bash bin/install-wp-tests.sh wordpress_test root root 127.0.0.1:${{ job.services.mysql.ports['3306'] }} latest true
- name: Test with phpunit
run: vendor/bin/phpunit

View File

@@ -1,86 +0,0 @@
# GitHub Action for Yii Framework with MySQL
name: Testing Yii2 with MySQL
on: [push, pull_request]
jobs:
yii:
name: Yii2 (PHP ${{ matrix.php-versions }})
runs-on: ubuntu-latest
env:
DB_USERNAME: root
DB_PASSWORD: yii
TEST_DB_USERNAME: root
TEST_DB_PASSWORD: yii
DB_CHARSET: utf8
# Docs: https://docs.github.com/en/actions/using-containerized-services
services:
mysql:
image: mysql:latest
env:
MYSQL_ALLOW_EMPTY_PASSWORD: false
MYSQL_ROOT_PASSWORD: yii
MYSQL_DATABASE: yii
ports:
- 3306/tcp
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
strategy:
fail-fast: false
matrix:
php-versions: ['7.4', '8.0']
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set Node.js 10.x
uses: actions/setup-node@v5
with:
node-version: 10.x
# Docs: https://github.com/shivammathur/setup-php
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
extensions: mbstring, intl, gd, imagick, zip, dom, mysql
coverage: xdebug
# Local MySQL service in GitHub hosted environments is disabled by default.
# If you are using it instead of service containers, make sure you start it.
# - name: Start mysql service
# run: sudo systemctl start mysql.service
- name: Get composer cache directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v3
with:
path: ${{ steps.composer-cache.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.
# key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: Install Composer dependencies
run: composer install --no-progress --prefer-dist --optimize-autoloader
- name: Prepare the application
run: |
php -r "file_exists('.env') || copy('.env.dist', '.env');"
php console/yii app/setup
npm install --development
npm run build
env:
DB_DSN: mysql:host=127.0.0.1;port=${{ job.services.mysql.ports['3306'] }};dbname=yii
TEST_DB_DSN: mysql:host=127.0.0.1;port=${{ job.services.mysql.ports['3306'] }};dbname=yii
- name: Run Tests
run: |
vendor/bin/codecept build
php tests/bin/yii app/setup --interactive=0
nohup php -S localhost:8080 > yii.log 2>&1 &
vendor/bin/codecept run
env:
DB_DSN: mysql:host=127.0.0.1;port=${{ job.services.mysql.ports['3306'] }};dbname=yii
TEST_DB_DSN: mysql:host=127.0.0.1;port=${{ job.services.mysql.ports['3306'] }};dbname=yii

View File

@@ -1,86 +0,0 @@
# GitHub Action for Yii Framework with PostgreSQL
name: Testing Yii2 with PostgreSQL
on: [push, pull_request]
jobs:
yii:
name: Yii2 (PHP ${{ matrix.php-versions }})
runs-on: ubuntu-latest
env:
DB_USERNAME: postgres
DB_PASSWORD: postgres
TEST_DB_USERNAME: postgres
TEST_DB_PASSWORD: postgres
DB_CHARSET: utf8
# Docs: https://docs.github.com/en/actions/using-containerized-services
services:
postgres:
image: postgres:latest
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: postgres
ports:
- 5432/tcp
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 3
strategy:
fail-fast: false
matrix:
php-versions: ['7.4', '8.0']
steps:
- name: Checkout
uses: actions/checkout@v6
- name: Set Node.js 10.x
uses: actions/setup-node@v5
with:
node-version: 10.x
# Docs: https://github.com/shivammathur/setup-php
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
extensions: mbstring, intl, gd, imagick, zip, dom, pgsql
coverage: xdebug
# Local PostgreSQL service in GitHub hosted environments is disabled by default.
# If you are using it instead of service containers, make sure you start it.
# - name: Start postgresql service
# run: sudo systemctl start postgresql.service
- name: Get composer cache directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v3
with:
path: ${{ steps.composer-cache.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.
# key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: Install Composer dependencies
run: composer install --no-progress --prefer-dist --optimize-autoloader
- name: Prepare the application
run: |
php -r "file_exists('.env') || copy('.env.dist', '.env');"
php console/yii app/setup
npm install --development
npm run build
env:
DB_DSN: pgsql:host=127.0.0.1;port=${{ job.services.postgres.ports['5432'] }};dbname=postgres
TEST_DB_DSN: pgsql:host=127.0.0.1;port=${{ job.services.postgres.ports['5432'] }};dbname=postgres
- name: Run Tests
run: |
vendor/bin/codecept build
php tests/bin/yii app/setup --interactive=0
nohup php -S localhost:8080 > yii.log 2>&1 &
vendor/bin/codecept run
env:
DB_DSN: pgsql:host=127.0.0.1;port=${{ job.services.postgres.ports['5432'] }};dbname=postgres
TEST_DB_DSN: pgsql:host=127.0.0.1;port=${{ job.services.postgres.ports['5432'] }};dbname=postgres

78
examples/yii3-mysql.yml Normal file
View File

@@ -0,0 +1,78 @@
# GitHub Action for Yii3 web application with MySQL
# Tested with https://github.com/yiisoft/app
name: Testing Yii3 with MySQL
on: [push, pull_request]
jobs:
yii:
name: Yii3 (PHP ${{ matrix.php-versions }})
runs-on: ubuntu-latest
env:
APP_C3: true
APP_ENV: test
APP_DEBUG: false
# Docs: https://docs.github.com/en/actions/using-containerized-services
services:
mysql:
image: mysql:8.4
env:
MYSQL_ALLOW_EMPTY_PASSWORD: false
MYSQL_ROOT_PASSWORD: password
MYSQL_DATABASE: app
ports:
- 3306/tcp
options: --health-cmd="mysqladmin ping" --health-interval=10s --health-timeout=5s --health-retries=3
strategy:
fail-fast: false
matrix:
php-versions: ['8.4', '8.5']
# The latest yiisoft/app release resolves Symfony 8.0 packages that require PHP 8.4+.
steps:
- name: Checkout
uses: actions/checkout@v6
# Docs: https://github.com/shivammathur/setup-php
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
extensions: fileinfo, intl, pdo_mysql
ini-values: date.timezone='UTC', register_argc_argv=On
coverage: none
- name: Get composer cache directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.
# key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: Install Composer dependencies
run: composer install --no-progress --prefer-dist --optimize-autoloader
- name: Run migrations
run: php yii migrate:up --no-interaction
env:
DB_HOST: 127.0.0.1
DB_PORT: ${{ job.services.mysql.ports['3306'] }}
DB_NAME: app
DB_USERNAME: root
DB_PASSWORD: password
- name: Run codeception build
run: vendor/bin/codecept build
- name: Run tests with Codeception
run: vendor/bin/codecept run
env:
DB_HOST: 127.0.0.1
DB_PORT: ${{ job.services.mysql.ports['3306'] }}
DB_NAME: app
DB_USERNAME: root
DB_PASSWORD: password

View File

@@ -0,0 +1,78 @@
# GitHub Action for Yii3 web application with PostgreSQL
# Tested with https://github.com/yiisoft/app
name: Testing Yii3 with PostgreSQL
on: [push, pull_request]
jobs:
yii:
name: Yii3 (PHP ${{ matrix.php-versions }})
runs-on: ubuntu-latest
env:
APP_C3: true
APP_ENV: test
APP_DEBUG: false
# Docs: https://docs.github.com/en/actions/using-containerized-services
services:
postgres:
image: postgres:16
env:
POSTGRES_USER: postgres
POSTGRES_PASSWORD: postgres
POSTGRES_DB: app
ports:
- 5432/tcp
options: --health-cmd pg_isready --health-interval 10s --health-timeout 5s --health-retries 3
strategy:
fail-fast: false
matrix:
php-versions: ['8.4', '8.5']
# The latest yiisoft/app release resolves Symfony 8.0 packages that require PHP 8.4+.
steps:
- name: Checkout
uses: actions/checkout@v6
# Docs: https://github.com/shivammathur/setup-php
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
extensions: fileinfo, intl, pdo_pgsql
ini-values: date.timezone='UTC', register_argc_argv=On
coverage: none
- name: Get composer cache directory
id: composer-cache
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.
# key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: Install Composer dependencies
run: composer install --no-progress --prefer-dist --optimize-autoloader
- name: Run migrations
run: php yii migrate:up --no-interaction
env:
DB_HOST: 127.0.0.1
DB_PORT: ${{ job.services.postgres.ports['5432'] }}
DB_NAME: app
DB_USERNAME: postgres
DB_PASSWORD: postgres
- name: Run codeception build
run: vendor/bin/codecept build
- name: Run tests with Codeception
run: vendor/bin/codecept run
env:
DB_HOST: 127.0.0.1
DB_PORT: ${{ job.services.postgres.ports['5432'] }}
DB_NAME: app
DB_USERNAME: postgres
DB_PASSWORD: postgres

53
examples/yii3.yml Normal file
View File

@@ -0,0 +1,53 @@
# GitHub Action for Yii3 web application
# Tested with https://github.com/yiisoft/app
name: Testing Yii3
on: [push, pull_request]
jobs:
yii:
name: Yii3 (PHP ${{ matrix.php-versions }} on ${{ matrix.operating-system }})
runs-on: ${{ matrix.operating-system }}
env:
APP_C3: true
APP_ENV: test
APP_DEBUG: false
strategy:
fail-fast: false
matrix:
operating-system: [ubuntu-latest, windows-latest]
php-versions: ['8.4', '8.5']
# The latest yiisoft/app release resolves Symfony 8.0 packages that require PHP 8.4+.
steps:
- name: Checkout
uses: actions/checkout@v6
# Docs: https://github.com/shivammathur/setup-php
- name: Setup PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-versions }}
extensions: fileinfo, intl
ini-values: date.timezone='UTC', register_argc_argv=On
coverage: none
- name: Get composer cache directory
id: composer-cache
shell: bash
run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT
- name: Cache composer dependencies
uses: actions/cache@v5
with:
path: ${{ steps.composer-cache.outputs.dir }}
# Use composer.json for key, if composer.lock is not committed.
# key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }}
restore-keys: ${{ runner.os }}-composer-
- name: Install Composer dependencies
run: composer install --no-progress --prefer-dist --optimize-autoloader
- name: Run codeception build
run: vendor/bin/codecept build
- name: Run tests with Codeception
run: vendor/bin/codecept run

4592
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "setup-php",
"version": "2.36.0",
"version": "2.37.1",
"private": false,
"description": "Setup PHP for use with GitHub Actions",
"main": "lib/install.js",
@@ -34,36 +34,36 @@
"author": "shivammathur",
"license": "MIT",
"dependencies": {
"@actions/core": "^1.11.1",
"@actions/exec": "^1.1.1",
"@actions/io": "^2.0.0",
"@actions/exec": "^2.0.0",
"compare-versions": "^6.1.1"
},
"devDependencies": {
"@eslint/compat": "^2.0.0",
"@eslint/js": "9.39.1",
"@eslint/compat": "^2.1.0",
"@eslint/js": "^10.0.1",
"@types/jest": "^30.0.0",
"@types/node": "^24.10.1",
"@typescript-eslint/eslint-plugin": "^8.48.0",
"@typescript-eslint/parser": "^8.48.0",
"@types/node": "^25.7.0",
"@typescript-eslint/eslint-plugin": "^8.59.3",
"@typescript-eslint/parser": "^8.59.3",
"@vercel/ncc": "^0.38.4",
"eslint": "9.39.1",
"eslint": "^10.3.0",
"eslint-config-prettier": "^10.1.8",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-jest": "^29.2.1",
"eslint-plugin-prettier": "^5.5.4",
"globals": "^16.5.0",
"jest": "^30.2.0",
"jest-circus": "^30.2.0",
"nock": "^14.0.10",
"prettier": "^3.6.2",
"eslint-import-resolver-typescript": "^4.4.4",
"eslint-plugin-import-x": "^4.16.2",
"eslint-plugin-jest": "^29.15.2",
"eslint-plugin-prettier": "^5.5.5",
"globals": "^17.6.0",
"jest": "^30.4.2",
"jest-circus": "^30.4.2",
"nock": "^14.0.15",
"prettier": "^3.8.3",
"simple-git-hooks": "^2.13.1",
"ts-jest": "^29.4.5",
"ts-jest": "^29.4.9",
"typescript": "^5.9.3"
},
"overrides": {
"test-exclude": "^7.0.1",
"glob": "^11.1.0"
"glob": "^13.0.6",
"minimatch": "^10.2.1"
},
"bugs": {
"url": "https://github.com/shivammathur/setup-php/issues"

View File

@@ -16,8 +16,8 @@ export async function addINIValuesUnix(
});
return (
'echo "' +
ini_values.join('\n') +
'" | sudo tee -a "${pecl_file:-${ini_file[@]}}" >/dev/null 2>&1' +
ini_values.map(v => utils.escapeForShell(v, 'linux')).join('\n') +
'" | sudo tee -a "${pecl_file:-${ini_file[@]}}" ' +
script
);
}
@@ -37,7 +37,10 @@ export async function addINIValuesWindows(
(await utils.addLog('$tick', line, 'Added to php.ini', 'win32')) + '\n';
});
return (
'Add-Content "$php_dir\\php.ini" "' + ini_values.join('\n') + '"' + script
'Add-Content "$php_dir\\php.ini" "' +
ini_values.map(v => utils.escapeForShell(v, 'win32')).join('\n') +
'"' +
script
);
}

View File

@@ -1,29 +1,40 @@
amqp=amqp
apcu=apcu
ast=ast
brotli=brotli
couchbase=couchbase
ds=ds
event=event
excimer=excimer
expect=expect
gearman=gearman
gmagick=gmagick
gnupg=gnupg
grpc=grpc
igbinary=igbinary
imagick=imagick
imap=imap
interbase=interbase
lua=lua
mailparse=mailparse
maxminddb=maxminddb
mcrypt=mcrypt
memcache=memcache
memcached=memcached
mongodb=mongodb
mongodb1=mongodb1
msgpack=msgpack
newrelic=newrelic
oauth=oauth
opentelemetry=opentelemetry
pcov=pcov
pdo_firebird=pdo_firebird
pdo_sqlsrv=pdo_sqlsrv
pecl_http=http
phalcon3=phalcon
phalcon4=phalcon
phalcon5=phalcon
pinba=pinba
propro=propro
protobuf=protobuf
psr=psr
@@ -31,16 +42,24 @@ raphf=raphf
rdkafka=rdkafka
phpredis=redis
redis=redis
seaslog=seaslog
scalar_objects=scalar_objects
snmp=snmp
sqlsrv=sqlsrv
spx=spx
ssh2=ssh2
swoole=swoole
swow=swow
uopz=uopz
uploadprogress=uploadprogress
uuid=uuid
v8js=v8js
vips=vips
vld=vld
xdebug=xdebug
xdebug2=xdebug
xhprof=xhprof
xlswriter=xlswriter
yaml=yaml
zmq=zmq
zstd=zstd

View File

@@ -0,0 +1,3 @@
1.0.0-0 1.10.28
2.0.0-0 2.2.28
2.3.0-0 2.9.8

View File

@@ -0,0 +1 @@
Composer %s has a known GitHub token parsing bug that exposes GitHub tokens in the error output. So, GitHub authentication has not been configured for this Composer version. Please update to the latest version of Composer. See: https://github.com/composer/composer/security/advisories/GHSA-f9f8-rm49-7jv2

View File

@@ -21,4 +21,7 @@
23.10,mantic
24.04,noble
24.10,oracular
25.10,plucky
25.05,plucky
25.10,questing
26.04,resolute
26.10,stonking
1 8 jessie
21 23.10 mantic
22 24.04 noble
23 24.10 oracular
24 25.10 25.05 plucky
25 25.10 questing
26 26.04 resolute
27 26.10 stonking

112
src/core.ts Normal file
View File

@@ -0,0 +1,112 @@
import {EOL} from 'os';
/**
* Commands
*
* Command Format:
* ::name key=value,key=value::message
*
* @see https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions
*/
interface CommandProperties {
[key: string]: string;
}
/**
* Sanitizes the message for use in a workflow command.
* @param message
*/
function toCommandValue(message: string | Error): string {
if (message instanceof Error) {
return message.toString();
}
return message;
}
/**
* Escapes data for safe use in workflow command messages.
* @param s
*/
function escapeData(s: string | Error): string {
return toCommandValue(s)
.replace(/%/g, '%25')
.replace(/\r/g, '%0D')
.replace(/\n/g, '%0A');
}
/**
* Escapes property values for safe use in workflow command properties.
* @param s
*/
function escapeProperty(s: string): string {
return s
.replace(/%/g, '%25')
.replace(/\r/g, '%0D')
.replace(/\n/g, '%0A')
.replace(/:/g, '%3A')
.replace(/,/g, '%2C');
}
/**
* Issues a command to the GitHub Actions runner.
*
* @param command - The command name to issue
* @param properties - Additional properties for the command (key-value pairs)
* @param message - The message to include with the command
*/
export function issueCommand(
command: string,
properties: CommandProperties,
message: string | Error
): void {
let cmdStr = `::${command}`;
if (properties && Object.keys(properties).length > 0) {
cmdStr += ' ';
const props = Object.entries(properties)
.filter(([, val]) => val)
.map(([key, val]) => `${key}=${escapeProperty(val)}`)
.join(',');
cmdStr += props;
}
cmdStr += `::${escapeData(message)}`;
process.stdout.write(cmdStr + EOL);
}
/**
* Adds an error issue.
* @param message - error issue message
*/
export function error(message: string | Error): void {
issueCommand('error', {}, message);
}
/**
* Sets the action status to failed.
* When the action exits it will be with an exit code of 1.
* @param message - add error issue message
*/
export function setFailed(message: string | Error): void {
process.exitCode = 1;
error(message);
}
/**
* Gets the value of an input.
* The value is trimmed.
* Returns an empty string if the value is not defined.
*
* @param name - name of the input to get
* @param required - whether the input is required
* @returns string
*/
export function getInput(name: string, required = false): string {
const val: string =
process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`] || '';
if (required && !val) {
throw new Error(`Input required and not supplied: ${name}`);
}
return val.trim();
}

View File

@@ -35,9 +35,9 @@ export async function addExtensionDarwin(
// match 5.3blackfire...8.5blackfire
// match 5.3blackfire-(semver)...8.5blackfire-(semver)
// match couchbase, event, geos, ibm_db2, pdo_ibm, pdo_oci, oci8, http, pecl_http
// match 5.3ioncube...8.4ioncube
// match 5.3ioncube...8.5ioncube
// match 7.0phalcon3...7.3phalcon3, 7.2phalcon4...7.4phalcon4, and 7.4phalcon5...8.4phalcon5
// match 7.0zephir_parser...8.4zephir_parser
// match 7.0zephir_parser...8.5zephir_parser
case /^(7\.4|8\.[0-5])relay(-v?\d+\.\d+\.\d+|-nightly)?$/.test(
version_extension
):
@@ -47,12 +47,12 @@ export async function addExtensionDarwin(
case /^couchbase|^event|^gearman$|^geos$|^ibm_db2$|^pdo_ibm$|^pdo_oci$|^oci8$|^(pecl_)?http|^pdo_firebird$/.test(
extension
):
case /^(5\.[3-6]|7\.[0-4]|8\.[0-4])ioncube$/.test(version_extension):
case /^(5\.[3-6]|7\.[0-4]|8\.[0-5])ioncube$/.test(version_extension):
case /(5\.6|7\.[0-3])phalcon3|7\.[2-4]phalcon4|(7\.4|8\.[0-4])phalcon5?/.test(
version_extension
):
case /(?<!5\.[3-6])(pdo_)?sqlsrv$/.test(version_extension):
case /^(7\.[0-4]|8\.[0-4])zephir_parser(-v?\d+\.\d+\.\d+)?$/.test(
case /^(7\.[0-4]|8\.[0-5])zephir_parser(-v?\d+\.\d+\.\d+)?$/.test(
version_extension
):
add_script += await utils.customPackage(
@@ -85,13 +85,14 @@ export async function addExtensionDarwin(
add_script += await utils.getUnsupportedLog('pcov', version, 'darwin');
return;
// match brew extensions
case /(?<!5\.[3-5])(amqp|apcu|expect|gnupg|grpc|igbinary|imagick|imap|mailparse|mcrypt|memcache|memcached|mongodb|msgpack|protobuf|psr|raphf|rdkafka|redis|snmp|ssh2|swoole|uuid|vld|xdebug|xdebug2|yaml|zmq)/.test(
case /(?<!5\.[3-5])(amqp|apcu|brotli|excimer|expect|gmagick|gnupg|grpc|igbinary|imagick|imap|interbase|mailparse|maxminddb|mcrypt|memcache|memcached|mongodb|mongodb1|msgpack|newrelic|oauth|opentelemetry|pdo_firebird|pinba|protobuf|psr|raphf|rdkafka|redis|scalar_objects|seaslog|snmp|spx|ssh2|swoole|uopz|uploadprogress|uuid|vld|xdebug|xdebug2|xhprof|yaml|zmq|zstd)/.test(
version_extension
):
case /(?<!5\.[3-6])(ds|v8js)/.test(version_extension):
case /(5\.6|7\.[0-4])(propro|lua)/.test(version_extension):
case /(?<!5\.[3-6]|7\.0)pcov/.test(version_extension):
case /(?<!5\.[3-6])(ast|vips|xlswriter)/.test(version_extension):
case /^(8\.[0-5])swow$/.test(version_extension):
add_script += await utils.joins(
'\nadd_brew_extension',
ext_name,
@@ -139,23 +140,23 @@ export async function addExtensionWindows(
// match 5.3blackfire...8.5blackfire
// match 5.3blackfire-(semver)...8.5blackfire-(semver)
// match ibm_db2, pdo_ibm, pdo_oci and oci8
// match 5.3ioncube...8.4ioncube
// match 5.3ioncube...8.5ioncube
// match 7.0phalcon3...7.3phalcon3, 7.2phalcon4...7.4phalcon4, and 7.4phalcon5...8.4phalcon5
// match 7.1pecl_http...8.1pecl_http and 7.1http...8.1http
// match 7.0zephir_parser...8.4zephir_parser
// match 7.1pecl_http...8.5pecl_http and 7.1http...8.5http
// match 7.0zephir_parser...8.5zephir_parser
case /^(5\.[3-6]|7\.[0-4]|8\.[0-5])blackfire(-\d+\.\d+\.\d+)?$/.test(
version_extension
):
case /^ibm_db2$|^pdo_ibm$|^pdo_oci$|^oci8$|^pdo_firebird$/.test(
extension
):
case /^(5\.[3-6]|7\.[0-4]|8\.[0-4])ioncube$/.test(version_extension):
case /^(5\.[3-6]|7\.[0-4]|8\.[0-5])ioncube$/.test(version_extension):
case /^7\.[0-3]phalcon3$|^7\.[2-4]phalcon4$|^(7\.4|8\.[0-4])phalcon5?$/.test(
version_extension
):
case /^(7\.[1-4]|8\.1)(pecl_)?http/.test(version_extension):
case /^(7\.[1-4]|8\.[0-5])(pecl_)?http/.test(version_extension):
case /(?<!5\.[3-6])(pdo_)?sqlsrv$/.test(version_extension):
case /^(7\.[0-4]|8\.[0-4])zephir_parser(-v?\d+\.\d+\.\d+)?$/.test(
case /^(7\.[0-4]|8\.[0-5])zephir_parser(-v?\d+\.\d+\.\d+)?$/.test(
version_extension
):
add_script += await utils.customPackage(
@@ -272,9 +273,9 @@ export async function addExtensionLinux(
// match 5.3blackfire-(semver)...8.5blackfire-(semver)
// match 5.3pdo_cubrid...7.2php_cubrid, 5.3cubrid...7.4cubrid
// match couchbase, geos, ibm_db2, pdo_ibm, pdo_oci, oci8, http, pecl_http
// match 5.3ioncube...8.4ioncube
// match 5.3ioncube...8.5ioncube
// match 7.0phalcon3...7.3phalcon3, 7.2phalcon4...7.4phalcon4, 7.4phalcon5...8.4phalcon5
// match 7.0zephir_parser...8.4zephir_parser
// match 7.0zephir_parser...8.5zephir_parser
case /^(7\.4|8\.[0-5])relay(-v?\d+\.\d+\.\d+|-nightly)?$/.test(
version_extension
):
@@ -288,12 +289,12 @@ export async function addExtensionLinux(
extension
):
case /(?<!5\.[3-5])intl-\d+\.\d+$/.test(version_extension):
case /^(5\.[3-6]|7\.[0-4]|8\.[0-4])ioncube$/.test(version_extension):
case /^(5\.[3-6]|7\.[0-4]|8\.[0-5])ioncube$/.test(version_extension):
case /^7\.[0-3]phalcon3$|^7\.[2-4]phalcon4$|^(7\.4|8\.[0-4])phalcon5?$/.test(
version_extension
):
case /(?<!5\.[3-6])(pdo_)?sqlsrv$/.test(version_extension):
case /^(7\.[0-4]|8\.[0-4])zephir_parser(-v?\d+\.\d+\.\d+)?$/.test(
case /^(7\.[0-4]|8\.[0-5])zephir_parser(-v?\d+\.\d+\.\d+)?$/.test(
version_extension
):
add_script += await utils.customPackage(

View File

@@ -1,9 +1,10 @@
import {IncomingMessage, OutgoingHttpHeaders} from 'http';
import * as https from 'https';
import * as url from 'url';
/**
* Redirect status codes set for O(1) lookup
*/
const REDIRECT_CODES = new Set([301, 302, 303, 307, 308]);
/**
* Function to fetch a URL
* Function to fetch a URL using native fetch API (Node 24+)
*
* @param input_url
* @param auth_token
@@ -14,43 +15,28 @@ export async function fetch(
auth_token?: string,
redirect_count = 5
): Promise<Record<string, string>> {
const fetch_promise: Promise<Record<string, string>> = new Promise(
resolve => {
const url_object: url.UrlObject = new url.URL(input_url);
const headers: OutgoingHttpHeaders = {
const headers: Record<string, string> = {
'User-Agent': `Mozilla/5.0 (${process.platform} ${process.arch}) setup-php`
};
if (auth_token) {
headers.authorization = 'Bearer ' + auth_token;
}
const options: https.RequestOptions = {
hostname: url_object.hostname,
path: url_object.pathname,
headers: headers,
agent: new https.Agent({keepAlive: false})
};
const req = https.get(options, (res: IncomingMessage) => {
if (res.statusCode === 200) {
let body = '';
res.setEncoding('utf8');
res.on('data', chunk => (body += chunk));
res.on('end', () => resolve({data: `${body}`}));
} else if (
[301, 302, 303, 307, 308].includes(res.statusCode as number)
) {
if (redirect_count > 0 && res.headers.location) {
fetch(res.headers.location, auth_token, redirect_count--).then(
resolve
);
} else {
resolve({error: `${res.statusCode}: Redirect error`});
}
} else {
resolve({error: `${res.statusCode}: ${res.statusMessage}`});
headers['Authorization'] = 'Bearer ' + auth_token;
}
try {
const response = await globalThis.fetch(input_url, {
headers,
redirect: redirect_count > 0 ? 'follow' : 'manual'
});
req.end();
if (response.ok) {
const data = await response.text();
return {data};
} else if (REDIRECT_CODES.has(response.status) && redirect_count <= 0) {
return {error: `${response.status}: Redirect error`};
} else {
return {error: `${response.status}: ${response.statusText}`};
}
} catch (error) {
return {error: `Fetch error: ${(error as Error).message}`};
}
);
return await fetch_promise;
}

View File

@@ -1,8 +1,8 @@
import path from 'path';
import fs from 'fs';
import {exec} from '@actions/exec';
import * as core from '@actions/core';
import * as config from './config';
import * as core from './core';
import * as coverage from './coverage';
import * as extensions from './extensions';
import * as tools from './tools';
@@ -18,7 +18,10 @@ export async function getScript(os: string): Promise<string> {
const filename = os + (await utils.scriptExtension(os));
const script_path = path.join(__dirname, '../src/scripts', filename);
const run_path = script_path.replace(os, 'run');
const extension_csv: string = await utils.getInput('extensions', false);
const extension_csv: string = utils.sanitizeShellInput(
await utils.getInput('extensions', false),
true
);
const ini_values_csv: string = await utils.getInput('ini-values', false);
const coverage_driver: string = await utils.getInput('coverage', false);
const tools_csv: string = await utils.getInput('tools', false);
@@ -28,7 +31,7 @@ export async function getScript(os: string): Promise<string> {
const ini_file: string = await utils.parseIniFile(
await utils.getInput('ini-file', false)
);
let script = await utils.joins('.', script_path, version, ini_file);
let script = await utils.joins('.', script_path, `'${version}'`, ini_file);
if (extension_csv) {
script += await extensions.addExtension(extension_csv, version, os);
}

View File

@@ -13,9 +13,11 @@ handle_dependency_extensions() {
suffix="$(get_php_formula_suffix)"
if [[ -n "$suffix" ]]; then
brew_opts=(-sf)
patch_abstract_file >/dev/null 2>&1
patch_abstract_file
for dependency_extension in "${dependency_extensions[@]}"; do
brew install "${brew_opts[@]}" "$ext_tap/$dependency_extension@$version" >/dev/null 2>&1 && copy_brew_extensions "$dependency_extension"
safe_brew install --skip-link "${brew_opts[@]}" "$ext_tap/$dependency_extension@$version" &&
brew link --overwrite --force "$dependency_extension@$version" &&
copy_brew_extensions "$dependency_extension"
done
fi
}
@@ -31,7 +33,7 @@ disable_extension_helper() {
sudo sed -Ei '' "/=(.*\/)?\"?$extension(.so)?$/d" "${ini_file:?}"
sudo rm -rf "$scan_dir"/*"$extension"* /tmp/php"$version"_extensions
mkdir -p /tmp/extdisabled/"$version"
echo '' | sudo tee /tmp/extdisabled/"$version"/"$extension" >/dev/null 2>&1
echo '' | sudo tee /tmp/extdisabled/"$version"/"$extension"
}
# Function to get extension name from brew formula.
@@ -81,9 +83,13 @@ add_brew_extension() {
add_brew_tap "$php_tap"
add_brew_tap "$ext_tap"
formula="$(get_renamed_formula "$formula")"
update_dependencies >/dev/null 2>&1
handle_dependency_extensions "$formula" "$extension" >/dev/null 2>&1
(brew install "${brew_opts[@]}" "$ext_tap/$formula@$version" >/dev/null 2>&1 && copy_brew_extensions "$formula") || pecl_install "$extension" >/dev/null 2>&1
update_dependencies
handle_dependency_extensions "$formula" "$extension"
(
safe_brew install --skip-link "${brew_opts[@]}" "$ext_tap/$formula@$version" &&
brew link --overwrite --force "$formula@$version" &&
copy_brew_extensions "$formula"
) || pecl_install "$extension"
add_extension_log "$extension" "Installed and enabled"
fi
}
@@ -92,7 +98,7 @@ add_brew_extension() {
patch_abstract_file() {
abstract_path="$tap_dir"/"$ext_tap"/Abstract/abstract-php-extension.rb
if [[ -e "$abstract_path" && ! -e /tmp/abstract_patch ]]; then
echo '' | sudo tee /tmp/abstract_patch >/dev/null 2>&1
echo '' | sudo tee /tmp/abstract_patch
sudo sed -i '' -e "s|php@#{\(.*\)}|php@#{\1}$suffix|g" -e "s|php_version /|\"#{php_version}$suffix\" /|g" "$abstract_path"
fi
}
@@ -102,9 +108,9 @@ add_extension_helper() {
local extension=$1
prefix=$2
if [[ "$version" =~ ${old_versions:?} ]] && [ "$extension" = "imagick" ]; then
run_script "php5-darwin" "${version/./}" "$extension" >/dev/null 2>&1
run_script "php5-darwin" "${version/./}" "$extension"
else
pecl_install "$extension" >/dev/null 2>&1 &&
pecl_install "$extension" &&
if [[ "$version" =~ ${old_versions:?} ]]; then echo "$prefix=$ext_dir/$extension.so" >>"$ini_file"; fi
fi
add_extension_log "$extension" "Installed and enabled"
@@ -118,8 +124,8 @@ add_devtools() {
# Function to handle request to add PECL.
add_pecl() {
enable_extension xml extension >/dev/null 2>&1
configure_pecl >/dev/null 2>&1
enable_extension xml extension
configure_pecl
pear_version=$(get_tool_version "pecl" "version")
add_log "${tick:?}" "PECL" "Found PECL $pear_version"
}
@@ -151,7 +157,7 @@ update_dependencies() {
git_retry -C "$repo" fetch origin main && git -C "$repo" reset --hard origin/main
fi
done
echo '' | sudo tee /tmp/update_dependencies >/dev/null 2>&1
echo '' | sudo tee /tmp/update_dependencies
fi
}
@@ -181,14 +187,14 @@ add_php() {
fi
if [[ "$existing_version" != "false" && -z "$suffix" ]]; then
if [ "$action" = "upgrade" ]; then
brew install --only-dependencies "$php_formula"
brew upgrade -f --overwrite "$php_formula"
safe_brew install --only-dependencies "$php_formula"
safe_brew upgrade -f --overwrite "$php_formula"
else
brew unlink "$php_keg"
fi
else
brew install --only-dependencies "$php_formula"
brew install -f --overwrite "$php_formula" 2>/dev/null || brew upgrade -f --overwrite "$php_formula"
safe_brew install --only-dependencies "$php_formula"
safe_brew install --skip-link -f --overwrite "$php_formula" 2>/dev/null || safe_brew upgrade -f --overwrite "$php_formula"
fi
brew link --force --overwrite "$php_keg" || (sudo chown -R "$(id -un)":"$(id -gn)" "$brew_prefix" && brew link --force --overwrite "$php_keg")
}
@@ -217,7 +223,7 @@ add_php_config() {
if [[ "$ini" = "production" || "$ini" = "development" ]]; then
sudo cp "$ini_dir"/php.ini-"$ini" "$ini_dir"/php.ini
elif [ "$ini" = "none" ]; then
echo '' | sudo tee "${ini_file[@]}" >/dev/null 2>&1
echo '' | sudo tee "${ini_file[@]}"
fi
}
@@ -232,7 +238,7 @@ get_scan_dir() {
# Function to handle self-hosted runner setup.
self_hosted_helper() {
sudo mkdir -p /opt/hostedtoolcache >/dev/null 2>&1 || true
sudo mkdir -p /opt/hostedtoolcache || true
}
# Function to Setup PHP.
@@ -244,15 +250,15 @@ setup_php() {
existing_version=$(get_brewed_php)
status="Found"
if [[ "$version" =~ ${old_versions:?} ]]; then
run_script "php5-darwin" "${version/./}" >/dev/null 2>&1
run_script "php5-darwin" "${version/./}"
status="Installed"
elif [ "${existing_version:0:3}" != "$version" ]; then
add_php "install" "$existing_version" >/dev/null 2>&1
add_php "install" "$existing_version"
status="Installed"
elif [[ "${existing_version:0:3}" = "$version" && "${update:?}" = "true" ]]; then
brew_php_version="$(brew info --json "php@$version" 2>/dev/null | jq -r '.[].versions.stable')"
if [ "$brew_php_version" != "$existing_version" ]; then
add_php "upgrade" "$existing_version" >/dev/null 2>&1
add_php "upgrade" "$existing_version"
status="Upgraded"
fi
fi

View File

@@ -50,7 +50,7 @@ enable_extension() {
[ -d "$modules_dir" ] && sudo find "$modules_dir" -path "*disabled*$1" -delete
enable_extension_dependencies "$1" "$2"
enable_cache_extension_dependencies "$1" "$2"
if ! [[ "${version:?}" =~ ${old_versions:?} ]] && command -v phpenmod >/dev/null 2>&1; then
if ! [[ "${version:?}" =~ ${old_versions:?} ]] && command -v phpenmod ; then
sudo sed -Ei "/=(.*\/)?\"?$extension(.so)?\"?$/d" "$pecl_file"
mod="${ini_dir:?}"/../mods-available/"$1".ini
if ! [ -e "$mod" ]; then
@@ -59,7 +59,7 @@ enable_extension() {
[ -n "$mod_priority_line" ] && priority=$(echo "$mod_priority_line" | cut -d'=' -f 2)
(echo "; priority=$priority"; echo "$2=${ext_dir:?}/$1.so") | sudo tee "$mod" >/dev/null
fi
sudo phpenmod -v "$version" "$1" >/dev/null 2>&1
sudo phpenmod -v "$version" "$1"
else
echo "$2=${ext_dir:?}/$1.so" | sudo tee -a "${pecl_file:-${ini_file[@]}}" >/dev/null
fi
@@ -71,7 +71,7 @@ enable_extensions() {
local extensions=("$@")
to_wait=()
for ext in "${extensions[@]}"; do
enable_extension "$ext" extension >/dev/null 2>&1 &
enable_extension "$ext" extension &
to_wait+=($!)
done
wait "${to_wait[@]}"
@@ -79,7 +79,7 @@ enable_extensions() {
# Function to get a map of extensions and their dependent shared extensions.
get_extension_map() {
php -d'error_reporting=0' "${src:?}"/scripts/extensions/extension_map.php /tmp/map"$version".orig >/dev/null 2>&1
php -d'error_reporting=0' "${src:?}"/scripts/extensions/extension_map.php /tmp/map"$version".orig
}
# Function to enable extension dependencies which are also extensions.
@@ -125,7 +125,7 @@ disable_extension() {
disable_all_shared() {
get_extension_map
sudo sed -i.orig -E -e "/^(zend_)?extension\s*=/d" "${ini_file[@]}" "$pecl_file" 2>/dev/null || true
sudo find "${ini_dir:-$scan_dir}"/.. -name "*.ini" -not -path "*php.ini" -not -path "*phar.ini" -not -path "*pecl.ini" -not -path "*mods-available*" -delete >/dev/null 2>&1 || true
sudo find "${ini_dir:-$scan_dir}"/.. -name "*.ini" -not -path "*php.ini" -not -path "*phar.ini" -not -path "*pecl.ini" -not -path "*mods-available*" -delete || true
mkdir -p /tmp/extdisabled/"$version"
sudo rm -f /tmp/php"$version"_extensions
sudo find "$ext_dir" -name '*.so' -print0 | xargs -0 -n 1 basename -s .so | xargs -I{} touch /tmp/extdisabled/"$version"/{}
@@ -139,7 +139,7 @@ configure_pecl() {
for script in pear pecl; do
sudo "$script" channel-update "$script".php.net
done
echo '' | sudo tee /tmp/pecl_config >/dev/null 2>&1
echo '' | sudo tee /tmp/pecl_config
fi
}
@@ -177,8 +177,8 @@ get_pecl_version() {
pecl_install() {
local extension=$1
local prefix=${2:-extension}
add_pecl >/dev/null 2>&1
disable_extension_helper "${extension%-*}" >/dev/null 2>&1
add_pecl
disable_extension_helper "${extension%-*}"
# Compare version with 8.3 so it runs only on 8.4 and above
# Install using the source interface as it allows for patching.
if [[ $(printf "%s\n%s" "${version:?}" "8.3" | sort -V | head -n1) != "$version" ]]; then
@@ -191,11 +191,11 @@ pecl_install() {
prefix_opts="$(parse_args "$extension" CONFIGURE_PREFIX_OPTS) MAKEFLAGS='-j $cpu_count'"
suffix_opts="$(parse_args "$extension" CONFIGURE_OPTS) $(parse_args "$extension" CONFIGURE_SUFFIX_OPTS)"
IFS=' ' read -r -a libraries <<<"$(parse_args "$extension" LIBS) $(parse_args "$extension" "$(uname -s)"_LIBS)"
(( ${#libraries[@]} )) && add_libs "${libraries[@]}" >/dev/null 2>&1
(( ${#libraries[@]} )) && add_libs "${libraries[@]}"
if [ "$version" = "5.3" ]; then
yes '' 2>/dev/null | sudo "$prefix_opts" pecl install -f "$extension" >/dev/null 2>&1
yes '' 2>/dev/null | sudo "$prefix_opts" pecl install -f "$extension"
else
yes '' 2>/dev/null | sudo "$prefix_opts" pecl install -f -D "$(parse_pecl_configure_options "$suffix_opts")" "$extension" >/dev/null 2>&1
yes '' 2>/dev/null | sudo "$prefix_opts" pecl install -f -D "$(parse_pecl_configure_options "$suffix_opts")" "$extension"
fi
local exit_code=$?
sudo pecl info "$extension" | grep -iq 'zend extension' && prefix=zend_extension
@@ -218,7 +218,7 @@ add_pecl_extension() {
add_log "${tick:?}" "$extension" "Enabled"
else
[ -n "$pecl_version" ] && pecl_version="-$pecl_version"
pecl_install "$extension$pecl_version" || ( [ "${fail_fast:?}" = "false" ] && add_extension "$extension" "$(get_extension_prefix "$extension")" >/dev/null 2>&1)
pecl_install "$extension$pecl_version" || ( [ "${fail_fast:?}" = "false" ] && add_extension "$extension" "$(get_extension_prefix "$extension")" )
extension_version="$(php -r "echo phpversion('$extension');")"
[ -n "$extension_version" ] && extension_version="-$extension_version"
add_extension_log "$extension$extension_version" "Installed and enabled"

View File

@@ -19,11 +19,11 @@ add_blackfire() {
extension_version=$(get -s -n "" https://blackfire.io/api/v1/releases | grep -Eo 'php":"([0-9]+.[0-9]+.[0-9]+)' | cut -d '"' -f 3)
fi
fi
get -q -n "${ext_dir:?}/blackfire.so" https://packages.blackfire.io/binaries/blackfire-php/"$extension_version"/blackfire-php-"$platform"_"$arch_name"-php-"$no_dot_version".so >/dev/null 2>&1
get -q -n "${ext_dir:?}/blackfire.so" https://packages.blackfire.io/binaries/blackfire-php/"$extension_version"/blackfire-php-"$platform"_"$arch_name"-php-"$no_dot_version".so
fi
if [ -e "${ext_dir:?}/blackfire.so" ]; then
disable_extension xdebug >/dev/null 2>&1
disable_extension pcov >/dev/null 2>&1
disable_extension xdebug
disable_extension pcov
enable_extension blackfire extension
add_extension_log blackfire "$status"
else

View File

@@ -59,9 +59,9 @@ add_couchbase() {
ext=$(get_couchbase_version)
fi
if [[ "$ext" =~ couchbase-[2-3].+ ]]; then
add_couchbase_clibs "$ext" >/dev/null 2>&1
add_couchbase_clibs "$ext"
else
add_couchbase_cxxlibs >/dev/null 2>&1
add_couchbase_cxxlibs
fi
enable_extension "couchbase" "extension"
if check_extension "couchbase"; then
@@ -72,16 +72,27 @@ add_couchbase() {
n_proc="$(nproc)"
export COUCHBASE_SUFFIX_OPTS="CMAKE_BUILD_TYPE=Release"
export CMAKE_BUILD_PARALLEL_LEVEL="$n_proc"
add_extension_from_source couchbase https://pecl.php.net couchbase couchbase "${ext##*-}" extension pecl >/dev/null 2>&1
add_extension_from_source couchbase https://pecl.php.net couchbase couchbase "${ext##*-}" extension pecl
else
pecl_install "${ext}" >/dev/null 2>&1
pecl_install "${ext}"
fi
add_extension_log "couchbase" "Installed and enabled"
fi
else
if [ -e "${ext_dir:?}"/libcouchbase_php_core.dylib ]; then
sudo cp "${ext_dir:?}"/libcouchbase_php_core.dylib "${brew_prefix:?}"/lib
if [ -e "${ext_dir:?}/couchbase.so" ]; then
couchbase_rpath="$(otool -l "${ext_dir:?}/couchbase.so" 2>/dev/null | awk '$1 == "path" && $2 ~ /\/couchbase@'"${version:?}"'\// {print $2; exit}')"
couchbase_rpath="${couchbase_rpath/@loader_path/${ext_dir:?}}"
otool -L "${ext_dir:?}/couchbase.so" 2>/dev/null |
awk -v rpath="$couchbase_rpath" '/libcouchbase_php.*\.dylib/ {if ($1 ~ /^@rpath\// && rpath != "") {sub(/^@rpath/, rpath, $1)}; print $1}' |
while read -r dylib; do
dylib="${dylib/@loader_path/${ext_dir:?}}"
[ -e "${ext_dir:?}/$(basename "$dylib")" ] || continue
sudo mkdir -p "$(dirname "$dylib")"
sudo cp "${ext_dir:?}/$(basename "$dylib")" "$dylib"
done
fi
add_brew_extension couchbase extension
find "${brew_prefix:?}/lib" "${brew_prefix:?}/opt/couchbase@${version:?}" "${brew_prefix:?}/Cellar/couchbase@${version:?}" \
-name 'libcouchbase_php*.dylib' -exec sudo cp {} "${ext_dir:?}" \;
fi
}

View File

@@ -42,7 +42,7 @@ add_cubrid_helper() {
add_cubrid() {
ext=$1
status='Enabled'
add_cubrid_helper "$ext" >/dev/null 2>&1
add_cubrid_helper "$ext"
add_extension_log "$ext" "$status"
check_extension "$ext" && add_license_log
}

View File

@@ -39,9 +39,9 @@ add_event() {
add_log "${tick:?}" "event" "Enabled"
else
if ! [[ "${version:?}" =~ ${old_versions:?} ]] && [ "$os" = "Darwin" ]; then
add_brew_extension event extension >/dev/null 2>&1
add_brew_extension event extension
else
add_event_helper "$ext" >/dev/null 2>&1
add_event_helper "$ext"
fi
add_extension_log "event" "Installed and enabled"
fi

View File

@@ -1,17 +1,3 @@
add_firebird_client_darwin() {
firebird_tag='v5.0.0'
arch_name='x64'
arch="$(uname -m)"
[[ "$arch" = "arm64" || "$arch" = "aarch64" ]] && arch_name='arm64'
pkg_name=$(get -s -n "" https://api.github.com/repos/FirebirdSQL/firebird/releases/tags/"$firebird_tag" | grep -Eo "Firebird-.*.-$arch_name.pkg" | head -n 1)
[ -z "$pkg_name" ] && pkg_name=$(get -s -n "" https://github.com/FirebirdSQL/firebird/releases/expanded_assets/"$firebird_tag" | grep -Eo "Firebird-.*.-$arch_name.pkg" | head -n 1)
get -q -e "/tmp/firebird.pkg" https://github.com/FirebirdSQL/firebird/releases/download/"$firebird_tag"/"$pkg_name"
sudo installer -pkg /tmp/firebird.pkg -target /
sudo mkdir -p /opt/firebird/include /opt/firebird/lib
sudo cp -a /Library/Frameworks/Firebird.framework/Headers/* /opt/firebird/include/
sudo find /Library/Frameworks/Firebird.framework -name '*.dylib' -exec cp "{}" /opt/firebird/lib \;
}
add_firebird_helper() {
firebird_dir=$1
tag="$(php_src_tag)"
@@ -23,22 +9,19 @@ add_firebird_helper() {
}
add_firebird() {
if [ "$(uname -s )" = "Darwin" ]; then
add_firebird_client_darwin >/dev/null 2>&1
fi
enable_extension pdo_firebird extension
status="Enabled"
if ! check_extension pdo_firebird; then
status="Installed and enabled"
if check_extension pdo_firebird; then
add_log "${tick:?}" pdo_firebird Enabled
else
if [ "$(uname -s)" = "Linux" ]; then
if [[ "${version:?}" =~ 5.3|${nightly_versions:?} ]]; then
add_firebird_helper /usr >/dev/null 2>&1
if [[ "${version:?}" =~ 5.3|${php_builder_versions:?} ]]; then
add_firebird_helper /usr
else
add_pdo_extension firebird >/dev/null 2>&1
add_pdo_extension firebird
fi
else
add_firebird_helper /opt/firebird >/dev/null 2>&1
add_brew_extension pdo_firebird extension
fi
add_extension_log pdo_firebird "Installed and enabled"
fi
add_extension_log pdo_firebird "$status"
}

View File

@@ -1,9 +1,9 @@
# Helper function to add gearman extension.
add_gearman_helper() {
install_packages libgearman-dev
enable_extension gearman extension
if ! check_extension gearman; then
status="Installed and enabled"
install_packages libgearman-dev
if [[ "${version:?}" =~ 5.[3-6] ]]; then
pecl_install gearman-1.1.2
elif [[ "${version:?}" =~ 7.0 ]]; then
@@ -19,7 +19,7 @@ add_gearman_helper() {
add_gearman() {
status="Enabled"
if [ "$(uname -s)" = 'Linux' ]; then
add_gearman_helper >/dev/null 2>&1
add_gearman_helper
add_extension_log "gearman" "$status"
else
add_brew_extension gearman extension

View File

@@ -11,7 +11,7 @@ add_geos() {
if check_extension "geos"; then
add_log "${tick:?}" "geos" "Enabled"
else
add_geos_helper >/dev/null 2>&1
add_geos_helper
add_extension_log "geos" "Installed and enabled"
fi
}

View File

@@ -10,13 +10,15 @@ Function Get-ICUUrl() {
[ValidateNotNull()]
$vs_version
)
$trunk = "https://windows.php.net"
$urls=@("${trunk}/downloads/php-sdk/deps/${vs_version}/${arch}", "${trunk}/downloads/php-sdk/deps/archives/${vs_version}/${arch}")
$trunk = "https://downloads.php.net"
$urls=@("${trunk}/~windows/php-sdk/deps/${vs_version}/${arch}/", "${trunk}/~windows/php-sdk/deps/archives/${vs_version}/${arch}/")
foreach ($url in $urls) {
$web_content = Get-File -Url $url
try {
$web_content = Get-File -Url $url 2>$null
} catch { continue }
foreach ($link in $web_content.Links) {
if ($link -match "/.*ICU-${icu_version}.*/") {
return $trunk + $link.HREF
if ($link.href -match ".*ICU-${icu_version}.*") {
return $url + $link.HREF
}
}
}
@@ -40,12 +42,12 @@ Function Repair-ICU() {
}
Function Add-Http() {
Add-Extension iconv >$null 2>&1
Add-Extension raphf >$null 2>&1
Add-Extension iconv
Add-Extension raphf
if($version -lt '8.0') {
Add-Extension propro >$null 2>&1
Add-Extension propro
}
Add-Extension pecl_http >$null 2>&1
Add-Extension pecl_http
Repair-ICU
Add-ExtensionLog http "Installed and enabled"
}

View File

@@ -75,7 +75,7 @@ add_http_latest() {
if [ "$os" = "Linux" ]; then
add_http_dependencies
package="php$version-http"
add_ppa ondrej/php >/dev/null 2>&1 || update_ppa ondrej/php
add_ppa ondrej/php || update_ppa ondrej/php
(check_package "$package" && install_packages "$package") || add_http_helper "$(get_http_version)" "$os"
else
if ! [[ "${version:?}" =~ ${old_versions:?} ]]; then
@@ -103,9 +103,9 @@ add_http() {
ext=$1
status="Enabled"
if [[ "$ext" =~ ^(pecl_http|http)$ ]]; then
add_http_latest >/dev/null 2>&1
add_http_latest
else
add_http_version "$ext" >/dev/null 2>&1
add_http_version "$ext"
fi
add_extension_log "http" "$status"
}

View File

@@ -61,7 +61,7 @@ add_cli_driver() {
else
libs='/usr/local/lib'
sudo mkdir -p "$libs"
sudo ln -sf "$ibm_cli"/lib/*.dylib "$libs" >/dev/null 2>&1 || true
sudo ln -sf "$ibm_cli"/lib/*.dylib "$libs" || true
fi
}
@@ -93,11 +93,11 @@ add_ibm() {
status='Enabled'
ibm_home='/opt/ibm'
ibm_cli=$ibm_home/clidriver
if ! add_cli_driver >/dev/null 2>&1; then
if ! add_cli_driver ; then
add_log "${cross:?}" "$ext" "IBM Db2 CLI driver is not available on $(uname -s)/$(uname -m)"
return 1
fi
add_ibm_helper >/dev/null 2>&1
add_ibm_helper
add_extension_log "$ext" "$status"
check_extension "$ext" && add_license_log
}

View File

@@ -17,7 +17,7 @@ add_intl() {
add_log "${cross:?}" "intl" "ICU $icu is not supported for PHP $version"
else
[ "${ts:?}" = 'zts' ] && suffix='-zts'
install_icu "$icu" >/dev/null 2>&1
install_icu "$icu"
get -q -n "${ext_dir:?}/intl.so" "https://github.com/shivammathur/icu-intl/releases/download/intl-$icu/php${version:?}-intl-$icu$suffix$arch_suffix.so"
enable_extension intl extension
add_extension_log intl "Installed and enabled with ICU $icu"

View File

@@ -24,10 +24,10 @@ add_ioncube() {
if [ -e "$loader_file" ]; then
sudo mv /tmp/ioncube/ioncube_loader_"${os_suffix%%_*}_${version:?}$ts_part".so "${ext_dir:?}/ioncube.so"
sudo cp /tmp/ioncube/LICENSE.txt "$ext_dir"/IONCUBE_LICENSE.txt
echo "zend_extension=$ext_dir/ioncube.so" | sudo tee "${scan_dir:?}/00-ioncube.ini" >/dev/null 2>&1
echo "zend_extension=$ext_dir/ioncube.so" | sudo tee "${scan_dir:?}/00-ioncube.ini"
fi
else
echo "zend_extension=$ext_dir/ioncube.so" | sudo tee "${scan_dir:?}/00-ioncube.ini" >/dev/null 2>&1
echo "zend_extension=$ext_dir/ioncube.so" | sudo tee "${scan_dir:?}/00-ioncube.ini"
fi
add_extension_log "ioncube" "$status"
check_extension "ioncube" && add_license_log

View File

@@ -71,7 +71,7 @@ Function Add-Oci() {
}
} else {
$status = 'Installed and enabled'
Add-Extension $extension >$null 2>&1
Add-Extension $extension
}
Add-ExtensionLog $extension $status
Add-LicenseLog

View File

@@ -72,8 +72,8 @@ add_oci() {
oracle_home='/opt/oracle'
oracle_client=$oracle_home/instantclient
os=$(uname -s)
add_client >/dev/null 2>&1
add_oci_helper >/dev/null 2>&1
add_client
add_oci_helper
add_extension_log "$ext" "$status"
check_extension "$ext" && add_license_log
}

View File

@@ -19,26 +19,6 @@ Function Get-PhalconReleaseAssetUrl() {
$match = (Get-File -Url "$github/$releases/expanded_assets/v$Semver").Links.href | Select-String -Pattern "(phalcon_${arch}_.*_php${version}_${extension_version}.*[0-9]${nts}.zip)"
} catch { }
}
} else {
$nts = if (!$installed.ThreadSafe) { "-nts" } else { "-ts" }
try {
$match = (Invoke-RestMethod -Uri "$domain/$releases/tags/v$Semver").assets | Select-String -Pattern "browser_download_url=.*(php_phalcon-php${version}${nts}-windows.*-x64.zip)"
} catch { }
if($null -eq $match) {
try {
$match = (Get-File -Url "$github/$releases/expanded_assets/v$Semver").Links.href | Select-String -Pattern "(php_phalcon-php${version}${nts}-windows.*-x64.zip)"
} catch { }
}
if($null -eq $match) {
try {
$match = (Invoke-RestMethod -Uri "$domain/$releases/tags/v$Semver").assets | Select-String -Pattern "browser_download_url=.*(phalcon-php${version}${nts}-windows.*-x64.zip)"
} catch { }
}
if($null -eq $match) {
try {
$match = (Get-File -Url "$github/$releases/expanded_assets/v$Semver").Links.href | Select-String -Pattern "(phalcon-php${version}${nts}-windows.*-x64.zip)"
} catch { }
}
}
if($NULL -ne $match) {
return "$github/$releases/download/v$Semver/$($match.Matches[0].Groups[1].Value)"
@@ -73,6 +53,8 @@ Function Get-PhalconSemver() {
return '4.1.0'
} elseif (($extension_version -eq '5') -and ($version -eq '7.4')) {
return '5.4.0'
} elseif (($extension_version -eq '5') -and ($version -eq '8.0')) {
return '5.10.0'
}
return Get-PeclPackageVersion phalcon $extension_version stable stable | Select-Object -First 1
}
@@ -80,7 +62,7 @@ Function Get-PhalconSemver() {
# Function to install phalcon
Function Add-PhalconHelper() {
$semver = Get-PhalconSemver
if ($extension_version -eq '3') {
if ($extension_version -match '[3-4]') {
Add-PhalconFromGitHub $semver
} else {
Add-Extension -Extension phalcon -Stability stable -Extension_version $semver

View File

@@ -3,6 +3,8 @@ get_phalcon_version() {
if [ "$extension" = "phalcon5" ]; then
if [ "${version:?}" = "7.4" ]; then
echo '5.4.0'
elif [ "${version:?}" = "8.0" ]; then
echo '5.10.0'
else
get_pecl_version phalcon stable 5
fi
@@ -34,7 +36,7 @@ add_phalcon_helper() {
add_brew_extension "$extension" extension
else
package="php${version:?}-$extension"
add_ppa ondrej/php >/dev/null 2>&1 || update_ppa ondrej/php
add_ppa ondrej/php || update_ppa ondrej/php
[[ "$extension" =~ phalcon[4|5] ]] && (install_packages "php${version:?}-psr" || pecl_install psr || pecl_install psr-1.1.0)
(check_package "$package" && install_packages "$package") || pecl_install phalcon-"$(get_phalcon_version)" || add_phalcon_from_repo
fi
@@ -94,7 +96,7 @@ add_phalcon() {
[ "$extension" = "phalcon" ] && extension=phalcon5
extension_major_version=${extension: -1}
if [[ "$extension_major_version" =~ [3-5] ]]; then
add_phalcon"$extension_major_version" >/dev/null 2>&1
add_phalcon"$extension_major_version"
fi
add_extension_log "phalcon" "$status"
}

View File

@@ -36,7 +36,10 @@ get_openssl_suffix() {
change_library_paths() {
if [ "$os" = "Darwin" ]; then
otool -L "${ext_dir:?}"/relay.so | grep -q 'ssl.1' && openssl_version='1.1' || openssl_version='3'
[ -e "${brew_prefix:?}"/opt/openssl@"$openssl_version" ] || brew install openssl@"$openssl_version"
[ -e "${brew_prefix:?}"/opt/openssl@"$openssl_version" ] || {
safe_brew install --skip-link openssl@"$openssl_version" &&
brew link --overwrite --force openssl@"$openssl_version"
}
dylibs="$(otool -L "${ext_dir:?}"/relay.so | grep -Eo '.*\.dylib' | cut -f1 -d ' ')"
install_name_tool -change "$(echo "${dylibs}" | grep -E "libzstd.*dylib" | xargs)" "$brew_prefix"/opt/zstd/lib/libzstd.dylib "$ext_dir"/relay.so
install_name_tool -change "$(echo "${dylibs}" | grep -E "liblz4.*dylib" | xargs)" "$brew_prefix"/opt/lz4/lib/liblz4.dylib "$ext_dir"/relay.so
@@ -54,7 +57,7 @@ add_relay_dependencies() {
if [ "$os" = "Darwin" ]; then
. "${0%/*}"/tools/brew.sh
configure_brew
brew install lz4 hiredis zstd concurrencykit
safe_brew install lz4 hiredis zstd concurrencykit
fi
}
@@ -142,14 +145,14 @@ add_relay() {
error="Relay extension is not available for macOS x86_64 architecture"
else
relay_version=$(get_relay_version "$ext")
add_relay_dependencies >/dev/null 2>&1
add_relay_dependencies
if shared_extension relay; then
message="Enabled"
else
add_relay_helper "$arch" >/dev/null 2>&1
add_relay_helper "$arch"
message="Installed and enabled ${relay_version}"
fi
configure_relay >/dev/null 2>&1
configure_relay
fi
add_extension_log relay "$message" "$error"
}

View File

@@ -51,7 +51,7 @@ check_lib() {
add_linux_libs() {
local lib=$1
if ! check_lib "$lib"; then
install_packages "$lib" >/dev/null 2>&1 || true
install_packages "$lib" || true
fi
add_lib_log "$lib"
}
@@ -60,9 +60,11 @@ add_linux_libs() {
add_darwin_libs() {
local lib=$1
if ! check_lib "$lib"; then
brew install "$lib" >/dev/null 2>&1 || true
if [[ "$lib" = *@* ]]; then
brew link --overwrite --force "$lib" >/dev/null 2>&1 || true
safe_brew install --skip-link "$lib" || true
brew link --overwrite --force "$lib" || true
else
safe_brew install "$lib" || true
fi
fi
add_lib_log "$lib"
@@ -97,7 +99,7 @@ get_libraries() {
run_group() {
local command=$1
local log=$2
echo "$command" | sudo tee ./run_group.sh >/dev/null 2>&1
echo "$command" | sudo tee ./run_group.sh
echo "$GROUP$log"
. ./run_group.sh
local status=$?
@@ -161,7 +163,7 @@ add_extension_from_source() {
sub_dir="$(parse_args "$extension" PATH)"
step_log "Setup $slug"
(
add_devtools phpize >/dev/null 2>&1
add_devtools phpize
disable_extension_helper "$extension"
fetch_extension "$extension" "$fetch"
if ! [ "$(find . -maxdepth 1 -name '*.m4' -exec grep -H 'PHP_NEW_EXTENSION' {} \; | wc -l)" != "0" ]; then
@@ -169,7 +171,7 @@ add_extension_from_source() {
else
[[ -n "${libraries// }" ]] && run_group "add_libs $libraries" "add libraries"
[ "${debug:?}" = "debug" ] && suffix_opts="$suffix_opts --enable-debug"
patch_extension "$extension" >/dev/null 2>&1
patch_extension "$extension"
run_group "phpize" "phpize" && \
run_group "sudo $prefix_opts ./configure $suffix_opts $opts" "configure" && \
run_group "sudo $prefix_opts make -j$(nproc 2>/dev/null || sysctl -n hw.ncpu)" "make" && \

View File

@@ -9,6 +9,8 @@ Function Get-SqlsrvReleaseVersion() {
return '5.10.1'
} elseif ($version -eq '8.0') {
return '5.11.1'
} elseif ($version -match '8.[1-2]') {
return '5.12.0'
} else {
return 'latest'
}
@@ -67,10 +69,10 @@ Function Add-Sqlsrv() {
Add-ExtensionFromGithub $extension > $null 2>&1
} catch {}
if (-not(Test-Extension $extension)) {
Add-SqlsrvFromMSGithub $extension >$null 2>&1
Add-SqlsrvFromMSGithub $extension
}
if (-not(Test-Extension $extension)) {
Add-Extension $extension >$null 2>&1
Add-Extension $extension
}
$status = 'Installed and enabled'
}

View File

@@ -6,6 +6,8 @@ get_sqlsrv_version() {
echo '5.10.1'
elif [[ "${version:?}" =~ 8.0 ]]; then
echo '5.11.1'
elif [[ "${version:?}" =~ 8.[1-2] ]]; then
echo '5.12.0'
else
# Return an empty string so that pecl will install the latest version.
echo ''

View File

@@ -77,7 +77,7 @@ Function Add-ZephirParser() {
try {
Add-ZephirParserFromGitHub $extension
} catch {
Add-Extension $extension >$null 2>&1
Add-Extension $extension
}
}
Add-ExtensionLog zephir_parser $status

View File

@@ -33,7 +33,7 @@ add_zephir_parser() {
zp_releases=https://github.com/"$repo"/releases
if ! shared_extension zephir_parser; then
message='Installed and enabled'
add_zephir_parser_helper "$ext" >/dev/null 2>&1
add_zephir_parser_helper "$ext"
else
message='Enabled'
enable_extension zephir_parser extension

View File

@@ -20,23 +20,23 @@ self_hosted_helper() {
sudo ln -sf /usr/bin/apt-get /usr/bin/apt-fast
trap "sudo rm -f /usr/bin/apt-fast 2>/dev/null" exit
fi
sudo mkdir -p /opt/hostedtoolcache >/dev/null 2>&1 || true
sudo mkdir -p /opt/hostedtoolcache || true
install_packages apt-transport-https ca-certificates curl file make jq unzip autoconf automake gcc g++ gnupg
}
# Function to fix broken packages.
fix_broken_packages() {
sudo apt --fix-broken install >/dev/null 2>&1
sudo apt --fix-broken install
}
# Function to install a package
install_packages() {
packages=("$@")
if ! [ -e /etc/dpkg/dpkg.cfg.d/force-confnew ]; then
echo "force-confnew" | sudo tee /etc/dpkg/dpkg.cfg.d/force-confnew >/dev/null 2>&1
echo "force-confnew" | sudo tee /etc/dpkg/dpkg.cfg.d/force-confnew
trap "sudo rm -f /etc/dpkg/dpkg.cfg.d/force-confnew 2>/dev/null" exit
fi
$apt_install "${packages[@]}" >/dev/null 2>&1 || (update_lists && fix_broken_packages && $apt_install "${packages[@]}" >/dev/null 2>&1)
$apt_install "${packages[@]}" || (update_lists && fix_broken_packages && $apt_install "${packages[@]}" )
}
# Function to disable an extension.
@@ -48,10 +48,10 @@ disable_extension_helper() {
disable_extension_dependents "$extension"
fi
sudo sed -Ei "/=(.*\/)?\"?$extension(.so)?\"?$/d" "${ini_file[@]}" "$pecl_file"
sudo find "$ini_dir"/.. -name "*-$extension.ini" -not -path "*phar.ini" -not -path "*pecl.ini" -not -path "*mods-available*" -delete >/dev/null 2>&1 || true
sudo find "$ini_dir"/.. -name "*-$extension.ini" -not -path "*phar.ini" -not -path "*pecl.ini" -not -path "*mods-available*" -delete || true
sudo rm -f /tmp/php"$version"_extensions
mkdir -p /tmp/extdisabled/"$version"
echo '' | sudo tee /tmp/extdisabled/"$version"/"$extension" >/dev/null 2>&1
echo '' | sudo tee /tmp/extdisabled/"$version"/"$extension"
}
# Function to add PDO extension.
@@ -64,7 +64,7 @@ add_pdo_extension() {
ext_name=$1
if shared_extension pdo; then
disable_extension_helper pdo
echo "extension=pdo.so" | sudo tee "${ini_file[@]/php.ini/conf.d/10-pdo.ini}" >/dev/null 2>&1
echo "extension=pdo.so" | sudo tee "${ini_file[@]/php.ini/conf.d/10-pdo.ini}"
fi
if [ "$ext" = "mysql" ]; then
enable_extension "mysqlnd" "extension"
@@ -72,15 +72,15 @@ add_pdo_extension() {
elif [ "$ext" = "dblib" ]; then
ext_name="sybase"
elif [ "$ext" = "firebird" ]; then
install_packages libfbclient2 >/dev/null 2>&1
install_packages libfbclient2
enable_extension "pdo_firebird" "extension"
ext_name="interbase"
elif [ "$ext" = "sqlite" ]; then
ext="sqlite3"
ext_name="sqlite3"
fi
add_extension "$ext_name" "extension" >/dev/null 2>&1
add_extension "$pdo_ext" "extension" >/dev/null 2>&1
add_extension "$ext_name" "extension"
add_extension "$pdo_ext" "extension"
add_extension_log "$pdo_ext" "Enabled"
fi
}
@@ -94,7 +94,7 @@ check_package() {
add_extension_helper() {
local extension=$1
packages=(php"$version"-"$extension")
add_ppa ondrej/php >/dev/null 2>&1 || update_ppa ondrej/php
add_ppa ondrej/php || update_ppa ondrej/php
[ "${debug:?}" = "debug" ] && check_package php"$version"-"$extension"-dbgsym && packages+=(php"$version"-"$extension"-dbgsym)
(check_package "${packages[0]}" && install_packages "${packages[@]}") || pecl_install "$extension"
add_extension_log "$extension" "Installed and enabled"
@@ -108,7 +108,7 @@ add_devtools() {
install_packages "php$version-dev"
fi
switch_version "phpize" "php-config"
add_extension xml extension >/dev/null 2>&1
add_extension xml extension
add_log "${tick:?}" "$tool" "Added $tool $semver"
}
@@ -127,13 +127,58 @@ setup_cached_versions() {
run_script "php-ubuntu" "$version" "${debug:?}" "${ts:?}"
}
# Function to get the link path for an alternative.
alternative_link() {
case "$1" in
php-cgi-bin) echo "/usr/lib/cgi-bin/php" ;;
php-fpm) echo "/usr/sbin/php-fpm" ;;
php-fpm.sock) echo "/run/php/php-fpm.sock" ;;
*) echo "/usr/bin/$1" ;;
esac
}
# Function to get the target path for an alternative.
alternative_target() {
case "$1" in
php-cgi-bin) echo "/usr/lib/cgi-bin/php$version" ;;
php-fpm) echo "/usr/sbin/php-fpm$version" ;;
php-fpm.sock) echo "/run/php/php$version-fpm.sock" ;;
*) echo "/usr/bin/$1$version" ;;
esac
}
# Function to register an alternative if the versioned binary exists but is missing.
register_alternative() {
local tool=$1
local link target priority state_file
target="$(alternative_target "$tool")"
[ -e "$target" ] || return 0
state_file="/var/lib/dpkg/alternatives/$tool"
if sudo test -r "$state_file" && sudo grep -Fxq "$target" "$state_file"; then
return 0
fi
link="$(alternative_link "$tool")"
priority="${version//./}"
sudo update-alternatives --install "$link" "$tool" "$target" "$priority"
}
# Function to register and switch an alternative.
set_alternative() {
local tool=$1
local target
target="$(alternative_target "$tool")"
[ -e "$target" ] || return 0
register_alternative "$tool" || return 1
sudo update-alternatives --set "$tool" "$target"
}
# Function to add PECL.
add_pecl() {
add_devtools phpize >/dev/null 2>&1
add_devtools phpize
if ! command -v pecl >/dev/null; then
install_packages php-pear
fi
configure_pecl >/dev/null 2>&1
configure_pecl
pear_version=$(get_tool_version "pecl" "version")
add_log "${tick:?}" "PECL" "Added PECL $pear_version"
}
@@ -143,16 +188,11 @@ switch_version() {
tools=("$@")
to_wait=()
if ! (( ${#tools[@]} )); then
tools+=(pear pecl php phar phar.phar php-cgi php-config phpize phpdbg)
[ -e /usr/lib/cgi-bin/php"$version" ] && sudo update-alternatives --set php-cgi-bin /usr/lib/cgi-bin/php"$version" & to_wait+=($!)
[ -e /usr/sbin/php-fpm"$version" ] && sudo update-alternatives --set php-fpm /usr/sbin/php-fpm"$version" & to_wait+=($!)
[ -e /run/php/php"$version"-fpm.sock ] && sudo update-alternatives --set php-fpm.sock /run/php/php"$version"-fpm.sock & to_wait+=($!)
tools+=(php-cgi-bin php-fpm php-fpm.sock pear pecl php phar phar.phar php-cgi php-config phpize phpdbg)
fi
for tool in "${tools[@]}"; do
if [ -e "/usr/bin/$tool$version" ]; then
sudo update-alternatives --set "$tool" /usr/bin/"$tool$version" &
set_alternative "$tool" &
to_wait+=($!)
fi
done
wait "${to_wait[@]}"
}
@@ -167,7 +207,7 @@ get_php_packages() {
# Function to install packaged PHP
add_packaged_php() {
add_ppa ondrej/php >/dev/null 2>&1 || update_ppa ondrej/php
add_ppa ondrej/php || update_ppa ondrej/php
IFS=' ' read -r -a packages <<<"$(get_php_packages)"
install_packages "${packages[@]}"
}
@@ -187,11 +227,11 @@ update_php() {
# Function to install PHP.
add_php() {
if [ "${runner:?}" = "self-hosted" ] || [ "${use_package_cache:-true}" = "false" ]; then
if [[ "$version" =~ ${nightly_versions:?} || "$ts" = "zts" ]]; then
if [[ "$version" =~ ${php_builder_versions:?} || "$ts" = "zts" ]]; then
setup_php_builder
else
add_packaged_php
switch_version >/dev/null 2>&1
switch_version
add_pecl
fi
elif [[ "$version" =~ ${old_versions:?} ]]; then
@@ -204,7 +244,7 @@ add_php() {
# Function to ini file for pear and link it to each SAPI.
link_pecl_file() {
echo '' | sudo tee "$pecl_file" >/dev/null 2>&1
echo '' | sudo tee "$pecl_file"
for file in "${ini_file[@]}"; do
sapi_scan_dir="$(realpath -m "$(dirname "$file")")/conf.d"
if [ "$sapi_scan_dir" != "$scan_dir" ] && ! [ -h "$sapi_scan_dir" ]; then
@@ -237,9 +277,9 @@ add_php_config() {
elif [ "$ini" = "development" ]; then
echo "${ini_file[@]}" | xargs -n 1 -P 6 sudo cp "$php_lib_dir"/php.ini-development
elif [ "$ini" = "none" ]; then
echo '' | sudo tee "${ini_file[@]}" >/dev/null 2>&1
echo '' | sudo tee "${ini_file[@]}"
fi
echo "$ini" | sudo tee "$current_ini" >/dev/null 2>&1
echo "$ini" | sudo tee "$current_ini"
}
# Function to Setup PHP
@@ -250,13 +290,13 @@ setup_php() {
check_pre_installed
if [[ -z "$php_config" ]] || [ "$(php_semver | cut -c 1-3)" != "$version" ]; then
if [ ! -e "/usr/bin/php$version" ] || [ ! -e "/usr/bin/php-config$version" ]; then
add_php >/dev/null 2>&1
add_php
else
if ! [[ "$version" =~ ${old_versions:?} ]]; then
switch_version >/dev/null 2>&1
switch_version
fi
if [ "${update:?}" = "true" ]; then
update_php >/dev/null 2>&1
update_php
else
status="Switched to"
fi
@@ -264,7 +304,7 @@ setup_php() {
php_config="$(command -v php-config)"
else
if [ "$update" = "true" ]; then
update_php >/dev/null 2>&1
update_php
else
status="Found"
fi
@@ -284,7 +324,7 @@ setup_php() {
link_pecl_file
configure_php
set_output "php-version" "$semver"
sudo rm -rf /usr/local/bin/phpunit >/dev/null 2>&1
sudo rm -rf /usr/local/bin/phpunit
sudo chmod 777 "${ini_file[@]}" "$pecl_file" "${tool_path_dir:?}"
sudo cp "$src"/configs/pm/*.json "$RUNNER_TOOL_CACHE/"
add_log "${tick:?}" "PHP" "$status PHP $semver$extra_version"
@@ -298,8 +338,8 @@ debconf_fix="DEBIAN_FRONTEND=noninteractive"
apt_install="sudo $debconf_fix apt-fast install -y --no-install-recommends"
scripts="$src"/scripts
add_sudo >/dev/null 2>&1
link_apt_fast >/dev/null 2>&1
add_sudo
link_apt_fast
. /etc/os-release
# shellcheck source=.

View File

@@ -3,6 +3,7 @@ $composer_home = "$env:APPDATA\Composer"
$composer_bin = "$composer_home\vendor\bin"
$composer_json = "$composer_home\composer.json"
$composer_lock = "$composer_home\composer.lock"
$skip_composer_github_auth = $false
# Function to configure composer.
Function Edit-ComposerConfig() {
@@ -23,6 +24,7 @@ Function Edit-ComposerConfig() {
if (-not(Test-Path $composer_json)) {
Set-Content -Path $composer_json -Value "{}"
}
Get-ToolVersion "composer" $null | Out-Null
Set-ComposerEnv
Add-Path $composer_bin
Set-ComposerAuth
@@ -74,8 +76,18 @@ function Test-GitHubPublicAccess {
}
}
Function Write-ComposerGhAuthNoOpWarning() {
$message = (Get-Content (Join-Path $src 'configs\composer-gh-auth-warn') -Raw).Trim().Replace('%s', $composer_version)
if($env:fail_fast -eq 'true') {
Add-Log "$cross" "composer" $message
} else {
Write-Output "::warning::$message"
}
}
# Function to setup authentication in composer.
Function Set-ComposerAuth() {
$token = if ($env:COMPOSER_TOKEN) { $env:COMPOSER_TOKEN } else { $env:GITHUB_TOKEN }
if(Test-Path env:COMPOSER_AUTH_JSON) {
if(Test-Json -JSON $env:COMPOSER_AUTH_JSON) {
Set-Content -Path $composer_home\auth.json -Value $env:COMPOSER_AUTH_JSON
@@ -83,13 +95,18 @@ Function Set-ComposerAuth() {
Add-Log "$cross" "composer" "Could not parse COMPOSER_AUTH_JSON as valid JSON"
}
}
if($skip_composer_github_auth) {
Write-ComposerGhAuthNoOpWarning
}
$composer_auth = @()
if(Test-Path env:PACKAGIST_TOKEN) {
$composer_auth += '"http-basic": {"repo.packagist.com": { "username": "token", "password": "' + $env:PACKAGIST_TOKEN + '"}}'
}
$write_token = $true
$token = if ($env:COMPOSER_TOKEN) { $env:COMPOSER_TOKEN } else { $env:GITHUB_TOKEN }
if ($token) {
if ($skip_composer_github_auth) {
$write_token = $false
}
if ($env:GITHUB_SERVER_URL -ne "https://github.com" -and -not(Test-GitHubPublicAccess $token)) {
$write_token = $false
}
@@ -115,6 +132,18 @@ Function Set-ComposerEnv() {
}
}
# Function to identify latest-like URLs that should bypass the persistent cache.
Function Test-MutableToolUrl() {
Param(
[Parameter(Position = 0, Mandatory = $true)]
[string]
$Url
)
$mutableUrlRegex = '(^|[/?#._=-])(latest|stable|preview|snapshot|nightly|master)([/?#._=-]|$)|/releases/latest/download/'
$versionLikeRegex = '(^|[^0-9])[0-9]+\.[0-9]+([.-][0-9A-Za-z]+)*'
return ($Url -match $mutableUrlRegex) -or (($Url -match '\.phar([?#].*)?$') -and -not ($Url -match $versionLikeRegex))
}
# Function to extract tool version.
Function Get-ToolVersion() {
Param (
@@ -158,12 +187,7 @@ Function Add-ToolsHelper() {
} elseif($tool -eq "cs2pr") {
(Get-Content $bin_dir/cs2pr).replace('exit(9)', 'exit(0)') | Set-Content $bin_dir/cs2pr
} elseif($tool -eq "deployer") {
if(Test-Path $composer_bin\deployer.phar.bat) {
Copy-Item $composer_bin\deployer.phar.bat -Destination $composer_bin\dep.bat
}
if(Test-Path $composer_bin\dep.bat) {
Copy-Item $composer_bin\dep.bat -Destination $composer_bin\deployer.bat
}
Copy-Item $bin_dir\deployer.bat -Destination $bin_dir\dep.bat
} elseif($tool -eq "phan") {
$extensions += @('fileinfo', 'ast')
} elseif($tool -eq "phinx") {
@@ -176,7 +200,7 @@ Function Add-ToolsHelper() {
$extensions += @('json', 'tokenizer')
} elseif($tool -eq "phpDocumentor") {
$extensions+=('ctype', 'hash', 'json', 'fileinfo', 'iconv', 'mbstring', 'simplexml', 'xml')
Add-Extension fileinfo >$null 2>&1
Add-Extension fileinfo
Copy-Item $bin_dir\phpDocumentor.bat -Destination $bin_dir\phpdoc.bat
} elseif($tool -eq "phpunit") {
$extensions += @('dom', 'json', 'libxml', 'mbstring', 'xml', 'xmlwriter')
@@ -189,7 +213,7 @@ Function Add-ToolsHelper() {
Copy-Item $bin_dir\wp-cli.bat -Destination $bin_dir\wp.bat
}
foreach($extension in $extensions) {
Add-Extension $extension >$null 2>&1
Add-Extension $extension
}
}
@@ -203,16 +227,29 @@ Function Add-Tool() {
[ValidateNotNull()]
$tool,
[Parameter(Position = 2, Mandatory = $false)]
$ver_param
$ver_param,
[Parameter(Position = 3, Mandatory = $false)]
$skip_composer_github_auth
)
if (Test-Path $bin_dir\$tool) {
Copy-Item $bin_dir\$tool -Destination $bin_dir\$tool.old -Force
if($tool -eq "composer") {
$script:skip_composer_github_auth = $skip_composer_github_auth -eq 'true'
}
$urls = $urls -split ','
$tool_path = "$bin_dir\$tool"
$is_exe = ((($urls[0] | Split-Path -Extension).ToLowerInvariant()) -eq '.exe')
if ($is_exe) { $tool_path = "$tool_path.exe" }
$tool_ext = if ($is_exe) { '.exe' } else { '' }
$url_stream = [System.IO.MemoryStream]::New([System.Text.Encoding]::UTF8.GetBytes($urls[0]))
$cache_key = (Get-FileHash -InputStream $url_stream -Algorithm SHA256).Hash.Substring(0, 16)
$cache_path = "$env:TEMP\$tool-$cache_key$tool_ext"
$use_cache = -not (Test-MutableToolUrl $urls[0])
$status_code = 200
if ($use_cache -and (Test-Path $cache_path -PathType Leaf)) {
Copy-Item $cache_path -Destination $tool_path -Force
} else {
$backup_path = "$tool_path.bak"
if (Test-Path $tool_path) { Copy-Item $tool_path -Destination $backup_path -Force }
foreach ($url in $urls){
if (($url | Split-Path -Extension) -eq ".exe") {
$tool_path = "$tool_path.exe"
}
try {
$status_code = (Invoke-WebRequest -Passthru -Uri $url -OutFile $tool_path).StatusCode
} catch {
@@ -220,15 +257,28 @@ Function Add-Tool() {
try {
$url = $url.replace("releases/latest/download", "releases/download/" + ([regex]::match((Get-File -Url ($url.split('/release')[0] + "/releases")).Content, "([0-9]+\.[0-9]+\.[0-9]+)/" + ($url.Substring($url.LastIndexOf("/") + 1))).Groups[0].Value).split('/')[0])
$status_code = (Invoke-WebRequest -Passthru -Uri $url -OutFile $tool_path).StatusCode
} catch { }
} catch {
$status_code = 0
}
} else {
$status_code = 0
}
}
if($status_code -eq 200 -and (Test-Path $tool_path)) {
if ($use_cache) {
Copy-Item $tool_path -Destination $cache_path -Force
}
break
}
}
if ($status_code -ne 200 -and (Test-Path $backup_path)) {
Copy-Item $backup_path -Destination $tool_path -Force
}
Remove-Item $backup_path -Force -ErrorAction SilentlyContinue
}
if (((Get-ChildItem -Path $bin_dir/* | Where-Object Name -Match "^$tool(.exe|.phar)*$").Count -gt 0)) {
$escaped_tool = [regex]::Escape($tool)
if (((Get-ChildItem -Path $bin_dir/* | Where-Object Name -Match "^$escaped_tool(\.exe|\.phar)?$").Count -gt 0)) {
$bat_content = @()
$bat_content += "@ECHO off"
$bat_content += "setlocal DISABLEDELAYEDEXPANSION"
@@ -236,14 +286,12 @@ Function Add-Tool() {
$bat_content += "php %BIN_TARGET% %*"
Set-Content -Path $bin_dir\$tool.bat -Value $bat_content
Add-ToolsHelper $tool
Add-ToProfile $current_profile $tool "New-Alias $tool $bin_dir\$tool.bat" >$null 2>&1
Add-ToProfile $current_profile $tool "New-Alias $tool $bin_dir\$tool.bat"
$tool_version = Get-ToolVersion $tool $ver_param
Add-Log $tick $tool "Added $tool $tool_version"
} else {
if($tool -eq "composer") {
$env:fail_fast = 'true'
} elseif (Test-Path $bin_dir\$tool.old) {
Copy-Item $bin_dir\$tool.old -Destination $bin_dir\$tool -Force
}
Add-Log $cross $tool "Could not add $tool"
}
@@ -276,9 +324,9 @@ Function Add-ComposerToolHelper() {
Remove-Item -Path $composer_lock -Force
}
if((composer global show $prefix$tool $tool_version -a 2>&1 | findstr '^type *: *composer-plugin') -and ($composer_args -ne '')) {
composer global config --no-plugins allow-plugins."$prefix$tool" true >$null 2>&1
composer global config --no-plugins allow-plugins."$prefix$tool" true
}
composer global require $prefix$release $composer_args >$null 2>&1
composer global require $prefix$release $composer_args
return composer global show $prefix$tool 2>&1 | findstr '^versions'
} else {
$release_stream = [System.IO.MemoryStream]::New([System.Text.Encoding]::ASCII.GetBytes($release))
@@ -289,9 +337,9 @@ Function Add-ComposerToolHelper() {
New-Item -ItemType Directory -Force -Path $scoped_dir > $null 2>&1
Set-Content -Path $scoped_dir\composer.json -Value "{}"
if((composer show $prefix$tool $tool_version -d $unix_scoped_dir -a 2>&1 | findstr '^type *: *composer-plugin') -and ($composer_args -ne '')) {
composer config -d $unix_scoped_dir --no-plugins allow-plugins."$prefix$tool" true >$null 2>&1
composer config -d $unix_scoped_dir --no-plugins allow-plugins."$prefix$tool" true
}
composer require $prefix$release -d $unix_scoped_dir $composer_args >$null 2>&1
composer require $prefix$release -d $unix_scoped_dir $composer_args
}
[System.Environment]::SetEnvironmentVariable(($tool.replace('-', '_') + '_bin'), "$scoped_dir\vendor\bin")
Add-Path $scoped_dir\vendor\bin

View File

@@ -3,6 +3,7 @@ export composer_home="$HOME/.composer"
export composer_bin="$composer_home/vendor/bin"
export composer_json="$composer_home/composer.json"
export composer_lock="$composer_home/composer.lock"
skip_composer_github_auth=false
# Function to extract tool version.
get_tool_version() {
@@ -41,6 +42,7 @@ configure_composer() {
echo '{}' | tee "$composer_json" >/dev/null
chmod 644 "$composer_json"
fi
get_tool_version composer >/dev/null
set_composer_env
add_path "$composer_bin"
set_composer_auth
@@ -67,25 +69,42 @@ update_auth_json() {
# Function to check if public GitHub token authentication is possible.
can_access_public_github() {
curl --fail -s -H "Authorization: token $1" 'https://api.github.com/' >/dev/null 2>&1
curl --fail -s -H "Authorization: token $1" 'https://api.github.com/'
}
composer_gh_auth_no_op() {
local message
message="$(<"${src:?}"/configs/composer-gh-auth-warn)"
message="${message//%s/$composer_version}"
if [ "${fail_fast:-false}" = "true" ]; then
add_log "${cross:?}" "composer" "$message"
else
echo "::warning::$message"
fi
}
# Function to setup authentication in composer.
set_composer_auth() {
if [ -n "$COMPOSER_AUTH_JSON" ]; then
if php -r "json_decode('$COMPOSER_AUTH_JSON'); if(json_last_error() !== JSON_ERROR_NONE) { throw new Exception('invalid json'); }"; then
echo "$COMPOSER_AUTH_JSON" | tee "$composer_home/auth.json" >/dev/null
token="${COMPOSER_TOKEN:-$GITHUB_TOKEN}"
if [ -n "${COMPOSER_AUTH_JSON:-}" ]; then
if printf '%s' "$COMPOSER_AUTH_JSON" | jq -e . >/dev/null; then
printf '%s' "$COMPOSER_AUTH_JSON" | tee "$composer_home/auth.json" >/dev/null
else
add_log "${cross:?}" "composer" "Could not parse COMPOSER_AUTH_JSON as valid JSON"
fi
fi
if [ "$skip_composer_github_auth" = "true" ]; then
composer_gh_auth_no_op
fi
composer_auth=()
if [ -n "$PACKAGIST_TOKEN" ]; then
composer_auth+=( '"http-basic": {"repo.packagist.com": { "username": "token", "password": "'"$PACKAGIST_TOKEN"'"}}' )
fi
token="${COMPOSER_TOKEN:-$GITHUB_TOKEN}"
if [ -n "$token" ]; then
write_token=true
if [ "$skip_composer_github_auth" = "true" ]; then
write_token=false
fi
if [ "$GITHUB_SERVER_URL" != "https://github.com" ]; then
can_access_public_github "$token" || write_token=false
fi
@@ -108,11 +127,21 @@ set_composer_env() {
add_env_path "$composer_env"
if [ -n "$COMPOSER_ALLOW_PLUGINS" ]; then
echo "$COMPOSER_ALLOW_PLUGINS" | tr ',' '\n' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//' | grep -v '^$' | while IFS= read -r plugin; do
composer global config --no-plugins "allow-plugins.$plugin" true >/dev/null 2>&1
composer global config --no-plugins "allow-plugins.$plugin" true
done
fi
}
# Function to identify latest-like URLs that should bypass the persistent cache.
is_mutable_tool_url() {
local tool_url=$1
local mutable_url_regex='(^|[/?#._=-])(latest|stable|preview|snapshot|nightly|master)([/?#._=-]|$)|/releases/latest/download/'
local version_like_regex='(^|[^0-9])[0-9]+\.[0-9]+([.-][0-9A-Za-z]+)*'
[[ "$tool_url" =~ $mutable_url_regex ]] && return 0
[[ "$tool_url" =~ \.phar([?#].*)?$ && ! "$tool_url" =~ $version_like_regex ]] && return 0
return 1
}
# Helper function to configure tools.
add_tools_helper() {
tool=$1
@@ -123,19 +152,15 @@ add_tools_helper() {
extensions+=(iconv mbstring phar sodium)
elif [ "$tool" = "codeception" ]; then
extensions+=(json mbstring)
sudo ln -s "$scoped_dir"/vendor/bin/codecept "$scoped_dir"/vendor/bin/codeception
sudo ln -s "$scoped_dir"/vendor/bin/codecept "$scoped_dir"/vendor/bin/codeception 2>/dev/null || true
elif [ "$tool" = "composer" ]; then
configure_composer "$tool_path"
elif [ "$tool" = "cs2pr" ]; then
sudo sed -i 's/\r$//; s/exit(9)/exit(0)/' "$tool_path" 2>/dev/null ||
sudo sed -i '' 's/\r$//; s/exit(9)/exit(0)/' "$tool_path"
elif [ "$tool" = "deployer" ]; then
if [ -e "$composer_bin"/deployer.phar ]; then
sudo ln -s "$composer_bin"/deployer.phar "$composer_bin"/dep
fi
if [ -e "$composer_bin"/dep ]; then
sudo ln -s "$composer_bin"/dep "$composer_bin"/deployer
fi
sudo ln -s "$tool_path" "$tool_path_dir"/deployer 2>/dev/null || true
sudo ln -s "$tool_path" "$tool_path_dir"/dep 2>/dev/null || true
elif [ "$tool" = "phan" ]; then
extensions+=(fileinfo ast)
elif [ "$tool" = "phinx" ]; then
@@ -151,7 +176,7 @@ add_tools_helper() {
elif [ "$tool" = "phpDocumentor" ]; then
extensions+=(ctype hash json fileinfo iconv mbstring simplexml xml)
sudo ln -s "$tool_path" "$tool_path_dir"/phpdocumentor 2>/dev/null || true
sudo ln -s "$tool_path" "$tool_path_dir"/phpdoc
sudo ln -s "$tool_path" "$tool_path_dir"/phpdoc 2>/dev/null || true
elif [ "$tool" = "phpunit" ]; then
extensions+=(dom json libxml mbstring xml xmlwriter)
elif [ "$tool" = "phpunit-bridge" ]; then
@@ -162,12 +187,12 @@ add_tools_helper() {
fi
elif [ "$tool" = "vapor-cli" ]; then
extensions+=(fileinfo json mbstring zip simplexml)
sudo ln -s "$scoped_dir"/vendor/bin/vapor "$scoped_dir"/vendor/bin/vapor-cli
sudo ln -s "$scoped_dir"/vendor/bin/vapor "$scoped_dir"/vendor/bin/vapor-cli 2>/dev/null || true
elif [ "$tool" = wp-cli ]; then
sudo ln -s "$tool_path" "$tool_path_dir"/"${tool%-*}"
sudo ln -s "$tool_path" "$tool_path_dir"/"${tool%-*}" 2>/dev/null || true
fi
for extension in "${extensions[@]}"; do
add_extension "$extension" extension >/dev/null 2>&1
add_extension "$extension" extension
done
}
@@ -176,29 +201,48 @@ add_tool() {
url=$1
tool=$2
ver_param=$3
if [ "$tool" = "composer" ]; then
skip_composer_github_auth="${4:-false}"
fi
tool_path="$tool_path_dir/$tool"
if ! [ -d "$tool_path_dir" ]; then
sudo mkdir -p "$tool_path_dir"
fi
add_path "$tool_path_dir"
if [ -e "$tool_path" ]; then
sudo cp -aL "$tool_path" /tmp/"$tool"
if ! [ -d "$tool_cache_path_dir" ]; then
sudo mkdir -p "$tool_cache_path_dir"
fi
add_path "$tool_path_dir" verify
add_path "$tool_cache_path_dir"
IFS="," read -r -a url <<<"$url"
cache_key=$(get_sha256 "${url[0]}" | head -c 16)
cache_path="$tool_cache_path_dir/${tool}-${cache_key}"
use_cache=true
is_mutable_tool_url "${url[0]}" && use_cache=false
status_code="200"
if [ "$use_cache" = "true" ] && [ -f "$cache_path" ]; then
sudo cp -a "$cache_path" "$tool_path"
else
[ -f "$tool_path" ] && sudo cp -a "$tool_path" "$tool_path.bak"
status_code=$(get -v -e "$tool_path" "${url[@]}")
if [ "$status_code" != "200" ] && [[ "${url[0]}" =~ .*github.com.*releases.*latest.* ]]; then
url[0]="${url[0]//releases\/latest\/download/releases/download/$(get -s -n "" "$(echo "${url[0]}" | cut -d '/' -f '1-5')/releases" | grep -Eo -m 1 "([0-9]+\.[0-9]+\.[0-9]+)/$(echo "${url[0]}" | sed -e "s/.*\///")" | cut -d '/' -f 1)}"
status_code=$(get -v -e "$tool_path" "${url[0]}")
fi
if [ "$status_code" = "200" ]; then
[ "$use_cache" = "true" ] && sudo cp -a "$tool_path" "$cache_path"
elif [ -f "$tool_path.bak" ]; then
sudo mv "$tool_path.bak" "$tool_path"
fi
sudo rm -f "$tool_path.bak"
fi
if [ "$status_code" = "200" ]; then
add_tools_helper "$tool"
tool_version=$(get_tool_version "$tool" "$ver_param")
sudo ln -sfn "$tool_path" "$tool_cache_path_dir/$tool" 2>/dev/null || true
add_log "${tick:?}" "$tool" "Added $tool $tool_version"
else
if [ "$tool" = "composer" ]; then
export fail_fast=true
elif [ -e /tmp/"$tool" ]; then
sudo cp -a /tmp/"$tool" "$tool_path"
fi
if [ "$status_code" = "404" ]; then
add_log "$cross" "$tool" "Failed to download $tool from ${url[*]}"
@@ -218,22 +262,22 @@ add_composer_tool_helper() {
enable_extensions curl mbstring openssl
tool_version=${release##*:}; [ "$tool_version" = "$tool" ] && tool_version="*"
if [ "$scope" = "global" ]; then
sudo rm -f "$composer_lock" >/dev/null 2>&1 || true
sudo rm -f "$composer_lock" || true
if composer global show "$prefix$tool" "$tool_version" -a 2>&1 | grep -qE '^type *: *composer-plugin' && [ -n "$composer_args" ]; then
composer global config --no-plugins allow-plugins."$prefix$tool" true >/dev/null 2>&1
composer global config --no-plugins allow-plugins."$prefix$tool" true
fi
composer global require "$prefix$release" "$composer_args" >/dev/null 2>&1
composer global show "$prefix$tool" 2>&1 | grep -E ^versions | sudo tee /tmp/composer.log >/dev/null 2>&1
composer global require "$prefix$release" "$composer_args"
composer global show "$prefix$tool" 2>&1 | grep -E ^versions | sudo tee /tmp/composer.log
else
scoped_dir="$composer_bin/_tools/$tool-$(echo -n "$release" | shasum -a 256 | cut -d ' ' -f 1)"
if ! [ -d "$scoped_dir" ]; then
mkdir -p "$scoped_dir"
echo '{}' | tee "$scoped_dir/composer.json" >/dev/null
if composer show "$prefix$tool" "$tool_version" -d "$scoped_dir" -a 2>&1 | grep -qE '^type *: *composer-plugin' && [ -n "$composer_args" ]; then
composer config -d "$scoped_dir" --no-plugins allow-plugins."$prefix$tool" true >/dev/null 2>&1
composer config -d "$scoped_dir" --no-plugins allow-plugins."$prefix$tool" true
fi
composer require "$prefix$release" -d "$scoped_dir" "$composer_args" >/dev/null 2>&1
composer show "$prefix$tool" -d "$scoped_dir" 2>&1 | grep -E ^versions | sudo tee /tmp/composer.log >/dev/null 2>&1
composer require "$prefix$release" -d "$scoped_dir" "$composer_args"
composer show "$prefix$tool" -d "$scoped_dir" 2>&1 | grep -E ^versions | sudo tee /tmp/composer.log
fi
add_path "$scoped_dir"/vendor/bin
fi

View File

@@ -6,14 +6,14 @@ Function Add-Blackfire() {
}
$cli_version = (Invoke-RestMethod https://blackfire.io/api/v1/releases).cli
$url = "https://packages.blackfire.io/binaries/blackfire/${cli_version}/blackfire-windows_${arch_name}.zip"
Get-File -Url $url -OutFile $bin_dir\blackfire.zip >$null 2>&1
Expand-Archive -Path $bin_dir\blackfire.zip -DestinationPath $bin_dir -Force >$null 2>&1
Get-File -Url $url -OutFile $bin_dir\blackfire.zip
Expand-Archive -Path $bin_dir\blackfire.zip -DestinationPath $bin_dir -Force
Add-ToProfile $current_profile 'blackfire' "New-Alias blackfire $bin_dir\blackfire.exe"
if ((Test-Path env:BLACKFIRE_SERVER_ID) -and (Test-Path env:BLACKFIRE_SERVER_TOKEN)) {
blackfire agent:config --server-id=$env:BLACKFIRE_SERVER_ID --server-token=$env:BLACKFIRE_SERVER_TOKEN >$null 2>&1
blackfire agent:config --server-id=$env:BLACKFIRE_SERVER_ID --server-token=$env:BLACKFIRE_SERVER_TOKEN
}
if ((Test-Path env:BLACKFIRE_CLIENT_ID) -and (Test-Path env:BLACKFIRE_CLIENT_TOKEN)) {
blackfire client:config --client-id=$env:BLACKFIRE_CLIENT_ID --client-token=$env:BLACKFIRE_CLIENT_TOKEN --ca-cert=$php_dir\ssl\cacert.pem >$null 2>&1
blackfire client:config --client-id=$env:BLACKFIRE_CLIENT_ID --client-token=$env:BLACKFIRE_CLIENT_TOKEN --ca-cert=$php_dir\ssl\cacert.pem
}
Add-Log $tick "blackfire" "Added blackfire $cli_version"
}

View File

@@ -8,7 +8,7 @@ add_blackfire_linux() {
add_blackfire_darwin() {
sudo mkdir -p /usr/local/var/run
add_brew_tap blackfireio/homebrew-blackfire
brew install blackfire
safe_brew install blackfire
}
blackfire_config() {
@@ -32,9 +32,9 @@ blackfire_config() {
# Function to add blackfire cli.
add_blackfire() {
os="$(uname -s)"
[ "$os" = "Linux" ] && add_blackfire_linux >/dev/null 2>&1
[ "$os" = "Darwin" ] && add_blackfire_darwin >/dev/null 2>&1
blackfire_config >/dev/null 2>&1
[ "$os" = "Linux" ] && add_blackfire_linux
[ "$os" = "Darwin" ] && add_blackfire_darwin
blackfire_config
tool_version=$(get_tool_version "blackfire" "version")
add_log "${tick:?}" "blackfire" "Added blackfire $tool_version"
}

View File

@@ -14,11 +14,11 @@ add_brew_tap() {
tap=$1
if ! [ -d "$tap_dir/$tap" ]; then
if [ "${runner:?}" = "self-hosted" ]; then
brew tap "$tap" >/dev/null 2>&1
brew tap "$tap"
else
fetch_brew_tap "$tap" >/dev/null 2>&1
fetch_brew_tap "$tap"
if ! [ -d "$tap_dir/$tap" ]; then
brew tap "$tap" >/dev/null 2>&1
brew tap "$tap"
fi
fi
fi
@@ -44,12 +44,141 @@ add_brew_bins_to_path() {
add_path "$brew_prefix"/sbin
}
# Function to get file modification time.
get_file_mtime() {
local file=$1
if [ "$(uname -s)" = "Darwin" ]; then
stat -f "%m" "$file" 2>/dev/null || echo 0
else
stat -c "%Y" "$file" 2>/dev/null || echo 0
fi
}
# Function to terminate a process and its direct children.
terminate_process_tree() {
local pid=$1
local children child
children=$(pgrep -P "$pid" 2>/dev/null || true)
kill -TERM "$pid" || true
for child in $children; do
terminate_process_tree "$child"
done
sleep 2
kill -KILL "$pid" || true
for child in $children; do
terminate_process_tree "$child"
done
}
# Function to run a command with an inactivity watchdog.
run_with_inactivity_watchdog() {
local timeout_secs="${SETUP_PHP_BREW_INACTIVITY_TIMEOUT:-180}"
local poll_secs="${SETUP_PHP_BREW_WATCHDOG_POLL:-5}"
local tmp_dir stdout_fifo stderr_fifo stdout_log stderr_log timeout_file
local command_pid stdout_reader_pid stderr_reader_pid monitor_pid exit_code
tmp_dir="$(mktemp -d "${TMPDIR:-/tmp}/setup-php-brew.XXXXXX")" || return 1
stdout_fifo="$tmp_dir/stdout.fifo"
stderr_fifo="$tmp_dir/stderr.fifo"
stdout_log="$tmp_dir/stdout.log"
stderr_log="$tmp_dir/stderr.log"
timeout_file="$tmp_dir/timed_out"
mkfifo "$stdout_fifo" "$stderr_fifo" || {
rm -rf "$tmp_dir"
return 1
}
: >"$stdout_log"
: >"$stderr_log"
("$@" >"$stdout_fifo" 2>"$stderr_fifo") &
command_pid=$!
(
while IFS= read -r line || [ -n "$line" ]; do
printf '%s\n' "$line"
printf '%s\n' "$line" >>"$stdout_log"
done <"$stdout_fifo"
) &
stdout_reader_pid=$!
(
while IFS= read -r line || [ -n "$line" ]; do
printf '%s\n' "$line" >&2
printf '%s\n' "$line" >>"$stderr_log"
done <"$stderr_fifo"
) &
stderr_reader_pid=$!
(
local last_activity current_activity current_err_activity now
last_activity=$(get_file_mtime "$stdout_log")
current_err_activity=$(get_file_mtime "$stderr_log")
[ "$current_err_activity" -gt "$last_activity" ] && last_activity="$current_err_activity"
while kill -0 "$command_pid" ; do
sleep "$poll_secs"
current_activity=$(get_file_mtime "$stdout_log")
[ "$current_activity" -gt "$last_activity" ] && last_activity="$current_activity"
current_err_activity=$(get_file_mtime "$stderr_log")
[ "$current_err_activity" -gt "$last_activity" ] && last_activity="$current_err_activity"
now=$(date +%s)
if [ $((now - last_activity)) -ge "$timeout_secs" ]; then
printf "\nsetup-php: brew produced no output for %ss; terminating and retrying...\n" "$timeout_secs" >&2
: >"$timeout_file"
terminate_process_tree "$command_pid"
break
fi
done
) &
monitor_pid=$!
wait "$command_pid"
exit_code=$?
wait "$stdout_reader_pid" 2>/dev/null || true
wait "$stderr_reader_pid" 2>/dev/null || true
kill "$monitor_pid" || true
wait "$monitor_pid" 2>/dev/null || true
if [ -e "$timeout_file" ]; then
rm -rf "$tmp_dir"
return 124
fi
rm -rf "$tmp_dir"
return "$exit_code"
}
# Function to run brew with retries and an inactivity watchdog.
safe_brew() {
local max_attempts="${SETUP_PHP_BREW_RETRY_ATTEMPTS:-3}"
local attempt=1
local exit_code=0
if [ "${SETUP_PHP_BREW_WATCHDOG:-true}" = "false" ]; then
brew "$@"
return $?
fi
while [ "$attempt" -le "$max_attempts" ]; do
run_with_inactivity_watchdog brew "$@" && return 0
exit_code=$?
if [ "$attempt" -ge "$max_attempts" ]; then
return "$exit_code"
fi
printf "setup-php: retrying brew command (attempt %s/%s, exit %s)\n" "$((attempt + 1))" "$max_attempts" "$exit_code" >&2
sleep "$((attempt * 5))"
attempt=$((attempt + 1))
done
return "$exit_code"
}
# Function to add brew.
add_brew() {
brew_prefix="$(get_brew_prefix)"
if ! [ -d "$brew_prefix"/bin ]; then
step_log "Setup Brew"
get -s "" "/tmp/install.sh" "https://raw.githubusercontent.com/Homebrew/install/main/install.sh" | bash -s >/dev/null 2>&1
get -s "" "/tmp/install.sh" "https://raw.githubusercontent.com/Homebrew/install/main/install.sh" | bash -s
add_log "${tick:?}" "Brew" "Installed Homebrew"
fi
add_brew_bins_to_path "$brew_prefix"
@@ -74,6 +203,7 @@ configure_brew() {
export HOMEBREW_NO_ENV_HINTS=1
export HOMEBREW_NO_INSTALL_CLEANUP=1
export HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1
export HOMEBREW_DOWNLOAD_CONCURRENCY="${HOMEBREW_DOWNLOAD_CONCURRENCY:-6}"
export brew_opts
export brew_path
export brew_path_dir

View File

@@ -1,7 +1,7 @@
Function Add-Msys2() {
$msys_location = 'C:\msys64'
if (-not(Test-Path $msys_location)) {
choco install msys2 -y >$null 2>&1
choco install msys2 -y
$msys_location = 'C:\tools\msys64'
}
return $msys_location
@@ -15,7 +15,7 @@ Function Add-GrpcPhpPlugin() {
} else {
$grpc_package = 'mingw-w64-x86_64-grpc'
}
$logs = . $msys_location\usr\bin\bash -l -c "pacman -S --noconfirm $grpc_package" >$null 2>&1
$logs = . $msys_location\usr\bin\bash -l -c "pacman -S --noconfirm $grpc_package"
$grpc_version = Get-ToolVersion 'Write-Output' "$logs"
Add-Path $msys_location\mingw64\bin
Set-Output grpc_php_plugin_path "$msys_location\mingw64\bin\grpc_php_plugin.exe"

View File

@@ -4,7 +4,7 @@ add_bazel() {
add_list bazel/apt https://storage.googleapis.com/bazel-apt https://bazel.build/bazel-release.pub.gpg stable jdk1.8
install_packages bazel
else
brew install bazel
safe_brew install bazel
fi
fi
}
@@ -25,8 +25,8 @@ add_grpc_php_plugin_brew() {
. "${0%/*}"/tools/brew.sh
configure_brew
[ -e /usr/local/bin/protoc ] && sudo mv /usr/local/bin/protoc /tmp/protoc && sudo mv /usr/local/include/google /tmp
brew install grpc
brew link --force --overwrite grpc >/dev/null 2>&1
safe_brew install grpc
brew link --force --overwrite grpc
[ -e /tmp/protoc ] && sudo mv /tmp/protoc /usr/local/bin/protoc && sudo mv /tmp/google /usr/local/include/
grpc_tag="v$(brew info grpc | grep "grpc:" | grep -Eo "[0-9]+\.[0-9]+\.[0-9]+")"
license_path="$(brew --prefix grpc)/LICENSE"
@@ -50,9 +50,9 @@ add_grpc_php_plugin() {
grpc_tag=$1
license_path=""
if [ "$grpc_tag" = "latest" ]; then
add_grpc_php_plugin_brew >/dev/null 2>&1
add_grpc_php_plugin_brew
else
add_grpc_php_plugin_compile >/dev/null 2>&1
add_grpc_php_plugin_compile
fi
set_output grpc_php_plugin_path "$(command -v grpc_php_plugin)"
add_log "${tick:?}" "grpc_php_plugin" "Added grpc_php_plugin ${grpc_tag:1}"

View File

@@ -28,8 +28,8 @@ Function Add-Mago() {
$arch_name = 'i686'
}
$url = "https://github.com/carthage-software/mago/releases/download/$mago_tag/mago-$mago_tag-$arch_name-pc-windows-msvc.zip"
Get-File -Url $url -OutFile $bin_dir\mago.zip >$null 2>&1
Expand-Archive -Path $bin_dir\mago.zip -DestinationPath $bin_dir\mago -Force >$null 2>&1
Get-File -Url $url -OutFile $bin_dir\mago.zip
Expand-Archive -Path $bin_dir\mago.zip -DestinationPath $bin_dir\mago -Force
Move-Item -Path $bin_dir\mago\mago-$mago_tag-$arch_name-pc-windows-msvc\mago.exe -Destination $bin_dir\mago.exe -Force
Add-ToProfile $current_profile 'mago' "New-Alias mago $bin_dir\mago.exe"
Add-Log $tick "mago" "Added mago $mago_tag"

View File

@@ -24,6 +24,6 @@ add_mago() {
sudo tar -xzf /tmp/mago.tar.gz -C /tmp/
sudo mv /tmp/mago-$mago_tag-$arch-$platform/mago /usr/local/bin/mago
sudo chmod +x /usr/local/bin/mago
) >/dev/null 2>&1
)
add_log "${tick:?}" "mago" "Added mago $mago_tag"
}

View File

@@ -31,7 +31,7 @@ set_base_version() {
else
set_base_version_codename
set_base_version_id
printf "ID=%s\nVERSION_ID=%s\nVERSION_CODENAME=%s\n" "$ID" "$VERSION_ID" "$VERSION_CODENAME" | tee /tmp/os-release >/dev/null 2>&1
printf "ID=%s\nVERSION_ID=%s\nVERSION_CODENAME=%s\n" "$ID" "$VERSION_ID" "$VERSION_CODENAME" | tee /tmp/os-release
fi
}
@@ -70,8 +70,8 @@ update_lists() {
list="$list_file"
fi
if [ ! -e "$status_file" ]; then
update_lists_helper "$list" >/dev/null 2>&1
echo '' | tee "$status_file" >/dev/null 2>&1
update_lists_helper "$list"
echo '' | tee "$status_file"
fi
}
@@ -186,7 +186,7 @@ add_key() {
key_urls+=("$ppa_sp/keys/$ppa.gpg")
[ ! -e "$key_source" ] && get -q -n "$key_file" "${key_urls[@]}"
if [[ "$(file "$key_file")" =~ .*('Public-Key (old)'|'Secret-Key') ]]; then
sudo gpg --batch --yes --dearmor "$key_file" >/dev/null 2>&1 && sudo mv "$key_file".gpg "$key_file"
sudo gpg --batch --yes --dearmor "$key_file" && sudo mv "$key_file".gpg "$key_file"
fi
}
@@ -230,7 +230,7 @@ Architectures: $arch
Signed-By: $key_file
EOF
else
echo "deb [arch=$arch signed-by=$key_file] $url $suite $components" | sudo tee "$list_dir"/"$list_basename".list >/dev/null 2>&1
echo "deb [arch=$arch signed-by=$key_file] $url $suite $components" | sudo tee "$list_dir"/"$list_basename".list
fi
}

View File

@@ -29,8 +29,8 @@ Function Add-Protoc() {
$arch_num = '32'
}
$url = "https://github.com/protocolbuffers/protobuf/releases/download/$protobuf_tag/protoc-$($protobuf_tag -replace 'v', '')-win$arch_num.zip"
Get-File -Url $url -OutFile $bin_dir\protoc.zip >$null 2>&1
Expand-Archive -Path $bin_dir\protoc.zip -DestinationPath $bin_dir\protoc -Force >$null 2>&1
Get-File -Url $url -OutFile $bin_dir\protoc.zip
Expand-Archive -Path $bin_dir\protoc.zip -DestinationPath $bin_dir\protoc -Force
Move-Item -Path $bin_dir\protoc\bin\protoc.exe -Destination $bin_dir\protoc.exe
Add-ToProfile $current_profile 'protoc' "New-Alias protoc $bin_dir\protoc.exe"
Add-Log $tick "protoc" "Added protoc $($protobuf_tag -replace 'v', '')"

View File

@@ -22,7 +22,7 @@ add_protoc() {
get -q -n /tmp/protobuf.zip "https://github.com/protocolbuffers/protobuf/releases/download/$protobuf_tag/protoc-${protobuf_tag:1}-$platform-$arch.zip"
sudo unzip /tmp/protobuf.zip -d /usr/local/
sudo chmod -R 777 /usr/local/bin/protoc /usr/local/include/google
) >/dev/null 2>&1
)
add_log "${tick:?}" "protoc" "Added protoc ${protobuf_tag:1}"
printf "$GROUP\033[34;1m%s \033[0m\033[90;1m%s \033[0m\n" "protoc" "Click to read the protoc related license information"
curl "${curl_opts[@]:?}" https://raw.githubusercontent.com/protocolbuffers/protobuf/main/LICENSE

View File

@@ -31,7 +31,7 @@ add_symfony() {
if ! [[ "$symfony_tag" =~ ^[0-9]+(\.[0-9]+)*$ || "$symfony_tag" == 'latest' ]]; then
add_log "${cross:?}" "symfony-cli" "Version '$symfony_tag' is not valid for symfony-cli"
else
add_symfony_helper "$symfony_tag" >/dev/null 2>&1
add_symfony_helper "$symfony_tag"
symfony_path="$(command -v symfony)"
if [[ -n "$symfony_path" ]]; then
sudo ln -s "$symfony_path" "${tool_path_dir:?}"/symfony-cli

View File

@@ -4,7 +4,8 @@ export cross="✗"
export curl_opts=(-sL)
export old_versions="5.[3-5]"
export jit_versions="8.[0-9]"
export nightly_versions="8.[3-9]"
export php_builder_versions="8.[3-9]"
export nightly_versions="8.[6-9]"
export xdebug3_versions="7.[2-4]|8.[0-9]"
export latest="releases/latest/download"
export github="https://github.com/shivammathur"
@@ -43,7 +44,7 @@ set_output() {
name=$1
value=$2
if [ "${GITHUB_ACTIONS}" = "true" ]; then
echo "${name}=${value}" | tee -a "$GITHUB_OUTPUT" >/dev/null 2>&1
echo "${name}=${value}" | tee -a "$GITHUB_OUTPUT"
fi
}
@@ -58,6 +59,7 @@ read_env() {
-n "$ACT" || -n "$CONTAINER" ]] && _runner=self-hosted || _runner=github
runner="${runner:-${RUNNER:-$_runner}}"
tool_path_dir="${setup_php_tools_dir:-${SETUP_PHP_TOOLS_DIR:-/usr/local/bin}}"
tool_cache_path_dir="${setup_php_tool_cache_dir:-${SETUP_PHP_TOOL_CACHE_DIR:-${RUNNER_TOOL_CACHE:-/opt/hostedtoolcache}/setup-php/tools}}"
if [[ "$runner" = "github" && $_runner = "self-hosted" ]]; then
fail_fast=true
@@ -68,7 +70,7 @@ read_env() {
if [[ "$runner" = "github" && "${ImageOS}" =~ ubuntu.* ]]; then
if ! check_ppa ondrej/php; then
update=true
echo '' | sudo tee /tmp/sp_update >/dev/null 2>&1
echo '' | sudo tee /tmp/sp_update
elif [ -e /tmp/sp_update ]; then
update=true
fi
@@ -79,6 +81,7 @@ read_env() {
export update
export ts
export tool_path_dir
export tool_cache_path_dir
}
# Function to create a lock.
@@ -91,7 +94,7 @@ acquire_lock() {
else
if sudo test -f "$lock_path/pid"; then
lock_pid=$(sudo cat "$lock_path/pid")
if ! ps -p "$lock_pid" >/dev/null 2>&1; then
if ! ps -p "$lock_pid" ; then
sudo rm -rf "$lock_path"
continue
fi
@@ -169,14 +172,15 @@ get_shell_profile() {
# Function to add a path to the PATH variable.
add_path() {
path_to_add=$1
[[ ":$PATH:" == *":$path_to_add:"* ]] && return
action=$2
[[ "$action" == "verify" && ":$PATH:" == *":$path_to_add:"* ]] && return
if [[ -n "$GITHUB_PATH" ]]; then
echo "$path_to_add" | tee -a "$GITHUB_PATH" >/dev/null 2>&1
printf '%s\n%s' "$path_to_add" "$(grep -v "^${path_to_add}$" "$GITHUB_PATH" 2>/dev/null)" > "$GITHUB_PATH"
else
profile=$(get_shell_profile)
([ -e "$profile" ] && grep -q ":$path_to_add\"" "$profile" 2>/dev/null) || echo "export PATH=\"\${PATH:+\${PATH}:}\"$path_to_add" | sudo tee -a "$profile" >/dev/null 2>&1
([ -e "$profile" ] && grep -q ":$path_to_add\"" "$profile" 2>/dev/null) || echo "export PATH=\"\${PATH:+\${PATH}:}\"$path_to_add" | sudo tee -a "$profile"
fi
export PATH="${PATH:+${PATH}:}$path_to_add"
[[ ":$PATH:" == *":$path_to_add:"* ]] || export PATH="${PATH:+${PATH}:}$path_to_add"
}
# Function to add environment variables using a PATH.
@@ -196,10 +200,10 @@ add_env() {
env_name=$1
env_value=$2
if [[ -n "$GITHUB_ENV" ]]; then
echo "$env_name=$env_value" | tee -a "$GITHUB_ENV" >/dev/null 2>&1
echo "$env_name=$env_value" | tee -a "$GITHUB_ENV"
else
profile=$(get_shell_profile)
echo "export $env_name=\"$env_value\"" | sudo tee -a "$profile" >/dev/null 2>&1
echo "export $env_name=\"$env_value\"" | sudo tee -a "$profile"
fi
export "$env_name"="$env_value"
}
@@ -220,7 +224,7 @@ self_hosted_setup() {
add_log "$cross" "PHP" "PHP $version is not supported on self-hosted runner"
exit 1
else
self_hosted_helper >/dev/null 2>&1
self_hosted_helper
add_env RUNNER_TOOL_CACHE /opt/hostedtoolcache
fi
fi
@@ -248,8 +252,8 @@ configure_php() {
[[ "$arch" = "arm64" || "$arch" = "aarch64" ]] && jit_ini="$ini_config_dir"/jit_aarch64.ini || jit_ini="$ini_config_dir"/jit.ini
jit_config_files=("$jit_ini")
[[ "$version" =~ $xdebug3_versions ]] && ini_config_files+=("$ini_config_dir"/xdebug.ini)
cat "${ini_config_files[@]}" | sudo tee -a "${ini_file[@]:?}" >/dev/null 2>&1
[[ "$version" =~ $jit_versions ]] && cat "${jit_config_files[@]}" | sudo tee -a "${pecl_file:-${ini_file[@]}}" >/dev/null 2>&1
cat "${ini_config_files[@]}" | sudo tee -a "${ini_file[@]:?}"
[[ "$version" =~ $jit_versions ]] && cat "${jit_config_files[@]}" | sudo tee -a "${pecl_file:-${ini_file[@]}}"
}
# Function to get PHP version in semver format.

View File

@@ -81,9 +81,10 @@ Function Get-PathFromRegistry {
# Function to add a location to PATH.
Function Add-Path {
param(
[string]$PathItem
[string]$PathItem,
[switch]$Force
)
if("$env:PATH;".contains("$PathItem;")) {
if(-not($Force) -and "$env:PATH;".contains("$PathItem;")) {
return
}
if ($env:GITHUB_PATH) {
@@ -202,16 +203,20 @@ Function Install-PSPackage() {
$cmdlet
)
$module_path = "$bin_dir\$psm1_path.psm1"
$imported = $false
try {
if(-not (Test-Path $module_path -PathType Leaf)) {
$zip_file = "$bin_dir\$package.zip"
Get-File -Url $url -OutFile $zip_file
Expand-Archive -Path $zip_file -DestinationPath $bin_dir -Force
Expand-Archive -Path $zip_file -DestinationPath $bin_dir -Force -ErrorAction Stop
}
Import-Module $module_path
if($null -eq (Get-Command $cmdlet -ErrorAction SilentlyContinue)) {
Install-Module -Name $package -Force
} else {
Import-Module $module_path -ErrorAction Stop
$imported = $null -ne (Get-Command $cmdlet -ErrorAction SilentlyContinue)
} catch { }
if($imported) {
Add-ToProfile $current_profile "$package-search" "Import-Module $module_path"
} else {
Install-Module -Name $package -Force
}
}
@@ -355,7 +360,7 @@ if(-not($env:ImageOS) -and -not($env:ImageVersion)) {
$bin_dir = 'C:\tools\bin'
$php_dir = "$php_dir$version"
$ext_dir = "$php_dir\ext"
Get-CleanPSProfile >$null 2>&1
Get-CleanPSProfile
New-Item $bin_dir -Type Directory -Force > $null 2>&1
Add-Path -PathItem $bin_dir
if($version -lt 5.6) {
@@ -368,22 +373,23 @@ if(-not($env:ImageOS) -and -not($env:ImageVersion)) {
}
New-Item $php_dir -Type Directory -Force > $null 2>&1
Add-Path -PathItem $php_dir
setx PHPROOT $php_dir >$null 2>&1
setx PHPROOT $php_dir
Add-Env -EnvName RUNNER_TOOL_CACHE -EnvValue $env:TEMP
} else {
$current_profile = "$PSHOME\Profile.ps1"
if(-not(Test-Path -LiteralPath $current_profile)) {
New-Item -Path $current_profile -ItemType "file" -Force >$null 2>&1
New-Item -Path $current_profile -ItemType "file" -Force
}
Add-Path -PathItem $bin_dir -Force
}
$src = Join-Path -Path $PSScriptRoot -ChildPath \..
. $src\scripts\tools\add_tools.ps1
. $src\scripts\extensions\add_extensions.ps1
Add-Printf >$null 2>&1
Add-Printf
Step-Log "Setup PhpManager"
Install-PSPackage PhpManager PhpManager\PhpManager "$github/mlocati/powershell-phpmanager/releases/latest/download/PhpManager.zip" Get-Php >$null 2>&1
Install-PSPackage PhpManager PhpManager\PhpManager "$github/mlocati/powershell-phpmanager/releases/latest/download/PhpManager.zip" Get-Php
Add-Log $tick "PhpManager" "Installed"
Step-Log "Setup PHP"
@@ -412,7 +418,7 @@ if($version -eq 'pre') {
}
if ($null -eq $installed -or -not("$($installed.Version).".StartsWith(($version -replace '^(\d+(\.\d+)*).*', '$1.'))) -or $ts -ne $installed.ThreadSafe) {
if ($version -lt '7.0' -and ($null -eq (Get-Module -ListAvailable -Name VcRedist))) {
Install-PSPackage VcRedist VcRedist-main\VcRedist\VcRedist "$github/aaronparker/VcRedist/archive/main.zip" Get-VcList >$null 2>&1
Install-PSPackage VcRedist VcRedist-main\VcRedist\VcRedist "$github/aaronparker/VcRedist/archive/main.zip" Get-VcList
}
try {
if ($version -match $nightly_versions) {
@@ -425,7 +431,7 @@ if ($null -eq $installed -or -not("$($installed.Version).".StartsWith(($version
} catch { }
} else {
if($env:update -eq 'true') {
Update-Php $php_dir >$null 2>&1
Update-Php $php_dir
$status = "Updated to"
} else {
$status = "Found"
@@ -443,7 +449,7 @@ if($installed.MajorMinorVersion -ne $version) {
Write-Error "Could not setup PHP $version" -ErrorAction Stop
}
if($version -lt "5.5") {
('libeay32.dll', 'ssleay32.dll') | ForEach-Object -Parallel { Invoke-WebRequest -Uri "$using:php_builder/releases/download/openssl-1.0.2u/$_" -OutFile $using:php_dir\$_ >$null 2>&1 }
('libeay32.dll', 'ssleay32.dll') | ForEach-Object -Parallel { Invoke-WebRequest -Uri "$using:php_builder/releases/download/openssl-1.0.2u/$_" -OutFile $using:php_dir\$_ }
} elseif($version -lt "8.5") {
$enable_extensions += ('opcache')
}

View File

@@ -5,14 +5,130 @@ import * as fetch from './fetch';
import * as packagist from './packagist';
import * as utils from './utils';
type RS = Record<string, string>;
type RSRS = Record<string, RS>;
/**
* Valid function names for custom tool handlers
*/
type ToolFunction =
| 'castor'
| 'composer'
| 'deployer'
| 'dev_tools'
| 'phive'
| 'blackfire_player'
| 'pecl'
| 'phing'
| 'phpunit'
| 'phpcpd'
| 'wp_cli';
interface IRef {
/**
* Tool data interface containing all properties for tool installation
*/
export interface ToolData {
tool: string;
version: string;
os: string;
php_version: string;
github: string;
domain: string;
extension: string;
repository: string;
prefix: string;
verb: string;
fetch_latest: 'true' | 'false';
scope: string;
version_parameter: string;
version_prefix: string;
release: string;
packagist: string;
type?: string;
function?: ToolFunction;
alias?: string;
url: string;
uri?: string;
error?: string;
}
/**
* Input type for functions that may receive partial/unresolved tool data
* Used by getUrl, getLatestVersion etc. before version is fully resolved
*/
export type ToolInput = Omit<ToolData, 'version' | 'url'> & {version?: string};
/**
* Partial tool data from tools.json configuration
*/
interface ToolConfig {
tool?: string;
repository?: string;
type?: string;
function?: ToolFunction;
alias?: string;
domain?: string;
extension?: string;
fetch_latest?: 'true' | 'false';
scope?: string;
version_parameter?: string;
version_prefix?: string;
packagist?: string;
}
/**
* GitHub reference object from API response
*/
interface GitHubRef {
ref: string;
node_id: string;
url: string;
object: RS;
object: {
sha: string;
type: string;
url: string;
};
}
/**
* Deployer manifest entry
*/
interface DeployerManifestEntry {
version: string;
url: string;
}
export function skipGitHubAuthForComposerVersion(version: string): boolean {
if (!/^\d+\.\d+\.\d+(?:-[\w-]+)?$/.test(version)) {
return false;
}
return fs
.readFileSync(
path.join(__dirname, '../src/configs/composer-gh-auth-no-op'),
'utf8'
)
.trim()
.split(/\r?\n/)
.some(range => {
const [min, max] = range.trim().split(/\s+/);
return (
cv.compareVersions(version, min) >= 0 &&
cv.compareVersions(version, max) < 0
);
});
}
export function cleanComposerAuthJson(): void {
try {
const auth_json = process.env['COMPOSER_AUTH_JSON'] || '';
if (!auth_json.includes('github-oauth')) return;
const auth = JSON.parse(auth_json);
delete auth['github-oauth'];
if (!Object.keys(auth).length) {
delete process.env['COMPOSER_AUTH_JSON'];
} else {
process.env['COMPOSER_AUTH_JSON'] = JSON.stringify(auth);
}
} catch {
return;
}
}
/**
@@ -20,7 +136,7 @@ interface IRef {
*
* @param data
*/
export async function getSemverVersion(data: RS): Promise<string> {
export async function getSemverVersion(data: ToolData): Promise<string> {
const fixSemver = (t: string): string => {
if (/^\d+\.\d+\.\d+(-|$)/.test(t)) return t;
const m = t.match(/^(\d+\.\d+\.\d+)([A-Za-z]+[0-9A-Za-z.]+)$/);
@@ -31,14 +147,16 @@ export async function getSemverVersion(data: RS): Promise<string> {
const github_token: string =
(await utils.readEnv('GITHUB_TOKEN')) ||
(await utils.readEnv('COMPOSER_TOKEN'));
const response: RS = await fetch.fetch(url, github_token);
const response = await fetch.fetch(url, github_token);
if (response.error || response.data === '[]') {
data['error'] = response.error ?? `No version found with prefix ${search}.`;
return data['version'];
data.error = response.error ?? `No version found with prefix ${search}.`;
return data.version;
} else {
const refs: IRef[] = JSON.parse(response['data']);
const refs: GitHubRef[] = JSON.parse(response.data);
const tags = refs
.map((i: IRef) => (i.ref?.split('/').pop() ?? '').replace(/^v(?=\d)/, ''))
.map((i: GitHubRef) =>
(i.ref?.split('/').pop() ?? '').replace(/^v(?=\d)/, '')
)
.filter((t: string) => t.length > 0);
const fixedToOriginal = new Map<string, string>();
const fixed = tags.map(t => {
@@ -46,14 +164,14 @@ export async function getSemverVersion(data: RS): Promise<string> {
fixedToOriginal.set(f, t);
return f;
});
fixed.sort((a, b) => {
const sorted = fixed.toSorted((a, b) => {
try {
return cv.compareVersions(b, a);
} catch {
return b.localeCompare(a, 'en', {numeric: true, sensitivity: 'base'});
}
});
return fixedToOriginal.get(fixed[0]) ?? fixed[0];
return fixedToOriginal.get(sorted[0]) ?? sorted[0];
}
}
@@ -62,25 +180,25 @@ export async function getSemverVersion(data: RS): Promise<string> {
*
* @param data
*/
export async function getLatestVersion(data: RS): Promise<string> {
if (!data['version'] && data['fetch_latest'] === 'false') {
export async function getLatestVersion(data: ToolInput): Promise<string> {
if (!data.version && data.fetch_latest === 'false') {
return 'latest';
}
const resp: Record<string, string> = await fetch.fetch(
`${data['github']}/${data['repository']}/releases.atom`
if (data.fetch_latest === 'true' && !data.repository) {
return 'latest';
}
const resp = await fetch.fetch(
`${data.github}/${data.repository}/releases.atom`
);
if (resp['data']) {
if (resp.data) {
const releases: string[] = [
...resp['data'].matchAll(/releases\/tag\/([a-zA-Z]*)?(\d+.\d+.\d+)"/g)
...resp.data.matchAll(/releases\/tag\/([a-zA-Z]*)?(\d+\.\d+\.\d+)"/g)
].map(match => match[2]);
return (
releases
.sort((a: string, b: string) =>
const sorted = releases.toSorted((a: string, b: string) =>
a.localeCompare(b, undefined, {numeric: true})
)
.pop() || 'latest'
);
return sorted.at(-1) || 'latest';
}
return 'latest';
}
@@ -91,26 +209,29 @@ export async function getLatestVersion(data: RS): Promise<string> {
* @param version
* @param data
*/
export async function getVersion(version: string, data: RS): Promise<string> {
export async function getVersion(
version: string,
data: ToolData
): Promise<string> {
// semver_regex - https://semver.org/
const semver_regex =
/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/;
const composer_regex = /^composer:(stable|preview|snapshot|[1|2])$/;
const composer_regex = /^composer:(stable|preview|snapshot|[12])$/;
const constraint_regex = /[><=^~]+.*/;
const major_minor_regex = /^\d+(\.\d+)?$/;
data['version'] = version.replace(/v?(\d)/, '$1').replace(/\.x/, '');
data.version = version.replace(/v?(\d)/, '$1').replace(/\.x/, '');
switch (true) {
case composer_regex.test(data['release']):
case semver_regex.test(data['version']):
case constraint_regex.test(data['version']) && data['type'] === 'composer':
return data['version'];
case major_minor_regex.test(data['version']) && data['type'] === 'composer':
data['release'] = `${data['tool']}:${data['version']}.*`;
return `${data['version']}.*`;
case data['repository'] && major_minor_regex.test(data['version']):
case composer_regex.test(data.release):
case semver_regex.test(data.version):
case constraint_regex.test(data.version) && data.type === 'composer':
return data.version;
case major_minor_regex.test(data.version) && data.type === 'composer':
data.release = `${data.tool}:${data.version}.*`;
return `${data.version}.*`;
case !!data.repository && major_minor_regex.test(data.version):
return await getSemverVersion(data);
default:
return data['version'].replace(/[><=^~]*/, '');
return data.version.replace(/[^a-zA-Z0-9_.:@+,/-]/g, '');
}
}
@@ -120,11 +241,14 @@ export async function getVersion(version: string, data: RS): Promise<string> {
* @param release
* @param data
*/
export async function getRelease(release: string, data: RS): Promise<string> {
export async function getRelease(
release: string,
data: ToolData
): Promise<string> {
release = release.includes('/') ? release.split('/')[1] : release;
return release.includes(':')
? [data['tool'], release.split(':')[1]].join(':')
: data['tool'];
? [data.tool, release.split(':')[1]].join(':')
: data.tool;
}
/**
@@ -143,7 +267,7 @@ export async function filterList(tools_list: string[]): Promise<string[]> {
case matches[0] == undefined:
break;
default:
composer = matches[matches.length - 1].replace(/v(\d\S*)/, '$1');
composer = matches.at(-1)!.replace(/v(\d\S*)/, '$1');
break;
}
tools_list.unshift(composer);
@@ -155,26 +279,27 @@ export async function filterList(tools_list: string[]): Promise<string[]> {
*
* @param data
*/
export async function getUrl(data: RS): Promise<string> {
if ((data['version'] ?? 'latest') === 'latest') {
export async function getUrl(data: ToolInput): Promise<string> {
const version = data.version ?? 'latest';
if (version === 'latest' || version === '') {
return [
data['domain'],
data['repository'],
data['prefix'],
data['version'],
data['verb'],
data['tool'] + data['extension']
data.domain,
data.repository,
data.prefix,
'latest',
data.verb,
data.tool + data.extension
]
.filter(Boolean)
.join('/');
} else {
return [
data['domain'],
data['repository'],
data['prefix'],
data['verb'],
data['version_prefix'] + data['version'],
data['tool'] + data['extension']
data.domain,
data.repository,
data.prefix,
data.verb,
data.version_prefix + data.version,
data.tool + data.extension
]
.filter(Boolean)
.join('/');
@@ -186,17 +311,17 @@ export async function getUrl(data: RS): Promise<string> {
*
* @param data
*/
export async function getPharUrl(data: RS): Promise<string> {
if (data['version'] === 'latest') {
return data['domain'] + '/' + data['tool'] + '.phar';
export async function getPharUrl(data: ToolData): Promise<string> {
if (data.version === 'latest') {
return data.domain + '/' + data.tool + '.phar';
} else {
return (
data['domain'] +
data.domain +
'/' +
data['tool'] +
data.tool +
'-' +
data['version_prefix'] +
data['version'] +
data.version_prefix +
data.version +
'.phar'
);
}
@@ -207,10 +332,10 @@ export async function getPharUrl(data: RS): Promise<string> {
*
* @param data
*/
export async function addArchive(data: RS): Promise<string> {
export async function addArchive(data: ToolData): Promise<string> {
return (
(await utils.getCommand(data['os'], 'tool')) +
(await utils.joins(data['url'], data['tool'], data['version_parameter']))
(await utils.getCommand(data.os, 'tool')) +
(await utils.joins(data.url, data.tool, data.version_parameter))
);
}
@@ -219,15 +344,12 @@ export async function addArchive(data: RS): Promise<string> {
*
* @param data
*/
export async function addPackage(data: RS): Promise<string> {
const command = await utils.getCommand(data['os'], 'composer_tool');
const parts: string[] = data['repository'].split('/');
const args: string = await utils.joins(
parts[1],
data['release'],
parts[0] + '/',
data['scope']
);
export async function addPackage(data: ToolData): Promise<string> {
const command = await utils.getCommand(data.os, 'composer_tool');
const parts: string[] = data.repository.split('/');
const args = [parts[1], data.release, parts[0] + '/', data.scope]
.map(a => utils.safeArg(a, data.os))
.join(' ');
return command + args;
}
@@ -236,24 +358,24 @@ export async function addPackage(data: RS): Promise<string> {
*
* @param data
*/
export async function addBlackfirePlayer(data: RS): Promise<string> {
switch (data['os']) {
export async function addBlackfirePlayer(data: ToolData): Promise<string> {
switch (data.os) {
case 'win32':
return await utils.addLog(
'$cross',
data['tool'],
data['tool'] + ' is not a windows tool',
data.tool,
data.tool + ' is not a windows tool',
'win32'
);
default:
if (data['version'] == 'latest') {
if (/5\.[5-6]|7\.0/.test(data['php_version'])) {
data['version'] = '1.9.3';
} else if (/7\.[1-4]|8\.0/.test(data['php_version'])) {
data['version'] = '1.22.0';
if (data.version == 'latest') {
if (/5\.[5-6]|7\.0/.test(data.php_version)) {
data.version = '1.9.3';
} else if (/7\.[1-4]|8\.0/.test(data.php_version)) {
data.version = '1.22.0';
}
}
data['url'] = await getPharUrl(data);
data.url = await getPharUrl(data);
return addArchive(data);
}
}
@@ -263,12 +385,12 @@ export async function addBlackfirePlayer(data: RS): Promise<string> {
*
* @param data
*/
export async function addCastor(data: RS): Promise<string> {
data['tool'] = 'castor.' + data['os'].replace('win32', 'windows') + '-amd64';
data['url'] = await getUrl(data);
data['tool'] = 'castor';
data['version_parameter'] = fs.existsSync('castor.php')
? data['version_parameter']
export async function addCastor(data: ToolData): Promise<string> {
data.tool = 'castor.' + data.os.replace('win32', 'windows') + '-amd64';
data.url = await getUrl(data);
data.tool = 'castor';
data.version_parameter = fs.existsSync('castor.php')
? data.version_parameter
: '';
return await addArchive(data);
}
@@ -278,22 +400,23 @@ export async function addCastor(data: RS): Promise<string> {
*
* @param data
*/
export async function addComposer(data: RS): Promise<string> {
const channel = data['version'].replace('latest', 'stable');
const github = data['github'];
const getcomposer = data['domain'];
export async function addComposer(data: ToolData): Promise<string> {
const channel = data.version.replace('latest', 'stable');
const github = data.github;
const getcomposer = data.domain;
const cds = 'https://dl.cloudsmith.io';
const spc = 'https://artifacts.setup-php.com';
const filename = `composer-${data['php_version']}-${channel}.phar`;
const filename = `composer-${data.php_version}-${channel}.phar`;
const releases_url = `${github}/shivammathur/composer-cache/releases/latest/download/${filename}`;
const cds_url = `${cds}/public/shivammathur/composer-cache/raw/files/${filename}`;
const spc_url = `${spc}/composer/${filename}`;
const lts_url = `${getcomposer}/download/latest-2.2.x/composer.phar`;
const is_lts = /^5\.[3-6]$|^7\.[0-1]$/.test(data['php_version']);
const is_lts = /^5\.[3-6]$|^7\.[0-1]$/.test(data.php_version);
const channel_source_url = `${getcomposer}/composer-${channel}.phar`;
const version_source_url = `${getcomposer}/download/${channel}/composer.phar`;
let cache_url = `${releases_url},${spc_url},${cds_url}`;
let source_url = `${getcomposer}/composer.phar`;
let skip_composer_github_auth = '';
switch (true) {
case /^snapshot$/.test(channel):
source_url = is_lts ? lts_url : source_url;
@@ -304,16 +427,20 @@ export async function addComposer(data: RS): Promise<string> {
case /^1$/.test(channel):
source_url = channel_source_url;
break;
case /^\d+\.\d+\.\d+[\w-]*$/.test(data['version']):
cache_url = `${github}/${data['repository']}/releases/download/${data['version']}/composer.phar`;
case /^\d+\.\d+\.\d+(?:-[\w-]+)?$/.test(data.version):
if (skipGitHubAuthForComposerVersion(data.version)) {
cleanComposerAuthJson();
skip_composer_github_auth = ' true';
}
cache_url = `${github}/${data.repository}/releases/download/${data.version}/composer.phar`;
source_url = version_source_url;
break;
default:
source_url = is_lts ? lts_url : channel_source_url;
}
const use_cache: boolean = (await utils.readEnv('NO_TOOLS_CACHE')) !== 'true';
data['url'] = use_cache ? `${cache_url},${source_url}` : source_url;
data['version_parameter'] = data['version'];
data.url = use_cache ? `${cache_url},${source_url}` : source_url;
data.version_parameter = data.version + skip_composer_github_auth;
return await addArchive(data);
}
@@ -322,27 +449,27 @@ export async function addComposer(data: RS): Promise<string> {
*
* @param data
*/
export async function addDeployer(data: RS): Promise<string> {
if (data['version'] === 'latest') {
data['url'] = data['domain'] + '/deployer.phar';
export async function addDeployer(data: ToolData): Promise<string> {
if (data.version === 'latest') {
data.url = data.domain + '/deployer.phar';
} else {
const manifest: RS = await fetch.fetch(
'https://deployer.org/manifest.json'
const manifest = await fetch.fetch('https://deployer.org/manifest.json');
const version_data: Record<string, DeployerManifestEntry> = JSON.parse(
manifest.data
);
const version_data: RSRS = JSON.parse(manifest.data);
const version_key: string | undefined = Object.keys(version_data).find(
(key: string) => {
return version_data[key]['version'] === data['version'];
return version_data[key].version === data.version;
}
);
if (version_key) {
data['url'] = version_data[version_key]['url'];
data.url = version_data[version_key].url;
} else {
return await utils.addLog(
'$cross',
'deployer',
'Version missing in deployer manifest',
data['os']
data.os
);
}
}
@@ -354,22 +481,22 @@ export async function addDeployer(data: RS): Promise<string> {
*
* @param data
*/
export async function addDevTools(data: RS): Promise<string> {
switch (data['os']) {
export async function addDevTools(data: ToolData): Promise<string> {
switch (data.os) {
case 'linux':
case 'darwin':
return 'add_devtools ' + data['tool'];
return 'add_devtools ' + data.tool;
case 'win32':
return await utils.addLog(
'$tick',
data['tool'],
data['tool'] + ' is not a windows tool',
data.tool,
data.tool + ' is not a windows tool',
'win32'
);
default:
return await utils.log(
'Platform ' + data['os'] + ' is not supported',
data['os'],
'Platform ' + data.os + ' is not supported',
data.os,
'error'
);
}
@@ -380,8 +507,8 @@ export async function addDevTools(data: RS): Promise<string> {
*
* @param data
*/
export async function addPECL(data: RS): Promise<string> {
return await utils.getCommand(data['os'], 'pecl');
export async function addPECL(data: ToolData): Promise<string> {
return await utils.getCommand(data.os, 'pecl');
}
/**
@@ -389,14 +516,13 @@ export async function addPECL(data: RS): Promise<string> {
*
* @param data
*/
export async function addPhing(data: RS): Promise<string> {
data['url'] =
data['domain'] + '/get/phing-' + data['version'] + data['extension'];
if (data['version'] != 'latest') {
[data['prefix'], data['verb']] = ['releases', 'download'];
data['domain'] = data['github'];
data['extension'] = '-' + data['version'] + data['extension'];
data['url'] += ',' + (await getUrl(data));
export async function addPhing(data: ToolData): Promise<string> {
data.url = data.domain + '/get/phing-' + data.version + data.extension;
if (data.version != 'latest') {
[data.prefix, data.verb] = ['releases', 'download'];
data.domain = data.github;
data.extension = '-' + data.version + data.extension;
data.url += ',' + (await getUrl(data));
}
return await addArchive(data);
}
@@ -406,33 +532,33 @@ export async function addPhing(data: RS): Promise<string> {
*
* @param data
*/
export async function addPhive(data: RS): Promise<string> {
export async function addPhive(data: ToolData): Promise<string> {
switch (true) {
case /5\.[3-5]/.test(data['php_version']):
case /5\.[3-5]/.test(data.php_version):
return await utils.addLog(
'$cross',
'phive',
'Phive is not supported on PHP ' + data['php_version'],
data['os']
'Phive is not supported on PHP ' + data.php_version,
data.os
);
case /5\.6|7\.0/.test(data['php_version']):
data['version'] = '0.12.1';
case /5\.6|7\.0/.test(data.php_version):
data.version = '0.12.1';
break;
case /7\.1/.test(data['php_version']):
data['version'] = '0.13.5';
case /7\.1/.test(data.php_version):
data.version = '0.13.5';
break;
case /7\.2/.test(data['php_version']):
data['version'] = '0.14.5';
case /7\.2/.test(data.php_version):
data.version = '0.14.5';
break;
case /7\.3|7\.4/.test(data['php_version']):
data['version'] = '0.15.3';
case /7\.3|7\.4/.test(data.php_version):
data.version = '0.15.3';
break;
case /^latest$/.test(data['version']):
data['version'] = await getLatestVersion(data);
case /^latest$/.test(data.version):
data.version = await getLatestVersion(data);
break;
}
data['extension'] = '-' + data['version'] + data['extension'];
data['url'] = await getUrl(data);
data.extension = '-' + data.version + data.extension;
data.url = await getUrl(data);
return await addArchive(data);
}
@@ -441,16 +567,15 @@ export async function addPhive(data: RS): Promise<string> {
*
* @param data
*/
export async function addPHPUnitTools(data: RS): Promise<string> {
export async function addPHPUnitTools(data: ToolData): Promise<string> {
/* istanbul ignore next */
if (data['version'] === 'latest') {
data['version'] =
(await packagist.search(data['packagist'], data['php_version'])) ??
'latest';
if (data.version === 'latest') {
data.version =
(await packagist.search(data.packagist, data.php_version)) ?? 'latest';
}
data['url'] = await getPharUrl(data);
if (data['url'].match(/-\d+/)) {
data['url'] += ',' + data['url'].replace(/-(\d+)\.\d+\.\d+/, '-$1');
data.url = await getPharUrl(data);
if (data.url.match(/-\d+/)) {
data.url += ',' + data.url.replace(/-(\d+)\.\d+\.\d+/, '-$1');
}
return await addArchive(data);
}
@@ -460,13 +585,13 @@ export async function addPHPUnitTools(data: RS): Promise<string> {
*
* @param data
*/
export async function addWPCLI(data: RS): Promise<string> {
if (data['version'] === 'latest') {
data['uri'] = 'wp-cli/builds/blob/gh-pages/phar/wp-cli.phar?raw=true';
data['url'] = [data['domain'], data['uri']].join('/');
export async function addWPCLI(data: ToolData): Promise<string> {
if (data.version === 'latest') {
data.uri = 'wp-cli/builds/blob/gh-pages/phar/wp-cli.phar?raw=true';
data.url = [data.domain, data.uri].join('/');
} else {
data['extension'] = '-' + data['version'] + data['extension'];
data['url'] = await getUrl(data);
data.extension = '-' + data.version + data.extension;
data.url = await getUrl(data);
}
return await addArchive(data);
}
@@ -482,56 +607,74 @@ export async function getData(
release: string,
php_version: string,
os: string
): Promise<RS> {
): Promise<ToolData> {
const json_file_path = path.join(__dirname, '../src/configs/tools.json');
const json_file: string = fs.readFileSync(json_file_path, 'utf8');
const json_objects: RSRS = JSON.parse(json_file);
const json_objects: Record<string, ToolConfig> = JSON.parse(json_file);
release = release.replace(/\s+/g, '');
const parts: string[] = release.split(':');
const tool = parts[0];
const version = parts[1];
let data: RS;
if (Object.keys(json_objects).includes(tool)) {
data = json_objects[tool];
data['tool'] = tool;
let config: ToolConfig & {tool: string};
if (Object.hasOwn(json_objects, tool)) {
config = {...json_objects[tool], tool};
} else {
const key: string | undefined = Object.keys(json_objects).find(
(key: string) => {
return json_objects[key]['alias'] == tool;
return json_objects[key].alias == tool;
}
);
if (key) {
data = json_objects[key];
data['tool'] = key;
} else {
data = {
config = {...json_objects[key], tool: key};
} else if (tool.includes('/')) {
config = {
tool: tool.split('/')[1],
repository: tool,
type: 'composer'
};
data = !tool.includes('/') ? {tool: tool} : data;
} else {
config = {tool};
}
}
data['github'] = 'https://github.com';
data['domain'] ??= data['github'];
data['extension'] ??= '.phar';
data['os'] = os;
data['php_version'] = php_version;
data['packagist'] ??= data['repository'];
data['prefix'] = data['github'] === data['domain'] ? 'releases' : '';
data['verb'] = data['github'] === data['domain'] ? 'download' : '';
data['fetch_latest'] ??= 'false';
data['scope'] ??= 'global';
data['version_parameter'] = JSON.stringify(data['version_parameter']) || '';
data['version_prefix'] ??= '';
data['release'] = await getRelease(release, data);
data['version'] = version
const github = 'https://github.com';
const domain = config.domain ?? github;
const data: ToolData = {
tool: config.tool,
version: '',
url: '',
os,
php_version,
github,
domain,
extension: config.extension ?? '.phar',
repository: config.repository ?? '',
prefix: domain === github ? 'releases' : '',
verb: domain === github ? 'download' : '',
fetch_latest: config.fetch_latest ?? 'false',
scope: config.scope ?? 'global',
version_parameter:
config.version_parameter != null
? JSON.stringify(config.version_parameter)
: '',
version_prefix: config.version_prefix ?? '',
release: '',
packagist: config.packagist ?? config.repository ?? '',
type: config.type,
function: config.function,
alias: config.alias
};
data.release = await getRelease(release, data);
data.version = version
? await getVersion(version, data)
: await getLatestVersion(data);
data.url = await getUrl(data);
return data;
}
export const functionRecord: Record<string, (data: RS) => Promise<string>> = {
export const functionRecord: Record<
ToolFunction,
(data: ToolData) => Promise<string>
> = {
castor: addCastor,
composer: addComposer,
deployer: addDeployer,
@@ -565,43 +708,46 @@ export async function addTools(
}
const tools_list = await filterList(await utils.CSVArray(tools_csv));
await utils.asyncForEach(tools_list, async function (release: string) {
const data: RS = await getData(release, php_version, os);
const data: ToolData = await getData(release, php_version, os);
script += '\n';
switch (true) {
case data['error'] !== undefined:
script += await utils.addLog(
'$cross',
data['tool'],
data['error'],
data['os']
);
case data.error !== undefined:
script += await utils.addLog('$cross', data.tool, data.error, data.os);
break;
case 'phar' === data['type']:
data['url'] = await getUrl(data);
case 'phar' === data.type:
script += await addArchive(data);
break;
case 'composer' === data['type']:
case 'composer' === data.type:
script += await addPackage(data);
break;
case 'custom-package' === data['type']:
case 'custom-package' === data.type:
script += await utils.customPackage(
data['tool'].split('-')[0],
data.tool.split('-')[0],
'tools',
data['version'],
data['os']
data.version,
data.os
);
break;
case 'custom-function' === data['type']:
script += await functionRecord[data['function']](data);
case 'custom-function' === data.type:
if (!data.function) {
script += await utils.addLog(
'$cross',
data.tool,
data.tool + ' has no function defined. Please report this issue.',
data.os
);
} else {
script += await functionRecord[data.function](data);
}
break;
case /^none$/.test(data['tool']):
case /^none$/.test(data.tool):
break;
default:
script += await utils.addLog(
'$cross',
data['tool'],
'Tool ' + data['tool'] + ' is not supported',
data['os']
data.tool,
'Tool ' + data.tool + ' is not supported',
data.os
);
break;
}

View File

@@ -1,6 +1,6 @@
import fs from 'fs';
import * as path from 'path';
import * as core from '@actions/core';
import * as core from './core';
import * as fetch from './fetch';
/**
@@ -62,15 +62,31 @@ export async function getManifestURLS(): Promise<string[]> {
*/
export async function parseVersion(version: string): Promise<string> {
switch (true) {
case /^(latest|lowest|highest|nightly|\d+\.x)$/.test(version):
case /^pre(-installed)?$/.test(version):
return 'pre';
case /^(latest|lowest|highest|nightly|master|\d+\.x)$/.test(version):
for (const manifestURL of await getManifestURLS()) {
const fetchResult = await fetch.fetch(manifestURL);
if (fetchResult['data'] ?? false) {
return JSON.parse(fetchResult['data'])[version];
const resolved: string | undefined = JSON.parse(fetchResult['data'])[
version
];
if (resolved === undefined) {
throw new Error(`Invalid PHP version: ${version.slice(0, 20)}`);
}
if (!/^\d+\.\d+$/.test(resolved)) {
throw new Error(
`Invalid PHP version in manifest: ${resolved.slice(0, 10)}`
);
}
return resolved;
}
}
throw new Error(`Could not fetch the PHP version manifest.`);
default:
if (!/^\d+(\.\d+){0,2}$/.test(version)) {
throw new Error(`Invalid PHP version: ${version.slice(0, 20)}`);
}
switch (true) {
case version.length > 1:
return version.slice(0, 3);
@@ -86,20 +102,16 @@ export async function parseVersion(version: string): Promise<string> {
* @param ini_file
*/
export async function parseIniFile(ini_file: string): Promise<string> {
switch (true) {
case /^(production|development|none)$/.test(ini_file):
if (/^(production|development|none)$/.test(ini_file)) {
return ini_file;
case /php\.ini-(production|development)$/.test(ini_file):
return ini_file.split('-')[1];
default:
return 'production';
}
const match = ini_file.match(/php\.ini-(production|development)$/);
return match ? match[1] : 'production';
}
/**
* Async foreach loop
* Async foreach loop using modern for...of pattern
*
* @author https://github.com/Atinux
* @param array
* @param callback
*/
@@ -111,8 +123,8 @@ export async function asyncForEach(
array: Array<string>
) => Promise<void>
): Promise<void> {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array);
for (const [index, element] of array.entries()) {
await callback(element, index, array);
}
}
@@ -173,10 +185,10 @@ export async function log(
export async function stepLog(message: string, os: string): Promise<string> {
switch (os) {
case 'win32':
return 'Step-Log "' + message + '"';
return 'Step-Log "' + escapeForShell(message, os) + '"';
case 'linux':
case 'darwin':
return 'step_log "' + message + '"';
return 'step_log "' + escapeForShell(message, os) + '"';
default:
return await log('Platform ' + os + ' is not supported', os, 'error');
}
@@ -195,17 +207,40 @@ export async function addLog(
message: string,
os: string
): Promise<string> {
const sub = escapeForShell(subject, os);
const msg = escapeForShell(message, os);
switch (os) {
case 'win32':
return 'Add-Log "' + mark + '" "' + subject + '" "' + message + '"';
return `Add-Log "${mark}" "${sub}" "${msg}"`;
case 'linux':
case 'darwin':
return 'add_log "' + mark + '" "' + subject + '" "' + message + '"';
return `add_log "${mark}" "${sub}" "${msg}"`;
default:
return await log('Platform ' + os + ' is not supported', os, 'error');
}
}
export function escapeForShell(value: string, os: string): string {
if (os === 'win32') {
return value.replace(/[`$"]/g, '`$&');
}
return value.replace(/[\\`$"]/g, '\\$&');
}
export function safeArg(value: string, os: string): string {
if (!/^[a-zA-Z0-9_./:@+,~^-]*$/.test(value)) {
return '"' + escapeForShell(value, os) + '"';
}
return value;
}
export function sanitizeShellInput(value: string, strict = false): string {
const pattern = strict
? /[$`"';|&(){}[\]\\<>*?\n\r\t]/g
: /[$`"';|&(){}[\]\\\n\r\t]/g;
return value.replace(pattern, '');
}
/**
* Function to break extension csv into an array
*
@@ -225,11 +260,11 @@ export async function extensionArray(
.split(',')
.map(function (extension: string) {
extension = extension.trim().replace(/^\\\s*/, '');
if (/.+-.+\/.+@.+/.test(extension)) {
return extension;
}
return extension
.trim()
.toLowerCase()
.replace(/^(:)?(php[-_]|none|zend )|(-[^-]*)-/, '$1$3');
})
@@ -285,10 +320,10 @@ export async function getExtensionPrefix(extension: string): Promise<string> {
export async function suppressOutput(os: string): Promise<string> {
switch (os) {
case 'win32':
return ' >$null 2>&1';
return ' ';
case 'linux':
case 'darwin':
return ' >/dev/null 2>&1';
return ' ';
default:
return await log('Platform ' + os + ' is not supported', os, 'error');
}
@@ -432,22 +467,35 @@ export async function parseExtensionSource(
);
}
const VERSION_INPUT_REGEX =
/^(latest|lowest|highest|nightly|master|pre|pre-installed|\d+\.x|\d+(\.\d+){0,2})$/;
function validatePHPVersionInput(version: string, source: string): string {
if (!VERSION_INPUT_REGEX.test(version)) {
throw new Error(
`Invalid PHP version in ${source}: ${version.slice(0, 20)}`
);
}
return version;
}
/**
* Read php version from input or file
*/
export async function readPHPVersion(): Promise<string> {
const version = await getInput('php-version', false);
if (version) {
return version;
return validatePHPVersionInput(version, 'php-version input');
}
const versionFile =
(await getInput('php-version-file', false)) || '.php-version';
if (fs.existsSync(versionFile)) {
const contents: string = fs.readFileSync(versionFile, 'utf8');
const match: RegExpMatchArray | null = contents.match(
/^(?:php\s)?(\d+\.\d+\.\d+)$/m
const match = contents.match(/^(?:php\s)?(\d+\.\d+\.\d+)$/m);
return validatePHPVersionInput(
match ? match[1] : contents.trim(),
versionFile
);
return match ? match[1] : contents.trim();
} else if (versionFile !== '.php-version') {
throw new Error(`Could not find '${versionFile}' file.`);
}
@@ -457,11 +505,11 @@ export async function readPHPVersion(): Promise<string> {
if (fs.existsSync(composerLock)) {
const lockFileContents = JSON.parse(fs.readFileSync(composerLock, 'utf8'));
/* istanbul ignore next */
if (
lockFileContents['platform-overrides'] &&
lockFileContents['platform-overrides']['php']
) {
return lockFileContents['platform-overrides']['php'];
if (lockFileContents['platform-overrides']?.['php']) {
return validatePHPVersionInput(
lockFileContents['platform-overrides']['php'],
'composer.lock platform-overrides.php'
);
}
}
@@ -471,12 +519,11 @@ export async function readPHPVersion(): Promise<string> {
fs.readFileSync(composerJson, 'utf8')
);
/* istanbul ignore next */
if (
composerFileContents['config'] &&
composerFileContents['config']['platform'] &&
composerFileContents['config']['platform']['php']
) {
return composerFileContents['config']['platform']['php'];
if (composerFileContents['config']?.['platform']?.['php']) {
return validatePHPVersionInput(
composerFileContents['config']['platform']['php'],
'composer.json config.platform.php'
);
}
}

View File

@@ -3,7 +3,7 @@
"declaration": true,
"esModuleInterop": true,
"lib": [
"ES2021"
"ES2024"
],
"module": "commonjs",
"moduleResolution": "node",
@@ -13,7 +13,7 @@
"rootDir": "./src",
"sourceMap": true,
"strict": true,
"target": "ES2021"
"target": "ES2024"
},
"exclude": ["__tests__", "lib", "node_modules"]
}