This repository contains shared configuration, reusable workflows, and composite actions for all repositories within the Bisnow organization.
Run
bisnow repo:initfrom bisnow/local to ensure your repository has all the required Composer scripts and configuration files for these workflows.
- Reusable Workflows
- Composite Actions
- Shared Configuration
- Versioning
The tests-laravel.yml workflow runs PHPStan static analysis and PHPUnit/Pest tests for Laravel applications. It handles service setup, dependency caching, and environment preparation automatically.
At minimum, you only need to provide the COMPOSER_OAUTH_GITHUB_ACTIONS secret:
# .github/workflows/tests.yml
name: Tests
on: [push]
jobs:
tests:
uses: bisnow/.github/.github/workflows/tests-laravel.yml@v2
secrets:
COMPOSER_OAUTH_GITHUB_ACTIONS: ${{ secrets.COMPOSER_OAUTH_GITHUB_ACTIONS }}This gives you PHP 8.4, MySQL (runner service), and Redis out of the box.
Many applications require additional databases for multi-tenancy or service isolation:
jobs:
tests:
uses: bisnow/.github/.github/workflows/tests-laravel.yml@v2
with:
mysql_db_name: bisreach
mysql_db_names: '["contacts", "forage", "tidy", "salutary"]'
secrets:
COMPOSER_OAUTH_GITHUB_ACTIONS: ${{ secrets.COMPOSER_OAUTH_GITHUB_ACTIONS }}Note: The
mysql_db_namesinput accepts a JSON array. Invalid JSON will fail fast with a clear error message.
For applications that require Node.js asset compilation:
jobs:
tests:
uses: bisnow/.github/.github/workflows/tests-laravel.yml@v2
with:
enable_npm: true
secrets:
COMPOSER_OAUTH_GITHUB_ACTIONS: ${{ secrets.COMPOSER_OAUTH_GITHUB_ACTIONS }}Node reads the version from .nvmrc by default. Supported version files:
.nvmrc.node-version.tool-versionspackage.json(via theengines.nodefield)
To use a different file:
with:
enable_npm: true
node_version_file: '.node-version'For applications that need a search engine:
jobs:
tests:
uses: bisnow/.github/.github/workflows/tests-laravel.yml@v2
with:
enable_opensearch: true
secrets:
COMPOSER_OAUTH_GITHUB_ACTIONS: ${{ secrets.COMPOSER_OAUTH_GITHUB_ACTIONS }}OpenSearch runs as a single-node Docker container with security disabled, suitable for testing.
For applications using PostgreSQL alongside or instead of MySQL:
jobs:
tests:
uses: bisnow/.github/.github/workflows/tests-laravel.yml@v2
with:
enable_pgsql: true
pgsql_db_name: warehouse
pgsql_db_names: '["analytics"]'
secrets:
COMPOSER_OAUTH_GITHUB_ACTIONS: ${{ secrets.COMPOSER_OAUTH_GITHUB_ACTIONS }}By default, MySQL and PostgreSQL use the pre-installed runner services for faster startup. When you need a specific version, enable Docker mode:
jobs:
tests:
uses: bisnow/.github/.github/workflows/tests-laravel.yml@v2
with:
mysql_use_docker: true
mysql_version: '8.0.35'
enable_pgsql: true
pgsql_use_docker: true
pgsql_version: '16'
secrets:
COMPOSER_OAUTH_GITHUB_ACTIONS: ${{ secrets.COMPOSER_OAUTH_GITHUB_ACTIONS }}The pgsql_image and pgsql_version values correspond to Docker image tags. When using a custom image like pgvector, the tag format may differ:
# Standard postgres image: postgres:16
pgsql_image: postgres
pgsql_version: '16'
# pgvector image: pgvector/pgvector:pg16
pgsql_image: pgvector/pgvector
pgsql_version: 'pg16'For applications using Flux Pro components:
jobs:
tests:
uses: bisnow/.github/.github/workflows/tests-laravel.yml@v2
secrets:
COMPOSER_OAUTH_GITHUB_ACTIONS: ${{ secrets.COMPOSER_OAUTH_GITHUB_ACTIONS }}
FLUX_USERNAME: ${{ secrets.FLUX_USERNAME }}
FLUX_PASSWORD: ${{ secrets.FLUX_PASSWORD }}An application using every available option:
jobs:
tests:
uses: bisnow/.github/.github/workflows/tests-laravel.yml@v2
with:
# Matrix
php: '["8.3", "8.4"]'
os: '["ubuntu-24.04"]'
dependency_version: '["prefer-stable"]'
# Feature flags
enable_npm: true
enable_opensearch: true
enable_pgsql: true
# MySQL
mysql_use_docker: true
mysql_image: mysql
mysql_version: '8.0.35'
mysql_username: root
mysql_password: root
mysql_db_name: bisreach
mysql_db_names: '["contacts", "forage", "tidy", "salutary"]'
# Node
node_version_file: '.nvmrc'
# OpenSearch
opensearch_version: '2.13.0'
opensearch_java_opts: '-Xms512m -Xmx512m'
# PostgreSQL
pgsql_use_docker: true
pgsql_image: pgvector/pgvector
pgsql_version: 'pg16'
pgsql_username: postgres
pgsql_password: postgres
pgsql_db_name: warehouse
pgsql_db_names: '["analytics"]'
# PHP
php_extensions: 'mbstring, dom, fileinfo, mysql, pgsql'
php_coverage: pcov
# Redis
redis_image: valkey/valkey
redis_version: alpine
secrets:
COMPOSER_OAUTH_GITHUB_ACTIONS: ${{ secrets.COMPOSER_OAUTH_GITHUB_ACTIONS }}
FLUX_USERNAME: ${{ secrets.FLUX_USERNAME }}
FLUX_PASSWORD: ${{ secrets.FLUX_PASSWORD }}| Input | Type | Default | Description |
|---|---|---|---|
php |
string | '["8.4"]' |
JSON array of PHP versions |
os |
string | '["ubuntu-24.04"]' |
JSON array of runner OS versions |
dependency_version |
string | '["prefer-stable"]' |
JSON array of Composer stability flags |
| Input | Type | Default | Description |
|---|---|---|---|
enable_npm |
boolean | false |
Enable Node.js install and build |
enable_opensearch |
boolean | false |
Enable OpenSearch container |
enable_pgsql |
boolean | false |
Enable PostgreSQL |
| Input | Type | Default | Description |
|---|---|---|---|
mysql_use_docker |
boolean | false |
Use Docker instead of runner service |
mysql_image |
string | mysql |
Docker image (only with mysql_use_docker) |
mysql_version |
string | 8.0.32 |
Docker image version (only with mysql_use_docker) |
mysql_username |
string | root |
Database username |
mysql_password |
string | root |
Database password |
mysql_db_name |
string | laravel |
Primary database name (sets DB_DATABASE env) |
mysql_db_names |
string | [] |
JSON array of additional database names |
| Input | Type | Default | Description |
|---|---|---|---|
node_build_script |
string | npm script name to run (auto-detects Vite/Mix by default) | |
node_version_file |
string | .nvmrc |
Path to node version file |
| Input | Type | Default | Description |
|---|---|---|---|
opensearch_version |
string | 2.13.0 |
Docker image version |
opensearch_java_opts |
string | -Xms512m -Xmx512m |
JVM heap options |
| Input | Type | Default | Description |
|---|---|---|---|
pgsql_use_docker |
boolean | false |
Use Docker instead of runner service |
pgsql_image |
string | postgres |
Docker image (only with pgsql_use_docker) |
pgsql_version |
string | 16 |
Docker image version (only with pgsql_use_docker) |
pgsql_username |
string | postgres |
Database username |
pgsql_password |
string | postgres |
Database password |
pgsql_db_name |
string | laravel |
Primary database name |
pgsql_db_names |
string | [] |
JSON array of additional database names |
| Input | Type | Default | Description |
|---|---|---|---|
php_extensions |
string | mbstring, dom, fileinfo, mysql |
PHP extensions to install |
php_coverage |
string | pcov |
Coverage driver |
| Input | Type | Default | Description |
|---|---|---|---|
redis_image |
string | valkey/valkey |
Docker image |
redis_version |
string | alpine |
Docker image version |
| Secret | Required | Description |
|---|---|---|
COMPOSER_OAUTH_GITHUB_ACTIONS |
Yes | GitHub OAuth token for Composer |
FLUX_USERNAME |
No | Flux Pro username |
FLUX_PASSWORD |
No | Flux Pro password |
The workflow automatically imports Laravel schema dumps when present. Schema files are detected by convention:
MySQL looks for these files in order (first match wins):
database/schema/mysql.sqldatabase/schema/mysql-schema.sql
PostgreSQL looks for:
database/schema/pgsql.sqldatabase/schema/pgsql-schema.sql
Additional databases look for schemas named after the database:
database/schema/{db_name}.sqldatabase/schema/{db_name}-schema.sql
The workflow sets these environment variables automatically for your test suite:
| Variable | Value | Source |
|---|---|---|
DB_DATABASE |
mysql_db_name input |
MySQL primary database |
DB_HOST |
127.0.0.1 |
|
DB_PORT |
3306 |
|
DB_USERNAME |
mysql_username input |
|
DB_PASSWORD |
mysql_password input |
|
REDIS_HOST |
127.0.0.1 |
|
REDIS_PORT |
6379 |
|
REDIS_SCHEME |
tcp |
|
APP_URL |
http://localhost |
Database actions support two modes:
| Runner Service (default) | Docker | |
|---|---|---|
| Speed | Fast (pre-installed) | Slower (image pull) |
| Version control | Runner's installed version | Exact version pinning |
| Use when | Version doesn't matter | You need a specific version |
The runner service starts the pre-installed MySQL/PostgreSQL on the GitHub Actions runner. No image pull is required, which typically saves 20-30 seconds.
The workflow optimizes execution by overlapping service startup with language tool installation:
| Phase | Steps | Description |
|---|---|---|
| 1 | Start services | Launch all containers and runner services with wait: false |
| 2 | Install PHP, Composer, Node | Install language tools while services initialize in the background |
| 3 | Finish service setup | Health checks pass instantly, then databases are created |
After setup completes, the workflow runs PHPStan static analysis followed by tests.
The code-styling-laravel.yml workflow automatically fixes code style using Rector and Laravel Pint, then commits the changes directly to the branch.
# .github/workflows/code-styling.yml
name: Code Styling
on: [push]
jobs:
styling:
uses: bisnow/.github/.github/workflows/code-styling-laravel.yml@v2
secrets:
COMPOSER_OAUTH_GITHUB_ACTIONS: ${{ secrets.COMPOSER_OAUTH_GITHUB_ACTIONS }}jobs:
styling:
uses: bisnow/.github/.github/workflows/code-styling-laravel.yml@v2
secrets:
COMPOSER_OAUTH_GITHUB_ACTIONS: ${{ secrets.COMPOSER_OAUTH_GITHUB_ACTIONS }}
FLUX_USERNAME: ${{ secrets.FLUX_USERNAME }}
FLUX_PASSWORD: ${{ secrets.FLUX_PASSWORD }}jobs:
styling:
uses: bisnow/.github/.github/workflows/code-styling-laravel.yml@v2
with:
php: '8.3'
secrets:
COMPOSER_OAUTH_GITHUB_ACTIONS: ${{ secrets.COMPOSER_OAUTH_GITHUB_ACTIONS }}- Checks out the branch that triggered the workflow
- Installs Composer dependencies (with
--no-scriptsto skip post-install hooks) - Runs
composer rectorand commits any changes - Runs
composer cs-fixand commits any changes
Both commits use [skip actions] in the message to prevent triggering additional workflow runs.
| Input | Type | Default | Description |
|---|---|---|---|
php |
string | 8.4 |
PHP version |
| Secret | Required | Description |
|---|---|---|
COMPOSER_OAUTH_GITHUB_ACTIONS |
Yes | GitHub OAuth token for Composer |
FLUX_USERNAME |
No | Flux Pro username |
FLUX_PASSWORD |
No | Flux Pro password |
Most applications will use both workflows. The bisnow repo:init command generates this file automatically, but here's what the standard template looks like:
# .github/workflows/tests.yml
name: Tests
on:
push:
branches-ignore:
- main
jobs:
code-styling:
uses: bisnow/.github/.github/workflows/code-styling-laravel.yml@v2
with:
php: '8.4'
secrets:
COMPOSER_OAUTH_GITHUB_ACTIONS: ${{ secrets.COMPOSER_OAUTH_GITHUB_ACTIONS }}
FLUX_USERNAME: ${{ secrets.FLUX_USERNAME }}
FLUX_PASSWORD: ${{ secrets.FLUX_PASSWORD }}
tests:
needs: code-styling
uses: bisnow/.github/.github/workflows/tests-laravel.yml@v2
with:
php: '["8.4"]'
enable_npm: true
mysql_db_names: '["address", "contacts"]'
secrets:
COMPOSER_OAUTH_GITHUB_ACTIONS: ${{ secrets.COMPOSER_OAUTH_GITHUB_ACTIONS }}
FLUX_USERNAME: ${{ secrets.FLUX_USERNAME }}
FLUX_PASSWORD: ${{ secrets.FLUX_PASSWORD }}
tests-styling-complete:
name: Tests and Styling Complete
needs: tests
if: always()
runs-on: ubuntu-latest
steps:
- name: Check if Tests Passed
run: |
if [[ "${{ needs.tests.result }}" == "success" ]]; then
echo "All checks passed"
exit 0
else
echo "Some checks failed"
echo "tests: ${{ needs.tests.result }}"
exit 1
fiThe tests-styling-complete job acts as a single status check for branch protection rules, so you only need to require one check instead of two.
Each action can be used independently in your own workflows if the reusable workflow doesn't fit your needs.
Starts MySQL, creates databases, and imports schema dumps.
- uses: bisnow/.github/.github/actions/setup-mysql@v2
with:
db-name: my_app
db-names: '["my_app_testing", "my_app_audit"]'| Input | Default | Description |
|---|---|---|
wait |
true |
Wait for service to be ready before continuing |
use-docker |
false |
Use Docker instead of runner service |
image |
mysql |
Docker image |
version |
8.0.32 |
Docker image version |
container-name |
mysql-test |
Docker container name |
host |
127.0.0.1 |
Database host |
port |
3306 |
Database port |
username |
root |
Database username |
password |
root |
Database password |
db-name |
laravel |
Primary database name |
db-names |
[] |
JSON array of additional databases |
Starts an OpenSearch container configured for testing (single-node, security disabled).
- uses: bisnow/.github/.github/actions/setup-opensearch@v2| Input | Default | Description |
|---|---|---|
wait |
true |
Wait for service to be ready before continuing |
version |
2.13.0 |
Docker image version |
container-name |
opensearch-test |
Docker container name |
port |
9200 |
Port |
java-opts |
-Xms512m -Xmx512m |
JVM heap options |
Starts PostgreSQL, creates a user, creates databases, and imports schema dumps.
- uses: bisnow/.github/.github/actions/setup-pgsql@v2
with:
username: my_user
password: my_password
db-name: my_appWith a custom Docker image (e.g., pgvector):
- uses: bisnow/.github/.github/actions/setup-pgsql@v2
with:
use-docker: true
image: pgvector/pgvector
version: pg16
db-name: my_app| Input | Default | Description |
|---|---|---|
wait |
true |
Wait for service to be ready before continuing |
use-docker |
false |
Use Docker instead of runner service |
image |
postgres |
Docker image |
version |
16 |
Docker image version |
container-name |
pgsql-test |
Docker container name |
host |
127.0.0.1 |
Database host |
port |
5432 |
Database port |
username |
postgres |
Database username |
password |
postgres |
Database password |
db-name |
laravel |
Primary database name |
db-names |
[] |
JSON array of additional databases |
Sets up PHP with extensions, configures Composer authentication, caches dependencies, and installs packages.
- uses: bisnow/.github/.github/actions/setup-php-composer@v2
with:
php-version: '8.4'
composer-oauth-token: ${{ secrets.COMPOSER_OAUTH_GITHUB_ACTIONS }}With Flux Pro and additional extensions:
- uses: bisnow/.github/.github/actions/setup-php-composer@v2
with:
php-version: '8.4'
extensions: 'mbstring, dom, fileinfo, mysql, pgsql, redis'
composer-oauth-token: ${{ secrets.COMPOSER_OAUTH_GITHUB_ACTIONS }}
flux-username: ${{ secrets.FLUX_USERNAME }}
flux-password: ${{ secrets.FLUX_PASSWORD }}| Input | Required | Default | Description |
|---|---|---|---|
php-version |
Yes | PHP version | |
extensions |
No | mbstring, dom, fileinfo, mysql |
PHP extensions |
coverage |
No | pcov |
Coverage driver |
composer-oauth-token |
Yes | GitHub OAuth token | |
flux-username |
No | Flux Pro username | |
flux-password |
No | Flux Pro password |
Sets up Node.js (version from file), caches dependencies, installs packages, and builds assets. The build step automatically detects the bundler:
- Vite (
vite.config.jsorvite.config.ts) runsnpm run build - Laravel Mix (
webpack.mix.js) runsnpm run prod - No bundler detected fails with an error suggesting you set
build-script
- uses: bisnow/.github/.github/actions/setup-node-npm@v2With an explicit build script:
- uses: bisnow/.github/.github/actions/setup-node-npm@v2
with:
build-script: prod| Input | Default | Description |
|---|---|---|
build-script |
npm script name to run (auto-detects Vite/Mix by default) | |
node-version-file |
.nvmrc |
Path to node version file |
Starts a Redis-compatible container.
- uses: bisnow/.github/.github/actions/setup-redis@v2With a specific Redis image:
- uses: bisnow/.github/.github/actions/setup-redis@v2
with:
image: redis
version: '7-alpine'| Input | Default | Description |
|---|---|---|
wait |
true |
Wait for service to be ready before continuing |
image |
valkey/valkey |
Docker image |
version |
alpine |
Docker image version |
container-name |
redis-test |
Docker container name |
port |
6379 |
Port |
This repository provides two PR templates that are automatically available to all repositories in the Bisnow organization:
- Default PR Template - Used for feature branches. Includes sections for Jira ticket, release notes, description, testing instructions, QA, dependencies, and deployment notes.
- Release PR Template - Used for release PRs. Includes sections for related PRs, release notes, dependencies, and deployment notes.
To use the release template, append ?template=release.md to the PR creation URL, or select it from the template dropdown when creating a PR on GitHub.
The .editorconfig file defines consistent coding styles across editors and IDEs:
- UTF-8 charset, LF line endings, trailing newline
- 4-space indentation for most files
- 2-space indentation for YAML files (except
docker-compose.ymlwhich uses 4) - No trailing whitespace trimming in Markdown files
This file is automatically picked up by editors that support EditorConfig.
This repository follows semantic versioning. Reference a major version tag for automatic patch updates:
# Recommended: major version tag (gets patches automatically)
uses: bisnow/.github/.github/workflows/tests-laravel.yml@v2
# Exact version (pinned, no automatic updates)
uses: bisnow/.github/.github/workflows/tests-laravel.yml@v2.0.0Breaking changes (new major version) include renaming inputs, removing inputs, or changing default behavior. Non-breaking additions (new optional inputs, new actions) are released as minor versions.