Skip to content

Add GitHub workflows for building Alpine PHP#400

Open
tudor-timcu wants to merge 6 commits intomainfrom
alpine-support
Open

Add GitHub workflows for building Alpine PHP#400
tudor-timcu wants to merge 6 commits intomainfrom
alpine-support

Conversation

@tudor-timcu
Copy link
Copy Markdown
Collaborator

@tudor-timcu tudor-timcu commented Mar 13, 2026

Summary by Aikido

Security Issues: 0 🔍 Quality Issues: 10 Resolved Issues: 0

🚀 New Features

  • Added GitHub workflows and Dockerfiles for Alpine PHP NTS and ZTS images
  • Introduced build workflows for Alpine extension and libs toolchain images
  • Added package install and uninstall scripts for Aikido RPM/APK packages

🔧 Refactors

  • Refactored CI build.yml to simplify image tagging and architecture matrix

More info

…mages, and libs toolchain images

- Created workflows for building Alpine PHP test images in NTS mode.
- Added workflows for building Alpine PHP extension images in NTS mode.
- Introduced a workflow for building Alpine libs toolchain images.
- Each workflow includes multi-architecture support (amd64 and arm64) and publishes multi-arch manifests.
- Dockerfiles for building the respective images are also included.
FRANKENPHP_EXT_DIR="/usr/lib/frankenphp/modules"
FRANKENPHP_INI_DIR="/etc/frankenphp/php.d"

for PHP_VERSION in $PHP_VERSIONS; do
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The for-loop handling each PHP_VERSION has multiple nested conditionals (resolving PHP_BIN, PHP_MOD_DIR, PHP_EXT_DIR). Consider adding early-continue guards when required dirs aren't present to flatten the logic and improve readability.

Details

✨ AI Reasoning
​The for-loop iterating PHP_VERSION collects data and then performs multiple conditional blocks to remove files. Several code paths are nested (resolving PHP_BIN -> computing PHP_EXT_DIR / PHP_MOD_DIR -> branching on Debian mod dir vs generic mod dir -> removing files). If common failure cases (no mod dir and no ext dir) occur, much of the main logic is nested beneath checks instead of using early continue/guards to skip iterations quickly. Flattening with early continue would improve readability and maintainability by keeping the main uninstall path less indented and more obvious.

🔧 How do I fix it?
Place parameter validation and guard clauses at the function start. Use early returns to reduce nesting levels and improve readability.

Reply @AikidoSec feedback: [FEEDBACK] to get better review comments in the future.
Reply @AikidoSec ignore: [REASON] to ignore this issue.
More info

fi
fi

for PHP_VERSION in $PHP_VERSIONS; do
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The for-loop installing per PHP_VERSION contains multiple nested conditionals and an inner subdir loop; adding early-continue guards for missing prerequisites (ext/mod dirs) would flatten the logic and improve clarity.

Details

✨ AI Reasoning
​The for-loop iterating PHP_VERSION performs several sequential checks and nested blocks (detecting thread safety, conditionally linking extension files, then conditionally linking mod files with an inner for-loop for subdirs). While some 'continue' uses exist, other failure cases (e.g., missing mod dirs) still wrap large blocks of work. Introducing guard-clauses/early-continues earlier would reduce nesting and make the intended install flow clearer.

🔧 How do I fix it?
Place parameter validation and guard clauses at the function start. Use early returns to reduce nesting levels and improve readability.

Reply @AikidoSec feedback: [FEEDBACK] to get better review comments in the future.
Reply @AikidoSec ignore: [REASON] to ignore this issue.
More info

fi && \
mkdir -p "$EXTENSION_DIR"

RUN php -v | grep -v "ZTS" >/dev/null || (echo "ERROR: ZTS is enabled but should be NTS!" && exit 1) && \
Copy link
Copy Markdown
Contributor

@aikido-pr-checks aikido-pr-checks bot Mar 13, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ZTS guard uses php -v | grep -v "ZTS", which can still succeed when ZTS is enabled because other php -v lines don’t contain "ZTS", so the error branch may never run.

