From a2ddc4cbf0d9c5c55de1afbd67e2abadbbbc1180 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raimondas=20Rimkevi=C4=8Dius=20=28aka=20MekDrop=29?= Date: Fri, 27 Mar 2026 02:25:22 +0200 Subject: [PATCH 01/24] Rewrite action as Docker container with php-version matrix tests --- .dockerignore | 10 + .github/workflows/on-pull-request.yml | 28 +- .gitignore | 3 + Dockerfile | 37 + README.md | 25 +- action.yml | 42 +- bin/add-composer-packages.sh | 8 - bin/create-paths.sh | 6 - bin/entrypoint.sh | 101 + bin/generate-docs.sh | 5 - bin/generate-env.sh | 11 - bin/get-composer-dependencies.php | 18 - bin/phpdoc.sh | 21 - bin/realpath.php | 9 - bin/remove-composer-dependencies.sh | 8 - bin/remove-tmp-data.sh | 3 - composer.json | 15 - package-lock.json | 2131 +++++++++++++++++ package.json | 11 + tests/01-realpath.bats | 21 - tests/02-generate-env.bats | 34 - tests/03-create-paths.bats | 21 - tests/04-phpdocs.bats | 41 - tests/05-get-composer-dependencies.bats | 32 - tests/06-add-composer-packages.bats | 27 - tests/07-generate-docs.bats | 42 - tests/08-remove-composer-packages.bats | 29 - tests/09-remove-tmp-data.bats | 13 - tests/action-container.test.js | 93 + .../example-project/src/ExampleClass.php | 17 + .../example-project/src/IgnoredClass.php | 17 + 31 files changed, 2445 insertions(+), 434 deletions(-) create mode 100644 .dockerignore create mode 100644 Dockerfile delete mode 100755 bin/add-composer-packages.sh delete mode 100755 bin/create-paths.sh create mode 100644 bin/entrypoint.sh delete mode 100755 bin/generate-docs.sh delete mode 100755 bin/generate-env.sh delete mode 100755 bin/get-composer-dependencies.php delete mode 100755 bin/phpdoc.sh delete mode 100755 bin/realpath.php delete mode 100755 bin/remove-composer-dependencies.sh delete mode 100755 bin/remove-tmp-data.sh delete mode 100644 composer.json create mode 100644 package-lock.json create mode 100644 package.json delete mode 100644 tests/01-realpath.bats delete mode 100644 tests/02-generate-env.bats delete mode 100644 tests/03-create-paths.bats delete mode 100644 tests/04-phpdocs.bats delete mode 100644 tests/05-get-composer-dependencies.bats delete mode 100644 tests/06-add-composer-packages.bats delete mode 100644 tests/07-generate-docs.bats delete mode 100644 tests/08-remove-composer-packages.bats delete mode 100644 tests/09-remove-tmp-data.bats create mode 100644 tests/action-container.test.js create mode 100644 tests/fixtures/example-project/src/ExampleClass.php create mode 100644 tests/fixtures/example-project/src/IgnoredClass.php diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..e65b258 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,10 @@ +.git +.github +.idea +.zencoder +node_modules +tests +package.json +package-lock.json +README.md +LICENSE diff --git a/.github/workflows/on-pull-request.yml b/.github/workflows/on-pull-request.yml index 25decda..5558f41 100644 --- a/.github/workflows/on-pull-request.yml +++ b/.github/workflows/on-pull-request.yml @@ -9,35 +9,21 @@ jobs: test: runs-on: ubuntu-latest - strategy: - matrix: - php: - - 5.6 - - 7.0 - - 7.1 - - 7.2 - - 7.3 - - 7.4 steps: - name: Checkouting code... uses: actions/checkout@v5 - - name: Installing PHP... - uses: shivammathur/setup-php@2.35.5 + - name: Setup Node.js + uses: actions/setup-node@v4 with: - php-version: ${{ matrix.php }} - extensions: curl, gd, json, mbstring, pcre, session - coverage: none - tools: composer:v2 + node-version: 22 - - name: Setup BATS - uses: mig4/setup-bats@v1 - with: - bats-version: 1.9.0 + - name: Install dependencies + run: npm ci - - name: Test - run: bats tests + - name: Run integration tests + run: npm test dependabot: needs: diff --git a/.gitignore b/.gitignore index f4686f8..701123b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ /.idea/ /vendor/ /composer.lock +/node_modules/ +/.phpdoc/ +/phpDocumentor.phar diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..6506a06 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,37 @@ +ARG PHP_BASE_IMAGE=php:7.4-cli + +FROM php:8.3-cli-bookworm AS phpdocmd-builder + +COPY --from=composer:2.2 /usr/bin/composer /usr/local/bin/composer + +RUN apt-get update \ + && apt-get install -y --no-install-recommends \ + ca-certificates \ + git \ + openssh-client \ + unzip \ + && rm -rf /var/lib/apt/lists/* + +RUN GIT_SSH_COMMAND="ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5" \ + git clone --depth 1 git@github.com:evert/phpdoc-md.git /opt/phpdoc-md \ + || git clone --depth 1 https://github.com/evert/phpdoc-md.git /opt/phpdoc-md \ + && php -r '$composer = json_decode(file_get_contents("/opt/phpdoc-md/composer.json"), true); unset($composer["require-dev"]); file_put_contents("/opt/phpdoc-md/composer.json", json_encode($composer, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . PHP_EOL);' \ + && COMPOSER_ALLOW_SUPERUSER=1 COMPOSER_HOME=/tmp/composer composer config --global platform.php 5.5.0 \ + && COMPOSER_ALLOW_SUPERUSER=1 COMPOSER_HOME=/tmp/composer composer install \ + --working-dir=/opt/phpdoc-md \ + --no-dev \ + --no-plugins \ + --no-scripts \ + --no-interaction \ + --no-progress \ + --prefer-dist \ + && rm -rf /tmp/composer + +FROM ${PHP_BASE_IMAGE} + +COPY --from=phpdocmd-builder /opt/phpdoc-md /opt/phpdoc-md +COPY bin/entrypoint.sh /usr/local/bin/entrypoint.sh + +RUN chmod +x /usr/local/bin/entrypoint.sh + +ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] diff --git a/README.md b/README.md index 42ea8ca..2fe84a1 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ GitHub action to generate PHP project documentation in [MarkDown](https://docs.github.com/en/get-started/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax) format. Based on [evert/phpdoc-md](https://github.com/evert/phpdoc-md) library. -***Warning***: at current moment this action doesn't work with PHP newer than 7.4. +This action is container-based and runs on official PHP Docker images. It does not require setting up PHP or Composer on the runner. ## Usage @@ -22,19 +22,7 @@ jobs: steps: - name: Checkouting project code... uses: actions/checkout@v2 - - - name: Install PHP - uses: shivammathur/setup-php@v2.2 - with: - php-version: 7.4 - extensions: curl, gd, pdo_mysql, json, mbstring, pcre, session - ini-values: post_max_size=256M - coverage: none - tools: composer:v2 - - - name: Install Composer dependencies (with dev) - run: composer install --no-progress --no-suggest --prefer-dist --optimize-autoloader - + - name: Generating documentation... uses: impresscms-dev/generate-phpdocs-with-evert-phpdoc-md-action@v1.0.0 with: @@ -55,9 +43,16 @@ This action supports such arguments (used in `with` keyword): | Argument | Required | Default value | Description | |-------------|----------|----------------------|-----------------------------------| | ignored_files | No | | Defines files that can be ignored (supports glob rules; each line means one rule) | -| phpdocumentor_version | No | latest | What [PHP Documentator](https://www.phpdoc.org) version to use? (version = docker image tag) | +| phpdocumentor_version | No | v2.8.5 | What [phpDocumentor](https://www.phpdoc.org) version to use (latest or release tag like `v2.8.5`) | | output_path | Yes | | Path where to write generated documentation | +## Notes + +- Docker build clones `git@github.com:evert/phpdoc-md.git` directly and falls back to HTTPS clone when SSH credentials are not available. +- phpDocumentor release artifacts are downloaded during action runtime and are not stored in this repository. +- Dockerfile supports selecting the PHP base image at build time: `docker build --build-arg PHP_BASE_IMAGE=php:7.4-cli .` +- Tests are JavaScript integration tests based on [testcontainers-node](https://github.com/testcontainers/testcontainers-node). + ## How to contribute? If you want to add some functionality or fix bugs, you can fork, change and create pull request. If you not sure how this works, try [interactive GitHub tutorial](https://skills.github.com). diff --git a/action.yml b/action.yml index 1261b20..f3aeb86 100644 --- a/action.yml +++ b/action.yml @@ -11,43 +11,17 @@ inputs: required: false default: '' phpdocumentor_version: - description: "What PHP Documentator version to use? (version = docker image tag)" - default: latest + description: "What phpDocumentor version to use? (latest or release tag like v2.8.5)" + default: v2.8.5 required: false output_path: description: "Path where to write generated documentation" required: true runs: - using: 'composite' - steps: - - name: Setting env variables... - env: - GENERATOR_TMP_FILES_PATH: "${{ runner.temp }}/phpdocs-${{ github.sha }}-${{ github.run_id }}-${{ github.github.run_attempt }}" - GENERATOR_DOCS_PATH: ${{ inputs.output_path }} - run: $ACTION_BIN_PATH/generate-env.sh "$GENERATOR_TMP_FILES_PATH" "$GENERATOR_DOCS_PATH" >> $GITHUB_ENV - shell: bash - - - name: Creating required paths... - run: $ACTION_BIN_PATH/create-paths.sh - shell: bash - - - name: Running phpDocumentator... - run: $ACTION_BIN_PATH/phpdoc.sh "${{ inputs.ignored_files }}" "${{ inputs.phpdocumentor_version }}" - shell: bash - - - name: Installing evert/phpdoc-md... - run: $ACTION_BIN_PATH/add-composer-packages.sh - shell: bash - - - name: Generating documentation... - run: $ACTION_BIN_PATH/generate-docs.sh - shell: bash - - - name: Uninstalling evert/phpdoc-md... - run: $ACTION_BIN_PATH/remove-composer-dependencies.sh - shell: bash - - - name: Deleting tmp data... - run: $ACTION_BIN_PATH/remove-tmp-data.sh - shell: bash + using: 'docker' + image: 'Dockerfile' + args: + - ${{ inputs.output_path }} + - ${{ inputs.ignored_files }} + - ${{ inputs.phpdocumentor_version }} diff --git a/bin/add-composer-packages.sh b/bin/add-composer-packages.sh deleted file mode 100755 index 2188fb7..0000000 --- a/bin/add-composer-packages.sh +++ /dev/null @@ -1,8 +0,0 @@ -#!/usr/bin/env bash - -set -e - -SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) - -# shellcheck disable=SC2046 -composer global require $("$SCRIPT_DIR"/get-composer-dependencies.php) \ No newline at end of file diff --git a/bin/create-paths.sh b/bin/create-paths.sh deleted file mode 100755 index 5c5b6e8..0000000 --- a/bin/create-paths.sh +++ /dev/null @@ -1,6 +0,0 @@ -#!/usr/bin/env bash - -mkdir -p "$GENERATOR_DOCS_PATH" || true - -rm -rf "$GENERATOR_TMP_FILES_PATH" || true -mkdir -p "$GENERATOR_TMP_FILES_PATH" \ No newline at end of file diff --git a/bin/entrypoint.sh b/bin/entrypoint.sh new file mode 100644 index 0000000..0830ef3 --- /dev/null +++ b/bin/entrypoint.sh @@ -0,0 +1,101 @@ +#!/usr/bin/env bash + +set -euo pipefail + +OUTPUT_PATH=${1:-} +IGNORED_FILES=${2:-} +PHPDOC_VERSION=${3:-v2.8.5} + +if [[ -z "$OUTPUT_PATH" ]]; then + echo "Input 'output_path' is required." >&2 + exit 1 +fi + +WORKSPACE_PATH=${GITHUB_WORKSPACE:-/github/workspace} + +if [[ ! -d "$WORKSPACE_PATH" ]]; then + echo "Workspace path does not exist: $WORKSPACE_PATH" >&2 + exit 1 +fi + +if [[ "$OUTPUT_PATH" = /* ]]; then + DOCS_PATH=$OUTPUT_PATH +else + DOCS_PATH="$WORKSPACE_PATH/$OUTPUT_PATH" +fi + +TMP_ROOT=$(mktemp -d) +PHPDOC_XML_PATH="$TMP_ROOT/phpdoc-xml" +PHPDOC_PHAR_PATH="$TMP_ROOT/phpDocumentor.phar" +PHPDOC_TAR_GZ_PATH="$TMP_ROOT/phpDocumentor.tgz" +PHPDOC_EXTRACT_PATH="$TMP_ROOT/phpDocumentor" +PHPDOC_MD_PATH=/opt/phpdoc-md + +cleanup() { + rm -rf "$TMP_ROOT" +} +trap cleanup EXIT + +mkdir -p "$DOCS_PATH" +mkdir -p "$PHPDOC_XML_PATH" +mkdir -p "$PHPDOC_EXTRACT_PATH" + +download_file() { + local url=$1 + local target=$2 + php -r '$url = isset($argv[1]) ? $argv[1] : null; $target = isset($argv[2]) ? $argv[2] : null; if (!$url || !$target) { fwrite(STDERR, "Missing download arguments\n"); exit(1); } $data = @file_get_contents($url); if ($data === false) { exit(1); } if (@file_put_contents($target, $data) === false) { fwrite(STDERR, "Unable to write file: $target\n"); exit(1); }' "$url" "$target" +} + +extract_tgz() { + local archive=$1 + local target=$2 + php -r '$archive = isset($argv[1]) ? $argv[1] : null; $target = isset($argv[2]) ? $argv[2] : null; if (!$archive || !$target) { fwrite(STDERR, "Missing extract arguments\n"); exit(1); } if (!is_dir($target) && !mkdir($target, 0777, true)) { fwrite(STDERR, "Unable to create extract directory\n"); exit(1); } $tarPath = preg_replace("/\\.tgz$/", ".tar", $archive); if ($tarPath === null) { fwrite(STDERR, "Unable to derive tar path\n"); exit(1); } try { $tgz = new PharData($archive); if (!file_exists($tarPath)) { $tgz->decompress(); } $tar = new PharData($tarPath); $tar->extractTo($target, null, true); } catch (Exception $e) { fwrite(STDERR, "Unable to extract phpDocumentor archive: " . $e->getMessage() . "\n"); exit(1); }' "$archive" "$target" +} + +PHPDOC_COMMAND=() +if [[ "$PHPDOC_VERSION" == "latest" ]]; then + PHPDOC_PHAR_URL="https://phpdoc.org/phpDocumentor.phar" + download_file "$PHPDOC_PHAR_URL" "$PHPDOC_PHAR_PATH" + PHPDOC_COMMAND=(php "$PHPDOC_PHAR_PATH" run) +else + PHPDOC_VERSION_NO_PREFIX=${PHPDOC_VERSION#v} + PHPDOC_TAR_GZ_URL="https://github.com/phpDocumentor/phpDocumentor/releases/download/v${PHPDOC_VERSION_NO_PREFIX}/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}.tgz" + if download_file "$PHPDOC_TAR_GZ_URL" "$PHPDOC_TAR_GZ_PATH"; then + extract_tgz "$PHPDOC_TAR_GZ_PATH" "$PHPDOC_EXTRACT_PATH" + PHPDOC_BIN_PATH="$PHPDOC_EXTRACT_PATH/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}/bin/phpdoc" + if [[ ! -f "$PHPDOC_BIN_PATH" ]]; then + echo "Unable to find phpDocumentor binary after extraction." >&2 + exit 1 + fi + PHPDOC_COMMAND=(php "$PHPDOC_BIN_PATH") + else + PHPDOC_PHAR_URL="https://github.com/phpDocumentor/phpDocumentor/releases/download/v${PHPDOC_VERSION_NO_PREFIX}/phpDocumentor.phar" + download_file "$PHPDOC_PHAR_URL" "$PHPDOC_PHAR_PATH" + PHPDOC_COMMAND=(php "$PHPDOC_PHAR_PATH" run) + fi +fi + +declare -a PHPDOC_IGNORE_ARGS +PHPDOC_IGNORE_ARGS+=("--ignore=vendor/**") +while IFS= read -r LINE; do + LINE=${LINE%$'\r'} + if [[ -n "$LINE" ]]; then + PHPDOC_IGNORE_ARGS+=("--ignore=$LINE") + fi +done <<< "$IGNORED_FILES" + +"${PHPDOC_COMMAND[@]}" \ + --target="$PHPDOC_XML_PATH" \ + --directory="$WORKSPACE_PATH" \ + --cache-folder="$TMP_ROOT/cache" \ + --template=xml \ + --no-interaction \ + --ansi \ + "${PHPDOC_IGNORE_ARGS[@]}" + +if [[ ! -f "$PHPDOC_MD_PATH/bin/phpdocmd" ]]; then + echo "phpdoc-md binary is missing: $PHPDOC_MD_PATH/bin/phpdocmd" >&2 + exit 1 +fi + +php "$PHPDOC_MD_PATH/bin/phpdocmd" "$PHPDOC_XML_PATH/structure.xml" "$DOCS_PATH" diff --git a/bin/generate-docs.sh b/bin/generate-docs.sh deleted file mode 100755 index 9645bdf..0000000 --- a/bin/generate-docs.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/usr/bin/env bash - -set -e - -composer global exec phpdocmd "$GENERATOR_TMP_FILES_PATH/structure.xml" "$GENERATOR_DOCS_PATH" \ No newline at end of file diff --git a/bin/generate-env.sh b/bin/generate-env.sh deleted file mode 100755 index db813fb..0000000 --- a/bin/generate-env.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash -GENERATOR_TMP_FILES_PATH=$1 -GENERATOR_DOCS_PATH=$2 - -SCRIPT_DIR=$( cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) - -cat < 0); - -$contents = file_get_contents(dirname(__DIR__) . DIRECTORY_SEPARATOR . 'composer.json'); -$composer = json_decode($contents, true); - -$packages = []; -foreach ($composer['require'] as $package => $version) { - if ($withVersions) { - $packages[] = $package . "=" . $version; - } else { - $packages[] = $package; - } -} - -echo implode(" ", $packages); \ No newline at end of file diff --git a/bin/phpdoc.sh b/bin/phpdoc.sh deleted file mode 100755 index e37732d..0000000 --- a/bin/phpdoc.sh +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env bash - -IGNORED_FILES=$1 -PHPDOC_TAG=$2 - -# shellcheck disable=SC2089 -GENERATOR_DOCKER_APP_ARGS="--target=/result --directory=/data --cache-folder=/tmp -v --template=xml --ansi --no-interaction --ignore=\"vendor/**\"" -if [ "$IGNORED_FILES" != "" ]; then - while read -r line - do - GENERATOR_DOCKER_APP_ARGS="$GENERATOR_DOCKER_APP_ARGS --ignore=\"$line\"" - done <<< "$IGNORED_FILES" -fi; - -# shellcheck disable=SC2086 disable=SC1101 disable=SC2090 -docker run \ - --rm \ - -v ${PWD}:/data \ - -v ${GENERATOR_TMP_FILES_PATH}:/result \ - phpdoc/phpdoc:$PHPDOC_TAG \ - $GENERATOR_DOCKER_APP_ARGS \ No newline at end of file diff --git a/bin/realpath.php b/bin/realpath.php deleted file mode 100755 index 52ffa03..0000000 --- a/bin/realpath.php +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env php - /dev/null && pwd ) - -# shellcheck disable=SC2046 -composer global remove $("$SCRIPT_DIR"/get-composer-dependencies.php 0) \ No newline at end of file diff --git a/bin/remove-tmp-data.sh b/bin/remove-tmp-data.sh deleted file mode 100755 index e11d8e1..0000000 --- a/bin/remove-tmp-data.sh +++ /dev/null @@ -1,3 +0,0 @@ -#!/usr/bin/env bash - -rm -rf "$GENERATOR_TMP_FILES_PATH" || true \ No newline at end of file diff --git a/composer.json b/composer.json deleted file mode 100644 index 407c64f..0000000 --- a/composer.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "impresscms-dev/generate-phpdocs-with-evert-phpdoc-md-action", - "description": "Libraries needed for same name GitHub action", - "type": "metapackage", - "require": { - "evert/phpdoc-md": "^0.2.0" - }, - "license": "MIT", - "authors": [ - { - "name": "Raimondas Rimkevičius (aka MekDrop)", - "email": "github@mekdrop.name" - } - ] -} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..4b09818 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,2131 @@ +{ + "name": "generate-phpdocs-with-evert-phpdoc-md-action", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "generate-phpdocs-with-evert-phpdoc-md-action", + "devDependencies": { + "testcontainers": "^10.20.0" + } + }, + "node_modules/@balena/dockerignore": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@balena/dockerignore/-/dockerignore-1.0.2.tgz", + "integrity": "sha512-wMue2Sy4GAVTk6Ic4tJVcnfdau+gx2EnG7S+uAEe+TWJFqE4YoWN4/H8MSLj4eYJKxGg26lZwboEniNiNwZQ6Q==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/@fastify/busboy": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.1.tgz", + "integrity": "sha512-vBZP4NlzfOlerQTnba4aqZoMhE/a9HY7HRqoOPaETQcSQuWEIyZMHGfVu6w9wGtGK5fED5qRs2DteVCjOH60sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } + }, + "node_modules/@grpc/grpc-js": { + "version": "1.14.3", + "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.3.tgz", + "integrity": "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@grpc/proto-loader": "^0.8.0", + "@js-sdsl/ordered-map": "^4.4.2" + }, + "engines": { + "node": ">=12.10.0" + } + }, + "node_modules/@grpc/grpc-js/node_modules/@grpc/proto-loader": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.0.tgz", + "integrity": "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.5.3", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@grpc/proto-loader": { + "version": "0.7.15", + "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz", + "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "lodash.camelcase": "^4.3.0", + "long": "^5.0.0", + "protobufjs": "^7.2.5", + "yargs": "^17.7.2" + }, + "bin": { + "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@js-sdsl/ordered-map": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", + "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/@pkgjs/parseargs": { + "version": "0.11.0", + "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", + "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", + "dev": true, + "license": "MIT", + "optional": true, + "engines": { + "node": ">=14" + } + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@types/docker-modem": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/@types/docker-modem/-/docker-modem-3.0.6.tgz", + "integrity": "sha512-yKpAGEuKRSS8wwx0joknWxsmLha78wNMe9R2S3UNsVOkZded8UqOrV8KoeDXoXsjndxwyF3eIhyClGbO1SEhEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/ssh2": "*" + } + }, + "node_modules/@types/dockerode": { + "version": "3.3.47", + "resolved": "https://registry.npmjs.org/@types/dockerode/-/dockerode-3.3.47.tgz", + "integrity": "sha512-ShM1mz7rCjdssXt7Xz0u1/R2BJC7piWa3SJpUBiVjCf2A3XNn4cP6pUVaD8bLanpPVVn4IKzJuw3dOvkJ8IbYw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/docker-modem": "*", + "@types/node": "*", + "@types/ssh2": "*" + } + }, + "node_modules/@types/node": { + "version": "25.5.0", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.5.0.tgz", + "integrity": "sha512-jp2P3tQMSxWugkCUKLRPVUpGaL5MVFwF8RDuSRztfwgN1wmqJeMSbKlnEtQqU8UrhTmzEmZdu2I6v2dpp7XIxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.18.0" + } + }, + "node_modules/@types/ssh2": { + "version": "1.15.5", + "resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-1.15.5.tgz", + "integrity": "sha512-N1ASjp/nXH3ovBHddRJpli4ozpk6UdDYIX4RJWFa9L1YKnzdhTlVmiGHm4DZnj/jLbqZpes4aeR30EFGQtvhQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "^18.11.18" + } + }, + "node_modules/@types/ssh2-streams": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/@types/ssh2-streams/-/ssh2-streams-0.1.13.tgz", + "integrity": "sha512-faHyY3brO9oLEA0QlcO8N2wT7R0+1sHWZvQ+y3rMLwdY1ZyS1z0W3t65j9PqT4HmQ6ALzNe7RZlNuCNE0wBSWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/ssh2/node_modules/@types/node": { + "version": "18.19.130", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.130.tgz", + "integrity": "sha512-GRaXQx6jGfL8sKfaIDD6OupbIHBr9jv7Jnaml9tB7l4v068PAOXqfcujMMo5PhbIs6ggR1XODELqahT2R8v0fg==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/ssh2/node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true, + "license": "MIT" + }, + "node_modules/abort-controller": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", + "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", + "dev": true, + "license": "MIT", + "dependencies": { + "event-target-shim": "^5.0.0" + }, + "engines": { + "node": ">=6.5" + } + }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/archiver": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/archiver/-/archiver-7.0.1.tgz", + "integrity": "sha512-ZcbTaIqJOfCc03QwD468Unz/5Ir8ATtvAHsK+FdXbDIbGfihqh9mrvdcYunQzqn4HrvWWaFyaxJhGZagaJJpPQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "archiver-utils": "^5.0.2", + "async": "^3.2.4", + "buffer-crc32": "^1.0.0", + "readable-stream": "^4.0.0", + "readdir-glob": "^1.1.2", + "tar-stream": "^3.0.0", + "zip-stream": "^6.0.1" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/archiver-utils": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/archiver-utils/-/archiver-utils-5.0.2.tgz", + "integrity": "sha512-wuLJMmIBQYCsGZgYLTy5FIB2pF6Lfb6cXMSF8Qywwk3t20zWnAi7zLcQFdKQmIB8wyZpY5ER38x08GbwtR2cLA==", + "dev": true, + "license": "MIT", + "dependencies": { + "glob": "^10.0.0", + "graceful-fs": "^4.2.0", + "is-stream": "^2.0.1", + "lazystream": "^1.0.0", + "lodash": "^4.17.15", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/asn1": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.6.tgz", + "integrity": "sha512-ix/FxPn0MDjeyJ7i/yoHGFt/EX6LyNbxSEhPPXODPL+KB0VPk86UYfL0lMdy+KCnv+fmvIzySwaK5COwqVbWTQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "safer-buffer": "~2.1.0" + } + }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, + "node_modules/async-lock": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/async-lock/-/async-lock-1.4.1.tgz", + "integrity": "sha512-Az2ZTpuytrtqENulXwO3GGv1Bztugx6TT37NIo7imr/Qo0gsYiGtSdBa2B6fsXhTpVZDNfu1Qn3pk531e3q+nQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/b4a": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.8.0.tgz", + "integrity": "sha512-qRuSmNSkGQaHwNbM7J78Wwy+ghLEYF1zNrSeMxj4Kgw6y33O3mXcQ6Ie9fRvfU/YnxWkOchPXbaLb73TkIsfdg==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "dev": true, + "license": "MIT" + }, + "node_modules/bare-events": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", + "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, + "node_modules/bare-fs": { + "version": "4.5.6", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.5.6.tgz", + "integrity": "sha512-1QovqDrR80Pmt5HPAsMsXTCFcDYr+NSUKW6nd6WO5v0JBmnItc/irNRzm2KOQ5oZ69P37y+AMujNyNtG+1Rggw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4", + "bare-url": "^2.2.2", + "fast-fifo": "^1.3.2" + }, + "engines": { + "bare": ">=1.16.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } + } + }, + "node_modules/bare-os": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.8.0.tgz", + "integrity": "sha512-Dc9/SlwfxkXIGYhvMQNUtKaXCaGkZYGcd1vuNUUADVqzu4/vQfvnMkYYOUnt2VwQ2AqKr/8qAVFRtwETljgeFg==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "bare": ">=1.14.0" + } + }, + "node_modules/bare-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", + "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-os": "^3.0.1" + } + }, + "node_modules/bare-stream": { + "version": "2.11.0", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.11.0.tgz", + "integrity": "sha512-Y/+iQ49fL3rIn6w/AVxI/2+BRrpmzJvdWt5Jv8Za6Ngqc6V227c+pYjYYgLdpR3MwQ9ObVXD0ZrqoBztakM0rw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "streamx": "^2.25.0", + "teex": "^1.0.1" + }, + "peerDependencies": { + "bare-abort-controller": "*", + "bare-buffer": "*", + "bare-events": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + }, + "bare-buffer": { + "optional": true + }, + "bare-events": { + "optional": true + } + } + }, + "node_modules/bare-url": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/bare-url/-/bare-url-2.4.0.tgz", + "integrity": "sha512-NSTU5WN+fy/L0DDenfE8SXQna4voXuW0FHM7wH8i3/q9khUSchfPbPezO4zSFMnDGIf9YE+mt/RWhZgNRKRIXA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-path": "^3.0.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "tweetnacl": "^0.14.3" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/bl/node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/bl/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-crc32": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/buildcheck": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/buildcheck/-/buildcheck-0.0.7.tgz", + "integrity": "sha512-lHblz4ahamxpTmnsk+MNTRWsjYKv965MwOrSJyeD588rR3Jcu7swE+0wN5F+PbL5cjgu/9ObkhfzEPuofEMwLA==", + "dev": true, + "optional": true, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/byline": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", + "integrity": "sha512-s6webAy+R4SR8XVuJWt2V2rGvhnrhxN+9S15GNuTK3wKPOXFF6RNc+8ug2XhH+2s4f+uudG4kUVYmYOQWL2g0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true, + "license": "ISC" + }, + "node_modules/cliui": { + "version": "8.0.1", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", + "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", + "dev": true, + "license": "ISC", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.1", + "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/cliui/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/cliui/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/compress-commons": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/compress-commons/-/compress-commons-6.0.2.tgz", + "integrity": "sha512-6FqVXeETqWPoGcfzrXb37E50NP0LXT8kAMu5ooZayhWWdgEY4lBEEcbQNXtkuKQsGduxiIcI4gOTsxTmuq/bSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "crc32-stream": "^6.0.0", + "is-stream": "^2.0.1", + "normalize-path": "^3.0.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/cpu-features": { + "version": "0.0.10", + "resolved": "https://registry.npmjs.org/cpu-features/-/cpu-features-0.0.10.tgz", + "integrity": "sha512-9IkYqtX3YHPCzoVg1Py+o9057a3i0fp7S530UWokCSaFVTc7CwXPRiOjRjBQQ18ZCNafx78YfnG+HALxtVmOGA==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "dependencies": { + "buildcheck": "~0.0.6", + "nan": "^2.19.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/crc32-stream": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/crc32-stream/-/crc32-stream-6.0.0.tgz", + "integrity": "sha512-piICUB6ei4IlTv1+653yq5+KoqfBYmj9bw6LqXoOneTMDXk5nM1qt12mFW1caG3LlJXEKW1Bp0WggEmIfQB34g==", + "dev": true, + "license": "MIT", + "dependencies": { + "crc-32": "^1.2.0", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "dev": true, + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/docker-compose": { + "version": "0.24.8", + "resolved": "https://registry.npmjs.org/docker-compose/-/docker-compose-0.24.8.tgz", + "integrity": "sha512-plizRs/Vf15H+GCVxq2EUvyPK7ei9b/cVesHvjnX4xaXjM9spHe2Ytq0BitndFgvTJ3E3NljPNUEl7BAN43iZw==", + "dev": true, + "license": "MIT", + "dependencies": { + "yaml": "^2.2.2" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/docker-modem": { + "version": "5.0.7", + "resolved": "https://registry.npmjs.org/docker-modem/-/docker-modem-5.0.7.tgz", + "integrity": "sha512-XJgGhoR/CLpqshm4d3L7rzH6t8NgDFUIIpztYlLHIApeJjMZKYJMz2zxPsYxnejq5h3ELYSw/RBsi3t5h7gNTA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "debug": "^4.1.1", + "readable-stream": "^3.5.0", + "split-ca": "^1.0.1", + "ssh2": "^1.15.0" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/docker-modem/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/dockerode": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/dockerode/-/dockerode-4.0.10.tgz", + "integrity": "sha512-8L/P9JynLBiG7/coiA4FlQXegHltRqS0a+KqI44P1zgQh8QLHTg7FKOwhkBgSJwZTeHsq30WRoVFLuwkfK0YFg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@balena/dockerignore": "^1.0.2", + "@grpc/grpc-js": "^1.11.1", + "@grpc/proto-loader": "^0.7.13", + "docker-modem": "^5.0.7", + "protobufjs": "^7.3.2", + "tar-fs": "^2.1.4", + "uuid": "^10.0.0" + }, + "engines": { + "node": ">= 8.0" + } + }, + "node_modules/dockerode/node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "license": "MIT", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/dockerode/node_modules/tar-fs": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/dockerode/node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, + "node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/escalade": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", + "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/event-target-shim": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", + "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, + "node_modules/events": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.x" + } + }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true, + "license": "MIT" + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "dev": true, + "license": "ISC", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-port": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-7.2.0.tgz", + "integrity": "sha512-afP4W205ONCuMoPBqcR6PSXnzX35KTcJygfJfcp+QY+uwm3p20p1YczWXhlICIzGMCxYBQcySEcOgsJcrkyobg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/glob": { + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", + "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", + "dev": true, + "license": "ISC", + "dependencies": { + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", + "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "license": "ISC" + }, + "node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/lazystream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lazystream/-/lazystream-1.0.1.tgz", + "integrity": "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "^2.0.5" + }, + "engines": { + "node": ">= 0.6.3" + } + }, + "node_modules/lazystream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "license": "MIT", + "dependencies": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "node_modules/lazystream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "license": "MIT" + }, + "node_modules/lazystream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/lodash": { + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/minimatch": { + "version": "9.0.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.9.tgz", + "integrity": "sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.2" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "license": "MIT", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true, + "license": "MIT" + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" + }, + "node_modules/nan": { + "version": "2.26.2", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.26.2.tgz", + "integrity": "sha512-0tTvBTYkt3tdGw22nrAy50x7gpbGCCFH3AFcyS5WiUu7Eu4vWlri1woE6qHBSfy11vksDqkiwjOnlR7WV8G1Hw==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "license": "ISC", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/path-scurry": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", + "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^10.2.0", + "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + }, + "engines": { + "node": ">=16 || 14 >=14.18" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/process": { + "version": "0.11.10", + "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", + "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6.0" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "license": "MIT" + }, + "node_modules/proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, + "node_modules/proper-lockfile/node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/properties-reader": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/properties-reader/-/properties-reader-2.3.0.tgz", + "integrity": "sha512-z597WicA7nDZxK12kZqHr2TcvwNU1GCfA5UwfDY/HDp3hXPoPlb5rlEx9bwGTiJnc0OqbBTkU975jDToth8Gxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mkdirp": "^1.0.4" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "type": "github", + "url": "https://github.com/steveukx/properties?sponsor=1" + } + }, + "node_modules/protobufjs": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", + "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", + "dev": true, + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dev": true, + "license": "MIT", + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/readable-stream": { + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.7.0.tgz", + "integrity": "sha512-oIGGmcpTLwPga8Bn6/Z75SVaH1z5dUut2ibSyAMVhmUggWpmDn2dapB0n7f8nwaSiRtepAsfJyfXIO5DCVAODg==", + "dev": true, + "license": "MIT", + "dependencies": { + "abort-controller": "^3.0.0", + "buffer": "^6.0.3", + "events": "^3.3.0", + "process": "^0.11.10", + "string_decoder": "^1.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, + "node_modules/readdir-glob": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/readdir-glob/-/readdir-glob-1.1.3.tgz", + "integrity": "sha512-v05I2k7xN8zXvPD9N+z/uhXPaj0sUFCe2rcWZIpBsqxfP7xXFQ0tipAd/wjj1YxWyWtUS5IDJpOG82JKt2EAVA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.1.0" + } + }, + "node_modules/readdir-glob/node_modules/minimatch": { + "version": "5.1.9", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", + "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true, + "license": "MIT" + }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/split-ca": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split-ca/-/split-ca-1.0.1.tgz", + "integrity": "sha512-Q5thBSxp5t8WPTTJQS59LrGqOZqOsrhDGDVm8azCqIBjSBd7nd9o2PM+mDulQQkh8h//4U6hFZnc/mul8t5pWQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/ssh-remote-port-forward": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/ssh-remote-port-forward/-/ssh-remote-port-forward-1.0.4.tgz", + "integrity": "sha512-x0LV1eVDwjf1gmG7TTnfqIzf+3VPRz7vrNIjX6oYLbeCrf/PeVY6hkT68Mg+q02qXxQhrLjB0jfgvhevoCRmLQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/ssh2": "^0.5.48", + "ssh2": "^1.4.0" + } + }, + "node_modules/ssh-remote-port-forward/node_modules/@types/ssh2": { + "version": "0.5.52", + "resolved": "https://registry.npmjs.org/@types/ssh2/-/ssh2-0.5.52.tgz", + "integrity": "sha512-lbLLlXxdCZOSJMCInKH2+9V/77ET2J6NPQHpFI0kda61Dd1KglJs+fPQBchizmzYSOJBgdTajhPqBO1xxLywvg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*", + "@types/ssh2-streams": "*" + } + }, + "node_modules/ssh2": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/ssh2/-/ssh2-1.17.0.tgz", + "integrity": "sha512-wPldCk3asibAjQ/kziWQQt1Wh3PgDFpC0XpwclzKcdT1vql6KeYxf5LIt4nlFkUeR8WuphYMKqUA56X4rjbfgQ==", + "dev": true, + "hasInstallScript": true, + "dependencies": { + "asn1": "^0.2.6", + "bcrypt-pbkdf": "^1.0.2" + }, + "engines": { + "node": ">=10.16.0" + }, + "optionalDependencies": { + "cpu-features": "~0.0.10", + "nan": "^2.23.0" + } + }, + "node_modules/streamx": { + "version": "2.25.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.25.0.tgz", + "integrity": "sha512-0nQuG6jf1w+wddNEEXCF4nTg3LtufWINB5eFEN+5TNZW7KWJp6x87+JFL43vaAUPyCfH1wID+mNVyW6OHtFamg==", + "dev": true, + "license": "MIT", + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "license": "MIT", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/tar-fs": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.2.tgz", + "integrity": "sha512-QGxxTxxyleAdyM3kpFs14ymbYmNFrfY+pHj7Z8FgtbZ7w2//VAgLMac7sT6nRpIHjppXO2AwwEOg0bPFVRcmXw==", + "dev": true, + "license": "MIT", + "dependencies": { + "pump": "^3.0.0", + "tar-stream": "^3.1.5" + }, + "optionalDependencies": { + "bare-fs": "^4.0.1", + "bare-path": "^3.0.0" + } + }, + "node_modules/tar-stream": { + "version": "3.1.8", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-3.1.8.tgz", + "integrity": "sha512-U6QpVRyCGHva435KoNWy9PRoi2IFYCgtEhq9nmrPPpbRacPs9IH4aJ3gbrFC8dPcXvdSZ4XXfXT5Fshbp2MtlQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "b4a": "^1.6.4", + "bare-fs": "^4.5.5", + "fast-fifo": "^1.2.0", + "streamx": "^2.15.0" + } + }, + "node_modules/teex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", + "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "streamx": "^2.12.5" + } + }, + "node_modules/testcontainers": { + "version": "10.28.0", + "resolved": "https://registry.npmjs.org/testcontainers/-/testcontainers-10.28.0.tgz", + "integrity": "sha512-1fKrRRCsgAQNkarjHCMKzBKXSJFmzNTiTbhb5E/j5hflRXChEtHvkefjaHlgkNUjfw92/Dq8LTgwQn6RDBFbMg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@balena/dockerignore": "^1.0.2", + "@types/dockerode": "^3.3.35", + "archiver": "^7.0.1", + "async-lock": "^1.4.1", + "byline": "^5.0.0", + "debug": "^4.3.5", + "docker-compose": "^0.24.8", + "dockerode": "^4.0.5", + "get-port": "^7.1.0", + "proper-lockfile": "^4.1.2", + "properties-reader": "^2.3.0", + "ssh-remote-port-forward": "^1.0.4", + "tar-fs": "^3.0.7", + "tmp": "^0.2.3", + "undici": "^5.29.0" + } + }, + "node_modules/text-decoder": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", + "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/tmp": { + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha512-KXXFFdAbFXY4geFIwoyNK+f5Z1b7swfXABfL7HXCmoIWMKU3dmS26672A4EeQtDzLKy7SXmfBu51JolvEKwtGA==", + "dev": true, + "license": "Unlicense" + }, + "node_modules/undici": { + "version": "5.29.0", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.29.0.tgz", + "integrity": "sha512-raqeBD6NQK4SkWhQzeYKd1KmIG6dllBOTt55Rmkt4HtI9mwdWtJljnrXjAFUBLTSN67HWrOIZ3EPF4kjUw80Bg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/undici-types": { + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.18.2.tgz", + "integrity": "sha512-AsuCzffGHJybSaRrmr5eHr81mwJU3kjw6M+uprWvCXiNeN9SOGwQ3Jn8jb8m3Z6izVgknn1R0FTCEAP2QrLY/w==", + "dev": true, + "license": "MIT" + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "license": "MIT" + }, + "node_modules/uuid": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-10.0.0.tgz", + "integrity": "sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==", + "dev": true, + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "dev": true, + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "license": "ISC" + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=10" + } + }, + "node_modules/yaml": { + "version": "2.8.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.8.3.tgz", + "integrity": "sha512-AvbaCLOO2Otw/lW5bmh9d/WEdcDFdQp2Z2ZUH3pX9U2ihyUY0nvLv7J6TrWowklRGPYbB/IuIMfYgxaCPg5Bpg==", + "dev": true, + "license": "ISC", + "bin": { + "yaml": "bin.mjs" + }, + "engines": { + "node": ">= 14.6" + }, + "funding": { + "url": "https://github.com/sponsors/eemeli" + } + }, + "node_modules/yargs": { + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", + "dev": true, + "license": "MIT", + "dependencies": { + "cliui": "^8.0.1", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.3", + "y18n": "^5.0.5", + "yargs-parser": "^21.1.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs-parser": { + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=12" + } + }, + "node_modules/yargs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/zip-stream": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/zip-stream/-/zip-stream-6.0.1.tgz", + "integrity": "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "archiver-utils": "^5.0.0", + "compress-commons": "^6.0.2", + "readable-stream": "^4.0.0" + }, + "engines": { + "node": ">= 14" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..0c01022 --- /dev/null +++ b/package.json @@ -0,0 +1,11 @@ +{ + "name": "generate-phpdocs-with-evert-phpdoc-md-action", + "private": true, + "type": "module", + "scripts": { + "test": "node --test tests/*.test.js" + }, + "devDependencies": { + "testcontainers": "^10.20.0" + } +} diff --git a/tests/01-realpath.bats b/tests/01-realpath.bats deleted file mode 100644 index 54260f0..0000000 --- a/tests/01-realpath.bats +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env bats - -setup() { - export TMP_DATA_PATH=$(mktemp -d) - mkdir -p "$TMP_DATA_PATH/test" -} - -tear() { - rm -rf "$TMP_DATA_PATH" || true -} - -@test "realpath.php returns a directory" { - RET=$($BATS_TEST_DIRNAME/../bin/realpath.php "$TMP_DATA_PATH/test/../") - [ -d "$RET" ] -} - -@test "realpath.php returns correct realpath" { - RET1=$($BATS_TEST_DIRNAME/../bin/realpath.php "$TMP_DATA_PATH/test/../") - RET2=$(realpath "$TMP_DATA_PATH/test/../") - [ "$RET1" == "$RET2" ] -} \ No newline at end of file diff --git a/tests/02-generate-env.bats b/tests/02-generate-env.bats deleted file mode 100644 index 052104e..0000000 --- a/tests/02-generate-env.bats +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bats - -setup() { - export TMP_TEST_PATH_1=$(mktemp -d) - export TMP_TEST_PATH_2=$(mktemp -d) -} - -tear() { - rm -rf "$TMP_TEST_PATH_1" || true - rm -rf "$TMP_TEST_PATH_2" || true -} - -@test "GENERATOR_TMP_FILES_PATH var exists" { - eval $("$BATS_TEST_DIRNAME/../bin/generate-env.sh" "$TMP_TEST_PATH_1" "$TMP_TEST_PATH_2") - [ ! -z "$GENERATOR_TMP_FILES_PATH" ]; -} - -@test "GENERATOR_DOCS_PATH var exists" { - eval $("$BATS_TEST_DIRNAME/../bin/generate-env.sh" "$TMP_TEST_PATH_1" "$TMP_TEST_PATH_2") - [ ! -z "$GENERATOR_DOCS_PATH" ]; -} - -@test "ACTION_BIN_PATH var exists" { - eval $("$BATS_TEST_DIRNAME/../bin/generate-env.sh" "$TMP_TEST_PATH_1" "$TMP_TEST_PATH_2") - [ ! -z "$ACTION_BIN_PATH" ]; -} - -@test "ACTION_BIN_PATH is correct" { - eval $("$BATS_TEST_DIRNAME/../bin/generate-env.sh" "$TMP_TEST_PATH_1" "$TMP_TEST_PATH_2") - NORMALIZED_PATH_1=$(realpath "$ACTION_BIN_PATH") - NORMALIZED_PATH_2=$(realpath "$BATS_TEST_DIRNAME/../bin") - - [ "$NORMALIZED_PATH_1" == "$NORMALIZED_PATH_2" ]; -} \ No newline at end of file diff --git a/tests/03-create-paths.bats b/tests/03-create-paths.bats deleted file mode 100644 index 2efa25b..0000000 --- a/tests/03-create-paths.bats +++ /dev/null @@ -1,21 +0,0 @@ -#!/usr/bin/env bats - -setup_file() { - export TMP_DATA_PATH=$(mktemp -d) - export GENERATOR_DOCS_PATH=$TMP_DATA_PATH/docs - export GENERATOR_TMP_FILES_PATH=$TMP_DATA_PATH/files -} - -tear_file() { - rm -rf "$TMP_DATA_PATH" || true -} - -@test "GENERATOR_DOCS_PATH exists" { - "$BATS_TEST_DIRNAME/../bin/create-paths.sh" - [ -d "$GENERATOR_DOCS_PATH" ]; -} - -@test "GENERATOR_TMP_FILES_PATH exists" { - "$BATS_TEST_DIRNAME/../bin/create-paths.sh" - [ -d "$GENERATOR_TMP_FILES_PATH" ]; -} \ No newline at end of file diff --git a/tests/04-phpdocs.bats b/tests/04-phpdocs.bats deleted file mode 100644 index affe7b7..0000000 --- a/tests/04-phpdocs.bats +++ /dev/null @@ -1,41 +0,0 @@ -#!/usr/bin/env bats - -setup() { - export TMP_TEST_PATH=$(mktemp -d) - export GENERATOR_TMP_FILES_PATH=$(mktemp -d) - export IGNORED_FILES="" - export PHPDOC_TAG=latest - - pushd $TMP_TEST_PATH - git clone --quiet https://github.com/imponeer/toarray-interface.git . - git config --local advice.detachedHead false - git checkout 1.0.0 - popd -} - -tear() { - rm -rf "$TMP_TEST_PATH" || true - rm -rf "$GENERATOR_TMP_FILES_PATH" || true -} - -@test "docker is available" { - command -v docker -} - -@test "runs correctly" { - EXIT_CODE=0 - pushd $TMP_TEST_PATH - "$BATS_TEST_DIRNAME/../bin/phpdoc.sh" "$IGNORED_FILES" "$PHPDOC_TAG" - EXIT_CODE=$? - popd - - [ "$EXIT_CODE" == "0" ] -} - -@test "produce some results" { - pushd $TMP_TEST_PATH - "$BATS_TEST_DIRNAME/../bin/phpdoc.sh" "$IGNORED_FILES" "$PHPDOC_TAG" - popd - - [ -s "$GENERATOR_TMP_FILES_PATH" ] -} \ No newline at end of file diff --git a/tests/05-get-composer-dependencies.bats b/tests/05-get-composer-dependencies.bats deleted file mode 100644 index 76071b9..0000000 --- a/tests/05-get-composer-dependencies.bats +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env bats - -@test "composer.json exists" { - [ -f "$BATS_TEST_DIRNAME/../composer.json" ] -} - -@test "get-composer-dependencies.php runs (without args)" { - $BATS_TEST_DIRNAME/../bin/get-composer-dependencies.php -} - -@test "get-composer-dependencies.php returns output (without args)" { - RET=$($BATS_TEST_DIRNAME/../bin/get-composer-dependencies.php) - [ ! -z "$RET" ] -} - -@test "get-composer-dependencies.php runs (with versions)" { - $BATS_TEST_DIRNAME/../bin/get-composer-dependencies.php 1 -} - -@test "get-composer-dependencies.php returns output (with versions)" { - RET=$($BATS_TEST_DIRNAME/../bin/get-composer-dependencies.php 1) - [ ! -z "$RET" ] -} - -@test "get-composer-dependencies.php runs (without versions)" { - $BATS_TEST_DIRNAME/../bin/get-composer-dependencies.php 0 -} - -@test "get-composer-dependencies.php returns output (without versions)" { - RET=$($BATS_TEST_DIRNAME/../bin/get-composer-dependencies.php 0) - [ ! -z "$RET" ] -} \ No newline at end of file diff --git a/tests/06-add-composer-packages.bats b/tests/06-add-composer-packages.bats deleted file mode 100644 index fc82ca3..0000000 --- a/tests/06-add-composer-packages.bats +++ /dev/null @@ -1,27 +0,0 @@ -#!/usr/bin/env bats - -setup_file() { - export COMPOSER_GLOBAL_JSON_PATH=$(composer config data-dir)/composer.json - export BACKUP_PATH=$(mktemp -d) - export BACKUP_COMPOSER=$BACKUP_PATH/composer.json - - if [ -f "$COMPOSER_GLOBAL_JSON_PATH" ]; then - cp "$COMPOSER_GLOBAL_JSON_PATH" "$BACKUP_COMPOSER" - fi; -} - -tear_file() { - rm -rf "$COMPOSER_GLOBAL_JSON_PATH" - - if [ -f "$BACKUP_COMPOSER" ]; then - cp "$BACKUP_COMPOSER" "$COMPOSER_GLOBAL_JSON_PATH" - fi; - - rm -rf "$BACKUP_PATH" || true - - composer global install -q || true -} - -@test "Runs correctly" { - $BATS_TEST_DIRNAME/../bin/add-composer-packages.sh -} \ No newline at end of file diff --git a/tests/07-generate-docs.bats b/tests/07-generate-docs.bats deleted file mode 100644 index eadc1aa..0000000 --- a/tests/07-generate-docs.bats +++ /dev/null @@ -1,42 +0,0 @@ -#!/usr/bin/env bats - -setup_file() { - export COMPOSER_GLOBAL_JSON_PATH=$(composer config data-dir)/composer.json - export BACKUP_PATH=$(mktemp -d) - export BACKUP_COMPOSER=$BACKUP_PATH/composer.json - export TMP_TEST_PATH=$(mktemp -d) - export GENERATOR_TMP_FILES_PATH=$(mktemp -d) - export GENERATOR_DOCS_PATH=$(mktemp -d) - export IGNORED_FILES="" - export PHPDOC_TAG=latest - - if [ -f "$COMPOSER_GLOBAL_JSON_PATH" ]; then - cp "$COMPOSER_GLOBAL_JSON_PATH" "$BACKUP_COMPOSER" - fi; - - pushd $TMP_TEST_PATH - "$BATS_TEST_DIRNAME/../bin/phpdoc.sh" "$IGNORED_FILES" "$PHPDOC_TAG" - popd - - $BATS_TEST_DIRNAME/../bin/add-composer-packages.sh -} - -tear_file() { - rm -rf "$COMPOSER_GLOBAL_JSON_PATH" - - if [ -f "$BACKUP_COMPOSER" ]; then - cp "$BACKUP_COMPOSER" "$COMPOSER_GLOBAL_JSON_PATH" - fi; - - rm -rf "$BACKUP_PATH" || true - - composer global install -q || true - - rm -rf "$TMP_TEST_PATH" || true - rm -rf "$GENERATOR_TMP_FILES_PATH" || true - rm -rf "$GENERATOR_DOCS_PATH" || true -} - -@test "Runs correctly" { - $BATS_TEST_DIRNAME/../bin/generate-docs.sh -} \ No newline at end of file diff --git a/tests/08-remove-composer-packages.bats b/tests/08-remove-composer-packages.bats deleted file mode 100644 index 5a4465a..0000000 --- a/tests/08-remove-composer-packages.bats +++ /dev/null @@ -1,29 +0,0 @@ -#!/usr/bin/env bats - -setup_file() { - export COMPOSER_GLOBAL_JSON_PATH=$(composer config data-dir)/composer.json - export BACKUP_PATH=$(mktemp -d) - export BACKUP_COMPOSER=$BACKUP_PATH/composer.json - - if [ -f "$COMPOSER_GLOBAL_JSON_PATH" ]; then - cp "$COMPOSER_GLOBAL_JSON_PATH" "$BACKUP_COMPOSER" - fi; - - $BATS_TEST_DIRNAME/../bin/add-composer-packages.sh -} - -tear_file() { - rm -rf "$COMPOSER_GLOBAL_JSON_PATH" - - if [ -f "$BACKUP_COMPOSER" ]; then - cp "$BACKUP_COMPOSER" "$COMPOSER_GLOBAL_JSON_PATH" - fi; - - rm -rf "$BACKUP_PATH" || true - - composer global install -q || true -} - -@test "Runs correctly" { - $BATS_TEST_DIRNAME/../bin/remove-composer-dependencies.sh -} \ No newline at end of file diff --git a/tests/09-remove-tmp-data.bats b/tests/09-remove-tmp-data.bats deleted file mode 100644 index 39b2c39..0000000 --- a/tests/09-remove-tmp-data.bats +++ /dev/null @@ -1,13 +0,0 @@ -setup() { - export GENERATOR_TMP_FILES_PATH=$(mktemp -d) -} - -tear() { - rm -rf "$GENERATOR_TMP_FILES_PATH" || true -} - -@test "GENERATOR_TMP_FILES_PATH not exists" { - "$BATS_TEST_DIRNAME/../bin/remove-tmp-data.sh" - - [ ! -d "$GENERATOR_TMP_FILES_PATH" ]; -} \ No newline at end of file diff --git a/tests/action-container.test.js b/tests/action-container.test.js new file mode 100644 index 0000000..6f14d9a --- /dev/null +++ b/tests/action-container.test.js @@ -0,0 +1,93 @@ +import assert from "node:assert/strict"; +import { randomUUID } from "node:crypto"; +import { cp, mkdtemp, readFile, readdir, rm, stat } from "node:fs/promises"; +import { existsSync } from "node:fs"; +import os from "node:os"; +import path from "node:path"; +import test from "node:test"; +import { fileURLToPath } from "node:url"; + +import { GenericContainer, Wait } from "testcontainers"; + +const __filename = fileURLToPath(import.meta.url); +const __dirname = path.dirname(__filename); +const repoRoot = path.resolve(__dirname, ".."); +const fixtureProjectPath = path.join(__dirname, "fixtures", "example-project"); +const dockerDesktopPath = "C:\\Program Files\\Docker\\Docker\\resources\\bin"; + +if (process.platform === "win32" && existsSync(path.join(dockerDesktopPath, "docker.exe"))) { + process.env.PATH = `${dockerDesktopPath};${process.env.PATH}`; +} + +async function buildActionImage(phpBaseImage) { + return GenericContainer.fromDockerfile(repoRoot) + .withBuildArgs({ + PHP_BASE_IMAGE: phpBaseImage + }) + .build(); +} + +async function runAction(actionContainer, ignoredFiles, phpDocumentorVersion) { + const workspacePath = await mkdtemp(path.join(os.tmpdir(), "phpdoc-action-workspace-")); + + await cp(fixtureProjectPath, workspacePath, { recursive: true }); + + const docsOutputPath = path.join(workspacePath, "docs"); + const runId = randomUUID(); + + const container = await actionContainer + .withBindMounts([ + { + source: workspacePath, + target: "/github/workspace", + mode: "rw" + } + ]) + .withEnvironment({ + GITHUB_WORKSPACE: "/github/workspace", + GITHUB_RUN_ID: runId, + GITHUB_SHA: runId + }) + .withWorkingDir("/github/workspace") + .withWaitStrategy(Wait.forOneShotStartup()) + .withCommand(["docs", ignoredFiles, phpDocumentorVersion]) + .start(); + + return { + docsOutputPath, + workspacePath + }; +} + +const testMatrix = [ + { + phpBaseImage: "php:5.5.38-cli", + phpDocumentorVersion: "v2.8.5" + }, + { + phpBaseImage: "php:7.4-cli", + phpDocumentorVersion: "v2.8.5" + } +]; + +for (const scenario of testMatrix) { + test(`boots container and generates markdown (${scenario.phpBaseImage})`, async () => { + const actionContainer = await buildActionImage(scenario.phpBaseImage); + const { docsOutputPath, workspacePath } = await runAction(actionContainer, "", scenario.phpDocumentorVersion); + + try { + const indexStat = await stat(path.join(docsOutputPath, "ApiIndex.md")); + assert.equal(indexStat.isFile(), true); + + const docsFiles = await readdir(docsOutputPath); + const classDocFiles = docsFiles.filter((fileName) => fileName.endsWith(".md") && fileName !== "ApiIndex.md"); + assert.equal(classDocFiles.length > 0, true); + + const indexContents = await readFile(path.join(docsOutputPath, "ApiIndex.md"), "utf8"); + assert.match(indexContents, /ExampleClass/); + assert.match(indexContents, /IgnoredClass/); + } finally { + await rm(workspacePath, { force: true, recursive: true }); + } + }); +} diff --git a/tests/fixtures/example-project/src/ExampleClass.php b/tests/fixtures/example-project/src/ExampleClass.php new file mode 100644 index 0000000..aeafc7a --- /dev/null +++ b/tests/fixtures/example-project/src/ExampleClass.php @@ -0,0 +1,17 @@ + Date: Fri, 27 Mar 2026 02:28:53 +0200 Subject: [PATCH 02/24] Use explicit PHP image references for container build --- Dockerfile | 4 ++-- README.md | 2 +- tests/action-container.test.js | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Dockerfile b/Dockerfile index 6506a06..dcac6ff 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -ARG PHP_BASE_IMAGE=php:7.4-cli +ARG PHP_IMAGE=php:7.4-cli FROM php:8.3-cli-bookworm AS phpdocmd-builder @@ -27,7 +27,7 @@ RUN GIT_SSH_COMMAND="ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o --prefer-dist \ && rm -rf /tmp/composer -FROM ${PHP_BASE_IMAGE} +FROM ${PHP_IMAGE} COPY --from=phpdocmd-builder /opt/phpdoc-md /opt/phpdoc-md COPY bin/entrypoint.sh /usr/local/bin/entrypoint.sh diff --git a/README.md b/README.md index 2fe84a1..dbf3ff9 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ This action supports such arguments (used in `with` keyword): - Docker build clones `git@github.com:evert/phpdoc-md.git` directly and falls back to HTTPS clone when SSH credentials are not available. - phpDocumentor release artifacts are downloaded during action runtime and are not stored in this repository. -- Dockerfile supports selecting the PHP base image at build time: `docker build --build-arg PHP_BASE_IMAGE=php:7.4-cli .` +- Dockerfile supports selecting the PHP base image by explicit image reference: `docker build --build-arg PHP_IMAGE=php:7.4-cli .` - Tests are JavaScript integration tests based on [testcontainers-node](https://github.com/testcontainers/testcontainers-node). ## How to contribute? diff --git a/tests/action-container.test.js b/tests/action-container.test.js index 6f14d9a..0a63b6d 100644 --- a/tests/action-container.test.js +++ b/tests/action-container.test.js @@ -19,10 +19,10 @@ if (process.platform === "win32" && existsSync(path.join(dockerDesktopPath, "doc process.env.PATH = `${dockerDesktopPath};${process.env.PATH}`; } -async function buildActionImage(phpBaseImage) { +async function buildActionImage(phpImage) { return GenericContainer.fromDockerfile(repoRoot) .withBuildArgs({ - PHP_BASE_IMAGE: phpBaseImage + PHP_IMAGE: phpImage }) .build(); } @@ -61,18 +61,18 @@ async function runAction(actionContainer, ignoredFiles, phpDocumentorVersion) { const testMatrix = [ { - phpBaseImage: "php:5.5.38-cli", + phpImage: "php:5.5.38-cli", phpDocumentorVersion: "v2.8.5" }, { - phpBaseImage: "php:7.4-cli", + phpImage: "php:7.4-cli", phpDocumentorVersion: "v2.8.5" } ]; for (const scenario of testMatrix) { - test(`boots container and generates markdown (${scenario.phpBaseImage})`, async () => { - const actionContainer = await buildActionImage(scenario.phpBaseImage); + test(`boots container and generates markdown (${scenario.phpImage})`, async () => { + const actionContainer = await buildActionImage(scenario.phpImage); const { docsOutputPath, workspacePath } = await runAction(actionContainer, "", scenario.phpDocumentorVersion); try { From 63931f92213de22058f45cd9250a0d8422972799 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raimondas=20Rimkevi=C4=8Dius=20=28aka=20MekDrop=29?= Date: Fri, 27 Mar 2026 02:46:32 +0200 Subject: [PATCH 03/24] Switch container build arg to PHP_VERSION only --- Dockerfile | 4 ++-- README.md | 2 +- tests/action-container.test.js | 12 ++++++------ 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Dockerfile b/Dockerfile index dcac6ff..04029fc 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -ARG PHP_IMAGE=php:7.4-cli +ARG PHP_VERSION=7.4 FROM php:8.3-cli-bookworm AS phpdocmd-builder @@ -27,7 +27,7 @@ RUN GIT_SSH_COMMAND="ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o --prefer-dist \ && rm -rf /tmp/composer -FROM ${PHP_IMAGE} +FROM php:${PHP_VERSION}-cli COPY --from=phpdocmd-builder /opt/phpdoc-md /opt/phpdoc-md COPY bin/entrypoint.sh /usr/local/bin/entrypoint.sh diff --git a/README.md b/README.md index dbf3ff9..ae4028d 100644 --- a/README.md +++ b/README.md @@ -50,7 +50,7 @@ This action supports such arguments (used in `with` keyword): - Docker build clones `git@github.com:evert/phpdoc-md.git` directly and falls back to HTTPS clone when SSH credentials are not available. - phpDocumentor release artifacts are downloaded during action runtime and are not stored in this repository. -- Dockerfile supports selecting the PHP base image by explicit image reference: `docker build --build-arg PHP_IMAGE=php:7.4-cli .` +- Dockerfile supports selecting PHP by version: `docker build --build-arg PHP_VERSION=7.4 .` - Tests are JavaScript integration tests based on [testcontainers-node](https://github.com/testcontainers/testcontainers-node). ## How to contribute? diff --git a/tests/action-container.test.js b/tests/action-container.test.js index 0a63b6d..217e315 100644 --- a/tests/action-container.test.js +++ b/tests/action-container.test.js @@ -19,10 +19,10 @@ if (process.platform === "win32" && existsSync(path.join(dockerDesktopPath, "doc process.env.PATH = `${dockerDesktopPath};${process.env.PATH}`; } -async function buildActionImage(phpImage) { +async function buildActionImage(phpVersion) { return GenericContainer.fromDockerfile(repoRoot) .withBuildArgs({ - PHP_IMAGE: phpImage + PHP_VERSION: phpVersion }) .build(); } @@ -61,18 +61,18 @@ async function runAction(actionContainer, ignoredFiles, phpDocumentorVersion) { const testMatrix = [ { - phpImage: "php:5.5.38-cli", + phpVersion: "5.5.38", phpDocumentorVersion: "v2.8.5" }, { - phpImage: "php:7.4-cli", + phpVersion: "7.4", phpDocumentorVersion: "v2.8.5" } ]; for (const scenario of testMatrix) { - test(`boots container and generates markdown (${scenario.phpImage})`, async () => { - const actionContainer = await buildActionImage(scenario.phpImage); + test(`boots container and generates markdown (php:${scenario.phpVersion}-cli)`, async () => { + const actionContainer = await buildActionImage(scenario.phpVersion); const { docsOutputPath, workspacePath } = await runAction(actionContainer, "", scenario.phpDocumentorVersion); try { From 8bff6687eb45b60eab5ab2a6ff7f68d4ec8d3b72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raimondas=20Rimkevi=C4=8Dius=20=28aka=20MekDrop=29?= Date: Fri, 27 Mar 2026 02:48:58 +0200 Subject: [PATCH 04/24] Extract phpdoc-md setup into builder script --- Dockerfile | 17 +++-------------- bin/setup-phpdoc-md.sh | 26 ++++++++++++++++++++++++++ 2 files changed, 29 insertions(+), 14 deletions(-) create mode 100644 bin/setup-phpdoc-md.sh diff --git a/Dockerfile b/Dockerfile index 04029fc..869337b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,20 +12,9 @@ RUN apt-get update \ unzip \ && rm -rf /var/lib/apt/lists/* -RUN GIT_SSH_COMMAND="ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5" \ - git clone --depth 1 git@github.com:evert/phpdoc-md.git /opt/phpdoc-md \ - || git clone --depth 1 https://github.com/evert/phpdoc-md.git /opt/phpdoc-md \ - && php -r '$composer = json_decode(file_get_contents("/opt/phpdoc-md/composer.json"), true); unset($composer["require-dev"]); file_put_contents("/opt/phpdoc-md/composer.json", json_encode($composer, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . PHP_EOL);' \ - && COMPOSER_ALLOW_SUPERUSER=1 COMPOSER_HOME=/tmp/composer composer config --global platform.php 5.5.0 \ - && COMPOSER_ALLOW_SUPERUSER=1 COMPOSER_HOME=/tmp/composer composer install \ - --working-dir=/opt/phpdoc-md \ - --no-dev \ - --no-plugins \ - --no-scripts \ - --no-interaction \ - --no-progress \ - --prefer-dist \ - && rm -rf /tmp/composer +COPY bin/setup-phpdoc-md.sh /usr/local/bin/setup-phpdoc-md.sh +RUN chmod +x /usr/local/bin/setup-phpdoc-md.sh \ + && /usr/local/bin/setup-phpdoc-md.sh FROM php:${PHP_VERSION}-cli diff --git a/bin/setup-phpdoc-md.sh b/bin/setup-phpdoc-md.sh new file mode 100644 index 0000000..c52faf5 --- /dev/null +++ b/bin/setup-phpdoc-md.sh @@ -0,0 +1,26 @@ +#!/usr/bin/env bash + +set -euo pipefail + +TARGET_DIR=${1:-/opt/phpdoc-md} +COMPOSER_HOME_DIR=${2:-/tmp/composer} + +GIT_SSH_OPTIONS="ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5" + +if ! GIT_SSH_COMMAND="$GIT_SSH_OPTIONS" git clone --depth 1 git@github.com:evert/phpdoc-md.git "$TARGET_DIR"; then + git clone --depth 1 https://github.com/evert/phpdoc-md.git "$TARGET_DIR" +fi + +php -r '$composer = json_decode(file_get_contents($argv[1]), true); unset($composer["require-dev"]); file_put_contents($argv[1], json_encode($composer, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . PHP_EOL);' "$TARGET_DIR/composer.json" + +COMPOSER_ALLOW_SUPERUSER=1 COMPOSER_HOME="$COMPOSER_HOME_DIR" composer config --global platform.php 5.5.0 +COMPOSER_ALLOW_SUPERUSER=1 COMPOSER_HOME="$COMPOSER_HOME_DIR" composer install \ + --working-dir="$TARGET_DIR" \ + --no-dev \ + --no-plugins \ + --no-scripts \ + --no-interaction \ + --no-progress \ + --prefer-dist + +rm -rf "$COMPOSER_HOME_DIR" From 7dc7f31e45d59b6945f20e7ec2ddf9e28eeb4d6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raimondas=20Rimkevi=C4=8Dius=20=28aka=20MekDrop=29?= Date: Fri, 27 Mar 2026 02:50:37 +0200 Subject: [PATCH 05/24] Extract builder apt dependencies install into script --- Dockerfile | 10 +++------- bin/install-builder-deps.sh | 11 +++++++++++ 2 files changed, 14 insertions(+), 7 deletions(-) create mode 100644 bin/install-builder-deps.sh diff --git a/Dockerfile b/Dockerfile index 869337b..b344a05 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,13 +4,9 @@ FROM php:8.3-cli-bookworm AS phpdocmd-builder COPY --from=composer:2.2 /usr/bin/composer /usr/local/bin/composer -RUN apt-get update \ - && apt-get install -y --no-install-recommends \ - ca-certificates \ - git \ - openssh-client \ - unzip \ - && rm -rf /var/lib/apt/lists/* +COPY bin/install-builder-deps.sh /usr/local/bin/install-builder-deps.sh +RUN chmod +x /usr/local/bin/install-builder-deps.sh \ + && /usr/local/bin/install-builder-deps.sh COPY bin/setup-phpdoc-md.sh /usr/local/bin/setup-phpdoc-md.sh RUN chmod +x /usr/local/bin/setup-phpdoc-md.sh \ diff --git a/bin/install-builder-deps.sh b/bin/install-builder-deps.sh new file mode 100644 index 0000000..f85228f --- /dev/null +++ b/bin/install-builder-deps.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +set -euo pipefail + +apt-get update +apt-get install -y --no-install-recommends \ + ca-certificates \ + git \ + openssh-client \ + unzip +rm -rf /var/lib/apt/lists/* From d1e785dc8dc779c0245250d1fb9e830fd771dac1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raimondas=20Rimkevi=C4=8Dius=20=28aka=20MekDrop=29?= Date: Fri, 27 Mar 2026 02:55:14 +0200 Subject: [PATCH 06/24] Use COPY --chmod and docker build in integration tests --- Dockerfile | 13 ++++-------- tests/action-container.test.js | 38 ++++++++++++++++++++++++++-------- 2 files changed, 33 insertions(+), 18 deletions(-) diff --git a/Dockerfile b/Dockerfile index b344a05..8f002c6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -4,19 +4,14 @@ FROM php:8.3-cli-bookworm AS phpdocmd-builder COPY --from=composer:2.2 /usr/bin/composer /usr/local/bin/composer -COPY bin/install-builder-deps.sh /usr/local/bin/install-builder-deps.sh -RUN chmod +x /usr/local/bin/install-builder-deps.sh \ - && /usr/local/bin/install-builder-deps.sh +COPY --chmod=0755 bin/install-builder-deps.sh bin/setup-phpdoc-md.sh /usr/local/bin/ +RUN /usr/local/bin/install-builder-deps.sh -COPY bin/setup-phpdoc-md.sh /usr/local/bin/setup-phpdoc-md.sh -RUN chmod +x /usr/local/bin/setup-phpdoc-md.sh \ - && /usr/local/bin/setup-phpdoc-md.sh +RUN /usr/local/bin/setup-phpdoc-md.sh FROM php:${PHP_VERSION}-cli COPY --from=phpdocmd-builder /opt/phpdoc-md /opt/phpdoc-md -COPY bin/entrypoint.sh /usr/local/bin/entrypoint.sh - -RUN chmod +x /usr/local/bin/entrypoint.sh +COPY --chmod=0755 bin/entrypoint.sh /usr/local/bin/entrypoint.sh ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] diff --git a/tests/action-container.test.js b/tests/action-container.test.js index 217e315..619e5ea 100644 --- a/tests/action-container.test.js +++ b/tests/action-container.test.js @@ -1,4 +1,5 @@ import assert from "node:assert/strict"; +import { spawn } from "node:child_process"; import { randomUUID } from "node:crypto"; import { cp, mkdtemp, readFile, readdir, rm, stat } from "node:fs/promises"; import { existsSync } from "node:fs"; @@ -14,20 +15,39 @@ const __dirname = path.dirname(__filename); const repoRoot = path.resolve(__dirname, ".."); const fixtureProjectPath = path.join(__dirname, "fixtures", "example-project"); const dockerDesktopPath = "C:\\Program Files\\Docker\\Docker\\resources\\bin"; +const dockerExecutable = + process.platform === "win32" && existsSync(path.join(dockerDesktopPath, "docker.exe")) + ? path.join(dockerDesktopPath, "docker.exe") + : "docker"; if (process.platform === "win32" && existsSync(path.join(dockerDesktopPath, "docker.exe"))) { process.env.PATH = `${dockerDesktopPath};${process.env.PATH}`; } async function buildActionImage(phpVersion) { - return GenericContainer.fromDockerfile(repoRoot) - .withBuildArgs({ - PHP_VERSION: phpVersion - }) - .build(); + const imageTag = `local/phpdocs-action-test:${randomUUID()}`; + const args = ["build", "--build-arg", `PHP_VERSION=${phpVersion}`, "-t", imageTag, "."]; + + await new Promise((resolve, reject) => { + const childProcess = spawn(dockerExecutable, args, { + cwd: repoRoot, + stdio: "inherit" + }); + + childProcess.on("error", reject); + childProcess.on("exit", (code) => { + if (code === 0) { + resolve(); + } else { + reject(new Error(`docker build failed for PHP_VERSION=${phpVersion}`)); + } + }); + }); + + return imageTag; } -async function runAction(actionContainer, ignoredFiles, phpDocumentorVersion) { +async function runAction(actionImage, ignoredFiles, phpDocumentorVersion) { const workspacePath = await mkdtemp(path.join(os.tmpdir(), "phpdoc-action-workspace-")); await cp(fixtureProjectPath, workspacePath, { recursive: true }); @@ -35,7 +55,7 @@ async function runAction(actionContainer, ignoredFiles, phpDocumentorVersion) { const docsOutputPath = path.join(workspacePath, "docs"); const runId = randomUUID(); - const container = await actionContainer + await new GenericContainer(actionImage) .withBindMounts([ { source: workspacePath, @@ -72,8 +92,8 @@ const testMatrix = [ for (const scenario of testMatrix) { test(`boots container and generates markdown (php:${scenario.phpVersion}-cli)`, async () => { - const actionContainer = await buildActionImage(scenario.phpVersion); - const { docsOutputPath, workspacePath } = await runAction(actionContainer, "", scenario.phpDocumentorVersion); + const actionImage = await buildActionImage(scenario.phpVersion); + const { docsOutputPath, workspacePath } = await runAction(actionImage, "", scenario.phpDocumentorVersion); try { const indexStat = await stat(path.join(docsOutputPath, "ApiIndex.md")); From 27f07cc7d91e0b2f98ac41c1fe8542b588ddc884 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raimondas=20Rimkevi=C4=8Dius=20=28aka=20MekDrop=29?= Date: Fri, 27 Mar 2026 03:15:15 +0200 Subject: [PATCH 07/24] Remove hardcoded Docker path from integration tests --- tests/action-container.test.js | 15 ++++----------- 1 file changed, 4 insertions(+), 11 deletions(-) diff --git a/tests/action-container.test.js b/tests/action-container.test.js index 619e5ea..294d260 100644 --- a/tests/action-container.test.js +++ b/tests/action-container.test.js @@ -2,7 +2,6 @@ import assert from "node:assert/strict"; import { spawn } from "node:child_process"; import { randomUUID } from "node:crypto"; import { cp, mkdtemp, readFile, readdir, rm, stat } from "node:fs/promises"; -import { existsSync } from "node:fs"; import os from "node:os"; import path from "node:path"; import test from "node:test"; @@ -14,15 +13,7 @@ const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const repoRoot = path.resolve(__dirname, ".."); const fixtureProjectPath = path.join(__dirname, "fixtures", "example-project"); -const dockerDesktopPath = "C:\\Program Files\\Docker\\Docker\\resources\\bin"; -const dockerExecutable = - process.platform === "win32" && existsSync(path.join(dockerDesktopPath, "docker.exe")) - ? path.join(dockerDesktopPath, "docker.exe") - : "docker"; - -if (process.platform === "win32" && existsSync(path.join(dockerDesktopPath, "docker.exe"))) { - process.env.PATH = `${dockerDesktopPath};${process.env.PATH}`; -} +const dockerExecutable = process.env.DOCKER_BIN || "docker"; async function buildActionImage(phpVersion) { const imageTag = `local/phpdocs-action-test:${randomUUID()}`; @@ -34,7 +25,9 @@ async function buildActionImage(phpVersion) { stdio: "inherit" }); - childProcess.on("error", reject); + childProcess.on("error", (error) => { + reject(new Error(`Failed to execute '${dockerExecutable}'. Set DOCKER_BIN if Docker is not on PATH. ${error.message}`)); + }); childProcess.on("exit", (code) => { if (code === 0) { resolve(); From 38a99e16a1f8c3e51c9dd96f091866770c1a6871 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raimondas=20Rimkevi=C4=8Dius=20=28aka=20MekDrop=29?= Date: Fri, 27 Mar 2026 03:19:12 +0200 Subject: [PATCH 08/24] Move PHP version matrix to CI workflow --- .github/workflows/on-pull-request.yml | 12 +++++++ tests/action-container.test.js | 52 +++++++++++---------------- 2 files changed, 33 insertions(+), 31 deletions(-) diff --git a/.github/workflows/on-pull-request.yml b/.github/workflows/on-pull-request.yml index 5558f41..fda9be9 100644 --- a/.github/workflows/on-pull-request.yml +++ b/.github/workflows/on-pull-request.yml @@ -9,6 +9,16 @@ jobs: test: runs-on: ubuntu-latest + strategy: + matrix: + php_version: + - '5.3' + - '5.4' + - '7.0' + - '7.1' + - '7.2' + - '7.3' + - '7.4' steps: - name: Checkouting code... @@ -24,6 +34,8 @@ jobs: - name: Run integration tests run: npm test + env: + PHP_VERSION: ${{ matrix.php_version }} dependabot: needs: diff --git a/tests/action-container.test.js b/tests/action-container.test.js index 294d260..5310d64 100644 --- a/tests/action-container.test.js +++ b/tests/action-container.test.js @@ -72,35 +72,25 @@ async function runAction(actionImage, ignoredFiles, phpDocumentorVersion) { }; } -const testMatrix = [ - { - phpVersion: "5.5.38", - phpDocumentorVersion: "v2.8.5" - }, - { - phpVersion: "7.4", - phpDocumentorVersion: "v2.8.5" +const phpVersion = process.env.PHP_VERSION || "7.4"; +const phpDocumentorVersion = process.env.PHPDOC_VERSION || "v2.8.5"; + +test(`boots container and generates markdown (php:${phpVersion}-cli)`, async () => { + const actionImage = await buildActionImage(phpVersion); + const { docsOutputPath, workspacePath } = await runAction(actionImage, "", phpDocumentorVersion); + + try { + const indexStat = await stat(path.join(docsOutputPath, "ApiIndex.md")); + assert.equal(indexStat.isFile(), true); + + const docsFiles = await readdir(docsOutputPath); + const classDocFiles = docsFiles.filter((fileName) => fileName.endsWith(".md") && fileName !== "ApiIndex.md"); + assert.equal(classDocFiles.length > 0, true); + + const indexContents = await readFile(path.join(docsOutputPath, "ApiIndex.md"), "utf8"); + assert.match(indexContents, /ExampleClass/); + assert.match(indexContents, /IgnoredClass/); + } finally { + await rm(workspacePath, { force: true, recursive: true }); } -]; - -for (const scenario of testMatrix) { - test(`boots container and generates markdown (php:${scenario.phpVersion}-cli)`, async () => { - const actionImage = await buildActionImage(scenario.phpVersion); - const { docsOutputPath, workspacePath } = await runAction(actionImage, "", scenario.phpDocumentorVersion); - - try { - const indexStat = await stat(path.join(docsOutputPath, "ApiIndex.md")); - assert.equal(indexStat.isFile(), true); - - const docsFiles = await readdir(docsOutputPath); - const classDocFiles = docsFiles.filter((fileName) => fileName.endsWith(".md") && fileName !== "ApiIndex.md"); - assert.equal(classDocFiles.length > 0, true); - - const indexContents = await readFile(path.join(docsOutputPath, "ApiIndex.md"), "utf8"); - assert.match(indexContents, /ExampleClass/); - assert.match(indexContents, /IgnoredClass/); - } finally { - await rm(workspacePath, { force: true, recursive: true }); - } - }); -} +}); From e70f8f8e2b20fcbabab96017b7f5faad4a6782dc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raimondas=20Rimkevi=C4=8Dius=20=28aka=20MekDrop=29?= Date: Fri, 27 Mar 2026 03:29:51 +0200 Subject: [PATCH 09/24] Enable legacy schema image pulls in CI matrix --- .github/workflows/on-pull-request.yml | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/.github/workflows/on-pull-request.yml b/.github/workflows/on-pull-request.yml index fda9be9..24f6ff0 100644 --- a/.github/workflows/on-pull-request.yml +++ b/.github/workflows/on-pull-request.yml @@ -10,6 +10,7 @@ jobs: test: runs-on: ubuntu-latest strategy: + fail-fast: false matrix: php_version: - '5.3' @@ -29,6 +30,17 @@ jobs: with: node-version: 22 + - name: Enable legacy Docker schema v1 pulls for old PHP tags + run: | + sudo mkdir -p /etc/systemd/system/docker.service.d + cat <<'EOF' | sudo tee /etc/systemd/system/docker.service.d/10-enable-schema1.conf + [Service] + Environment=CONTAINERD_ENABLE_DEPRECATED_PULL_SCHEMA_1_IMAGE=1 + EOF + sudo systemctl daemon-reload + sudo systemctl restart docker + docker version + - name: Install dependencies run: npm ci From 057967dbb0f072f7622a06deb8e88e8a66360ceb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raimondas=20Rimkevi=C4=8Dius=20=28aka=20MekDrop=29?= Date: Fri, 27 Mar 2026 03:33:44 +0200 Subject: [PATCH 10/24] Fix legacy PHP matrix runtime and test cleanup --- bin/setup-phpdoc-md.sh | 2 +- tests/action-container.test.js | 13 ++++++++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/bin/setup-phpdoc-md.sh b/bin/setup-phpdoc-md.sh index c52faf5..5b95292 100644 --- a/bin/setup-phpdoc-md.sh +++ b/bin/setup-phpdoc-md.sh @@ -13,7 +13,7 @@ fi php -r '$composer = json_decode(file_get_contents($argv[1]), true); unset($composer["require-dev"]); file_put_contents($argv[1], json_encode($composer, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . PHP_EOL);' "$TARGET_DIR/composer.json" -COMPOSER_ALLOW_SUPERUSER=1 COMPOSER_HOME="$COMPOSER_HOME_DIR" composer config --global platform.php 5.5.0 +COMPOSER_ALLOW_SUPERUSER=1 COMPOSER_HOME="$COMPOSER_HOME_DIR" composer config --global platform.php 5.3.3 COMPOSER_ALLOW_SUPERUSER=1 COMPOSER_HOME="$COMPOSER_HOME_DIR" composer install \ --working-dir="$TARGET_DIR" \ --no-dev \ diff --git a/tests/action-container.test.js b/tests/action-container.test.js index 5310d64..6e9dabd 100644 --- a/tests/action-container.test.js +++ b/tests/action-container.test.js @@ -14,6 +14,8 @@ const __dirname = path.dirname(__filename); const repoRoot = path.resolve(__dirname, ".."); const fixtureProjectPath = path.join(__dirname, "fixtures", "example-project"); const dockerExecutable = process.env.DOCKER_BIN || "docker"; +const hostUid = typeof process.getuid === "function" ? process.getuid() : null; +const hostGid = typeof process.getgid === "function" ? process.getgid() : null; async function buildActionImage(phpVersion) { const imageTag = `local/phpdocs-action-test:${randomUUID()}`; @@ -48,7 +50,7 @@ async function runAction(actionImage, ignoredFiles, phpDocumentorVersion) { const docsOutputPath = path.join(workspacePath, "docs"); const runId = randomUUID(); - await new GenericContainer(actionImage) + const container = new GenericContainer(actionImage) .withBindMounts([ { source: workspacePath, @@ -63,8 +65,13 @@ async function runAction(actionImage, ignoredFiles, phpDocumentorVersion) { }) .withWorkingDir("/github/workspace") .withWaitStrategy(Wait.forOneShotStartup()) - .withCommand(["docs", ignoredFiles, phpDocumentorVersion]) - .start(); + .withCommand(["docs", ignoredFiles, phpDocumentorVersion]); + + if (hostUid !== null && hostGid !== null) { + container.withUser(`${hostUid}:${hostGid}`); + } + + await container.start(); return { docsOutputPath, From d299341410bcba8d891c3f40e27e7cc94c2c18b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raimondas=20Rimkevi=C4=8Dius=20=28aka=20MekDrop=29?= Date: Fri, 27 Mar 2026 03:35:44 +0200 Subject: [PATCH 11/24] Include container logs on startup failures in tests --- tests/action-container.test.js | 43 +++++++++++++++++++++++++++++++++- 1 file changed, 42 insertions(+), 1 deletion(-) diff --git a/tests/action-container.test.js b/tests/action-container.test.js index 6e9dabd..f407d70 100644 --- a/tests/action-container.test.js +++ b/tests/action-container.test.js @@ -17,6 +17,31 @@ const dockerExecutable = process.env.DOCKER_BIN || "docker"; const hostUid = typeof process.getuid === "function" ? process.getuid() : null; const hostGid = typeof process.getgid === "function" ? process.getgid() : null; +async function runDockerCommand(args) { + return await new Promise((resolve, reject) => { + let output = ""; + const childProcess = spawn(dockerExecutable, args, { + cwd: repoRoot, + stdio: ["ignore", "pipe", "pipe"] + }); + + childProcess.stdout.on("data", (chunk) => { + output += chunk.toString(); + }); + childProcess.stderr.on("data", (chunk) => { + output += chunk.toString(); + }); + childProcess.on("error", reject); + childProcess.on("exit", (code) => { + if (code === 0) { + resolve(output.trim()); + } else { + reject(new Error(output.trim() || `docker ${args.join(" ")} failed with exit code ${code ?? "unknown"}`)); + } + }); + }); +} + async function buildActionImage(phpVersion) { const imageTag = `local/phpdocs-action-test:${randomUUID()}`; const args = ["build", "--build-arg", `PHP_VERSION=${phpVersion}`, "-t", imageTag, "."]; @@ -71,7 +96,23 @@ async function runAction(actionImage, ignoredFiles, phpDocumentorVersion) { container.withUser(`${hostUid}:${hostGid}`); } - await container.start(); + try { + await container.start(); + } catch (error) { + const containerId = /Container failed to start for ([a-f0-9]+)/i.exec(String(error?.message ?? ""))?.[1]; + if (!containerId) { + throw error; + } + + let containerLogs = ""; + try { + containerLogs = await runDockerCommand(["logs", containerId]); + } catch (logsError) { + containerLogs = `Unable to read container logs: ${logsError.message}`; + } + + throw new Error(`Container failed to start for ${containerId}\n${containerLogs}`); + } return { docsOutputPath, From 1a650251b57f8ef002f02fb3542834a65221e903 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raimondas=20Rimkevi=C4=8Dius=20=28aka=20MekDrop=29?= Date: Fri, 27 Mar 2026 03:38:13 +0200 Subject: [PATCH 12/24] Keep failed test container for log collection --- tests/action-container.test.js | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/action-container.test.js b/tests/action-container.test.js index f407d70..99e77b8 100644 --- a/tests/action-container.test.js +++ b/tests/action-container.test.js @@ -76,6 +76,7 @@ async function runAction(actionImage, ignoredFiles, phpDocumentorVersion) { const runId = randomUUID(); const container = new GenericContainer(actionImage) + .withAutoRemove(false) .withBindMounts([ { source: workspacePath, @@ -97,7 +98,8 @@ async function runAction(actionImage, ignoredFiles, phpDocumentorVersion) { } try { - await container.start(); + const startedContainer = await container.start(); + await startedContainer.stop(); } catch (error) { const containerId = /Container failed to start for ([a-f0-9]+)/i.exec(String(error?.message ?? ""))?.[1]; if (!containerId) { From 0aaf4bca96f266056ebfbd5599ac0acbe0a1108a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raimondas=20Rimkevi=C4=8Dius=20=28aka=20MekDrop=29?= Date: Fri, 27 Mar 2026 03:41:02 +0200 Subject: [PATCH 13/24] Cache phpDocumentor archive at build time --- Dockerfile | 6 +++++- bin/entrypoint.sh | 17 ++++++++++++----- bin/setup-phpdocumentor-cache.sh | 13 +++++++++++++ 3 files changed, 30 insertions(+), 6 deletions(-) create mode 100644 bin/setup-phpdocumentor-cache.sh diff --git a/Dockerfile b/Dockerfile index 8f002c6..f4ca658 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,17 +1,21 @@ ARG PHP_VERSION=7.4 +ARG PHPDOC_VERSION=v2.8.5 FROM php:8.3-cli-bookworm AS phpdocmd-builder +ARG PHPDOC_VERSION COPY --from=composer:2.2 /usr/bin/composer /usr/local/bin/composer -COPY --chmod=0755 bin/install-builder-deps.sh bin/setup-phpdoc-md.sh /usr/local/bin/ +COPY --chmod=0755 bin/install-builder-deps.sh bin/setup-phpdoc-md.sh bin/setup-phpdocumentor-cache.sh /usr/local/bin/ RUN /usr/local/bin/install-builder-deps.sh RUN /usr/local/bin/setup-phpdoc-md.sh +RUN /usr/local/bin/setup-phpdocumentor-cache.sh /opt/phpdocumentor-cache "$PHPDOC_VERSION" FROM php:${PHP_VERSION}-cli COPY --from=phpdocmd-builder /opt/phpdoc-md /opt/phpdoc-md +COPY --from=phpdocmd-builder /opt/phpdocumentor-cache /opt/phpdocumentor-cache COPY --chmod=0755 bin/entrypoint.sh /usr/local/bin/entrypoint.sh ENTRYPOINT ["/usr/local/bin/entrypoint.sh"] diff --git a/bin/entrypoint.sh b/bin/entrypoint.sh index 0830ef3..99c7039 100644 --- a/bin/entrypoint.sh +++ b/bin/entrypoint.sh @@ -30,6 +30,7 @@ PHPDOC_PHAR_PATH="$TMP_ROOT/phpDocumentor.phar" PHPDOC_TAR_GZ_PATH="$TMP_ROOT/phpDocumentor.tgz" PHPDOC_EXTRACT_PATH="$TMP_ROOT/phpDocumentor" PHPDOC_MD_PATH=/opt/phpdoc-md +PHPDOC_CACHE_PATH=/opt/phpdocumentor-cache cleanup() { rm -rf "$TMP_ROOT" @@ -59,8 +60,18 @@ if [[ "$PHPDOC_VERSION" == "latest" ]]; then PHPDOC_COMMAND=(php "$PHPDOC_PHAR_PATH" run) else PHPDOC_VERSION_NO_PREFIX=${PHPDOC_VERSION#v} + CACHED_PHPDOC_TAR_GZ_PATH="$PHPDOC_CACHE_PATH/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}.tgz" PHPDOC_TAR_GZ_URL="https://github.com/phpDocumentor/phpDocumentor/releases/download/v${PHPDOC_VERSION_NO_PREFIX}/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}.tgz" - if download_file "$PHPDOC_TAR_GZ_URL" "$PHPDOC_TAR_GZ_PATH"; then + + if [[ -f "$CACHED_PHPDOC_TAR_GZ_PATH" ]]; then + cp "$CACHED_PHPDOC_TAR_GZ_PATH" "$PHPDOC_TAR_GZ_PATH" + elif ! download_file "$PHPDOC_TAR_GZ_URL" "$PHPDOC_TAR_GZ_PATH"; then + PHPDOC_PHAR_URL="https://github.com/phpDocumentor/phpDocumentor/releases/download/v${PHPDOC_VERSION_NO_PREFIX}/phpDocumentor.phar" + download_file "$PHPDOC_PHAR_URL" "$PHPDOC_PHAR_PATH" + PHPDOC_COMMAND=(php "$PHPDOC_PHAR_PATH" run) + fi + + if [[ -z "${PHPDOC_COMMAND[*]}" ]]; then extract_tgz "$PHPDOC_TAR_GZ_PATH" "$PHPDOC_EXTRACT_PATH" PHPDOC_BIN_PATH="$PHPDOC_EXTRACT_PATH/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}/bin/phpdoc" if [[ ! -f "$PHPDOC_BIN_PATH" ]]; then @@ -68,10 +79,6 @@ else exit 1 fi PHPDOC_COMMAND=(php "$PHPDOC_BIN_PATH") - else - PHPDOC_PHAR_URL="https://github.com/phpDocumentor/phpDocumentor/releases/download/v${PHPDOC_VERSION_NO_PREFIX}/phpDocumentor.phar" - download_file "$PHPDOC_PHAR_URL" "$PHPDOC_PHAR_PATH" - PHPDOC_COMMAND=(php "$PHPDOC_PHAR_PATH" run) fi fi diff --git a/bin/setup-phpdocumentor-cache.sh b/bin/setup-phpdocumentor-cache.sh new file mode 100644 index 0000000..e0add83 --- /dev/null +++ b/bin/setup-phpdocumentor-cache.sh @@ -0,0 +1,13 @@ +#!/usr/bin/env bash + +set -euo pipefail + +TARGET_DIR=${1:-/opt/phpdocumentor-cache} +PHPDOC_VERSION=${2:-v2.8.5} +PHPDOC_VERSION_NO_PREFIX=${PHPDOC_VERSION#v} +PHPDOC_TAR_GZ_URL="https://github.com/phpDocumentor/phpDocumentor/releases/download/v${PHPDOC_VERSION_NO_PREFIX}/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}.tgz" +TARGET_FILE="${TARGET_DIR}/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}.tgz" + +mkdir -p "$TARGET_DIR" + +php -r '$url = isset($argv[1]) ? $argv[1] : null; $target = isset($argv[2]) ? $argv[2] : null; if (!$url || !$target) { fwrite(STDERR, "Missing download arguments\n"); exit(1); } $data = @file_get_contents($url); if ($data === false) { fwrite(STDERR, "Unable to download file: $url\n"); exit(1); } if (@file_put_contents($target, $data) === false) { fwrite(STDERR, "Unable to write file: $target\n"); exit(1); }' "$PHPDOC_TAR_GZ_URL" "$TARGET_FILE" From 4ab84d1dfd64f48b363c96fc80d5d71711959ca0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raimondas=20Rimkevi=C4=8Dius=20=28aka=20MekDrop=29?= Date: Fri, 27 Mar 2026 03:44:13 +0200 Subject: [PATCH 14/24] Run action via exec inside testcontainers container --- tests/action-container.test.js | 57 +++++++++++++++++++++++----------- 1 file changed, 39 insertions(+), 18 deletions(-) diff --git a/tests/action-container.test.js b/tests/action-container.test.js index 99e77b8..afceb71 100644 --- a/tests/action-container.test.js +++ b/tests/action-container.test.js @@ -7,7 +7,7 @@ import path from "node:path"; import test from "node:test"; import { fileURLToPath } from "node:url"; -import { GenericContainer, Wait } from "testcontainers"; +import { GenericContainer } from "testcontainers"; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -76,7 +76,8 @@ async function runAction(actionImage, ignoredFiles, phpDocumentorVersion) { const runId = randomUUID(); const container = new GenericContainer(actionImage) - .withAutoRemove(false) + .withEntrypoint(["/bin/sh", "-lc"]) + .withCommand(["while true; do sleep 3600; done"]) .withBindMounts([ { source: workspacePath, @@ -89,31 +90,51 @@ async function runAction(actionImage, ignoredFiles, phpDocumentorVersion) { GITHUB_RUN_ID: runId, GITHUB_SHA: runId }) - .withWorkingDir("/github/workspace") - .withWaitStrategy(Wait.forOneShotStartup()) - .withCommand(["docs", ignoredFiles, phpDocumentorVersion]); + .withWorkingDir("/github/workspace"); if (hostUid !== null && hostGid !== null) { container.withUser(`${hostUid}:${hostGid}`); } + let startedContainer; try { - const startedContainer = await container.start(); - await startedContainer.stop(); + startedContainer = await container.start(); + const execResult = await startedContainer.exec([ + "/usr/local/bin/entrypoint.sh", + "docs", + ignoredFiles, + phpDocumentorVersion + ]); + + if (execResult.exitCode !== 0) { + const stderr = execResult.output + .filter((line) => line.type === "STDERR") + .map((line) => line.content) + .join("\n"); + const stdout = execResult.output + .filter((line) => line.type === "STDOUT") + .map((line) => line.content) + .join("\n"); + + throw new Error( + `Action execution failed with exit code ${execResult.exitCode}\nSTDOUT:\n${stdout}\nSTDERR:\n${stderr}` + ); + } } catch (error) { - const containerId = /Container failed to start for ([a-f0-9]+)/i.exec(String(error?.message ?? ""))?.[1]; - if (!containerId) { - throw error; + if (startedContainer) { + const containerId = startedContainer.getId(); + try { + const containerLogs = await runDockerCommand(["logs", containerId]); + error = new Error(`${error.message}\nContainer logs:\n${containerLogs}`); + } catch { + // Fall back to the original error if container logs cannot be read. + } } - - let containerLogs = ""; - try { - containerLogs = await runDockerCommand(["logs", containerId]); - } catch (logsError) { - containerLogs = `Unable to read container logs: ${logsError.message}`; + throw error; + } finally { + if (startedContainer) { + await startedContainer.stop().catch(() => {}); } - - throw new Error(`Container failed to start for ${containerId}\n${containerLogs}`); } return { From 69eaf66c24b2a215a90a9e23920c4e825040ca87 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raimondas=20Rimkevi=C4=8Dius=20=28aka=20MekDrop=29?= Date: Fri, 27 Mar 2026 03:46:15 +0200 Subject: [PATCH 15/24] Handle testcontainers exec output formats --- tests/action-container.test.js | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/tests/action-container.test.js b/tests/action-container.test.js index afceb71..8d55c87 100644 --- a/tests/action-container.test.js +++ b/tests/action-container.test.js @@ -107,14 +107,28 @@ async function runAction(actionImage, ignoredFiles, phpDocumentorVersion) { ]); if (execResult.exitCode !== 0) { - const stderr = execResult.output - .filter((line) => line.type === "STDERR") - .map((line) => line.content) - .join("\n"); - const stdout = execResult.output - .filter((line) => line.type === "STDOUT") - .map((line) => line.content) - .join("\n"); + let stdout = ""; + let stderr = ""; + + if (Array.isArray(execResult.output)) { + stdout = execResult.output + .filter((line) => line.type === "STDOUT") + .map((line) => line.content) + .join("\n"); + stderr = execResult.output + .filter((line) => line.type === "STDERR") + .map((line) => line.content) + .join("\n"); + } else if (typeof execResult.output === "string") { + stdout = execResult.output; + } + + if (typeof execResult.stdout === "string") { + stdout = execResult.stdout; + } + if (typeof execResult.stderr === "string") { + stderr = execResult.stderr; + } throw new Error( `Action execution failed with exit code ${execResult.exitCode}\nSTDOUT:\n${stdout}\nSTDERR:\n${stderr}` From 2eaa0a0ec110d076c4e1216cd41ecf76b1c19563 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raimondas=20Rimkevi=C4=8Dius=20=28aka=20MekDrop=29?= Date: Fri, 27 Mar 2026 03:48:14 +0200 Subject: [PATCH 16/24] Fix empty phpdoc command check for legacy bash --- bin/entrypoint.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/bin/entrypoint.sh b/bin/entrypoint.sh index 99c7039..69b9d41 100644 --- a/bin/entrypoint.sh +++ b/bin/entrypoint.sh @@ -54,10 +54,12 @@ extract_tgz() { } PHPDOC_COMMAND=() +PHPDOC_COMMAND_SET=0 if [[ "$PHPDOC_VERSION" == "latest" ]]; then PHPDOC_PHAR_URL="https://phpdoc.org/phpDocumentor.phar" download_file "$PHPDOC_PHAR_URL" "$PHPDOC_PHAR_PATH" PHPDOC_COMMAND=(php "$PHPDOC_PHAR_PATH" run) + PHPDOC_COMMAND_SET=1 else PHPDOC_VERSION_NO_PREFIX=${PHPDOC_VERSION#v} CACHED_PHPDOC_TAR_GZ_PATH="$PHPDOC_CACHE_PATH/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}.tgz" @@ -69,9 +71,10 @@ else PHPDOC_PHAR_URL="https://github.com/phpDocumentor/phpDocumentor/releases/download/v${PHPDOC_VERSION_NO_PREFIX}/phpDocumentor.phar" download_file "$PHPDOC_PHAR_URL" "$PHPDOC_PHAR_PATH" PHPDOC_COMMAND=(php "$PHPDOC_PHAR_PATH" run) + PHPDOC_COMMAND_SET=1 fi - if [[ -z "${PHPDOC_COMMAND[*]}" ]]; then + if [[ "$PHPDOC_COMMAND_SET" -eq 0 ]]; then extract_tgz "$PHPDOC_TAR_GZ_PATH" "$PHPDOC_EXTRACT_PATH" PHPDOC_BIN_PATH="$PHPDOC_EXTRACT_PATH/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}/bin/phpdoc" if [[ ! -f "$PHPDOC_BIN_PATH" ]]; then @@ -79,6 +82,7 @@ else exit 1 fi PHPDOC_COMMAND=(php "$PHPDOC_BIN_PATH") + PHPDOC_COMMAND_SET=1 fi fi From 1d36f693065684624b2d99cd1e2bdb00fe632505 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raimondas=20Rimkevi=C4=8Dius=20=28aka=20MekDrop=29?= Date: Fri, 27 Mar 2026 03:50:49 +0200 Subject: [PATCH 17/24] Fallback to cached phpDocumentor phar when zlib is missing --- bin/entrypoint.sh | 19 ++++++++++++++++--- bin/setup-phpdocumentor-cache.sh | 3 +++ 2 files changed, 19 insertions(+), 3 deletions(-) diff --git a/bin/entrypoint.sh b/bin/entrypoint.sh index 69b9d41..89e4208 100644 --- a/bin/entrypoint.sh +++ b/bin/entrypoint.sh @@ -63,12 +63,25 @@ if [[ "$PHPDOC_VERSION" == "latest" ]]; then else PHPDOC_VERSION_NO_PREFIX=${PHPDOC_VERSION#v} CACHED_PHPDOC_TAR_GZ_PATH="$PHPDOC_CACHE_PATH/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}.tgz" + CACHED_PHPDOC_PHAR_PATH="$PHPDOC_CACHE_PATH/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}.phar" PHPDOC_TAR_GZ_URL="https://github.com/phpDocumentor/phpDocumentor/releases/download/v${PHPDOC_VERSION_NO_PREFIX}/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}.tgz" + PHPDOC_PHAR_URL="https://github.com/phpDocumentor/phpDocumentor/releases/download/v${PHPDOC_VERSION_NO_PREFIX}/phpDocumentor.phar" + + if ! php -r 'exit(extension_loaded("zlib") ? 0 : 1);'; then + if [[ -f "$CACHED_PHPDOC_PHAR_PATH" ]]; then + cp "$CACHED_PHPDOC_PHAR_PATH" "$PHPDOC_PHAR_PATH" + PHPDOC_COMMAND=(php "$PHPDOC_PHAR_PATH" run) + PHPDOC_COMMAND_SET=1 + else + download_file "$PHPDOC_PHAR_URL" "$PHPDOC_PHAR_PATH" + PHPDOC_COMMAND=(php "$PHPDOC_PHAR_PATH" run) + PHPDOC_COMMAND_SET=1 + fi + fi - if [[ -f "$CACHED_PHPDOC_TAR_GZ_PATH" ]]; then + if [[ "$PHPDOC_COMMAND_SET" -eq 0 ]] && [[ -f "$CACHED_PHPDOC_TAR_GZ_PATH" ]]; then cp "$CACHED_PHPDOC_TAR_GZ_PATH" "$PHPDOC_TAR_GZ_PATH" - elif ! download_file "$PHPDOC_TAR_GZ_URL" "$PHPDOC_TAR_GZ_PATH"; then - PHPDOC_PHAR_URL="https://github.com/phpDocumentor/phpDocumentor/releases/download/v${PHPDOC_VERSION_NO_PREFIX}/phpDocumentor.phar" + elif [[ "$PHPDOC_COMMAND_SET" -eq 0 ]] && ! download_file "$PHPDOC_TAR_GZ_URL" "$PHPDOC_TAR_GZ_PATH"; then download_file "$PHPDOC_PHAR_URL" "$PHPDOC_PHAR_PATH" PHPDOC_COMMAND=(php "$PHPDOC_PHAR_PATH" run) PHPDOC_COMMAND_SET=1 diff --git a/bin/setup-phpdocumentor-cache.sh b/bin/setup-phpdocumentor-cache.sh index e0add83..a9cd037 100644 --- a/bin/setup-phpdocumentor-cache.sh +++ b/bin/setup-phpdocumentor-cache.sh @@ -6,8 +6,11 @@ TARGET_DIR=${1:-/opt/phpdocumentor-cache} PHPDOC_VERSION=${2:-v2.8.5} PHPDOC_VERSION_NO_PREFIX=${PHPDOC_VERSION#v} PHPDOC_TAR_GZ_URL="https://github.com/phpDocumentor/phpDocumentor/releases/download/v${PHPDOC_VERSION_NO_PREFIX}/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}.tgz" +PHPDOC_PHAR_URL="https://github.com/phpDocumentor/phpDocumentor/releases/download/v${PHPDOC_VERSION_NO_PREFIX}/phpDocumentor.phar" TARGET_FILE="${TARGET_DIR}/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}.tgz" +PHAR_TARGET_FILE="${TARGET_DIR}/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}.phar" mkdir -p "$TARGET_DIR" php -r '$url = isset($argv[1]) ? $argv[1] : null; $target = isset($argv[2]) ? $argv[2] : null; if (!$url || !$target) { fwrite(STDERR, "Missing download arguments\n"); exit(1); } $data = @file_get_contents($url); if ($data === false) { fwrite(STDERR, "Unable to download file: $url\n"); exit(1); } if (@file_put_contents($target, $data) === false) { fwrite(STDERR, "Unable to write file: $target\n"); exit(1); }' "$PHPDOC_TAR_GZ_URL" "$TARGET_FILE" +php -r '$url = isset($argv[1]) ? $argv[1] : null; $target = isset($argv[2]) ? $argv[2] : null; if (!$url || !$target) { fwrite(STDERR, "Missing download arguments\n"); exit(1); } $data = @file_get_contents($url); if ($data === false) { fwrite(STDERR, "Unable to download file: $url\n"); exit(1); } if (@file_put_contents($target, $data) === false) { fwrite(STDERR, "Unable to write file: $target\n"); exit(1); }' "$PHPDOC_PHAR_URL" "$PHAR_TARGET_FILE" From 5b862a1346f44b4eb9306a76a828fbd4c325e28d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raimondas=20Rimkevi=C4=8Dius=20=28aka=20MekDrop=29?= Date: Fri, 27 Mar 2026 03:59:17 +0200 Subject: [PATCH 18/24] Fix PHP 5.3 run by using patched cached phpDocumentor --- bin/entrypoint.sh | 6 +++++- bin/setup-phpdocumentor-cache.sh | 7 +++++++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/bin/entrypoint.sh b/bin/entrypoint.sh index 89e4208..a7abd62 100644 --- a/bin/entrypoint.sh +++ b/bin/entrypoint.sh @@ -64,11 +64,15 @@ else PHPDOC_VERSION_NO_PREFIX=${PHPDOC_VERSION#v} CACHED_PHPDOC_TAR_GZ_PATH="$PHPDOC_CACHE_PATH/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}.tgz" CACHED_PHPDOC_PHAR_PATH="$PHPDOC_CACHE_PATH/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}.phar" + CACHED_PHPDOC_BIN_PATH="$PHPDOC_CACHE_PATH/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}/bin/phpdoc" PHPDOC_TAR_GZ_URL="https://github.com/phpDocumentor/phpDocumentor/releases/download/v${PHPDOC_VERSION_NO_PREFIX}/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}.tgz" PHPDOC_PHAR_URL="https://github.com/phpDocumentor/phpDocumentor/releases/download/v${PHPDOC_VERSION_NO_PREFIX}/phpDocumentor.phar" if ! php -r 'exit(extension_loaded("zlib") ? 0 : 1);'; then - if [[ -f "$CACHED_PHPDOC_PHAR_PATH" ]]; then + if [[ -f "$CACHED_PHPDOC_BIN_PATH" ]]; then + PHPDOC_COMMAND=(php "$CACHED_PHPDOC_BIN_PATH") + PHPDOC_COMMAND_SET=1 + elif [[ -f "$CACHED_PHPDOC_PHAR_PATH" ]]; then cp "$CACHED_PHPDOC_PHAR_PATH" "$PHPDOC_PHAR_PATH" PHPDOC_COMMAND=(php "$PHPDOC_PHAR_PATH" run) PHPDOC_COMMAND_SET=1 diff --git a/bin/setup-phpdocumentor-cache.sh b/bin/setup-phpdocumentor-cache.sh index a9cd037..5541314 100644 --- a/bin/setup-phpdocumentor-cache.sh +++ b/bin/setup-phpdocumentor-cache.sh @@ -9,8 +9,15 @@ PHPDOC_TAR_GZ_URL="https://github.com/phpDocumentor/phpDocumentor/releases/downl PHPDOC_PHAR_URL="https://github.com/phpDocumentor/phpDocumentor/releases/download/v${PHPDOC_VERSION_NO_PREFIX}/phpDocumentor.phar" TARGET_FILE="${TARGET_DIR}/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}.tgz" PHAR_TARGET_FILE="${TARGET_DIR}/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}.phar" +EXTRACTED_ROOT="${TARGET_DIR}/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}" +XML_WRITER_PATH="${EXTRACTED_ROOT}/src/phpDocumentor/Plugin/Core/Transformer/Writer/Xml.php" mkdir -p "$TARGET_DIR" php -r '$url = isset($argv[1]) ? $argv[1] : null; $target = isset($argv[2]) ? $argv[2] : null; if (!$url || !$target) { fwrite(STDERR, "Missing download arguments\n"); exit(1); } $data = @file_get_contents($url); if ($data === false) { fwrite(STDERR, "Unable to download file: $url\n"); exit(1); } if (@file_put_contents($target, $data) === false) { fwrite(STDERR, "Unable to write file: $target\n"); exit(1); }' "$PHPDOC_TAR_GZ_URL" "$TARGET_FILE" php -r '$url = isset($argv[1]) ? $argv[1] : null; $target = isset($argv[2]) ? $argv[2] : null; if (!$url || !$target) { fwrite(STDERR, "Missing download arguments\n"); exit(1); } $data = @file_get_contents($url); if ($data === false) { fwrite(STDERR, "Unable to download file: $url\n"); exit(1); } if (@file_put_contents($target, $data) === false) { fwrite(STDERR, "Unable to write file: $target\n"); exit(1); }' "$PHPDOC_PHAR_URL" "$PHAR_TARGET_FILE" + +rm -rf "$EXTRACTED_ROOT" +php -r '$archive = isset($argv[1]) ? $argv[1] : null; $target = isset($argv[2]) ? $argv[2] : null; if (!$archive || !$target) { fwrite(STDERR, "Missing extract arguments\n"); exit(1); } if (!is_dir(dirname($target)) && !mkdir(dirname($target), 0777, true)) { fwrite(STDERR, "Unable to create target parent directory\n"); exit(1); } $root = dirname($target); $tarPath = preg_replace("/\\.tgz$/", ".tar", $archive); if ($tarPath === null) { fwrite(STDERR, "Unable to derive tar path\n"); exit(1); } try { if (!file_exists($tarPath)) { $tgz = new PharData($archive); $tgz->decompress(); } $tar = new PharData($tarPath); $tar->extractTo($root, null, true); } catch (Exception $e) { fwrite(STDERR, "Unable to extract phpDocumentor archive: " . $e->getMessage() . "\n"); exit(1); }' "$TARGET_FILE" "$EXTRACTED_ROOT" + +php -r '$path = isset($argv[1]) ? $argv[1] : null; if (!$path || !file_exists($path)) { fwrite(STDERR, "Xml writer file not found: " . ($path ?: "null") . "\n"); exit(1); } $content = file_get_contents($path); if ($content === false) { fwrite(STDERR, "Unable to read xml writer file\n"); exit(1); } $needle = '\''base64_encode(gzcompress($file->getSource()))'\''; $replacement = '\''(function_exists("gzcompress") ? base64_encode(gzcompress($file->getSource())) : "")'\''; $updated = str_replace($needle, $replacement, $content, $count); if ($count < 1) { fwrite(STDERR, "Unable to patch xml writer for missing zlib support\n"); exit(1); } if (file_put_contents($path, $updated) === false) { fwrite(STDERR, "Unable to write patched xml writer file\n"); exit(1); }' "$XML_WRITER_PATH" From 19bcca8410652ed07dd7dde5ae00e8730d07423d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raimondas=20Rimkevi=C4=8Dius=20=28aka=20MekDrop=29?= Date: Fri, 27 Mar 2026 04:05:08 +0200 Subject: [PATCH 19/24] Enable zlib in runtime image and pin phpdoc-md for PHP 5.3 --- Dockerfile | 4 ++++ bin/entrypoint.sh | 6 +----- bin/install-runtime-zlib.sh | 28 ++++++++++++++++++++++++++++ bin/setup-phpdoc-md.sh | 5 +++-- bin/setup-phpdocumentor-cache.sh | 7 ------- 5 files changed, 36 insertions(+), 14 deletions(-) create mode 100644 bin/install-runtime-zlib.sh diff --git a/Dockerfile b/Dockerfile index f4ca658..3e99f48 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,6 +14,10 @@ RUN /usr/local/bin/setup-phpdocumentor-cache.sh /opt/phpdocumentor-cache "$PHPDO FROM php:${PHP_VERSION}-cli +COPY --chmod=0755 bin/install-runtime-zlib.sh /usr/local/bin/install-runtime-zlib.sh +RUN /usr/local/bin/install-runtime-zlib.sh \ + && rm -f /usr/local/bin/install-runtime-zlib.sh + COPY --from=phpdocmd-builder /opt/phpdoc-md /opt/phpdoc-md COPY --from=phpdocmd-builder /opt/phpdocumentor-cache /opt/phpdocumentor-cache COPY --chmod=0755 bin/entrypoint.sh /usr/local/bin/entrypoint.sh diff --git a/bin/entrypoint.sh b/bin/entrypoint.sh index a7abd62..89e4208 100644 --- a/bin/entrypoint.sh +++ b/bin/entrypoint.sh @@ -64,15 +64,11 @@ else PHPDOC_VERSION_NO_PREFIX=${PHPDOC_VERSION#v} CACHED_PHPDOC_TAR_GZ_PATH="$PHPDOC_CACHE_PATH/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}.tgz" CACHED_PHPDOC_PHAR_PATH="$PHPDOC_CACHE_PATH/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}.phar" - CACHED_PHPDOC_BIN_PATH="$PHPDOC_CACHE_PATH/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}/bin/phpdoc" PHPDOC_TAR_GZ_URL="https://github.com/phpDocumentor/phpDocumentor/releases/download/v${PHPDOC_VERSION_NO_PREFIX}/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}.tgz" PHPDOC_PHAR_URL="https://github.com/phpDocumentor/phpDocumentor/releases/download/v${PHPDOC_VERSION_NO_PREFIX}/phpDocumentor.phar" if ! php -r 'exit(extension_loaded("zlib") ? 0 : 1);'; then - if [[ -f "$CACHED_PHPDOC_BIN_PATH" ]]; then - PHPDOC_COMMAND=(php "$CACHED_PHPDOC_BIN_PATH") - PHPDOC_COMMAND_SET=1 - elif [[ -f "$CACHED_PHPDOC_PHAR_PATH" ]]; then + if [[ -f "$CACHED_PHPDOC_PHAR_PATH" ]]; then cp "$CACHED_PHPDOC_PHAR_PATH" "$PHPDOC_PHAR_PATH" PHPDOC_COMMAND=(php "$PHPDOC_PHAR_PATH" run) PHPDOC_COMMAND_SET=1 diff --git a/bin/install-runtime-zlib.sh b/bin/install-runtime-zlib.sh new file mode 100644 index 0000000..4582efd --- /dev/null +++ b/bin/install-runtime-zlib.sh @@ -0,0 +1,28 @@ +#!/bin/sh + +set -eu + +if php -r 'exit(extension_loaded("zlib") ? 0 : 1);'; then + exit 0 +fi + +if php -d extension=zlib.so -r 'exit(function_exists("gzcompress") ? 0 : 1);'; then + echo "extension=zlib.so" > /usr/local/etc/php/conf.d/zz-zlib.ini + exit 0 +fi + +if ! command -v apt-get >/dev/null 2>&1 || ! command -v docker-php-ext-install >/dev/null 2>&1; then + echo "Unable to install zlib extension automatically." >&2 + exit 1 +fi + +apt-get update +if [ -n "${PHPIZE_DEPS:-}" ]; then + apt-get install -y --no-install-recommends $PHPIZE_DEPS zlib1g-dev +else + apt-get install -y --no-install-recommends zlib1g-dev +fi +docker-php-ext-install zlib +rm -rf /var/lib/apt/lists/* + +php -r 'if (!function_exists("gzcompress")) { fwrite(STDERR, "zlib extension is required but unavailable.\n"); exit(1); }' diff --git a/bin/setup-phpdoc-md.sh b/bin/setup-phpdoc-md.sh index 5b95292..15861a2 100644 --- a/bin/setup-phpdoc-md.sh +++ b/bin/setup-phpdoc-md.sh @@ -4,11 +4,12 @@ set -euo pipefail TARGET_DIR=${1:-/opt/phpdoc-md} COMPOSER_HOME_DIR=${2:-/tmp/composer} +PHPDOC_MD_REF=${3:-0.1.1} GIT_SSH_OPTIONS="ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5" -if ! GIT_SSH_COMMAND="$GIT_SSH_OPTIONS" git clone --depth 1 git@github.com:evert/phpdoc-md.git "$TARGET_DIR"; then - git clone --depth 1 https://github.com/evert/phpdoc-md.git "$TARGET_DIR" +if ! GIT_SSH_COMMAND="$GIT_SSH_OPTIONS" git clone --depth 1 --branch "$PHPDOC_MD_REF" git@github.com:evert/phpdoc-md.git "$TARGET_DIR"; then + git clone --depth 1 --branch "$PHPDOC_MD_REF" https://github.com/evert/phpdoc-md.git "$TARGET_DIR" fi php -r '$composer = json_decode(file_get_contents($argv[1]), true); unset($composer["require-dev"]); file_put_contents($argv[1], json_encode($composer, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . PHP_EOL);' "$TARGET_DIR/composer.json" diff --git a/bin/setup-phpdocumentor-cache.sh b/bin/setup-phpdocumentor-cache.sh index 5541314..a9cd037 100644 --- a/bin/setup-phpdocumentor-cache.sh +++ b/bin/setup-phpdocumentor-cache.sh @@ -9,15 +9,8 @@ PHPDOC_TAR_GZ_URL="https://github.com/phpDocumentor/phpDocumentor/releases/downl PHPDOC_PHAR_URL="https://github.com/phpDocumentor/phpDocumentor/releases/download/v${PHPDOC_VERSION_NO_PREFIX}/phpDocumentor.phar" TARGET_FILE="${TARGET_DIR}/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}.tgz" PHAR_TARGET_FILE="${TARGET_DIR}/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}.phar" -EXTRACTED_ROOT="${TARGET_DIR}/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}" -XML_WRITER_PATH="${EXTRACTED_ROOT}/src/phpDocumentor/Plugin/Core/Transformer/Writer/Xml.php" mkdir -p "$TARGET_DIR" php -r '$url = isset($argv[1]) ? $argv[1] : null; $target = isset($argv[2]) ? $argv[2] : null; if (!$url || !$target) { fwrite(STDERR, "Missing download arguments\n"); exit(1); } $data = @file_get_contents($url); if ($data === false) { fwrite(STDERR, "Unable to download file: $url\n"); exit(1); } if (@file_put_contents($target, $data) === false) { fwrite(STDERR, "Unable to write file: $target\n"); exit(1); }' "$PHPDOC_TAR_GZ_URL" "$TARGET_FILE" php -r '$url = isset($argv[1]) ? $argv[1] : null; $target = isset($argv[2]) ? $argv[2] : null; if (!$url || !$target) { fwrite(STDERR, "Missing download arguments\n"); exit(1); } $data = @file_get_contents($url); if ($data === false) { fwrite(STDERR, "Unable to download file: $url\n"); exit(1); } if (@file_put_contents($target, $data) === false) { fwrite(STDERR, "Unable to write file: $target\n"); exit(1); }' "$PHPDOC_PHAR_URL" "$PHAR_TARGET_FILE" - -rm -rf "$EXTRACTED_ROOT" -php -r '$archive = isset($argv[1]) ? $argv[1] : null; $target = isset($argv[2]) ? $argv[2] : null; if (!$archive || !$target) { fwrite(STDERR, "Missing extract arguments\n"); exit(1); } if (!is_dir(dirname($target)) && !mkdir(dirname($target), 0777, true)) { fwrite(STDERR, "Unable to create target parent directory\n"); exit(1); } $root = dirname($target); $tarPath = preg_replace("/\\.tgz$/", ".tar", $archive); if ($tarPath === null) { fwrite(STDERR, "Unable to derive tar path\n"); exit(1); } try { if (!file_exists($tarPath)) { $tgz = new PharData($archive); $tgz->decompress(); } $tar = new PharData($tarPath); $tar->extractTo($root, null, true); } catch (Exception $e) { fwrite(STDERR, "Unable to extract phpDocumentor archive: " . $e->getMessage() . "\n"); exit(1); }' "$TARGET_FILE" "$EXTRACTED_ROOT" - -php -r '$path = isset($argv[1]) ? $argv[1] : null; if (!$path || !file_exists($path)) { fwrite(STDERR, "Xml writer file not found: " . ($path ?: "null") . "\n"); exit(1); } $content = file_get_contents($path); if ($content === false) { fwrite(STDERR, "Unable to read xml writer file\n"); exit(1); } $needle = '\''base64_encode(gzcompress($file->getSource()))'\''; $replacement = '\''(function_exists("gzcompress") ? base64_encode(gzcompress($file->getSource())) : "")'\''; $updated = str_replace($needle, $replacement, $content, $count); if ($count < 1) { fwrite(STDERR, "Unable to patch xml writer for missing zlib support\n"); exit(1); } if (file_put_contents($path, $updated) === false) { fwrite(STDERR, "Unable to write patched xml writer file\n"); exit(1); }' "$XML_WRITER_PATH" From 2b27c07eafd164c5b92aa824765b161a50acb582 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raimondas=20Rimkevi=C4=8Dius=20=28aka=20MekDrop=29?= Date: Fri, 27 Mar 2026 04:08:21 +0200 Subject: [PATCH 20/24] Use patched cached phpDocumentor for PHP 5.3 without zlib --- Dockerfile | 4 ---- bin/entrypoint.sh | 6 +++++- bin/install-runtime-zlib.sh | 28 ---------------------------- bin/setup-phpdocumentor-cache.sh | 7 +++++++ 4 files changed, 12 insertions(+), 33 deletions(-) delete mode 100644 bin/install-runtime-zlib.sh diff --git a/Dockerfile b/Dockerfile index 3e99f48..f4ca658 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,10 +14,6 @@ RUN /usr/local/bin/setup-phpdocumentor-cache.sh /opt/phpdocumentor-cache "$PHPDO FROM php:${PHP_VERSION}-cli -COPY --chmod=0755 bin/install-runtime-zlib.sh /usr/local/bin/install-runtime-zlib.sh -RUN /usr/local/bin/install-runtime-zlib.sh \ - && rm -f /usr/local/bin/install-runtime-zlib.sh - COPY --from=phpdocmd-builder /opt/phpdoc-md /opt/phpdoc-md COPY --from=phpdocmd-builder /opt/phpdocumentor-cache /opt/phpdocumentor-cache COPY --chmod=0755 bin/entrypoint.sh /usr/local/bin/entrypoint.sh diff --git a/bin/entrypoint.sh b/bin/entrypoint.sh index 89e4208..a7abd62 100644 --- a/bin/entrypoint.sh +++ b/bin/entrypoint.sh @@ -64,11 +64,15 @@ else PHPDOC_VERSION_NO_PREFIX=${PHPDOC_VERSION#v} CACHED_PHPDOC_TAR_GZ_PATH="$PHPDOC_CACHE_PATH/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}.tgz" CACHED_PHPDOC_PHAR_PATH="$PHPDOC_CACHE_PATH/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}.phar" + CACHED_PHPDOC_BIN_PATH="$PHPDOC_CACHE_PATH/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}/bin/phpdoc" PHPDOC_TAR_GZ_URL="https://github.com/phpDocumentor/phpDocumentor/releases/download/v${PHPDOC_VERSION_NO_PREFIX}/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}.tgz" PHPDOC_PHAR_URL="https://github.com/phpDocumentor/phpDocumentor/releases/download/v${PHPDOC_VERSION_NO_PREFIX}/phpDocumentor.phar" if ! php -r 'exit(extension_loaded("zlib") ? 0 : 1);'; then - if [[ -f "$CACHED_PHPDOC_PHAR_PATH" ]]; then + if [[ -f "$CACHED_PHPDOC_BIN_PATH" ]]; then + PHPDOC_COMMAND=(php "$CACHED_PHPDOC_BIN_PATH") + PHPDOC_COMMAND_SET=1 + elif [[ -f "$CACHED_PHPDOC_PHAR_PATH" ]]; then cp "$CACHED_PHPDOC_PHAR_PATH" "$PHPDOC_PHAR_PATH" PHPDOC_COMMAND=(php "$PHPDOC_PHAR_PATH" run) PHPDOC_COMMAND_SET=1 diff --git a/bin/install-runtime-zlib.sh b/bin/install-runtime-zlib.sh deleted file mode 100644 index 4582efd..0000000 --- a/bin/install-runtime-zlib.sh +++ /dev/null @@ -1,28 +0,0 @@ -#!/bin/sh - -set -eu - -if php -r 'exit(extension_loaded("zlib") ? 0 : 1);'; then - exit 0 -fi - -if php -d extension=zlib.so -r 'exit(function_exists("gzcompress") ? 0 : 1);'; then - echo "extension=zlib.so" > /usr/local/etc/php/conf.d/zz-zlib.ini - exit 0 -fi - -if ! command -v apt-get >/dev/null 2>&1 || ! command -v docker-php-ext-install >/dev/null 2>&1; then - echo "Unable to install zlib extension automatically." >&2 - exit 1 -fi - -apt-get update -if [ -n "${PHPIZE_DEPS:-}" ]; then - apt-get install -y --no-install-recommends $PHPIZE_DEPS zlib1g-dev -else - apt-get install -y --no-install-recommends zlib1g-dev -fi -docker-php-ext-install zlib -rm -rf /var/lib/apt/lists/* - -php -r 'if (!function_exists("gzcompress")) { fwrite(STDERR, "zlib extension is required but unavailable.\n"); exit(1); }' diff --git a/bin/setup-phpdocumentor-cache.sh b/bin/setup-phpdocumentor-cache.sh index a9cd037..5541314 100644 --- a/bin/setup-phpdocumentor-cache.sh +++ b/bin/setup-phpdocumentor-cache.sh @@ -9,8 +9,15 @@ PHPDOC_TAR_GZ_URL="https://github.com/phpDocumentor/phpDocumentor/releases/downl PHPDOC_PHAR_URL="https://github.com/phpDocumentor/phpDocumentor/releases/download/v${PHPDOC_VERSION_NO_PREFIX}/phpDocumentor.phar" TARGET_FILE="${TARGET_DIR}/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}.tgz" PHAR_TARGET_FILE="${TARGET_DIR}/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}.phar" +EXTRACTED_ROOT="${TARGET_DIR}/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}" +XML_WRITER_PATH="${EXTRACTED_ROOT}/src/phpDocumentor/Plugin/Core/Transformer/Writer/Xml.php" mkdir -p "$TARGET_DIR" php -r '$url = isset($argv[1]) ? $argv[1] : null; $target = isset($argv[2]) ? $argv[2] : null; if (!$url || !$target) { fwrite(STDERR, "Missing download arguments\n"); exit(1); } $data = @file_get_contents($url); if ($data === false) { fwrite(STDERR, "Unable to download file: $url\n"); exit(1); } if (@file_put_contents($target, $data) === false) { fwrite(STDERR, "Unable to write file: $target\n"); exit(1); }' "$PHPDOC_TAR_GZ_URL" "$TARGET_FILE" php -r '$url = isset($argv[1]) ? $argv[1] : null; $target = isset($argv[2]) ? $argv[2] : null; if (!$url || !$target) { fwrite(STDERR, "Missing download arguments\n"); exit(1); } $data = @file_get_contents($url); if ($data === false) { fwrite(STDERR, "Unable to download file: $url\n"); exit(1); } if (@file_put_contents($target, $data) === false) { fwrite(STDERR, "Unable to write file: $target\n"); exit(1); }' "$PHPDOC_PHAR_URL" "$PHAR_TARGET_FILE" + +rm -rf "$EXTRACTED_ROOT" +php -r '$archive = isset($argv[1]) ? $argv[1] : null; $target = isset($argv[2]) ? $argv[2] : null; if (!$archive || !$target) { fwrite(STDERR, "Missing extract arguments\n"); exit(1); } if (!is_dir(dirname($target)) && !mkdir(dirname($target), 0777, true)) { fwrite(STDERR, "Unable to create target parent directory\n"); exit(1); } $root = dirname($target); $tarPath = preg_replace("/\\.tgz$/", ".tar", $archive); if ($tarPath === null) { fwrite(STDERR, "Unable to derive tar path\n"); exit(1); } try { if (!file_exists($tarPath)) { $tgz = new PharData($archive); $tgz->decompress(); } $tar = new PharData($tarPath); $tar->extractTo($root, null, true); } catch (Exception $e) { fwrite(STDERR, "Unable to extract phpDocumentor archive: " . $e->getMessage() . "\n"); exit(1); }' "$TARGET_FILE" "$EXTRACTED_ROOT" + +php -r '$path = isset($argv[1]) ? $argv[1] : null; if (!$path || !file_exists($path)) { fwrite(STDERR, "Xml writer file not found: " . ($path ?: "null") . "\n"); exit(1); } $content = file_get_contents($path); if ($content === false) { fwrite(STDERR, "Unable to read xml writer file\n"); exit(1); } $needle = '\''base64_encode(gzcompress($file->getSource()))'\''; $replacement = '\''(function_exists("gzcompress") ? base64_encode(gzcompress($file->getSource())) : "")'\''; $updated = str_replace($needle, $replacement, $content, $count); if ($count < 1) { fwrite(STDERR, "Unable to patch xml writer for missing zlib support\n"); exit(1); } if (file_put_contents($path, $updated) === false) { fwrite(STDERR, "Unable to write patched xml writer file\n"); exit(1); }' "$XML_WRITER_PATH" From ff1ff3993ba0eb50beee562ab33390fef0d4da68 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raimondas=20Rimkevi=C4=8Dius=20=28aka=20MekDrop=29?= Date: Fri, 27 Mar 2026 04:09:39 +0200 Subject: [PATCH 21/24] Drop PHP 5.3 from CI matrix --- .github/workflows/on-pull-request.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/on-pull-request.yml b/.github/workflows/on-pull-request.yml index 24f6ff0..067aba1 100644 --- a/.github/workflows/on-pull-request.yml +++ b/.github/workflows/on-pull-request.yml @@ -13,7 +13,6 @@ jobs: fail-fast: false matrix: php_version: - - '5.3' - '5.4' - '7.0' - '7.1' From 15c74ac42a78574cc4ec4a5eb34d76f446d69cdc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raimondas=20Rimkevi=C4=8Dius=20=28aka=20MekDrop=29?= Date: Fri, 27 Mar 2026 04:13:14 +0200 Subject: [PATCH 22/24] Remove PHP 5.3-specific action compatibility code --- bin/entrypoint.sh | 16 ---------------- bin/setup-phpdoc-md.sh | 4 ++-- bin/setup-phpdocumentor-cache.sh | 7 ------- 3 files changed, 2 insertions(+), 25 deletions(-) diff --git a/bin/entrypoint.sh b/bin/entrypoint.sh index a7abd62..a94577c 100644 --- a/bin/entrypoint.sh +++ b/bin/entrypoint.sh @@ -64,25 +64,9 @@ else PHPDOC_VERSION_NO_PREFIX=${PHPDOC_VERSION#v} CACHED_PHPDOC_TAR_GZ_PATH="$PHPDOC_CACHE_PATH/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}.tgz" CACHED_PHPDOC_PHAR_PATH="$PHPDOC_CACHE_PATH/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}.phar" - CACHED_PHPDOC_BIN_PATH="$PHPDOC_CACHE_PATH/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}/bin/phpdoc" PHPDOC_TAR_GZ_URL="https://github.com/phpDocumentor/phpDocumentor/releases/download/v${PHPDOC_VERSION_NO_PREFIX}/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}.tgz" PHPDOC_PHAR_URL="https://github.com/phpDocumentor/phpDocumentor/releases/download/v${PHPDOC_VERSION_NO_PREFIX}/phpDocumentor.phar" - if ! php -r 'exit(extension_loaded("zlib") ? 0 : 1);'; then - if [[ -f "$CACHED_PHPDOC_BIN_PATH" ]]; then - PHPDOC_COMMAND=(php "$CACHED_PHPDOC_BIN_PATH") - PHPDOC_COMMAND_SET=1 - elif [[ -f "$CACHED_PHPDOC_PHAR_PATH" ]]; then - cp "$CACHED_PHPDOC_PHAR_PATH" "$PHPDOC_PHAR_PATH" - PHPDOC_COMMAND=(php "$PHPDOC_PHAR_PATH" run) - PHPDOC_COMMAND_SET=1 - else - download_file "$PHPDOC_PHAR_URL" "$PHPDOC_PHAR_PATH" - PHPDOC_COMMAND=(php "$PHPDOC_PHAR_PATH" run) - PHPDOC_COMMAND_SET=1 - fi - fi - if [[ "$PHPDOC_COMMAND_SET" -eq 0 ]] && [[ -f "$CACHED_PHPDOC_TAR_GZ_PATH" ]]; then cp "$CACHED_PHPDOC_TAR_GZ_PATH" "$PHPDOC_TAR_GZ_PATH" elif [[ "$PHPDOC_COMMAND_SET" -eq 0 ]] && ! download_file "$PHPDOC_TAR_GZ_URL" "$PHPDOC_TAR_GZ_PATH"; then diff --git a/bin/setup-phpdoc-md.sh b/bin/setup-phpdoc-md.sh index 15861a2..c5357d9 100644 --- a/bin/setup-phpdoc-md.sh +++ b/bin/setup-phpdoc-md.sh @@ -4,7 +4,7 @@ set -euo pipefail TARGET_DIR=${1:-/opt/phpdoc-md} COMPOSER_HOME_DIR=${2:-/tmp/composer} -PHPDOC_MD_REF=${3:-0.1.1} +PHPDOC_MD_REF=${3:-0.2.0} GIT_SSH_OPTIONS="ssh -o BatchMode=yes -o StrictHostKeyChecking=accept-new -o ConnectTimeout=5" @@ -14,7 +14,7 @@ fi php -r '$composer = json_decode(file_get_contents($argv[1]), true); unset($composer["require-dev"]); file_put_contents($argv[1], json_encode($composer, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES) . PHP_EOL);' "$TARGET_DIR/composer.json" -COMPOSER_ALLOW_SUPERUSER=1 COMPOSER_HOME="$COMPOSER_HOME_DIR" composer config --global platform.php 5.3.3 +COMPOSER_ALLOW_SUPERUSER=1 COMPOSER_HOME="$COMPOSER_HOME_DIR" composer config --global platform.php 5.4.0 COMPOSER_ALLOW_SUPERUSER=1 COMPOSER_HOME="$COMPOSER_HOME_DIR" composer install \ --working-dir="$TARGET_DIR" \ --no-dev \ diff --git a/bin/setup-phpdocumentor-cache.sh b/bin/setup-phpdocumentor-cache.sh index 5541314..a9cd037 100644 --- a/bin/setup-phpdocumentor-cache.sh +++ b/bin/setup-phpdocumentor-cache.sh @@ -9,15 +9,8 @@ PHPDOC_TAR_GZ_URL="https://github.com/phpDocumentor/phpDocumentor/releases/downl PHPDOC_PHAR_URL="https://github.com/phpDocumentor/phpDocumentor/releases/download/v${PHPDOC_VERSION_NO_PREFIX}/phpDocumentor.phar" TARGET_FILE="${TARGET_DIR}/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}.tgz" PHAR_TARGET_FILE="${TARGET_DIR}/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}.phar" -EXTRACTED_ROOT="${TARGET_DIR}/phpDocumentor-${PHPDOC_VERSION_NO_PREFIX}" -XML_WRITER_PATH="${EXTRACTED_ROOT}/src/phpDocumentor/Plugin/Core/Transformer/Writer/Xml.php" mkdir -p "$TARGET_DIR" php -r '$url = isset($argv[1]) ? $argv[1] : null; $target = isset($argv[2]) ? $argv[2] : null; if (!$url || !$target) { fwrite(STDERR, "Missing download arguments\n"); exit(1); } $data = @file_get_contents($url); if ($data === false) { fwrite(STDERR, "Unable to download file: $url\n"); exit(1); } if (@file_put_contents($target, $data) === false) { fwrite(STDERR, "Unable to write file: $target\n"); exit(1); }' "$PHPDOC_TAR_GZ_URL" "$TARGET_FILE" php -r '$url = isset($argv[1]) ? $argv[1] : null; $target = isset($argv[2]) ? $argv[2] : null; if (!$url || !$target) { fwrite(STDERR, "Missing download arguments\n"); exit(1); } $data = @file_get_contents($url); if ($data === false) { fwrite(STDERR, "Unable to download file: $url\n"); exit(1); } if (@file_put_contents($target, $data) === false) { fwrite(STDERR, "Unable to write file: $target\n"); exit(1); }' "$PHPDOC_PHAR_URL" "$PHAR_TARGET_FILE" - -rm -rf "$EXTRACTED_ROOT" -php -r '$archive = isset($argv[1]) ? $argv[1] : null; $target = isset($argv[2]) ? $argv[2] : null; if (!$archive || !$target) { fwrite(STDERR, "Missing extract arguments\n"); exit(1); } if (!is_dir(dirname($target)) && !mkdir(dirname($target), 0777, true)) { fwrite(STDERR, "Unable to create target parent directory\n"); exit(1); } $root = dirname($target); $tarPath = preg_replace("/\\.tgz$/", ".tar", $archive); if ($tarPath === null) { fwrite(STDERR, "Unable to derive tar path\n"); exit(1); } try { if (!file_exists($tarPath)) { $tgz = new PharData($archive); $tgz->decompress(); } $tar = new PharData($tarPath); $tar->extractTo($root, null, true); } catch (Exception $e) { fwrite(STDERR, "Unable to extract phpDocumentor archive: " . $e->getMessage() . "\n"); exit(1); }' "$TARGET_FILE" "$EXTRACTED_ROOT" - -php -r '$path = isset($argv[1]) ? $argv[1] : null; if (!$path || !file_exists($path)) { fwrite(STDERR, "Xml writer file not found: " . ($path ?: "null") . "\n"); exit(1); } $content = file_get_contents($path); if ($content === false) { fwrite(STDERR, "Unable to read xml writer file\n"); exit(1); } $needle = '\''base64_encode(gzcompress($file->getSource()))'\''; $replacement = '\''(function_exists("gzcompress") ? base64_encode(gzcompress($file->getSource())) : "")'\''; $updated = str_replace($needle, $replacement, $content, $count); if ($count < 1) { fwrite(STDERR, "Unable to patch xml writer for missing zlib support\n"); exit(1); } if (file_put_contents($path, $updated) === false) { fwrite(STDERR, "Unable to write patched xml writer file\n"); exit(1); }' "$XML_WRITER_PATH" From 1d93fa2dbf3386587fdf426950072836d27c65c0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raimondas=20Rimkevi=C4=8Dius=20=28aka=20MekDrop=29?= Date: Fri, 27 Mar 2026 04:16:32 +0200 Subject: [PATCH 23/24] Tune CI parallelism and read Node version from package.json --- .github/workflows/on-pull-request.yml | 15 +++++++++++++-- package.json | 3 +++ 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/.github/workflows/on-pull-request.yml b/.github/workflows/on-pull-request.yml index 067aba1..48d1a65 100644 --- a/.github/workflows/on-pull-request.yml +++ b/.github/workflows/on-pull-request.yml @@ -10,7 +10,8 @@ jobs: test: runs-on: ubuntu-latest strategy: - fail-fast: false + fail-fast: true + max-parallel: 3 matrix: php_version: - '5.4' @@ -24,10 +25,20 @@ jobs: - name: Checkouting code... uses: actions/checkout@v5 + - name: Read Node.js version from package.json + id: node-version + run: | + NODE_VERSION=$(jq -r '.engines.node // empty' package.json) + if [[ -z "$NODE_VERSION" || "$NODE_VERSION" == "null" ]]; then + echo "package.json engines.node is required" >&2 + exit 1 + fi + echo "version=$NODE_VERSION" >> "$GITHUB_OUTPUT" + - name: Setup Node.js uses: actions/setup-node@v4 with: - node-version: 22 + node-version: ${{ steps.node-version.outputs.version }} - name: Enable legacy Docker schema v1 pulls for old PHP tags run: | diff --git a/package.json b/package.json index 0c01022..b7b6263 100644 --- a/package.json +++ b/package.json @@ -5,6 +5,9 @@ "scripts": { "test": "node --test tests/*.test.js" }, + "engines": { + "node": "22" + }, "devDependencies": { "testcontainers": "^10.20.0" } From c49e5642810a7300a8df6dd0ed2c0f0a66d53e72 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Raimondas=20Rimkevi=C4=8Dius=20=28aka=20MekDrop=29?= Date: Fri, 27 Mar 2026 04:22:22 +0200 Subject: [PATCH 24/24] Add php_version action input with 5.4-7.4 validation --- README.md | 3 +++ action.yml | 5 +++++ bin/entrypoint.sh | 17 ++++++++++++++--- tests/action-container.test.js | 5 +++-- 4 files changed, 25 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index ae4028d..69752af 100644 --- a/README.md +++ b/README.md @@ -26,6 +26,7 @@ jobs: - name: Generating documentation... uses: impresscms-dev/generate-phpdocs-with-evert-phpdoc-md-action@v1.0.0 with: + php_version: '7.4' output_path: ./docs/ ignored_files: | test/ @@ -42,6 +43,7 @@ jobs: This action supports such arguments (used in `with` keyword): | Argument | Required | Default value | Description | |-------------|----------|----------------------|-----------------------------------| +| php_version | No | 7.4 | PHP version to run (accepted range: `5.4` to `7.4`, inclusive) | | ignored_files | No | | Defines files that can be ignored (supports glob rules; each line means one rule) | | phpdocumentor_version | No | v2.8.5 | What [phpDocumentor](https://www.phpdoc.org) version to use (latest or release tag like `v2.8.5`) | | output_path | Yes | | Path where to write generated documentation | @@ -51,6 +53,7 @@ This action supports such arguments (used in `with` keyword): - Docker build clones `git@github.com:evert/phpdoc-md.git` directly and falls back to HTTPS clone when SSH credentials are not available. - phpDocumentor release artifacts are downloaded during action runtime and are not stored in this repository. - Dockerfile supports selecting PHP by version: `docker build --build-arg PHP_VERSION=7.4 .` +- `php_version` input must match the container runtime version (this image version is controlled by `PHP_VERSION` build arg). - Tests are JavaScript integration tests based on [testcontainers-node](https://github.com/testcontainers/testcontainers-node). ## How to contribute? diff --git a/action.yml b/action.yml index f3aeb86..24c828b 100644 --- a/action.yml +++ b/action.yml @@ -6,6 +6,10 @@ branding: color: red inputs: + php_version: + description: "PHP version to run (must be between 5.4 and 7.4 inclusive)" + required: false + default: '7.4' ignored_files: description: "Defines files that can be ignored (supports glob rules; each line means one rule)" required: false @@ -22,6 +26,7 @@ runs: using: 'docker' image: 'Dockerfile' args: + - ${{ inputs.php_version }} - ${{ inputs.output_path }} - ${{ inputs.ignored_files }} - ${{ inputs.phpdocumentor_version }} diff --git a/bin/entrypoint.sh b/bin/entrypoint.sh index a94577c..ec1b546 100644 --- a/bin/entrypoint.sh +++ b/bin/entrypoint.sh @@ -2,9 +2,20 @@ set -euo pipefail -OUTPUT_PATH=${1:-} -IGNORED_FILES=${2:-} -PHPDOC_VERSION=${3:-v2.8.5} +REQUESTED_PHP_VERSION=${1:-7.4} +OUTPUT_PATH=${2:-} +IGNORED_FILES=${3:-} +PHPDOC_VERSION=${4:-v2.8.5} + +if ! php -r '$version = isset($argv[1]) ? $argv[1] : ""; if (!preg_match("/^(5\\.[4-6]|7\\.[0-4])$/", $version)) { fwrite(STDERR, "Input '\''php_version'\'' must be between 5.4 and 7.4 (inclusive).\n"); exit(1); }' "$REQUESTED_PHP_VERSION"; then + exit 1 +fi + +RUNTIME_PHP_VERSION=$(php -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;') +if [[ "$REQUESTED_PHP_VERSION" != "$RUNTIME_PHP_VERSION" ]]; then + echo "Input 'php_version' is '$REQUESTED_PHP_VERSION' but action container uses PHP '$RUNTIME_PHP_VERSION'. Build the action image with matching PHP_VERSION." >&2 + exit 1 +fi if [[ -z "$OUTPUT_PATH" ]]; then echo "Input 'output_path' is required." >&2 diff --git a/tests/action-container.test.js b/tests/action-container.test.js index 8d55c87..b5d6803 100644 --- a/tests/action-container.test.js +++ b/tests/action-container.test.js @@ -67,7 +67,7 @@ async function buildActionImage(phpVersion) { return imageTag; } -async function runAction(actionImage, ignoredFiles, phpDocumentorVersion) { +async function runAction(actionImage, phpVersion, ignoredFiles, phpDocumentorVersion) { const workspacePath = await mkdtemp(path.join(os.tmpdir(), "phpdoc-action-workspace-")); await cp(fixtureProjectPath, workspacePath, { recursive: true }); @@ -101,6 +101,7 @@ async function runAction(actionImage, ignoredFiles, phpDocumentorVersion) { startedContainer = await container.start(); const execResult = await startedContainer.exec([ "/usr/local/bin/entrypoint.sh", + phpVersion, "docs", ignoredFiles, phpDocumentorVersion @@ -162,7 +163,7 @@ const phpDocumentorVersion = process.env.PHPDOC_VERSION || "v2.8.5"; test(`boots container and generates markdown (php:${phpVersion}-cli)`, async () => { const actionImage = await buildActionImage(phpVersion); - const { docsOutputPath, workspacePath } = await runAction(actionImage, "", phpDocumentorVersion); + const { docsOutputPath, workspacePath } = await runAction(actionImage, phpVersion, "", phpDocumentorVersion); try { const indexStat = await stat(path.join(docsOutputPath, "ApiIndex.md"));