Skip to content

Commit 550039d

Browse files
committed
ci: migrate from Travis to GitHub Actions
1 parent b1eb01c commit 550039d

10 files changed

Lines changed: 300 additions & 48 deletions

File tree

.github/workflows/ci.yml

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
name: CI
2+
3+
on:
4+
pull_request:
5+
push:
6+
branches:
7+
- master
8+
9+
concurrency:
10+
group: ci-${{ github.workflow }}-${{ github.ref }}
11+
cancel-in-progress: true
12+
13+
jobs:
14+
test:
15+
name: PHP ${{ matrix.php }} / ${{ matrix.driver }} / ${{ matrix.search_build }}
16+
runs-on: ubuntu-latest
17+
timeout-minutes: 30
18+
continue-on-error: ${{ matrix.search_build == 'SPHINX3' }}
19+
strategy:
20+
fail-fast: false
21+
matrix:
22+
php: ['7.4', '8.0', '8.1']
23+
driver: [mysqli, pdo]
24+
search_build: [SPHINX2, SPHINX3, MANTICORE]
25+
env:
26+
DRIVER: ${{ matrix.driver }}
27+
SEARCH_BUILD: ${{ matrix.search_build }}
28+
29+
steps:
30+
- name: Checkout
31+
uses: actions/checkout@v4
32+
33+
- name: Setup PHP
34+
uses: shivammathur/setup-php@v2
35+
with:
36+
php-version: ${{ matrix.php }}
37+
coverage: none
38+
extensions: mysqli, pdo_mysql, mbstring
39+
tools: composer:v2
40+
41+
- name: Install system dependencies
42+
run: |
43+
sudo apt-get update
44+
sudo apt-get install -y --no-install-recommends \
45+
libodbc1 \
46+
gcc \
47+
g++ \
48+
make \
49+
autoconf \
50+
automake \
51+
libtool \
52+
pkg-config \
53+
wget \
54+
tar
55+
56+
- name: Install search engine
57+
run: |
58+
mkdir -p "$HOME/search"
59+
pushd "$HOME/search"
60+
"$GITHUB_WORKSPACE/tests/install.sh"
61+
popd
62+
63+
- name: Install dependencies
64+
run: composer update --prefer-dist --no-interaction
65+
66+
- name: Prepare autoload
67+
run: composer dump-autoload
68+
69+
- name: Start search daemon
70+
run: |
71+
cd tests
72+
"$GITHUB_WORKSPACE/tests/run.sh"
73+
74+
- name: Run tests
75+
run: |
76+
EXCLUDE_GROUP=""
77+
if [ "$SEARCH_BUILD" != "MANTICORE" ]; then
78+
EXCLUDE_GROUP="--exclude-group=Manticore"
79+
fi
80+
./vendor/bin/phpunit --configuration "tests/travis/${DRIVER}.phpunit.xml" --coverage-text $EXCLUDE_GROUP
81+
82+
- name: Upload debug artifacts on failure
83+
if: failure()
84+
uses: actions/upload-artifact@v4
85+
with:
86+
name: ci-debug-php${{ matrix.php }}-${{ matrix.driver }}-${{ matrix.search_build }}
87+
if-no-files-found: ignore
88+
path: |
89+
tests/searchd.log
90+
tests/searchd.pid
91+
tests/data/*

.travis.yml

Lines changed: 0 additions & 42 deletions
This file was deleted.

Dockerfile.test

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
FROM php:8.3-cli
2+
3+
RUN apt-get update \
4+
&& apt-get install -y --no-install-recommends \
5+
ca-certificates \
6+
gcc \
7+
git \
8+
libonig-dev \
9+
unzip \
10+
wget \
11+
&& rm -rf /var/lib/apt/lists/*
12+
13+
RUN docker-php-ext-install mysqli pdo_mysql mbstring
14+
15+
RUN php -r "copy('https://composer.github.io/installer.sig', 'composer.sig');" \
16+
&& php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');" \
17+
&& php -r "if (trim(file_get_contents('composer.sig')) !== hash_file('sha384', 'composer-setup.php')) { fwrite(STDERR, 'Invalid Composer installer checksum'.PHP_EOL); exit(1); }" \
18+
&& php composer-setup.php --install-dir=/usr/local/bin --filename=composer \
19+
&& php -r "unlink('composer-setup.php'); unlink('composer.sig');"
20+
21+
WORKDIR /work
22+
23+
ENV COMPOSER_ALLOW_SUPERUSER=1

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
Query Builder for SphinxQL
22
==========================
33

4-
[![Build Status](https://travis-ci.org/FoolCode/SphinxQL-Query-Builder.png)](https://travis-ci.org/FoolCode/SphinxQL-Query-Builder)
4+
[![CI](https://github.com/FoolCode/SphinxQL-Query-Builder/actions/workflows/ci.yml/badge.svg)](https://github.com/FoolCode/SphinxQL-Query-Builder/actions/workflows/ci.yml)
55
[![Latest Stable Version](https://poser.pugx.org/foolz/sphinxql-query-builder/v/stable)](https://packagist.org/packages/foolz/sphinxql-query-builder)
66
[![Latest Unstable Version](https://poser.pugx.org/foolz/sphinxql-query-builder/v/unstable)](https://packagist.org/packages/foolz/sphinxql-query-builder)
77
[![Total Downloads](https://poser.pugx.org/foolz/sphinxql-query-builder/downloads)](https://packagist.org/packages/foolz/sphinxql-query-builder)

scripts/run-tests-docker.sh

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
5+
IMAGE_NAME="${IMAGE_NAME:-sphinxql-test-runner:local}"
6+
BUILD_LOG="$(mktemp)"
7+
USE_ISOLATED_DOCKER_CONFIG=0
8+
TEMP_DOCKER_CONFIG=""
9+
10+
cleanup() {
11+
rm -f "$BUILD_LOG"
12+
if [ -n "$TEMP_DOCKER_CONFIG" ] && [ -d "$TEMP_DOCKER_CONFIG" ]; then
13+
rm -rf "$TEMP_DOCKER_CONFIG"
14+
fi
15+
}
16+
trap cleanup EXIT
17+
18+
docker_cmd() {
19+
if [ "$USE_ISOLATED_DOCKER_CONFIG" -eq 1 ]; then
20+
DOCKER_CONFIG="$TEMP_DOCKER_CONFIG" docker "$@"
21+
else
22+
docker "$@"
23+
fi
24+
}
25+
26+
if ! docker_cmd build -f "$ROOT_DIR/Dockerfile.test" -t "$IMAGE_NAME" "$ROOT_DIR" 2>&1 | tee "$BUILD_LOG"; then
27+
if grep -Eq "error getting credentials|docker-credential-.*not found" "$BUILD_LOG"; then
28+
echo "Docker credential helper failure detected, retrying build with isolated anonymous Docker config." >&2
29+
TEMP_DOCKER_CONFIG="$(mktemp -d)"
30+
cat >"$TEMP_DOCKER_CONFIG/config.json" <<'JSON'
31+
{
32+
"auths": {}
33+
}
34+
JSON
35+
USE_ISOLATED_DOCKER_CONFIG=1
36+
docker_cmd build -f "$ROOT_DIR/Dockerfile.test" -t "$IMAGE_NAME" "$ROOT_DIR"
37+
else
38+
exit 1
39+
fi
40+
fi
41+
42+
DOCKER_RUN_ARGS=(
43+
run
44+
--rm
45+
-u "$(id -u):$(id -g)"
46+
-e HOME=/tmp/home
47+
-e SOURCE_DIR=/src
48+
-v "$ROOT_DIR:/src:ro"
49+
)
50+
51+
if [ -t 1 ]; then
52+
DOCKER_RUN_ARGS+=(-t)
53+
fi
54+
55+
docker_cmd "${DOCKER_RUN_ARGS[@]}" "$IMAGE_NAME" bash /src/scripts/run-tests-in-container.sh

scripts/run-tests-in-container.sh

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
SOURCE_DIR="${SOURCE_DIR:-/src}"
5+
WORK_DIR="$(mktemp -d /tmp/sphinxql-work-XXXXXX)"
6+
7+
cleanup() {
8+
rm -rf "$WORK_DIR"
9+
}
10+
trap cleanup EXIT
11+
12+
cp -a "$SOURCE_DIR/." "$WORK_DIR"
13+
14+
export SEARCH_BUILD=MANTICORE
15+
export COMPOSER_ALLOW_SUPERUSER=1
16+
export COMPOSER_HOME="${COMPOSER_HOME:-/tmp/composer}"
17+
export HOME="${HOME:-/tmp/home}"
18+
19+
mkdir -p "$HOME/search"
20+
pushd "$HOME/search" >/dev/null
21+
"$WORK_DIR/tests/install.sh"
22+
popd >/dev/null
23+
24+
cd "$WORK_DIR"
25+
composer install --prefer-dist --no-interaction --no-progress
26+
composer dump-autoload
27+
28+
cd "$WORK_DIR/tests"
29+
./run.sh
30+
31+
cd "$WORK_DIR"
32+
./vendor/bin/phpunit --configuration tests/travis/mysqli.phpunit.xml
33+
./vendor/bin/phpunit --configuration tests/travis/pdo.phpunit.xml

src/Drivers/Mysqli/Connection.php

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
use Foolz\SphinxQL\Exception\ConnectionException;
99
use Foolz\SphinxQL\Exception\DatabaseException;
1010
use Foolz\SphinxQL\Exception\SphinxQLException;
11+
use mysqli_sql_exception;
1112

1213
/**
1314
* SphinxQL connection class utilizing the MySQLi extension.
@@ -51,6 +52,12 @@ public function connect()
5152
if (!$conn->real_connect($data['host'], null, null, null, (int) $data['port'], $data['socket'])) {
5253
throw new ConnectionException('Connection Error: ['.$conn->connect_errno.']'.$conn->connect_error);
5354
}
55+
} catch (mysqli_sql_exception $exception) {
56+
throw new ConnectionException(
57+
'Connection Error: ['.$exception->getCode().']'.$exception->getMessage(),
58+
(int) $exception->getCode(),
59+
$exception
60+
);
5461
} finally {
5562
restore_error_handler();
5663
}
@@ -102,6 +109,12 @@ public function query($query)
102109
* ERROR mysqli::prepare(): (08S01/1047): unknown command (code=22) - prepare() not implemented by Sphinx/Manticore
103110
*/
104111
$resource = @$this->getConnection()->query($query);
112+
} catch (mysqli_sql_exception $exception) {
113+
throw new DatabaseException(
114+
'['.$exception->getCode().'] '.$exception->getMessage().' [ '.$query.']',
115+
(int) $exception->getCode(),
116+
$exception
117+
);
105118
} finally {
106119
restore_error_handler();
107120
}
@@ -127,7 +140,15 @@ public function multiQuery(array $queue)
127140