Suggested change
RUN php -v | grep -v "ZTS" >/dev/null || (echo "ERROR: ZTS is enabled but should be NTS!" && exit 1) && \
RUN php -v | grep "ZTS" >/dev/null && (echo "ERROR: ZTS is enabled but should be NTS!" && exit 1) || true && \
php -v | grep -v "ZTS" >/dev/null && \
Details

✨ AI Reasoning
​​1) The block is trying to fail the image build if PHP was compiled with ZTS.
2) It uses php -v | grep -v "ZTS" >/dev/null to decide success.
3) php -v outputs multiple lines; even if the version line contains "ZTS", other lines typically do not, so grep -v "ZTS" still outputs something and exits 0.
4) This means the code can silently pass the ZTS check when ZTS is enabled, contradicting the intended control-flow guard.

Reply @AikidoSec feedback: [FEEDBACK] to get better review comments in the future.
Reply @AikidoSec ignore: [REASON] to ignore this issue.
More info

ln -sf /usr/local/sbin/php-fpm /usr/sbin/php-fpm || true && \
ln -sf /usr/local/bin/php-cgi /usr/bin/php-cgi

# Create /etc/httpd so the test harness uses the CentOS-style php-fpm config path
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The RUN starting at line 75 chains many filesystem modifications, config writes, php-fpm tests, and symlink commands with &&/||. Split into smaller RUN steps to improve readability and isolate failures.

Details

✨ AI Reasoning
​A single RUN line performs many file system creations, symlink operations, multiple echo redirects to create config, php-fpm tests and an mv — all chained with many && and ||. This combines many distinct side-effecting steps into one line, increasing cognitive load and hiding failures in complex chaining.

🔧 How do I fix it?
Break long lines to enhance clarity. Aim for a maximum of 80-120 characters per line, depending on the context and coding standards.

Reply @AikidoSec feedback: [FEEDBACK] to get better review comments in the future.
Reply @AikidoSec ignore: [REASON] to ignore this issue.
More info

bash curl git make gcc g++ musl-dev \
protobuf protobuf-dev

RUN ARCH=$(uname -m) && \
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The RUN at line 11 combines architecture detection, conditional assignment, download, extraction and cleanup. Break into separate steps (detect/assign, download, extract, cleanup) for clarity and easier failure diagnosis.

Details

✨ AI Reasoning
​The RUN at line 11 contains shell architecture detection with if/elif, variable assignment, a curl download, tar extraction and cleanup — all in one line. It mixes control flow and multiple IO operations making it hard to reason about and debug.

🔧 How do I fix it?
Break long lines to enhance clarity. Aim for a maximum of 80-120 characters per line, depending on the context and coding standards.

Reply @AikidoSec feedback: [FEEDBACK] to get better review comments in the future.
Reply @AikidoSec ignore: [REASON] to ignore this issue.
More info

Comment on lines +20 to +29
ver=$(php -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;')
PHP_VERSIONS="$ver"
fi

for php_path in /usr/bin/php[0-9]* /usr/local/bin/php[0-9]*; do
if [ -x "$php_path" ]; then
ver=$("$php_path" -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;' 2>/dev/null) || continue
case " $PHP_VERSIONS " in
*" $ver "*) ;;
*) PHP_VERSIONS="$PHP_VERSIONS $ver" ;;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable 'ver' is ambiguous for the PHP version value; rename to 'php_version' or 'detected_php_version' for clarity.

