Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,6 @@
/AGENTS.md export-ignore
/CLAUDE.md export-ignore
/.github export-ignore
/phpunit.xml export-ignore
/phpstan.neon export-ignore
/pint.json export-ignore
11 changes: 11 additions & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
version: 2
updates:
- package-ecosystem: composer
directory: /
schedule:
interval: weekly
open-pull-requests-limit: 5
- package-ecosystem: github-actions
directory: /
schedule:
interval: weekly
14 changes: 14 additions & 0 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
## What changes and why

Describe the problem and the smallest complete solution.

## Evidence

List the commands run and their results. For native changes, include the real
compiled app, crash/relaunch results and screenshots, identifying the platform
and whether it was a simulator or physical device. CI does not prove native
crash capture.

## Compatibility

Describe public API, event format or minimum-version changes, if any.
65 changes: 65 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
name: CI

on:
push:
branches: [main]
pull_request:
workflow_dispatch:

permissions:
contents: read

concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

env:
COMPOSER_ROOT_VERSION: 0.2.x-dev

jobs:
quality:
name: Quality
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: shivammathur/setup-php@eb7c497e18156a6bbabfef1d3a82760b9eda3962 # v2
with:
php-version: '8.4'
extensions: mbstring, pdo_sqlite
tools: composer:v2
coverage: none
- run: composer validate --strict
- run: composer update --no-interaction --prefer-dist --no-progress
- run: composer audit
- run: composer test:lint
- run: composer test:types
- run: composer test:type-coverage

tests:
name: Tests (PHP ${{ matrix.php }}, Laravel ${{ matrix.laravel }})
runs-on: ubuntu-latest
timeout-minutes: 15
strategy:
fail-fast: false
matrix:
php: ['8.4', '8.5']
laravel: ['12', '13']
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6
with:
persist-credentials: false
- uses: shivammathur/setup-php@eb7c497e18156a6bbabfef1d3a82760b9eda3962 # v2
with:
php-version: ${{ matrix.php }}
extensions: mbstring, pdo_sqlite
tools: composer:v2
coverage: none
- name: Select Laravel version
env:
LARAVEL_VERSION: ${{ matrix.laravel }}
run: composer require --dev "orchestra/testbench:^$((LARAVEL_VERSION - 2)).0" --no-update --no-interaction
- run: composer update --no-interaction --prefer-dist --no-progress
- run: composer test:unit
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
/vendor/
/composer.lock
/.phpunit.cache/
/.phpunit.result.cache
/.phpstan-cache/
/.php-cs-fixer.cache
58 changes: 58 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Contributing

Use PHP 8.4 or newer and Composer 2. Open an issue before a substantial feature
or event-format change. Include SDK, Laravel and NativePHP versions, a minimal
reproduction and redacted logs. Never publish credentials or customer reports.
Report vulnerabilities privately to a repository maintainer.

Shared capture, redaction and delivery belong in `native-core`. Platform hooks
belong in `native-desktop` or `native-mobile`. Keep changes focused, classes
final, types explicit and files within 300 lines. Reporting must never replace
the original application error.

## Run the checks

```sh
COMPOSER_ROOT_VERSION=0.2.x-dev composer install
composer test
```

The root version identifies the development checkout as part of the 0.2 line.
Composer will resolve a lock file locally; libraries do not commit it. Tests use
Orchestra Testbench and fake HTTP, without the private server, production keys
or a running NativePHP app. `composer test` runs Pint, PHPStan, 100% PHP type coverage and Pest. GitHub
also validates Composer metadata, audits dependencies and runs Pest on PHP 8.4
and 8.5 with Laravel 12 and 13.

For one Laravel version, select its matching Testbench before installing:

```sh
composer require --dev 'orchestra/testbench:^10.0' --no-update
COMPOSER_ROOT_VERSION=0.2.x-dev composer update
composer test
```

Testbench 10 tests Laravel 12; Testbench 11 tests Laravel 13. Do not commit a local matrix selection to `composer.json`.

The runtime dependency constraint still accepts Laravel 11, but CI does not
verify it: Composer blocks its current releases due to security advisories.
No advisory ignores or disabled security gates are used.

Native reporting changes also require a compiled Release app, a real crash and
relaunch, receiver assertions and screenshots. Identify the platform and
whether evidence comes from a simulator or physical device. PHP CI does not
compile Swift or Kotlin or establish native crash capture.

## Submit a pull request

Explain the failing behavior, the smallest complete fix and the commands you
actually ran. Include a regression test, compatibility changes and remaining
limits. Use a branch and open a pull request against `main`. Merging requires
one approving review from another maintainer, resolved conversations and all
required CI checks passing against the latest `main`. New commits dismiss old
approvals. Force pushes and deletion of `main` are blocked.

Workflow actions are pinned to full commit hashes and updated by Dependabot.
Keep workflow permissions read-only and never expose secrets to forked code or
use `pull_request_target` to execute a contributor's checkout. Published `v*`
tags cannot be rewritten or deleted. Fix a release with a new version.
31 changes: 31 additions & 0 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,5 +38,36 @@
"docs": "https://native.watch/docs",
"issues": "https://github.com/zinxan/native.desktop/issues",
"source": "https://github.com/zinxan/native.desktop"
},
"require-dev": {
"larastan/larastan": "^3.9",
"laravel/pint": "^1.27",
"orchestra/testbench": "^10.0|^11.0",
"pestphp/pest": "^3.8|^4.0|^5.0",
"pestphp/pest-plugin-laravel": "^3.0|^4.0|^5.0",
"pestphp/pest-plugin-type-coverage": "^3.0|^4.0|^5.0"
},
"autoload-dev": {
"psr-4": {
"ZXN\\NativeWatch\\Desktop\\Tests\\": "tests/"
}
},
"config": {
"allow-plugins": {
"pestphp/pest-plugin": true
},
"sort-packages": true
},
"scripts": {
"test": [
"@test:lint",
"@test:types",
"@test:type-coverage",
"@test:unit"
],
"test:lint": "pint --test",
"test:types": "phpstan analyse --memory-limit=1G",
"test:unit": "pest --compact",
"test:type-coverage": "pest --type-coverage --min=100"
}
}
25 changes: 25 additions & 0 deletions docs/review/sdk-ci.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# Standalone SDK CI evidence

