Add GitHub publish script and bump version to 0.9-rc2
This commit is contained in:
13
CLAUDE.md
13
CLAUDE.md
@@ -59,22 +59,21 @@ php artisan route:cache # Cache routes
|
|||||||
|
|
||||||
**Important:** Only push to git when explicitly requested by the user. Do not auto-push after commits.
|
**Important:** Only push to git when explicitly requested by the user. Do not auto-push after commits.
|
||||||
|
|
||||||
### Version & Build Numbers
|
### Version Numbers
|
||||||
|
|
||||||
**IMPORTANT:** Before every push, bump the BUILD number in the `VERSION` file:
|
**IMPORTANT:** Before every push, bump the `VERSION` in the `VERSION` file:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# VERSION file format:
|
# VERSION file format:
|
||||||
VERSION=1.0.3
|
VERSION=0.9-rc
|
||||||
BUILD=005
|
|
||||||
|
|
||||||
# Increment BUILD before pushing (e.g., 005 → 006)
|
# Bump before pushing:
|
||||||
|
# 0.9-rc → 0.9-rc1 → 0.9-rc2 → 0.9-rc3 → ...
|
||||||
```
|
```
|
||||||
|
|
||||||
| Field | When to Bump | Format |
|
| Field | When to Bump | Format |
|
||||||
|-------|--------------|--------|
|
|-------|--------------|--------|
|
||||||
| `VERSION` | Major releases, new features | Semantic versioning (X.Y.Z) |
|
| `VERSION` | Every push | `0.9-rc`, `0.9-rc1`, `0.9-rc2`, ... |
|
||||||
| `BUILD` | Every push | Zero-padded number (001, 002, etc.) |
|
|
||||||
|
|
||||||
## Test Server
|
## Test Server
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
A modern web hosting control panel for WordPress and general PHP hosting. Built with Laravel 12, Filament v5, Livewire 4, and Tailwind CSS v4.
|
A modern web hosting control panel for WordPress and general PHP hosting. Built with Laravel 12, Filament v5, Livewire 4, and Tailwind CSS v4.
|
||||||
|
|
||||||
Version: 0.9-rc1 (release candidate)
|
Version: 0.9-rc2 (release candidate)
|
||||||
|
|
||||||
This is a release candidate. Expect rapid iteration and breaking changes until 1.0.
|
This is a release candidate. Expect rapid iteration and breaking changes until 1.0.
|
||||||
|
|
||||||
|
|||||||
@@ -4,41 +4,86 @@ declare(strict_types=1);
|
|||||||
|
|
||||||
namespace App\Filament\Jabali\Pages\Auth;
|
namespace App\Filament\Jabali\Pages\Auth;
|
||||||
|
|
||||||
use App\Models\User;
|
use DanHarrin\LivewireRateLimiting\Exceptions\TooManyRequestsException;
|
||||||
use Filament\Auth\Http\Responses\Contracts\LoginResponse;
|
use Filament\Auth\Http\Responses\Contracts\LoginResponse;
|
||||||
|
use Filament\Auth\MultiFactor\Contracts\HasBeforeChallengeHook;
|
||||||
use Filament\Auth\Pages\Login as BaseLogin;
|
use Filament\Auth\Pages\Login as BaseLogin;
|
||||||
use Filament\Facades\Filament;
|
use Filament\Facades\Filament;
|
||||||
|
use Filament\Models\Contracts\FilamentUser;
|
||||||
|
use Illuminate\Contracts\Auth\Guard;
|
||||||
use Illuminate\Support\Facades\Auth;
|
use Illuminate\Support\Facades\Auth;
|
||||||
|
|
||||||
class Login extends BaseLogin
|
class Login extends BaseLogin
|
||||||
{
|
{
|
||||||
public function authenticate(): ?LoginResponse
|
public function authenticate(): ?LoginResponse
|
||||||
{
|
{
|
||||||
|
$panel = Filament::getPanel('jabali');
|
||||||
|
Filament::setCurrentPanel($panel);
|
||||||
|
|
||||||
|
try {
|
||||||
|
$this->rateLimit(5);
|
||||||
|
} catch (TooManyRequestsException $exception) {
|
||||||
|
$this->getRateLimitedNotification($exception)?->send();
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
$data = $this->form->getState();
|
$data = $this->form->getState();
|
||||||
|
|
||||||
// Check credentials without logging in
|
/** @var Guard $authGuard */
|
||||||
$user = User::where('email', $data['email'])->first();
|
$authGuard = Auth::guard($panel->getAuthGuard());
|
||||||
|
$authProvider = $authGuard->getProvider();
|
||||||
|
$credentials = $this->getCredentialsFromFormData($data);
|
||||||
|
|
||||||
if ($user && \Hash::check($data['password'], $user->password)) {
|
$user = $authProvider->retrieveByCredentials($credentials);
|
||||||
// Check if 2FA is enabled
|
|
||||||
if ($user->two_factor_secret && $user->two_factor_confirmed_at) {
|
if ((! $user) || (! $authProvider->validateCredentials($user, $credentials))) {
|
||||||
// Store user ID in session for 2FA challenge
|
$this->userUndertakingMultiFactorAuthentication = null;
|
||||||
session(['login.id' => $user->id]);
|
|
||||||
session(['login.remember' => $data['remember'] ?? false]);
|
$this->fireFailedEvent($authGuard, $user, $credentials);
|
||||||
|
$this->throwFailureValidationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
filled($this->userUndertakingMultiFactorAuthentication) &&
|
||||||
|
(decrypt($this->userUndertakingMultiFactorAuthentication) === $user->getAuthIdentifier())
|
||||||
|
) {
|
||||||
|
$this->multiFactorChallengeForm->validate();
|
||||||
|
} else {
|
||||||
|
foreach (Filament::getMultiFactorAuthenticationProviders() as $multiFactorAuthenticationProvider) {
|
||||||
|
if (! $multiFactorAuthenticationProvider->isEnabled($user)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->userUndertakingMultiFactorAuthentication = encrypt($user->getAuthIdentifier());
|
||||||
|
|
||||||
|
if ($multiFactorAuthenticationProvider instanceof HasBeforeChallengeHook) {
|
||||||
|
$multiFactorAuthenticationProvider->beforeChallenge($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filled($this->userUndertakingMultiFactorAuthentication)) {
|
||||||
|
$this->multiFactorChallengeForm->fill();
|
||||||
|
|
||||||
// Redirect to 2FA challenge
|
|
||||||
$this->redirect(route('filament.jabali.auth.two-factor-challenge'));
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$response = parent::authenticate();
|
if ($user instanceof FilamentUser && ! $user->canAccessPanel($panel)) {
|
||||||
|
$this->fireFailedEvent($authGuard, $user, $credentials);
|
||||||
|
$this->throwFailureValidationException();
|
||||||
|
}
|
||||||
|
|
||||||
|
$authGuard->login($user, $data['remember'] ?? false);
|
||||||
|
|
||||||
|
session()->regenerate();
|
||||||
|
|
||||||
// If authentication successful, check if user is admin
|
// If authentication successful, check if user is admin
|
||||||
$user = Filament::auth()->user();
|
$user = $authGuard->user();
|
||||||
if ($user && $user->is_admin) {
|
if ($user && $user->is_admin) {
|
||||||
// Log out from user panel guard
|
$authGuard->logout();
|
||||||
Filament::auth()->logout();
|
|
||||||
|
|
||||||
// Redirect admins to admin panel using Livewire's redirect
|
// Redirect admins to admin panel using Livewire's redirect
|
||||||
$this->redirect(route('filament.admin.pages.dashboard'));
|
$this->redirect(route('filament.admin.pages.dashboard'));
|
||||||
@@ -46,6 +91,6 @@ class Login extends BaseLogin
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $response;
|
return app(LoginResponse::class);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1092,7 +1092,7 @@ server {
|
|||||||
|
|
||||||
location ~ \.php$ {
|
location ~ \.php$ {
|
||||||
fastcgi_pass unix:SOCKET_PLACEHOLDER;
|
fastcgi_pass unix:SOCKET_PLACEHOLDER;
|
||||||
fastcgi_next_upstream error timeout invalid_header http_500 http_503 http_504;
|
fastcgi_next_upstream error timeout invalid_header http_500 http_503;
|
||||||
fastcgi_next_upstream_tries 2;
|
fastcgi_next_upstream_tries 2;
|
||||||
fastcgi_next_upstream_timeout 5s;
|
fastcgi_next_upstream_timeout 5s;
|
||||||
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
|
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
|
||||||
@@ -6929,7 +6929,7 @@ server {
|
|||||||
|
|
||||||
location ~ \.php\$ {
|
location ~ \.php\$ {
|
||||||
fastcgi_pass unix:/var/run/php/php8.4-fpm-{$username}.sock;
|
fastcgi_pass unix:/var/run/php/php8.4-fpm-{$username}.sock;
|
||||||
fastcgi_next_upstream error timeout invalid_header http_500 http_503 http_504;
|
fastcgi_next_upstream error timeout invalid_header http_500 http_503;
|
||||||
fastcgi_next_upstream_tries 2;
|
fastcgi_next_upstream_tries 2;
|
||||||
fastcgi_next_upstream_timeout 5s;
|
fastcgi_next_upstream_timeout 5s;
|
||||||
fastcgi_index index.php;
|
fastcgi_index index.php;
|
||||||
@@ -11714,7 +11714,7 @@ server {
|
|||||||
location ~ \.php$ {
|
location ~ \.php$ {
|
||||||
include snippets/fastcgi-php.conf;
|
include snippets/fastcgi-php.conf;
|
||||||
fastcgi_pass unix:/var/run/php/php8.4-fpm-$username.sock;
|
fastcgi_pass unix:/var/run/php/php8.4-fpm-$username.sock;
|
||||||
fastcgi_next_upstream error timeout invalid_header http_500 http_503 http_504;
|
fastcgi_next_upstream error timeout invalid_header http_500 http_503;
|
||||||
fastcgi_next_upstream_tries 2;
|
fastcgi_next_upstream_tries 2;
|
||||||
fastcgi_next_upstream_timeout 5s;
|
fastcgi_next_upstream_timeout 5s;
|
||||||
fastcgi_param SCRIPT_FILENAME \$realpath_root\$fastcgi_script_name;
|
fastcgi_param SCRIPT_FILENAME \$realpath_root\$fastcgi_script_name;
|
||||||
|
|||||||
50
bin/publish-github
Executable file
50
bin/publish-github
Executable file
@@ -0,0 +1,50 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
repo_root="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||||
|
cd "$repo_root"
|
||||||
|
|
||||||
|
remote_name="github"
|
||||||
|
source_branch="main"
|
||||||
|
publish_branch="github-main"
|
||||||
|
|
||||||
|
if ! git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
||||||
|
echo "Not a git repository." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! git remote get-url "$remote_name" >/dev/null 2>&1; then
|
||||||
|
echo "Remote '$remote_name' is not configured." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! git diff --quiet || ! git diff --cached --quiet; then
|
||||||
|
echo "Working tree is not clean. Commit or stash changes first." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
original_branch="$(git rev-parse --abbrev-ref HEAD)"
|
||||||
|
if [[ "$original_branch" != "$source_branch" && "$original_branch" != "$publish_branch" ]]; then
|
||||||
|
echo "Switch to '$source_branch' or '$publish_branch' before publishing." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if git fetch "$remote_name" --quiet; then
|
||||||
|
current_version="$(grep -E '^VERSION=' VERSION | head -n1 | cut -d= -f2-)"
|
||||||
|
remote_version="$(git show "$remote_name/main:VERSION" 2>/dev/null | grep -E '^VERSION=' | head -n1 | cut -d= -f2- || true)"
|
||||||
|
|
||||||
|
if [[ -n "$remote_version" && "$current_version" == "$remote_version" ]]; then
|
||||||
|
echo "VERSION unchanged vs GitHub ($current_version). Bump VERSION before pushing." >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
git checkout -B "$publish_branch" "$source_branch"
|
||||||
|
|
||||||
|
FILTER_BRANCH_SQUELCH_WARNING=1 git filter-branch --force --index-filter \
|
||||||
|
"git rm --cached --ignore-unmatch CLAUDE.md" \
|
||||||
|
--prune-empty --tag-name-filter cat -- "$publish_branch"
|
||||||
|
|
||||||
|
git push "$remote_name" "$publish_branch:main" --force
|
||||||
|
|
||||||
|
git checkout "$original_branch"
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
set -e
|
set -e
|
||||||
|
|
||||||
# Version - will be read from VERSION file after clone, this is fallback
|
# Version - will be read from VERSION file after clone, this is fallback
|
||||||
JABALI_VERSION="0.9-rc"
|
JABALI_VERSION="0.9-rc2"
|
||||||
|
|
||||||
# Colors
|
# Colors
|
||||||
RED='\033[0;31m'
|
RED='\033[0;31m'
|
||||||
|
|||||||
25
tests/Unit/PublishGithubScriptTest.php
Normal file
25
tests/Unit/PublishGithubScriptTest.php
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
namespace Tests\Unit;
|
||||||
|
|
||||||
|
use PHPUnit\Framework\TestCase;
|
||||||
|
|
||||||
|
class PublishGithubScriptTest extends TestCase
|
||||||
|
{
|
||||||
|
public function test_publish_github_script_exists_and_protects_claude(): void
|
||||||
|
{
|
||||||
|
$scriptPath = dirname(__DIR__, 2).'/bin/publish-github';
|
||||||
|
|
||||||
|
$this->assertFileExists($scriptPath);
|
||||||
|
$this->assertTrue(is_executable($scriptPath));
|
||||||
|
|
||||||
|
$content = file_get_contents($scriptPath);
|
||||||
|
|
||||||
|
$this->assertNotFalse($content);
|
||||||
|
$this->assertStringContainsString('CLAUDE.md', $content);
|
||||||
|
$this->assertStringContainsString('github-main', $content);
|
||||||
|
$this->assertStringContainsString('github', $content);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,7 +14,7 @@ class VersionFileTest extends TestCase
|
|||||||
$content = file_get_contents($versionPath);
|
$content = file_get_contents($versionPath);
|
||||||
|
|
||||||
$this->assertNotFalse($content);
|
$this->assertNotFalse($content);
|
||||||
$this->assertStringContainsString('VERSION=0.9-rc1', $content);
|
$this->assertStringContainsString('VERSION=0.9-rc2', $content);
|
||||||
$this->assertStringNotContainsString('BUILD=', $content);
|
$this->assertStringNotContainsString('BUILD=', $content);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user