Show fix
Suggested change
ver=$(php -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;')
PHP_VERSIONS="$ver"
fi
for php_path in /usr/bin/php[0-9]* /usr/local/bin/php[0-9]*; do
if [ -x "$php_path" ]; then
ver=$("$php_path" -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;' 2>/dev/null) || continue
case " $PHP_VERSIONS " in
*" $ver "*) ;;
*) PHP_VERSIONS="$PHP_VERSIONS $ver" ;;
php_version=$(php -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;')
PHP_VERSIONS="$php_version"
fi
for php_path in /usr/bin/php[0-9]* /usr/local/bin/php[0-9]*; do
if [ -x "$php_path" ]; then
php_version=$("$php_path" -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;' 2>/dev/null) || continue
case " $PHP_VERSIONS " in
*" $php_version "*) ;;
*) PHP_VERSIONS="$PHP_VERSIONS $php_version" ;;
Details

✨ AI Reasoning
​The installation script introduces 'ver' to capture PHP's major.minor version. This short name hides its intent; readers must infer that it represents the PHP version when used later in the script. Using an explicit name improves readability and reduces mistakes during maintenance.

Reply @AikidoSec feedback: [FEEDBACK] to get better review comments in the future.
Reply @AikidoSec ignore: [REASON] to ignore this issue.
More info

Comment on lines +17 to +26
ver=$(php -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;')
PHP_VERSIONS="$ver"
fi

for php_path in /usr/bin/php[0-9]* /usr/local/bin/php[0-9]*; do
if [ -x "$php_path" ]; then
ver=$("$php_path" -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;' 2>/dev/null) || continue
case " $PHP_VERSIONS " in
*" $ver "*) ;;
*) PHP_VERSIONS="$PHP_VERSIONS $ver" ;;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Variable 'ver' is too short/ambiguous for holding a PHP version; use a descriptive name like 'php_version' or 'detected_php_version'.

Show fix
Suggested change
ver=$(php -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;')
PHP_VERSIONS="$ver"
fi
for php_path in /usr/bin/php[0-9]* /usr/local/bin/php[0-9]*; do
if [ -x "$php_path" ]; then
ver=$("$php_path" -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;' 2>/dev/null) || continue
case " $PHP_VERSIONS " in
*" $ver "*) ;;
*) PHP_VERSIONS="$PHP_VERSIONS $ver" ;;
php_version=$(php -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;')
PHP_VERSIONS="$php_version"
fi
for php_path in /usr/bin/php[0-9]* /usr/local/bin/php[0-9]*; do
if [ -x "$php_path" ]; then
php_version=$("$php_path" -r 'echo PHP_MAJOR_VERSION . "." . PHP_MINOR_VERSION;' 2>/dev/null) || continue
case " $PHP_VERSIONS " in
*" $php_version "*) ;;
*) PHP_VERSIONS="$PHP_VERSIONS $php_version" ;;
Details

✨ AI Reasoning
​A short, non-descriptive variable 'ver' was introduced to hold the PHP major.minor version string. Using 'ver' makes the code less explicit about its contents and purpose across the install/uninstall logic, increasing cognitive load when reading or maintaining the script. A more descriptive name would clarify intent.

Reply @AikidoSec feedback: [FEEDBACK] to get better review comments in the future.
Reply @AikidoSec ignore: [REASON] to ignore this issue.
More info

…n images in ZTS mode

- Introduced workflows for building Alpine PHP test images and extension images specifically for ZTS (Zend Thread Safety).
- Each workflow supports multi-architecture builds (amd64 and arm64) and includes steps for publishing multi-arch manifests.
- Added Dockerfiles for building the respective images, ensuring compatibility with musl-based systems.
- Enhanced existing build processes to accommodate ZTS requirements.
Comment thread .github/workflows/build.yml Fixed
Comment thread .github/workflows/build.yml Fixed
WORKDIR /usr/src/php-src
RUN ./buildconf --force

RUN if [ -f ext/openssl/openssl.c ] && grep -q 'REGISTER_LONG_CONSTANT("OPENSSL_SSLV23_PADDING"' ext/openssl/openssl.c; then \
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RUN with conditional + inline awk script + file move crams conditional logic and file IO on one line; split the awk processing and the mv into separate RUN steps for clarity.

Details