Verified on 3 November 2026 with PHP 8.4.27 and Laravel 13.

- `composer validate --strict`: passed.
- `composer audit`: no advisories or abandoned dependencies.
- `composer test`: Pint and PHPStan level 7 passed, 100% PHP type coverage,
5 Pest tests and 10 assertions passed.
- `git diff --check`: passed.

The workflow repeats Pest across PHP 8.4/8.5 and Laravel 12/13. Its Quality
job validates Composer, audits dependencies and runs lint, static analysis and
type coverage. Every job runs for every pull request, without path filters.
Both Actions are pinned to full commit hashes; checkout credentials are not
persisted, the token is read-only and the workflow uses no repository secrets.

The public suite runs against Orchestra Testbench rather than the private
NativeWatch server. Server acceptance and end-to-end native crash behavior
still require their respective integration checks. This CI does not compile
or execute Swift, Kotlin, an iOS simulator or an Android device.

The initial GitHub Laravel 11 matrix jobs were blocked by Composer security
advisories, including PKSA-b41t-wbzk-9q51 and PKSA-d5tc-s1qs-h781. CI covers
Laravel 12 and 13 without disabling advisory blocking. Runtime constraints are
unchanged, but this pipeline does not establish Laravel 11 compatibility.
10 changes: 10 additions & 0 deletions phpstan.neon
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
includes:
- vendor/larastan/larastan/extension.neon

parameters:
level: 7
paths:
- src
tmpDir: .phpstan-cache
dynamicConstantNames:
- FILTER_FLAG_GLOBAL_RANGE
23 changes: 23 additions & 0 deletions phpunit.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="vendor/phpunit/phpunit/phpunit.xsd"
bootstrap="vendor/autoload.php"
colors="true">
<testsuites>
<testsuite name="SDK">
<directory>tests</directory>
</testsuite>
</testsuites>
<source>
<include><directory>src</directory></include>
</source>
<php>
<env name="APP_ENV" value="testing" force="true"/>
<env name="DB_CONNECTION" value="sqlite"/>
<env name="DB_DATABASE" value=":memory:"/>
<env name="CACHE_STORE" value="array"/>
<env name="MAIL_MAILER" value="array"/>
<env name="QUEUE_CONNECTION" value="sync"/>
<env name="SESSION_DRIVER" value="array"/>
</php>
</phpunit>
58 changes: 58 additions & 0 deletions pint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{
"preset": "laravel",
"rules": {
"array_push": true,
"backtick_to_shell_exec": true,
"date_time_immutable": true,
"declare_strict_types": true,
"lowercase_keywords": true,
"lowercase_static_reference": true,
"final_class": true,
"final_internal_class": true,
"final_public_method_for_abstract_class": true,
"fully_qualified_strict_types": true,
"global_namespace_import": {
"import_classes": true,
"import_constants": true,
"import_functions": true
},
"mb_str_functions": true,
"modernize_types_casting": true,
"new_with_parentheses": false,
"no_superfluous_elseif": true,
"no_useless_else": true,
"no_multiple_statements_per_line": true,
"ordered_class_elements": {
"order": [
"use_trait",
"case",
"constant",
"constant_public",
"constant_protected",
"constant_private",
"property_public",
"property_protected",
"property_private",
"construct",
"destruct",
"magic",
"phpunit",
"method_abstract",
"method_public_static",
"method_public",
"method_protected_static",
"method_protected",
"method_private_static",
"method_private"
],
"sort_algorithm": "none"
},
"ordered_interfaces": true,
"ordered_traits": true,
"protected_to_private": true,
"self_accessor": true,
"self_static_accessor": true,
"strict_comparison": true,
"visibility_required": true
}
}
15 changes: 15 additions & 0 deletions tests/Pest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<?php

declare(strict_types=1);

use ZXN\NativeWatch\Desktop\Tests\TestCase;

pest()->extend(TestCase::class)->in(__DIR__);

function temporaryOutbox(): string
{
$path = sys_get_temp_dir().'/nativewatch-tests/'.bin2hex(random_bytes(6));
mkdir($path, 0755, true);

return $path;
}
26 changes: 26 additions & 0 deletions tests/TestCase.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<?php

declare(strict_types=1);

namespace ZXN\NativeWatch\Desktop\Tests;

use Illuminate\Foundation\Application;
use Orchestra\Testbench\TestCase as Orchestra;

abstract class TestCase extends Orchestra
{
/** @param Application $app */
protected function defineEnvironment($app): void
{
$app['config']->set([
'app.key' => 'base64:'.base64_encode(str_repeat('a', 32)),
'cache.default' => 'array',
'database.default' => 'testing',
'database.connections.testing' => ['driver' => 'sqlite', 'database' => ':memory:'],
'mail.default' => 'array',
'queue.default' => 'sync',
'session.driver' => 'array',
'nativewatch.key' => null,
]);
}
}
Loading