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.
|
||||
|
||||
### 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
|
||||
# VERSION file format:
|
||||
VERSION=1.0.3
|
||||
BUILD=005
|
||||
VERSION=0.9-rc
|
||||
|
||||
# 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 |
|
||||
|-------|--------------|--------|
|
||||
| `VERSION` | Major releases, new features | Semantic versioning (X.Y.Z) |
|
||||
| `BUILD` | Every push | Zero-padded number (001, 002, etc.) |
|
||||
| `VERSION` | Every push | `0.9-rc`, `0.9-rc1`, `0.9-rc2`, ... |
|
||||
|
||||
## 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.
|
||||
|
||||
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.
|
||||
|
||||
|
||||
@@ -4,41 +4,86 @@ declare(strict_types=1);
|
||||
|
||||
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\MultiFactor\Contracts\HasBeforeChallengeHook;
|
||||
use Filament\Auth\Pages\Login as BaseLogin;
|
||||
use Filament\Facades\Filament;
|
||||
use Filament\Models\Contracts\FilamentUser;
|
||||
use Illuminate\Contracts\Auth\Guard;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class Login extends BaseLogin
|
||||
{
|
||||
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();
|
||||
|
||||
// Check credentials without logging in
|
||||
$user = User::where('email', $data['email'])->first();
|
||||
/** @var Guard $authGuard */
|
||||
$authGuard = Auth::guard($panel->getAuthGuard());
|
||||
$authProvider = $authGuard->getProvider();
|
||||
$credentials = $this->getCredentialsFromFormData($data);
|
||||
|
||||
if ($user && \Hash::check($data['password'], $user->password)) {
|
||||
// Check if 2FA is enabled
|
||||
if ($user->two_factor_secret && $user->two_factor_confirmed_at) {
|
||||
// Store user ID in session for 2FA challenge
|
||||
session(['login.id' => $user->id]);
|
||||
session(['login.remember' => $data['remember'] ?? false]);
|
||||
$user = $authProvider->retrieveByCredentials($credentials);
|
||||
|
||||
if ((! $user) || (! $authProvider->validateCredentials($user, $credentials))) {
|
||||
$this->userUndertakingMultiFactorAuthentication = null;
|
||||
|
||||
$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;
|
||||
}
|
||||
}
|
||||
|
||||
$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
|
||||
$user = Filament::auth()->user();
|
||||
$user = $authGuard->user();
|
||||
if ($user && $user->is_admin) {
|
||||
// Log out from user panel guard
|
||||
Filament::auth()->logout();
|
||||
$authGuard->logout();
|
||||
|
||||
// Redirect admins to admin panel using Livewire's redirect
|
||||
$this->redirect(route('filament.admin.pages.dashboard'));
|
||||
@@ -46,6 +91,6 @@ class Login extends BaseLogin
|
||||
return null;
|
||||
}
|
||||
|
||||
return $response;
|
||||
return app(LoginResponse::class);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1092,7 +1092,7 @@ server {
|
||||
|
||||
location ~ \.php$ {
|
||||
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_timeout 5s;
|
||||
fastcgi_param SCRIPT_FILENAME $realpath_root$fastcgi_script_name;
|
||||
@@ -6929,7 +6929,7 @@ server {
|
||||
|
||||
location ~ \.php\$ {
|
||||
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_timeout 5s;
|
||||
fastcgi_index index.php;
|
||||
@@ -11714,7 +11714,7 @@ server {
|
||||
location ~ \.php$ {
|
||||
include snippets/fastcgi-php.conf;
|
||||
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_timeout 5s;
|
||||
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
|
||||
|
||||
# Version - will be read from VERSION file after clone, this is fallback
|
||||
JABALI_VERSION="0.9-rc"
|
||||
JABALI_VERSION="0.9-rc2"
|
||||
|
||||
# Colors
|
||||
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);
|
||||
|
||||
$this->assertNotFalse($content);
|
||||
$this->assertStringContainsString('VERSION=0.9-rc1', $content);
|
||||
$this->assertStringContainsString('VERSION=0.9-rc2', $content);
|
||||
$this->assertStringNotContainsString('BUILD=', $content);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user