Skip to content

Commit ebcb29a

Browse files
authored
Merge pull request #753 from nasirkhan/dev
Dev
2 parents 714ad9c + 6d5b85a commit ebcb29a

21 files changed

+349
-80
lines changed

README.md

Lines changed: 70 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,26 @@ Pass: secret
3232

3333
We have created a number of custom commands for the project. The commands are listed below with a brief about their use of it.
3434

35+
## Install / Setup
36+
37+
Run the interactive setup wizard for a fresh project. It copies `.env`, generates an app key, configures the database, runs migrations, seeds data, creates the storage link, and builds frontend assets.
38+
39+
```bash
40+
php artisan starter:install
41+
```
42+
43+
Use `--skip-npm` to skip the frontend build, `--demo` to seed demo data without prompting, or `--skip-db` if the database is already set up.
44+
45+
## Update
46+
47+
After pulling changes from the repository, run:
48+
49+
```bash
50+
php artisan starter:update
51+
```
52+
53+
This runs `composer update`, checks for new module migrations, runs outstanding migrations, and clears all caches.
54+
3555
## Create New module
3656

3757
To create a project use the following command, you have to replace the MODULE_NAME with the name of the module.
@@ -140,18 +160,61 @@ It is a modular application, and some modules are installed by default. It will
140160

141161
Follow the steps mentioned below to install and run the project. You may find more details about the installation in [Installation Wiki](https://github.com/nasirkhan/laravel-starter/wiki/Installation).
142162

143-
1. Open the terminal and run the following command, this will download and install the `Laravel Starter` and run the post-installation commands.
163+
### From GitHub Template (recommended)
164+
165+
If you created a new repository from this GitHub template, or cloned it directly:
166+
167+
```bash
168+
# 1. Install PHP dependencies
169+
composer install
170+
171+
# 2. Run the interactive setup wizard — handles .env, database, migrations, seeding, and npm assets
172+
php artisan starter:install
173+
```
174+
175+
Or as a single shortcut after `composer install`:
176+
177+
```bash
178+
composer setup
179+
```
180+
181+
For a true one-liner from a fresh clone, convenience scripts are included:
182+
183+
```bash
184+
# Linux / macOS
185+
bash setup.sh
186+
187+
# Windows (PowerShell)
188+
.\setup.ps1
189+
```
190+
191+
Both scripts run `composer install` and then launch `php artisan starter:install`.
192+
Pass any `starter:install` flags through, e.g. `bash setup.sh --demo`.
193+
194+
The setup wizard will guide you through environment configuration, database selection, migrations, seeding, and building frontend assets. When finished it prints the app URL and default login credentials.
195+
196+
**Available options:**
197+
198+
| Option | Description |
199+
|---|---|
200+
| `--skip-db` | Skip database setup |
201+
| `--skip-seed` | Skip database seeding |
202+
| `--skip-npm` | Skip `npm install` and asset build |
203+
| `--demo` | Seed with demo data (no prompt) |
204+
205+
### Via Composer create-project
206+
144207
```bash
145208
composer create-project nasirkhan/laravel-starter
146209
```
147-
2. The default database is `sqlite`, if you want to change please update the database settings at `.env` file
148-
3. To create a link from the storage directory, run the following command from the project root:
149-
```php
150-
php artisan storage:link
210+
211+
This runs migrations automatically. Afterwards run the setup wizard to seed and build assets:
212+
213+
```bash
214+
php artisan starter:install --skip-db
151215
```
152-
4. If you run the `create-project` command from `Laravel Hard` then the site will be available at [http://laravel-starter.test](http://laravel-starter.test). You may create a virtualhost entry to access the application or run `php artisan serve` from the project root and visit `http://127.0.0.1:8000`
153216

154-
*After creating the new permissions use the following commands to update cashed permissions.*
217+
*After creating the new permissions use the following commands to update cached permissions.*
155218

156219
`php artisan cache:forget spatie.permission.cache`
157220

app/Console/Commands/SeedEssentialData.php

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,10 +85,7 @@ protected function seedMenuModule(): void
8585
}
8686