✨ AI Reasoning
​This RUN performs a shell conditional, an inline awk script with branching, file redirection to a temp file, and a move operation, all chained on the same RUN. The line requires mentally evaluating the conditional, the awk script's control flow, and subsequent file operations together, increasing cognitive load.

🔧 How do I fix it?
Break long lines to enhance clarity. Aim for a maximum of 80-120 characters per line, depending on the context and coding standards.

Reply @AikidoSec feedback: [FEEDBACK] to get better review comments in the future.
Reply @AikidoSec ignore: [REASON] to ignore this issue.
More info

RUN sed -i 's/\[\([0-9]\+\.[0-9]\+\.[0-9]\+\)-dev\]/[\1]/' configure.ac
RUN ./buildconf --force

RUN if [ -f ext/openssl/openssl.c ] && grep -q 'REGISTER_LONG_CONSTANT("OPENSSL_SSLV23_PADDING"' ext/openssl/openssl.c; then \
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RUN with conditional, inline awk script, and file mv is densely packed; separate the awk transformation and the mv/conditional into clearer, smaller steps.

Details

✨ AI Reasoning
​The RUN contains a conditional that invokes awk to rewrite a source file, then moves the result into place, all combined with shell logical operators. The inline script has its own control flow; combining that with move and conditional logic in one RUN line makes it hard to parse and reason about.

🔧 How do I fix it?
Break long lines to enhance clarity. Aim for a maximum of 80-120 characters per line, depending on the context and coding standards.

Reply @AikidoSec feedback: [FEEDBACK] to get better review comments in the future.
Reply @AikidoSec ignore: [REASON] to ignore this issue.
More info

echo "pm.start_servers = 2" >> /usr/local/etc/php-fpm.d/www.conf && \
echo "pm.min_spare_servers = 1" >> /usr/local/etc/php-fpm.d/www.conf && \
echo "pm.max_spare_servers = 3" >> /usr/local/etc/php-fpm.d/www.conf && \
php-fpm -t -y /usr/local/etc/php-fpm.conf 2>&1 | grep -v "Nothing matches the include pattern" || true && \
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RUN that pipes php-fpm tests through grep, handles failures, and then creates symlinks is dense; separate validation, error handling, and file operations into distinct steps.

Details

✨ AI Reasoning
​This RUN contains php-fpm configuration tests with piping to grep, conditional fallbacks, and then a final symlink command, all interleaved with &&/||. The combination of test commands and side-effecting operations on one line increases the cognitive burden to understand failure modes and side effects.

🔧 How do I fix it?
Break long lines to enhance clarity. Aim for a maximum of 80-120 characters per line, depending on the context and coding standards.

Reply @AikidoSec feedback: [FEEDBACK] to get better review comments in the future.
Reply @AikidoSec ignore: [REASON] to ignore this issue.
More info

- Changed base image from Alpine 3.21 to Alpine 3.16 in Dockerfiles for PHP test images (NTS and ZTS modes) and extension images (NTS and ZTS modes).
- Updated the Dockerfile for building Alpine libs toolchain images to also use Alpine 3.16.
- Updated workflows for building Alpine PHP test and extension images to streamline the tagging process by removing architecture-specific tags.
- Consolidated tags to use a single format: `${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ matrix.php_version }}-${{ env.VERSION }}`.
- Removed unnecessary build steps for arm64 architecture across multiple workflows, focusing on amd64 builds.
Comment on lines +252 to +303
runs-on: ubuntu-24.04
container: ghcr.io/aikidosec/firewall-php-build-extension-alpine-nts:${{ matrix.php_version }}-v1
strategy:
matrix:
php_version: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5']
fail-fast: false

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Get Arch
run: echo "ARCH=$(uname -m)" >> $GITHUB_ENV

- name: Get Aikido version
run: |
AIKIDO_VERSION=$(grep '#define PHP_AIKIDO_VERSION' lib/php-extension/include/php_aikido.h | awk -F'"' '{print $2}')
echo $AIKIDO_VERSION
echo "AIKIDO_VERSION=$AIKIDO_VERSION" >> $GITHUB_ENV
echo "AIKIDO_ARTIFACT=aikido-extension-alpine-php-${{ matrix.php_version }}" >> $GITHUB_ENV