128141
$this->ensureConnection();
129142

130-
$this->getConnection()->multi_query(implode(';', $queue));
143+
try {
144+
$this->getConnection()->multi_query(implode(';', $queue));
145+
} catch (mysqli_sql_exception $exception) {
146+
throw new DatabaseException(
147+
'['.$exception->getCode().'] '.$exception->getMessage().' [ '.implode(';', $queue).']',
148+
(int) $exception->getCode(),
149+
$exception
150+
);
151+
}
131152

132153
if ($this->getConnection()->error) {
133154
throw new DatabaseException('['.$this->getConnection()->errno.'] '.
@@ -146,7 +167,13 @@ public function escape($value)
146167
{
147168
$this->ensureConnection();
148169

149-
if (($value = $this->getConnection()->real_escape_string((string) $value)) === false) {
170+
try {
171+
$value = $this->getConnection()->real_escape_string((string) $value);
172+
} catch (mysqli_sql_exception $exception) {
173+
throw new DatabaseException($exception->getMessage(), (int) $exception->getCode(), $exception);
174+
}
175+
176+
if ($value === false) {
150177
// @codeCoverageIgnoreStart
151178
throw new DatabaseException($this->getConnection()->error, $this->getConnection()->errno);
152179
// @codeCoverageIgnoreEnd

src/Drivers/Pdo/ResultSetAdapter.php

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public function isDml()
6969
*/
7070
public function store()
7171
{
72-
return $this->statement->fetchAll(PDO::FETCH_NUM);
72+
return $this->normalizeRows($this->statement->fetchAll(PDO::FETCH_NUM));
7373
}
7474

7575
/**
@@ -118,6 +118,8 @@ public function fetch($assoc = true)
118118
if (!$row) {
119119
$this->valid = false;
120120
$row = null;
121+
} else {
122+
$row = $this->normalizeRow($row);
121123
}
122124

123125
return $row;
@@ -138,6 +140,37 @@ public function fetchAll($assoc = true)
138140
$this->valid = false;
139141
}
140142

143+
return $this->normalizeRows($row);
144+
}
145+
146+
/**
147+
* Cast scalar non-string values to string to keep PDO and MySQLi
148+
* result typing aligned across PHP versions.
149+
*
150+
* @param array $row
151+
* @return array
152+
*/
153+
protected function normalizeRow(array $row)
154+
{
155+
foreach ($row as $key => $value) {
156+
if (is_scalar($value) && !is_string($value)) {
157+
$row[$key] = (string) $value;
158+
}
159+
}
160+
141161
return $row;
142162
}
163+
164+
/**
165+
* @param array $rows
166+
* @return array
167+
*/
168+
protected function normalizeRows(array $rows)
169+
{
170+
foreach ($rows as $index => $row) {
171+
$rows[$index] = $this->normalizeRow($row);
172+
}
173+
174+
return $rows;
175+
}
143176
}

0 commit comments

Comments
 (0)