8787
try {
88-
$this->call('db:seed', [
89-
'--class' => 'Modules\\Menu\\database\\seeders\\MenuDatabaseSeeder',
90-
'--force' => $this->option('force'),
91-
]);
88+
$this->call('menu:seed');
9289
$this->info('Menu module seeded successfully.');
9390
} catch (\Exception $e) {
9491
$this->error("Failed to seed Menu module: {$e->getMessage()}");

app/Console/Commands/StarterInstallCommand.php

Lines changed: 119 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ class StarterInstallCommand extends Command
1616
protected $signature = 'starter:install
1717
{--skip-db : Skip database setup}
1818
{--skip-seed : Skip database seeding}
19+
{--skip-npm : Skip npm install and asset build}
20+
{--fresh : Reset the database (migrate:fresh) before seeding}
1921
{--demo : Install with demo data}';
2022

2123
/**
@@ -40,8 +42,8 @@ public function handle(): int
4042
if ($this->setupEnvironment()) {
4143
// Step 2: Database Setup
4244
if ($this->option('skip-db') || $this->setupDatabase()) {
43-
// Step 3: Run Migrations
44-
if ($this->runMigrations()) {
45+
// Step 3: Run Migrations (skip entirely when --skip-db is passed)
46+
if ($this->option('skip-db') || $this->runMigrations()) {
4547
// Step 4: Seed Database
4648
if (! $this->option('skip-seed')) {
4749
$this->seedDatabase();
@@ -50,6 +52,11 @@ public function handle(): int
5052
// Step 5: Final Steps
5153
$this->finalizeInstallation();
5254

55+
// Step 6: Install npm dependencies & build assets
56+
if (! $this->option('skip-npm')) {
57+
$this->installNpmDependencies();
58+
}
59+
5360
$this->displaySuccessMessage();
5461

5562
return self::SUCCESS;
@@ -161,8 +168,12 @@ protected function setupSQLite(): bool
161168
File::put($dbPath, '');
162169
}
163170

171+
// Use forward slashes — dotenv treats backslashes as escape characters
172+
// and spaces in the path cause parse errors unless the path is normalised.
173+
$dotenvSafePath = str_replace('\\', '/', $dbPath);
174+
164175
$this->updateEnvFile('DB_CONNECTION', 'sqlite');
165-
$this->updateEnvFile('DB_DATABASE', $dbPath);
176+
$this->updateEnvFile('DB_DATABASE', $dotenvSafePath);
166177

167178
return true;
168179
});
@@ -172,18 +183,62 @@ protected function setupSQLite(): bool
172183

173184
/**
174185
* Run database migrations.
186+
*
187+
* Returns false only when the user explicitly skips migrations.
175188
*/
176189
protected function runMigrations(): bool
177190
{
178191
$this->newLine();
179192

180-
if (! $this->confirm('Run database migrations?', true)) {
193+
// --fresh flag skips the interactive prompt
194+
if ($this->option('fresh')) {
195+
return $this->runFreshMigrations();
196+
}
197+
198+
$choice = $this->choice(
199+
'How would you like to set up the database?',
200+
[
201+
'fresh' => 'Fresh install — drop all tables and re-run all migrations (⚠️ destroys existing data)',
202+
'migrate' => 'Run new migrations only — safe for existing data',
203+
'skip' => 'Skip migrations',
204+
],
205+
'fresh'
206+
);
207+
208+
if ($choice === 'skip') {
181209
$this->components->warn('Skipping migrations. Run manually: php artisan migrate');
182210

183211
return false;
184212
}
185213

186-
return $this->components->task('Running migrations', function () {
214+
if ($choice === 'fresh') {
215+
return $this->runFreshMigrations();
216+
}
217+
218+
// migrate only
219+
return (bool) $this->components->task('Running migrations', function () {
220+
Artisan::call('migrate', ['--force' => true], $this->output);
221+
222+
return true;
223+
});
224+
}
225+
226+
/**
227+
* Drop all tables and re-run every migration.
228+
*/
229+
protected function runFreshMigrations(): bool
230+
{
231+
$this->newLine();
232+
$this->components->warn('⚠️ This will drop ALL tables and delete ALL existing data.');
233+
234+
// Skip confirmation if the user explicitly passed --fresh
235+
if (! $this->option('fresh') && ! $this->confirm('Are you sure you want to reset the database?', false)) {
236+
$this->components->info('Database reset cancelled.');
237+
238+
return false;
239+
}
240+
241+
return (bool) $this->components->task('Resetting database (migrate:fresh)', function () {
187242
Artisan::call('migrate:fresh', ['--force' => true], $this->output);
188243

189244
return true;
@@ -192,21 +247,30 @@ protected function runMigrations(): bool
192247

193248
/**
194249
* Seed database.
250+
*
251+
* Always seeds essential data (users, roles, permissions, menus).
252+
* Optionally seeds demo data (posts, categories, tags) on top.
195253
*/
196254
protected function seedDatabase(): void
197255
{
198256
$this->newLine();
199257

200-
$seedDemo = $this->option('demo') || $this->confirm('Seed database with demo data?', true);
258+
// Seed essential data via db:seed-essential
259+
$this->components->task('Seeding essential data (users, roles, permissions, menus)', function () {
260+
Artisan::call('db:seed-essential', ['--force' => true], $this->output);
261+
262+
return true;
263+
});
264+
265+
// Optionally seed demo data on top
266+
$seedDemo = $this->option('demo') || $this->confirm('Also seed demo data (posts, categories, tags)?', false);
201267

202268
if ($seedDemo) {
203-
$this->components->task('Seeding database', function () {
269+
$this->components->task('Seeding demo data', function () {
204270
Artisan::call('db:seed', ['--force' => true], $this->output);
205271

206272
return true;
207273
});
208-
} else {
209-
$this->components->warn('Skipping seeding. Run manually: php artisan db:seed');
210274
}
211275
}
212276

@@ -236,6 +300,36 @@ protected function finalizeInstallation(): void
236300
});
237301
}
238302

303+
/**
304+
* Install npm dependencies and build frontend assets.
305+
*/
306+
protected function installNpmDependencies(): void
307+
{
308+
$this->newLine();
309+
310+
if (! $this->confirm('Install npm dependencies and build frontend assets?', true)) {
311+
$this->components->warn('Skipping npm. Run manually: npm install && npm run build');
312+
313+
return;
314+
}
315+
316+
$this->components->task('Installing npm dependencies', function () {
317+
$output = [];
318+
$exitCode = 0;
319+
exec('npm install --no-audit --no-fund 2>&1', $output, $exitCode);
320+
321+
return $exitCode === 0;
322+
});
323+
324+
$this->components->task('Building frontend assets', function () {
325+
$output = [];
326+
$exitCode = 0;
327+
exec('npm run build 2>&1', $output, $exitCode);
328+
329+
return $exitCode === 0;
330+
});
331+
}
332+
239333
/**
240334
* Display success message.
241335
*/
@@ -251,10 +345,17 @@ protected function displaySuccessMessage(): void
251345
$this->newLine();
252346

253347
$this->components->twoColumnDetail('<fg=bright-blue>Next Steps</>', '');
254-
$this->line(' 1. <fg=green>npm install && npm run dev</> - Build frontend assets');
255-
$this->line(' 2. <fg=green>php artisan serve</> - Start development server');
256-
$this->line(' 3. Visit <fg=cyan>http://localhost:8000</> in your browser');
257-
$this->line(' 4. Login with credentials above');
348+
349+
$step = 1;
350+
351+
if ($this->option('skip-npm')) {
352+
$this->line(" {$step}. <fg=green>npm install && npm run build</> - Build frontend assets");
353+
$step++;
354+
}
355+
356+
$this->line(" {$step}. <fg=green>php artisan serve</> - Start development server (or visit your Herd/Valet URL)");
357+
$step++;
358+
$this->line(" {$step}. Login with the credentials above");
258359
$this->newLine();
259360

260361
$this->components->twoColumnDetail('<fg=bright-blue>Useful Commands</>', '');
@@ -282,6 +383,11 @@ protected function updateEnvFile(string $key, string $value): void
282383
$escapedValue = str_replace(['\\', '$'], ['\\\\', '\\$'], $value);
283384
$replacement = "{$key}={$escapedValue}";
284385

386+
// Quote values that contain spaces so dotenv can parse them
387+
if (str_contains($value, ' ')) {
388+
$replacement = "{$key}=\"{$escapedValue}\"";
389+
}
390+
285391
if (preg_match($pattern, $content)) {
286392
$content = preg_replace($pattern, $replacement, $content);
287393
} else {

composer.json

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@
5858
}
5959
},
6060
"scripts": {
61+
"setup": [
62+
"@php artisan starter:install"
63+
],
6164
"post-autoload-dump": [
6265
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
6366
"@php artisan package:discover --ansi"

composer.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

public/build/assets/app-backend-3jjirE3K.css

Lines changed: 0 additions & 1 deletion
This file was deleted.

public/build/assets/app-backend-ChALy0bL.js

Lines changed: 0 additions & 4 deletions
This file was deleted.

public/build/assets/app-frontend-CUnJ57mD.css

Lines changed: 0 additions & 1 deletion
This file was deleted.

0 commit comments

Comments
 (0)