- name: Check PHP setup
run: |
php -v | grep -v "ZTS" > /dev/null || (echo "ERROR: PHP is ZTS, expected NTS!" && php -v && exit 1)

- name: Build extension
run: |
rm -rf build
mkdir build
cd lib/php-extension
phpize
cd ../../build
CXX=g++ CXXFLAGS="-fPIC -g -O2 -I../lib/php-extension/include" LDFLAGS="-lstdc++" ../lib/php-extension/configure
make -j"$(nproc)"

- name: Version Aikido extension
run: |
cd ./build/modules
mv aikido.so ${{ env.AIKIDO_ARTIFACT }}-nts.so

- name: Archive build artifacts
uses: actions/upload-artifact@v4
if: always()
with:
name: ${{ env.AIKIDO_ARTIFACT }}-nts-${{ env.ARCH }}
if-no-files-found: error
path: |
${{ github.workspace }}/build/modules/${{ env.AIKIDO_ARTIFACT }}-nts.so
${{ github.workspace }}/tests/*.diff

build_php_extension_alpine_zts:
name: Build php ${{ matrix.php_version }} extension Alpine ZTS x86_64

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

Copilot Autofix

AI about 1 month ago

In general, to fix this kind of issue, you explicitly specify the permissions: for the GITHUB_TOKEN either at the workflow root (applies to all jobs) or on individual jobs, granting only the minimal scopes required (typically contents: read for build/test jobs that only need to check out code). This documents the intended access and prevents accidental elevation if repository defaults change.

For this workflow, the simplest, least-disruptive fix is to add a single permissions: block at the top level, just under the existing on: block and before jobs:. All the shown jobs only use actions/checkout (which works with contents: read) and actions/upload-artifact (which uses the Actions API internally and does not need repository contents write access), plus local shell commands. They do not push commits, create releases, or modify issues/PRs. Therefore contents: read is sufficient and preserves existing functionality while constraining the token. Concretely, in .github/workflows/build.yml, add:

permissions:
  contents: read

after line 10 (workflow_call:) and before line 12 (jobs:). No imports or additional methods are needed.

Suggested changeset 1
.github/workflows/build.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -9,6 +9,9 @@
       - main
   workflow_call:
 
+permissions:
+  contents: read
+
 jobs:
   build_libs:
     name: Build Go libs ${{ matrix.arch == '' && 'x86_64' || 'arm' }}
EOF
@@ -9,6 +9,9 @@
- main
workflow_call:

permissions:
contents: read

jobs:
build_libs:
name: Build Go libs ${{ matrix.arch == '' && 'x86_64' || 'arm' }}
Copilot is powered by AI and may make mistakes. Always verify output.
Comment on lines +1151 to +1237
image: ghcr.io/aikidosec/firewall-php-test-alpine-nts:${{ matrix.php_version }}-v1
options: --privileged
needs: [ build_apk ]
strategy:
matrix:
php_version: ['7.4', '8.0', '8.1', '8.2', '8.3', '8.4', '8.5']
server: ['nginx-php-fpm', 'php-built-in']
fail-fast: false

steps:
- name: Checkout repository
uses: actions/checkout@v4

- name: Setup
run: |
uname -a
cat /etc/alpine-release
php -v
nginx -v || true
which php-fpm && php-fpm -v || true

- name: Install and start MariaDB
run: |
mkdir -p /var/lib/mysql /run/mysqld
chown -R mysql:mysql /var/lib/mysql /run/mysqld
mysql_install_db --user=mysql --datadir=/var/lib/mysql
mysqld --user=mysql --datadir=/var/lib/mysql --socket=/var/lib/mysql/mysql.sock &
sleep 10
mysql --socket=/var/lib/mysql/mysql.sock -u root -e "CREATE DATABASE IF NOT EXISTS db;"
mysql --socket=/var/lib/mysql/mysql.sock -u root -e "ALTER USER 'root'@'localhost' IDENTIFIED BY 'pwd'; FLUSH PRIVILEGES;"

- name: Test MySQL connection with mysqli
run: |
php -r '
$mysqli = new mysqli("localhost", "root", "pwd", "db");
if ($mysqli->connect_error) {
echo "MySQL connection failed: " . $mysqli->connect_error . "\n";
exit(1);
} else {
echo "MySQL connection successful\n";
$mysqli->close();
}
'

- name: Get Arch
run: echo "ARCH=$(uname -m)" >> $GITHUB_ENV

- name: Check PHP setup
run: |
uname -m
php -v
php -i

- name: Get Aikido version
run: |
AIKIDO_VERSION=$(grep '#define PHP_AIKIDO_VERSION' lib/php-extension/include/php_aikido.h | awk -F'"' '{print $2}')
echo $AIKIDO_VERSION
echo "AIKIDO_VERSION=$AIKIDO_VERSION" >> $GITHUB_ENV
echo "AIKIDO_APK=aikido-php-firewall.${{ env.ARCH }}.apk" >> $GITHUB_ENV

- name: Download artifacts
uses: actions/download-artifact@v4
with:
pattern: |
${{ env.AIKIDO_APK }}

- name: Install APK
run: |
apk add --allow-untrusted ${{ env.AIKIDO_APK }}/${{ env.AIKIDO_APK }}

- name: Run CLI tests
run: |
export TEST_PHP_EXECUTABLE=/usr/local/bin/php
cd lib/php-extension/
phpize
cd ../../
php lib/php-extension/run-tests.php ./tests/cli

- name: Run ${{ matrix.server }} server tests
run: |
cd tools
python3 run_server_tests.py ../tests/server ../tests/testlib --server=${{ matrix.server }} --max-runs=3

test_php_alpine_zts:
name: Alpine ZTS php-${{ matrix.php_version }} ${{ matrix.server }} x86_64
runs-on: ubuntu-24.04
container:

Check warning

Code scanning / CodeQL

Workflow does not contain permissions Medium

Actions job or workflow does not limit the permissions of the GITHUB_TOKEN. Consider setting an explicit permissions block, using the following as a minimal starting point: {contents: read}

Copilot Autofix

AI about 1 month ago

In general, fix this by adding an explicit permissions block to the workflow or to the specific job(s), granting only the scopes required. For this workflow, the jobs only need to read repository contents and artifacts, so contents: read is sufficient. Setting this at the top level under the workflow root (name/on) will apply to all jobs that don’t override permissions, which both documents the intent and prevents future jobs from accidentally inheriting broader defaults.

The single best fix with minimal functional impact is to add a root-level permissions block right after the on: section in .github/workflows/build.yml:

permissions:
  contents: read

This leaves all existing job logic unchanged while ensuring that the GITHUB_TOKEN is limited to read-only repository contents across the workflow, including test_php_alpine. No new methods, imports, or external libraries are required; the change is purely declarative in the YAML workflow.

Suggested changeset 1
.github/workflows/build.yml

Autofix patch

Autofix patch
Run the following command in your local git repository to apply this patch
cat << 'EOF' | git apply
diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml
--- a/.github/workflows/build.yml
+++ b/.github/workflows/build.yml
@@ -9,6 +9,9 @@
       - main
   workflow_call:
 
+permissions:
+  contents: read
+
 jobs:
   build_libs:
     name: Build Go libs ${{ matrix.arch == '' && 'x86_64' || 'arm' }}
EOF
@@ -9,6 +9,9 @@
- main
workflow_call:

permissions:
contents: read

jobs:
build_libs:
name: Build Go libs ${{ matrix.arch == '' && 'x86_64' || 'arm' }}
Copilot is powered by AI and may make mistakes. Always verify output.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants