feat: VivesPOS landing on Winter CMS 1.2 — theme + plugin + Dockerfile
Some checks are pending
Module sub-split / Sub-split (push) Waiting to run

- Base: wintercms/winter branch 1.2 (full framework)
- Theme vivespos: Canvas 7 + Bootstrap 5 CDN, custom CSS
- Layout: deferred GTM/GA4 tracking, JSON-LD SoftwareApplication
- Partials: hero (offline-first), features, modes (offline/nube toggle),
  screenshots, pricing (3 planes), comparison, FAQ, CTA
- Plugin VivesPOS.Site with ContactForm
- Dockerfile: PHP 8.2 Apache, port 80, healthcheck
- Added winter/wn-pages, blog, sitemap, seo plugins
- Active theme set to vivespos
This commit is contained in:
2026-08-21 19:29:00 -06:00
commit 1f72193a64
3266 changed files with 531480 additions and 0 deletions

View File

@@ -0,0 +1,78 @@
name: Bug Report
description: Report a general Winter CMS or Storm library issue. See our policy below if reporting a security issue.
labels: ["Status: Review Needed", "Type: Unconfirmed Bug"]
body:
- type: markdown
attributes:
value: |
Thanks for your interest in improving Winter CMS! To avoid duplicate issues please ensure that no previous issues already cover the problem you wish to report before you submit your report. Otherwise, feel free to fill out the form below to submit an issue.
**Please do not use this form to report a security issue. For security issues, review our [Security Policy](https://github.com/wintercms/winter/security/policy).**
- type: dropdown
id: build
attributes:
label: Winter CMS Build
description: Please select the Winter CMS build that you encountered your issue with. You can find the version in the **Updates & Plugins** section of the Settings page of the Backend, or by running the `php artisan winter:version` command.
options:
- dev-develop
- 1.2
- 1.1
- 1.0 (please try updating first)
- Other (please specify below)
validations:
required: true
- type: dropdown
id: php_version
attributes:
label: PHP Version
description: Please provide us the branch of PHP version. For example, for PHP version 7.4.9, select `7.4`, or for PHP version 8.0.1, select `8.0`.
options:
- 8.4
- 8.3
- 8.2
- 8.1
- 8.0
- 7.4
- 7.3
- 7.2
- Other (please specify below)
validations:
required: true
- type: dropdown
id: database
attributes:
label: Database engine
description: Please provide us with the database server type you are running.
options:
- MySQL/MariaDB
- PostgreSQL
- SQLite
- SQL Server
- No database
- Other (please specify below)
validations:
required: true
- type: input
id: plugins
attributes:
label: Plugins installed
description: If any plugins are installed, please list them here in the format `Author.PluginName, Author2.PluginName, etc`.
- type: textarea
id: description
attributes:
label: Issue description
description: Please describe the issue in as much detail as possible. Include screenshots of error messages or copy and paste any logs that result from the issue occurring.
validations:
required: true
- type: textarea
id: replication
attributes:
label: Steps to replicate
description: Please list the steps that you took in order for the issue to occur.
validations:
required: true
- type: textarea
id: workaround
attributes:
label: Workaround
description: If you have a workaround, please detail it here, for the benefit of other users who encounter the issue.

32
.github/ISSUE_TEMPLATE/2_PRE_PR.yaml vendored Normal file
View File

@@ -0,0 +1,32 @@
name: Pre-Pull Request Discussion
description: If you intend to submit a PR and wish to discuss it first, start here.
labels: ["Status: Review Needed", "Type: Conceptual Enhancement"]
body:
- type: markdown
attributes:
value: |
Thanks for your interest in submitting a pull request to Winter CMS. We always value contributions from our community. If you wish to start a discussion on the pull request before submission, such as discussing implementation details, please use this form.
- type: dropdown
id: subsystem
attributes:
label: Package targeted
description: Please select which portion of Winter CMS your PR is targeting.
options:
- Winter CMS
- Storm Library
- Both
- Other (please specify below)
validations:
required: true
- type: textarea
id: description
attributes:
label: Description
description: Please describe what your PR is intending to do.
validations:
required: true
- type: textarea
id: compatibility
attributes:
label: Will this change be backwards-compatible?
description: Please describe if this intended change may break backwards compatibility or not. If it does, state the rationale in which you believe this to be an acceptable break in backwards compatibility.

14
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,14 @@
blank_issues_enabled: true
contact_links:
- name: General Winter CMS Support
url: https://wintercms.com/support
about: If you need help with Winter CMS, please review our support options.
- name: Discord
url: https://discord.gg/D5MFSPH6Ux
about: Join us on Discord for general discussions and one-on-one help.
- name: Feature Requests
url: https://github.com/wintercms/winter/discussions
about: Please post an Idea thread to the Discussions section for feature requests.
- name: Documentation Issue
url: https://github.com/wintercms/docs
about: For documentation issues, please submit an issue or PR to the Docs repository.

BIN
.github/assets/Github Banner.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 500 KiB

BIN
.github/assets/sponsor-route4me.png vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

68
.github/workflows/code-quality.yaml vendored Normal file
View File

@@ -0,0 +1,68 @@
name: Code Quality
on:
pull_request:
push:
branches:
- 1.0
- 1.1
- 1.2
- develop
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
codeQuality:
runs-on: ubuntu-latest
name: PHP
steps:
- name: Checkout changes
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Install PHP and PHP Code Sniffer
uses: shivammathur/setup-php@v2
with:
php-version: 8.2
extensions: curl, fileinfo, gd, mbstring, openssl, pdo, pdo_sqlite, sqlite3, xml, zip
tools: phpcs
- name: Run code quality checks (on push)
if: github.event_name == 'push'
run: ./.github/workflows/utilities/phpcs-push ${{ github.sha }}
- name: Run code quality checks (on pull request)
if: github.event_name == 'pull_request'
run: ./.github/workflows/utilities/phpcs-pr ${{ github.base_ref }}
codeQualityJS:
runs-on: ubuntu-latest
name: JavaScript
steps:
- name: Checkout changes
uses: actions/checkout@v2
with:
fetch-depth: 0
- name: Install Node
uses: actions/setup-node@v1
with:
node-version: 12
- name: Install Node dependencies for System Module
working-directory: ./modules/system
run: npm install
- name: Run code quality checks on System Module
working-directory: ./modules/system
run: npx eslint .
- name: Install Node dependencies for Backend Module
working-directory: ./modules/backend
run: npm install
- name: Run code quality checks on Backend Module
working-directory: ./modules/backend
run: npx eslint .

128
.github/workflows/docker.yml vendored Normal file
View File

@@ -0,0 +1,128 @@
name: Docker
on:
push:
tags:
- "v[0-9]+.[0-9]+.[0-9]+"
concurrency:
group: docker-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
updateDockerImage:
name: Update Docker image
runs-on: ubuntu-latest
env:
version: ${{ github.ref_name }}
steps:
- name: Checkout changes
uses: actions/checkout@v4
with:
repository: wintercms/docker
ref: main
token: ${{ secrets.WINTER_BOT_TOKEN }}
- name: Determine latest FrankenPHP, PHP and Node versions
id: versions
env:
GH_TOKEN: ${{ github.token }}
run: |
set -euo pipefail
# Stay within the major versions currently used by the Dockerfile
frankenphpMajor=$(sed -n 's/^ARG FRANKENPHP_VERSION="\([0-9]*\)\..*"$/\1/p' Dockerfile)
phpMajor=$(sed -n 's/^ARG PHP_VERSION="\([0-9]*\)\..*"$/\1/p' Dockerfile)
nodeMajor=$(sed -n 's/^ARG NODE_VERSION="v\([0-9]*\)\..*"$/\1/p' Dockerfile)
# The base image is a combined FrankenPHP + PHP tag, so both versions must be resolved
# together - the latest of each individually may not exist as a published image. Walk
# back through the FrankenPHP releases until one with a Trixie image is found.
frankenphp=""
php=""
for candidate in $(gh api "repos/php/frankenphp/releases?per_page=100" \
--jq '.[] | select(.draft == false and .prerelease == false) | .tag_name' \
| sed -n "s/^v\($frankenphpMajor\.[0-9]\+\.[0-9]\+\)$/\1/p"); do
php=$(curl -fsSL "https://hub.docker.com/v2/repositories/dunglas/frankenphp/tags?page_size=100&name=$candidate-php$phpMajor" \
| jq -r '.results[].name' \
| sed -n "s/^$candidate-php\($phpMajor\.[0-9]\+\.[0-9]\+\)-trixie$/\1/p" \
| sort -V \
| tail -n 1)
if [ -n "$php" ]; then
frankenphp="$candidate"
break
fi
done
if [ -z "$frankenphp" ] || [ -z "$php" ]; then
echo "Unable to resolve a FrankenPHP $frankenphpMajor.x image for PHP $phpMajor.x" >&2
exit 1
fi
node=$(curl -fsSL "https://nodejs.org/dist/index.json" \
| jq -r --arg major "v$nodeMajor." 'map(select(.version | startswith($major))) | .[0].version // ""')
if [ -z "$node" ]; then
echo "Unable to resolve a Node $nodeMajor.x release" >&2
exit 1
fi
echo "frankenphp=$frankenphp" >> "$GITHUB_OUTPUT"
echo "php=$php" >> "$GITHUB_OUTPUT"
echo "node=$node" >> "$GITHUB_OUTPUT"
- name: Update versions
env:
frankenphp: ${{ steps.versions.outputs.frankenphp }}
php: ${{ steps.versions.outputs.php }}
node: ${{ steps.versions.outputs.node }}
run: |
set -euo pipefail
currentFrankenphp=$(sed -n 's/^ARG FRANKENPHP_VERSION="\(.*\)"$/\1/p' Dockerfile)
currentPhp=$(sed -n 's/^ARG PHP_VERSION="\(.*\)"$/\1/p' Dockerfile)
currentNode=$(sed -n 's/^ARG NODE_VERSION="\(.*\)"$/\1/p' Dockerfile)
sed -i -E "s/^ARG WINTER_VERSION=\".*\"$/ARG WINTER_VERSION=\"$version\"/" Dockerfile
sed -i -E "s/^ARG FRANKENPHP_VERSION=\".*\"$/ARG FRANKENPHP_VERSION=\"$frankenphp\"/" Dockerfile
sed -i -E "s/^ARG PHP_VERSION=\".*\"$/ARG PHP_VERSION=\"$php\"/" Dockerfile
sed -i -E "s/^ARG NODE_VERSION=\".*\"$/ARG NODE_VERSION=\"$node\"/" Dockerfile
grep -q "^ARG WINTER_VERSION=\"$version\"$" Dockerfile
grep -q "^ARG FRANKENPHP_VERSION=\"$frankenphp\"$" Dockerfile
grep -q "^ARG PHP_VERSION=\"$php\"$" Dockerfile
grep -q "^ARG NODE_VERSION=\"$node\"$" Dockerfile
# Written outside the checkout so that it is not picked up by the commit
notes="$RUNNER_TEMP/release-notes.md"
echo "- Updated Winter to version $version" > "$notes"
if [ "$currentFrankenphp" != "$frankenphp" ]; then
echo "- Updated FrankenPHP to version $frankenphp" >> "$notes"
fi
if [ "$currentPhp" != "$php" ]; then
echo "- Updated PHP to version $php" >> "$notes"
fi
if [ "$currentNode" != "$node" ]; then
echo "- Updated Node to version $node" >> "$notes"
fi
- name: Commit changes
uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: Update to Winter ${{ env.version }}
file_pattern: Dockerfile
commit_user_name: Winter Bot
commit_user_email: 80384029+WinterCMSBot@users.noreply.github.com
- name: Publish release
env:
GH_TOKEN: ${{ secrets.WINTER_BOT_TOKEN }}
run: |
gh release create "$version" \
--repo wintercms/docker \
--target main \
--title "$version" \
--notes-file "$RUNNER_TEMP/release-notes.md"

69
.github/workflows/manifest.yml vendored Normal file
View File

@@ -0,0 +1,69 @@
name: Manifest
on:
push:
tags:
- "*"
workflow_dispatch:
concurrency:
group: manifest-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
updateManifest:
name: Update manifest
runs-on: ubuntu-latest
env:
phpVersion: "8.4"
extensions: curl, fileinfo, gd, mbstring, openssl, pdo, pdo_sqlite, sqlite3, xml, zip
key: winter-cms-cache-develop
steps:
- name: Checkout changes
uses: actions/checkout@v4
- name: Install PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ env.phpVersion }}
extensions: ${{ env.extensions }}
- name: Install Composer dependencies
run: composer install --no-interaction --no-progress --no-scripts
- name: Download manifest
run: wget -O builds.json https://github.com/wintercms/meta/raw/master/manifest/builds.json
- name: Run manifest
run: php artisan winter:manifest builds.json
- name: Create artifact
uses: actions/upload-artifact@v4
with:
name: winter-manifest
path: builds.json
commitManifest:
name: Commit manifest
runs-on: ubuntu-latest
needs: updateManifest
steps:
- name: Checkout changes
uses: actions/checkout@v4
with:
repository: wintercms/meta
ref: master
token: ${{ secrets.WINTER_BOT_TOKEN }}
- name: Download artifact
uses: actions/download-artifact@v4
with:
name: winter-manifest
path: manifest
- name: Commit changes
uses: stefanzweifel/git-auto-commit-action@v4
with:
commit_message: Update manifest
commit_user_name: Winter Bot
commit_user_email: 80384029+WinterCMSBot@users.noreply.github.com

32
.github/workflows/subsplit.yml vendored Normal file
View File

@@ -0,0 +1,32 @@
name: Module sub-split
on:
push:
# Branches only. Tag pushes are handled by the `create` event below; without
# this filter they would also fire here and pass the tag name to `split -b`,
# which then fails looking for a branch that doesn't exist.
branches:
- "**"
create:
delete:
jobs:
split:
name: Sub-split
runs-on: ubuntu-latest
container: wintercms/cli:0.3.4
env:
WINTER_CLI_GITHUB_TOKEN: ${{ secrets.WINTER_SPLIT_TOKEN }}
steps:
- name: Create tag
if: github.event_name == 'create' && github.ref_type == 'tag'
run: winter split -a "${{ github.ref_name }}"
- name: Delete branch
if: github.event_name == 'delete' && github.ref_type == 'branch'
run: winter split --remove-branch="${{ github.event.ref }}"
- name: Delete tag
if: github.event_name == 'delete' && github.ref_type == 'tag'
run: winter split --remove-tag="${{ github.event.ref }}"
- name: Push
if: github.event_name == 'push'
run: winter split -b "${{ github.ref_name }}"

175
.github/workflows/tests.yml vendored Normal file
View File

@@ -0,0 +1,175 @@
name: Tests
on:
push:
branches:
- '1.2'
- develop
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
frontendTests:
strategy:
max-parallel: 2
matrix:
operatingSystem: [ubuntu-latest, windows-latest]
fail-fast: false
runs-on: ${{ matrix.operatingSystem }}
name: ${{ matrix.operatingSystem }} / JavaScript
env:
nodeVersion: 16
phpVersion: '8.2'
extensions: curl, fileinfo, gd, mbstring, openssl, pdo, pdo_sqlite, sqlite3, xml, zip
key: winter-cms-cache-develop
steps:
- name: Checkout changes
uses: actions/checkout@v3
- name: Setup extension cache
id: extcache
uses: shivammathur/cache-extensions@v1
with:
php-version: ${{ env.phpVersion }}
extensions: ${{ env.extensions }}
key: ${{ env.key }}
- name: Cache extensions
uses: actions/cache@v3
with:
path: ${{ steps.extcache.outputs.dir }}
key: ${{ steps.extcache.outputs.key }}
restore-keys: ${{ steps.extcache.outputs.key }}
- name: Install PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ env.phpVersion }}
extensions: ${{ env.extensions }}
- name: Install Node
uses: actions/setup-node@v3
with:
node-version: ${{ env.nodeVersion }}
- name: Switch library dependency (develop)
if: github.ref == 'refs/heads/develop' || github.base_ref == 'develop'
run: php ./.github/workflows/utilities/library-switcher "dev-develop as 1.2"
- name: Switch library dependency (1.2)
if: github.head_ref == '1.2' || github.ref == 'refs/heads/1.2' || github.base_ref == '1.2'
run: php ./.github/workflows/utilities/library-switcher "1.2.x-dev as 1.2"
- name: Setup dependency cache
id: composercache
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Cache dependencies
uses: actions/cache@v3
with:
path: ${{ steps.composercache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
restore-keys: ${{ runner.os }}-composer-
- name: Install Composer dependencies
run: composer install --no-interaction --no-progress --no-scripts
- name: Reset modules
run: |
git reset --hard
git clean -fd
- name: Run post-update Composer scripts
run: php artisan package:discover
- name: Run tests
run: |
php artisan mix:install
php artisan mix:run module-system test
phpUnitTests:
strategy:
max-parallel: 8
matrix:
operatingSystem: [ubuntu-latest, windows-latest]
phpVersion: ['8.1', '8.2', '8.3', '8.4', '8.5']
fail-fast: false
runs-on: ${{ matrix.operatingSystem }}
name: ${{ matrix.operatingSystem }} / PHP ${{ matrix.phpVersion }}
env:
extensions: curl, fileinfo, gd, mbstring, openssl, pdo, pdo_sqlite, sqlite3, xml, zip
key: winter-cms-cache-develop
steps:
- name: Checkout changes
uses: actions/checkout@v3
- name: Setup extension cache
id: extcache
uses: shivammathur/cache-extensions@v1
with:
php-version: ${{ matrix.phpVersion }}
extensions: ${{ env.extensions }}
key: ${{ env.key }}
- name: Cache extensions
uses: actions/cache@v3
with:
path: ${{ steps.extcache.outputs.dir }}
key: ${{ steps.extcache.outputs.key }}
restore-keys: ${{ steps.extcache.outputs.key }}
- name: Install PHP
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.phpVersion }}
extensions: ${{ env.extensions }}
- name: Switch library dependency (develop)
if: github.ref == 'refs/heads/develop' || github.base_ref == 'develop'
run: php ./.github/workflows/utilities/library-switcher "dev-develop as 1.2"
- name: Switch library dependency (1.0)
if: github.head_ref == '1.0' || github.ref == 'refs/heads/1.0' || github.base_ref == '1.0'
run: php ./.github/workflows/utilities/library-switcher "1.0.x-dev as 1.0"
- name: Switch library dependency (1.1)
if: github.head_ref == '1.1' || github.ref == 'refs/heads/1.1' || github.base_ref == '1.1'
run: php ./.github/workflows/utilities/library-switcher "1.1.x-dev as 1.1"
- name: Switch library dependency (1.2)
if: github.head_ref == '1.2' || github.ref == 'refs/heads/1.2' || github.base_ref == '1.2'
run: php ./.github/workflows/utilities/library-switcher "1.2.x-dev as 1.2"
- name: Setup dependency cache
id: composercache
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Cache dependencies
uses: actions/cache@v3
with:
path: ${{ steps.composercache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
restore-keys: ${{ runner.os }}-composer-
- name: Install Composer dependencies
run: composer install --no-interaction --no-progress --no-scripts
- name: Reset modules
run: |
git reset --hard
git clean -fd
- name: Run post-update Composer scripts
run: php artisan package:discover
- name: Setup problem matchers for PHPUnit
if: matrix.phpVersion == '8.1'
run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
- name: Run Linting and Tests
run: |
composer lint
php artisan winter:test -m system -m backend -m cms

18
.github/workflows/utilities/library-switcher vendored Executable file
View File

@@ -0,0 +1,18 @@
#!/usr/bin/env php
<?php
/**
* Storm library switch for CI
*
* Switches the version of the Storm library being required through Composer. The only argument is the branch or tag
* to switch to.
*/
if (empty($argv[1])) {
echo 'You must provide a version to switch the library dependency to.';
echo "\n";
exit(1);
}
$composer = json_decode(file_get_contents(getcwd() . '/composer.json'), true);
$composer['require']['winter/storm'] = $argv[1];
file_put_contents(getcwd() . '/composer.json', json_encode($composer, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES));

87
.github/workflows/utilities/phpcs-pr vendored Executable file
View File

@@ -0,0 +1,87 @@
#!/usr/bin/env php
<?php
/**
* Run PHPCS tests against a PR.
*
* The only argument is for the PR's base branch, which is then compared to the HEAD of the PR to retrieve the list
* of changed files. The PHPCS tests are only run against these changed files, to speed up the tests.
*/
if (empty($argv[1])) {
fwrite(STDERR, 'You must provide a base branch to check this PR against.');
fwrite(STDERR, "\n");
exit(1);
}
// Get a changelist of files from Git for this PR.
$fileList = shell_exec('git diff --name-only --diff-filter=ACMR origin/' . $argv[1] . ' HEAD');
$files = array_filter(explode("\n", $fileList));
foreach ($files as &$file) {
if (strpos($file, ' ') !== false) {
$file = str_replace(' ', '\\ ', $file);
}
}
// no changes found in diff, early exit
if (!count($files)) {
fwrite(STDOUT, "\e[0;32mFound no changed files.\e[0m");
fwrite(STDOUT, "\n");
exit(0);
}
// Run all changed files through the PHPCS code sniffer and generate a CSV report
$csv = shell_exec('phpcs --colors -nq --report="csv" --extensions="php" ' . implode(' ', $files));
$lines = array_map(function ($row) {
return array_map(function ($column) {
return trim($column, '"');
}, explode(',', $row));
}, array_filter(explode("\n", $csv)));
// Remove header row
array_shift($lines);
if (!count($lines)) {
fwrite(STDOUT, "\e[0;32mFound no issues with code quality.\e[0m");
fwrite(STDOUT, "\n");
exit(0);
}
// Group errors by file
$files = [];
foreach ($lines as $line) {
$filename = str_replace(dirname(dirname(dirname(__DIR__))), '', $line[0]);
if (empty($files[$filename])) {
$files[$filename] = [];
}
$files[$filename][] = [
'warning' => (($line[3] ?? 'err') === 'warning'),
'message' => $line[4] ?? 'unknown',
'line' => $line[1] ?? '0',
];
}
// Render report
fwrite(STDERR, "\e[0;31mFound "
. ((count($lines) === 1)
? '1 issue'
: count($lines) . ' issues')
. " with code quality.\e[0m");
fwrite(STDERR, "\n");
foreach ($files as $file => $errors) {
fwrite(STDERR, "\n");
fwrite(STDERR, "\e[1;37m" . str_replace('"', '', $file) . "\e[0m");
fwrite(STDERR, "\n\n");
foreach ($errors as $error) {
fwrite(STDERR, "\e[2m" . str_pad(' L' . $error['line'], 7) . " | \e[0m");
fwrite(STDERR, $error['warning'] ? "\e[1;33mWARN:\e[0m " : "\e[0;31mERR:\e[0m ");
fwrite(STDERR, $error['message']);
fwrite(STDERR, "\n");
}
}
exit(1);

87
.github/workflows/utilities/phpcs-push vendored Executable file
View File

@@ -0,0 +1,87 @@
#!/usr/bin/env php
<?php
/**
* Run PHPCS tests against a push.
*
* The only argument is for the commit, which a list of changed files is retrieved from. The PHPCS tests are only run
* against these changed files, to speed up the tests.
*/
if (empty($argv[1])) {
fwrite(STDERR, 'You must provide a commit SHA to check.');
fwrite(STDERR, "\n");
exit(1);
}
// Get a changelist of files from Git for this push.
$fileList = shell_exec('git show --name-only --pretty="" --diff-filter=ACMR ' . $argv[1]);
$files = array_filter(explode("\n", $fileList));
foreach ($files as &$file) {
if (strpos($file, ' ') !== false) {
$file = str_replace(' ', '\\ ', $file);
}
}
// no changes found in diff, early exit
if (!count($files)) {
fwrite(STDOUT, "\e[0;32mFound no changed files.\e[0m");
fwrite(STDOUT, "\n");
exit(0);
}
// Run all changed files through the PHPCS code sniffer and generate a CSV report
$csv = shell_exec('phpcs --colors -nq --report="csv" --extensions="php" ' . implode(' ', $files));
$lines = array_map(function ($row) {
return array_map(function ($column) {
return trim($column, '"');
}, explode(',', $row));
}, array_filter(explode("\n", $csv)));
// Remove header row
array_shift($lines);
if (!count($lines)) {
fwrite(STDOUT, "\e[0;32mFound no issues with code quality.\e[0m");
fwrite(STDOUT, "\n");
exit(0);
}
// Group errors by file
$files = [];
foreach ($lines as $line) {
$filename = str_replace(dirname(dirname(dirname(__DIR__))), '', $line[0]);
if (empty($files[$filename])) {
$files[$filename] = [];
}
$files[$filename][] = [
'warning' => (($line[3] ?? 'err') === 'warning'),
'message' => $line[4] ?? 'unknown',
'line' => $line[1] ?? '0',
];
}
// Render report
fwrite(STDERR, "\e[0;31mFound "
. ((count($lines) === 1)
? '1 issue'
: count($lines) . ' issues')
. " with code quality.\e[0m");
fwrite(STDERR, "\n");
foreach ($files as $file => $errors) {
fwrite(STDERR, "\n");
fwrite(STDERR, "\e[1;37m" . str_replace('"', '', $file) . "\e[0m");
fwrite(STDERR, "\n\n");
foreach ($errors as $error) {
fwrite(STDERR, "\e[2m" . str_pad(' L' . $error['line'], 7) . " | \e[0m");
fwrite(STDERR, $error['warning'] ? "\e[1;33mWARN:\e[0m " : "\e[0;31mERR:\e[0m ");
fwrite(STDERR, $error['message']);
fwrite(STDERR, "\n");
}
}
exit(1);