diff --git a/.gitattributes b/.gitattributes index d6a434b..2b5ac9b 100644 --- a/.gitattributes +++ b/.gitattributes @@ -3,5 +3,8 @@ .editorconfig export-ignore .gitattributes export-ignore .gitignore export-ignore -CHANGELOG.md export-ignore -phpunit.xml.dist export-ignore +phpunit.xml export-ignore +LICENSE export-ignore +.php-cs-fixer.dist.php export-ignore +package.json export-ignore +webpack.mix.js export-ignore diff --git a/.github/README.md b/.github/README.md index 161d525..ed2185b 100644 --- a/.github/README.md +++ b/.github/README.md @@ -173,7 +173,7 @@ Special thanks to @codepotato for the logo! ❤️ ## Security -If you find any security related issues, please send me an email to import.lorises_0c@icloud.com. +If you find any security related issues, please send an email to **29132017+Sammyjo20@users.noreply.github.com** ## And that's it! ✨ diff --git a/.github/SECURITY.md b/.github/SECURITY.md index 0fe0b25..4c9663b 100644 --- a/.github/SECURITY.md +++ b/.github/SECURITY.md @@ -1,3 +1,3 @@ # Security Policy -If you discover any security related issues, please email security@yeehaw.dev instead of using the issue tracker. +If you discover any security related issues, please email **29132017+Sammyjo20@users.noreply.github.com** instead of using the issue tracker. diff --git a/.github/workflows/php-cs-fixer.yml b/.github/workflows/php-cs-fixer.yml index 0180a90..08a497f 100644 --- a/.github/workflows/php-cs-fixer.yml +++ b/.github/workflows/php-cs-fixer.yml @@ -1,10 +1,15 @@ name: Code Style on: - pull_request: - branches: [ 'master', 'v2.0', 'v3.0' ] push: - branches: [ 'master', 'v2.0', 'v3.0' ] + branches: + - 'v3.0' + pull_request: + branches: + - '*' + +permissions: + contents: write jobs: php-cs-fixer: @@ -16,8 +21,8 @@ jobs: - name: Run PHP CS Fixer uses: docker://oskarstark/php-cs-fixer-ga with: - args: --config=.php-cs-fixer.dist.php + args: --config=.php-cs-fixer.dist.php --allow-risky=yes - name: Commit changes uses: stefanzweifel/git-auto-commit-action@v4 with: - commit_message: Apply Code Style Fixes + commit_message: 🪄 Code Style Fixes diff --git a/.github/workflows/phpstan.yml b/.github/workflows/phpstan.yml new file mode 100644 index 0000000..512c212 --- /dev/null +++ b/.github/workflows/phpstan.yml @@ -0,0 +1,28 @@ +name: PHPStan + +on: + push: + branches: + - 'main' + pull_request: + branches: + - '*' + +jobs: + phpstan: + name: phpstan + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: '8.3' + coverage: none + + - name: Install composer dependencies + uses: ramsey/composer-install@v2 + + - name: Run PHPStan + run: ./vendor/bin/phpstan analyse --error-format=github diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1444365..8769290 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -1,42 +1,51 @@ -name: Tests +name: tests -on: [pull_request, push] +on: + push: + branches: + - 'v3.0' + pull_request: + branches: + - '*' + +permissions: + contents: write jobs: tests: - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} strategy: fail-fast: true matrix: - php: [8.1, 8.2] - laravel: [^9.0, ^10.0] - dependency-version: [prefer-lowest, prefer-stable] - include: - - laravel: ^9.0 - testbench: ^7.0 - - laravel: ^10.0 - testbench: ^8.0 + os: [ ubuntu-latest, windows-latest ] + php: [ 8.1, 8.2, 8.3 ] + stability: [ prefer-lowest, prefer-stable ] - name: PHP ${{ matrix.php }} - Laravel ${{ matrix.laravel }} - ${{ matrix.dependency-version }} + name: P${{ matrix.php }} - ${{ matrix.stability }} - ${{ matrix.os }} steps: - name: Checkout code - uses: actions/checkout@v2 - - - name: Cache dependencies - uses: actions/cache@v2 - with: - path: ~/.composer/cache/files - key: dependencies-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }} + uses: actions/checkout@v3 - name: Setup PHP uses: shivammathur/setup-php@v2 with: php-version: ${{ matrix.php }} + extensions: mbstring, zip, fileinfo coverage: none - - name: Install composer dependencies + - name: Install Node Dependencies + run: | + npm install + + - name: Setup problem matchers + run: | + echo "::add-matcher::${{ runner.tool_cache }}/php.json" + echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" + + - name: Install dependencies run: | - composer update --${{ matrix.dependency-version }} --prefer-dist --no-interaction --no-suggest + composer update --${{ matrix.stability }} --prefer-dist --no-interaction + - name: Execute tests - run: composer test + run: ./vendor/bin/pest diff --git a/.gitignore b/.gitignore index 8ec15d1..09e6016 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,8 @@ composer.lock node_modules vendor .php-cs-fixer.cache +package-lock.json +tests/Fixtures/Cloud +tests/Fixtures/Public +lasso-bundle.json +.lasso diff --git a/.php-cs-fixer.dist.php b/.php-cs-fixer.dist.php index a603d96..110098c 100644 --- a/.php-cs-fixer.dist.php +++ b/.php-cs-fixer.dist.php @@ -37,4 +37,12 @@ 'return_type_declaration' => [ 'space_before' => 'none' ], + 'declare_strict_types' => true, + 'blank_line_after_opening_tag' => true, + 'single_import_per_statement' => true, + 'mb_str_functions' => true, + 'no_superfluous_phpdoc_tags' => true, + 'no_blank_lines_after_phpdoc' => true, + 'no_empty_phpdoc' => true, + 'phpdoc_trim' => true, ])->setFinder($finder); diff --git a/LICENSE b/LICENSE index 077d195..9f9905e 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2020 Sam Carré +Copyright (c) 2023 Sam Carré Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/composer.json b/composer.json index b00b01d..2834d4c 100644 --- a/composer.json +++ b/composer.json @@ -6,7 +6,8 @@ "authors": [ { "name": "Sam Carré", - "email": "sam.carre2000@gmail.com" + "email": "29132017+Sammyjo20@users.noreply.github.com", + "role": "Developer" } ], "require": { @@ -21,11 +22,11 @@ "league/flysystem": "^3.0" }, "require-dev": { - "phpunit/phpunit": "^9.3", "orchestra/testbench": "^7.0 || ^8.0", "friendsofphp/php-cs-fixer": "^3.1.0", "spatie/ray": "^1.33", - "pestphp/pest": "^1.21" + "pestphp/pest": "^2.25", + "phpstan/phpstan": "^1.10" }, "extra": { "laravel": { @@ -45,7 +46,10 @@ "./vendor/bin/pest" ], "fix-code": [ - "./vendor/bin/php-cs-fixer fix" + "./vendor/bin/php-cs-fixer fix --allow-risky=yes" + ], + "pstan": [ + "./vendor/bin/phpstan analyse" ] }, "minimum-stability": "stable", diff --git a/config/lasso.php b/config/lasso.php index 13390ab..bd65d32 100644 --- a/config/lasso.php +++ b/config/lasso.php @@ -1,7 +1,8 @@ [ /* diff --git a/mix-manifest.json b/mix-manifest.json new file mode 100644 index 0000000..c3ee70e --- /dev/null +++ b/mix-manifest.json @@ -0,0 +1,4 @@ +{ + "/tests/Fixtures/Public/app.css": "/tests/Fixtures/Public/app.css", + "/tests/Fixtures/Public/lasso-logo.png": "/tests/Fixtures/Public/lasso-logo.png" +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..ce915d9 --- /dev/null +++ b/package.json @@ -0,0 +1,11 @@ +{ + "name": "lasso", + "version": "1.0.0", + "description": "Yeehaw", + "scripts": { + "production": "echo \"Error: no test specified\" && exit 1" + }, + "devDependencies": { + "laravel-mix": "^6.0.49" + } +} diff --git a/phpstan.dist.neon b/phpstan.dist.neon new file mode 100644 index 0000000..074c0e6 --- /dev/null +++ b/phpstan.dist.neon @@ -0,0 +1,14 @@ +############################################################################################# +# Don't edit this config file directly. # +# For temporary, or local overrides, create a file named 'phpstan.neon' alongside this one. # +############################################################################################# + +parameters: + # Add one of the editorUrl's in your phpstan.neon file for direct editor/IDE links in the terminal. + #editorUrl: 'phpstorm://open?file=%%file%%&line=%%line%%' + #editorUrl: 'vscode://file/%%file%%:%%line%%' + + level: 7 + + paths: + - src diff --git a/phpunit.xml b/phpunit.xml new file mode 100644 index 0000000..31b0dda --- /dev/null +++ b/phpunit.xml @@ -0,0 +1,8 @@ + + + + + tests + + + diff --git a/phpunit.xml.dist b/phpunit.xml.dist deleted file mode 100644 index 82fb072..0000000 --- a/phpunit.xml.dist +++ /dev/null @@ -1,12 +0,0 @@ - - - - - tests - - - diff --git a/src/Commands/BaseCommand.php b/src/Commands/BaseCommand.php index 56b5ba6..4ccaa22 100644 --- a/src/Commands/BaseCommand.php +++ b/src/Commands/BaseCommand.php @@ -1,33 +1,31 @@ option('silent') === true; - $lassoEnvironment = config('lasso.storage.environment', null); + $silent = $this->option('silent') === true; + $environment = config('lasso.storage.environment'); $artisan->setCommand($this); - if ($noPrompt) { + if ($silent) { $artisan->silent(); } - if ($checkFilesystem === true && $noPrompt === false && ! is_null($lassoEnvironment)) { - $definedEnv = $this->ask('🐎 Which Lasso environment would you like to publish to?', $lassoEnvironment); + if ($checkFilesystem === true && $silent === false && ! is_null($environment)) { + $definedEnv = $this->ask('🐎 Which Lasso environment would you like to publish to?', $environment); $filesystem->setLassoEnvironment($definedEnv); } diff --git a/src/Commands/PublishCommand.php b/src/Commands/PublishCommand.php index cfe8eb5..6f1042c 100644 --- a/src/Commands/PublishCommand.php +++ b/src/Commands/PublishCommand.php @@ -1,5 +1,7 @@ validate(); + (new ConfigValidator)->validate(); $this->configureApplication($artisan, $filesystem, true); $dontUseGit = $this->option('no-git') === true; $useCommit = $this->option('use-commit') === true; $withCommit = $this->option('with-commit'); - $this->configureApplication($artisan, $filesystem); $job = new PublishJob; @@ -52,8 +50,8 @@ public function handle(Artisan $artisan, Filesystem $filesystem): int $job->useCommit(); } - if ($withCommit) { - $job->withCommit($withCommit); + if (is_string($withCommit)) { + $job->withCommit(mb_substr($withCommit, 0, 12)); } $artisan->note(sprintf( @@ -68,6 +66,6 @@ public function handle(Artisan $artisan, Filesystem $filesystem): int $filesystem->getCloudDisk() )); - return 0; + return self::SUCCESS; } } diff --git a/src/Commands/PullCommand.php b/src/Commands/PullCommand.php index 2e47826..135f943 100644 --- a/src/Commands/PullCommand.php +++ b/src/Commands/PullCommand.php @@ -1,5 +1,7 @@ option('use-commit') === true; $withCommit = $this->option('with-commit'); + $this->configureApplication($artisan, $filesystem); $artisan->setCommand($this); @@ -50,21 +51,21 @@ public function handle(Artisan $artisan, Filesystem $filesystem): int $filesystem->getCloudDisk() )); - $job = new PullJob(); + $job = new PullJob; if ($useCommit) { $job->useCommit(); } - if ($withCommit) { - $job->withCommit(substr($withCommit, 0, 12)); + if (is_string($withCommit)) { + $job->withCommit(mb_substr((string)$withCommit, 0, 12)); } $job->run(); $artisan->note('✅ Successfully downloaded the latest assets. Yee-haw!'); - $artisan->note('❤️ Thank you for using Lasso. https://getlasso.dev'); + $artisan->note('❤️ Thank you for using Lasso.'); - return 0; + return self::SUCCESS; } } diff --git a/src/Container/Artisan.php b/src/Container/Artisan.php index 0d74f19..216e087 100644 --- a/src/Container/Artisan.php +++ b/src/Container/Artisan.php @@ -1,5 +1,7 @@ setCompilerOutputMode(config('lasso.compiler.output', 'progress')); - } + private ?ProgressBar $progressBar = null; /** - * @param $name - * @param $arguments - * @return mixed|void - * @throws ConsoleMethodException + * Constructor */ - public function __call($name, $arguments) + public function __construct() { - if (method_exists($this->command, $name)) { - return call_user_func_array([$this->command, $name], $arguments); - } - - throw new ConsoleMethodException(sprintf( - 'Method %s::%s does not exist.', - get_class($this->command), - $name - )); + $this->compilerOutputMode = config('lasso.compiler.output', 'progress'); } /** * Create a note for the front end, set the second parameter to true for an error. * - * @param string $message - * @param bool $error * @return $this */ public function note(string $message, bool $error = false): self { if (! $this->isSilent) { $command = $error === true ? 'error' : 'info'; + $this->$command($message); } return $this; } - public function compilerOutput(string $line): void + /** + * Show compiler output + */ + public function showCompilerOutput(string $line): void { $mode = $this->compilerOutputMode; @@ -89,14 +81,22 @@ public function compilerOutput(string $line): void } } + /** + * Mark compiler as complete + */ public function compilerComplete(): void { - if ($this->progressBar instanceof ProgressBar) { - $this->progressBar->finish(); - $this->command->getOutput()->newLine(); + if (! $this->progressBar instanceof ProgressBar) { + return; } + + $this->progressBar->finish(); + $this->command->getOutput()->newLine(); } + /** + * Get the progress bar + */ private function getProgressBar(): ProgressBar { if ($bar = $this->progressBar) { @@ -110,11 +110,14 @@ private function getProgressBar(): ProgressBar $bar->setEmptyBarCharacter('-'); $bar->start(); - $this->setProgressBar($bar); + $this->progressBar = $bar; return $bar; } + /** + * Set the command being run + */ public function setCommand(Command $command): self { $this->command = $command; @@ -122,6 +125,9 @@ public function setCommand(Command $command): self return $this; } + /** + * Run the artisan console in silent mode + */ public function silent(): self { $this->isSilent = true; @@ -129,17 +135,18 @@ public function silent(): self return $this; } - private function setCompilerOutputMode(string $mode): self - { - $this->compilerOutputMode = $mode; - - return $this; - } - - private function setProgressBar(ProgressBar $bar): self + /** + * Handle a method call + * + * @param array $arguments + * @throws \Sammyjo20\Lasso\Exceptions\ConsoleMethodException + */ + public function __call(string $name, array $arguments): mixed { - $this->progressBar = $bar; + if (method_exists($this->command, $name)) { + return $this->command->$name(...$arguments); + } - return $this; + throw new ConsoleMethodException(sprintf('Method %s::%s does not exist.', get_class($this->command), $name)); } } diff --git a/src/Exceptions/BaseException.php b/src/Exceptions/BaseException.php index 416a8e2..1cda26a 100644 --- a/src/Exceptions/BaseException.php +++ b/src/Exceptions/BaseException.php @@ -1,24 +1,32 @@ */ public function create(): array { @@ -29,8 +36,7 @@ public function create(): array } /** - * @param string $bundleId - * @return $this + * Set the bundle ID */ public function setBundleId(string $bundleId): self { @@ -40,8 +46,7 @@ public function setBundleId(string $bundleId): self } /** - * @param string $path - * @return $this + * Set the ZIP path */ public function setZipPath(string $path): self { diff --git a/src/Helpers/BundleIntegrityHelper.php b/src/Helpers/BundleIntegrityHelper.php index 96ba62f..218c472 100644 --- a/src/Helpers/BundleIntegrityHelper.php +++ b/src/Helpers/BundleIntegrityHelper.php @@ -1,24 +1,29 @@ setCloudDisk(config('lasso.storage.disk')); - - $this->initCloudFilesystem() - ->initLocalFilesystem(); + $this->cloudFilesystem = Storage::disk(config('lasso.storage.disk')); + $this->localFilesystem = resolve(Filesystem::class); } /** - * @param $name - * @param $arguments - * @return mixed|void - * @throws ConsoleMethodException - */ - public function __call($name, $arguments) - { - if (method_exists($this->cloudFilesystem, $name)) { - return call_user_func_array([$this->cloudFilesystem, $name], $arguments); - } - - throw new ConsoleMethodException(sprintf( - 'Method %s::%s does not exist.', - get_class($this->cloudFilesystem), - $name - )); - } - - /** - * @param string $path - * @param string $name + * Upload a file to the cloud filesystem */ public function uploadFile(string $path, string $name): void { - $upload_path = $this->getUploadPath($name); + $uploadPath = $this->getUploadPath($name); $stream = fopen($path, 'rb'); - // Use the stream to write the bundle to the Filesystem. - if ($this->cloudFilesystem->writeStream($upload_path, $stream) === false) { - throw new UnableToWriteFile('Unable to write file at location ' . $upload_path); + if (! $stream) { + throw new LogicException('Unable to create a stream from the file path'); } - // Close the Stream pointer because it's good practice. - if (is_resource($stream)) { - fclose($stream); + // Use the stream to write the bundle to the Filesystem. + + if ($this->cloudFilesystem->writeStream($uploadPath, $stream) === false) { + throw new UnableToWriteFile(sprintf('Unable to write file at location [%s]', $uploadPath)); } + + fclose($stream); } /** - * Returns the Lasso upload directory. You can specify a file - * to create a fully qualified URL. + * Returns the Lasso upload directory. * - * @param string|null $file - * @return string + * You can specify a file to create a fully qualified URL. */ public function getUploadPath(string $file = null): string { $uploadPath = config('lasso.storage.upload_to'); - $dir = sprintf('%s/%s', $uploadPath, $this->localFilesystem->getLassoEnvironment()); + $directory = sprintf('%s/%s', $uploadPath, $this->localFilesystem->getLassoEnvironment()); if (is_null($file)) { - return $dir; + return $directory; } - return $dir . '/' . ltrim($file, '/'); + return $directory . '/' . ltrim($file, '/'); } /** - * @return string - */ - public function getCloudDisk(): string - { - return $this->cloudDisk; - } - - /** - * @param string $disk - * @return $this - */ - public function setCloudDisk(string $disk): self - { - $this->cloudDisk = $disk; - - return $this; - } - - /** - * @return $this - */ - public function initCloudFilesystem(): self - { - $this->cloudFilesystem = Storage::disk($this->cloudDisk); - - return $this; - } - - /** - * @return $this + * Call a method on the base Filesystem + * + * @param array $arguments + * @throws \Sammyjo20\Lasso\Exceptions\ConsoleMethodException */ - public function initLocalFilesystem(): self + public function __call(string $name, array $arguments): mixed { - $this->localFilesystem = resolve(LocalFilesystem::class); + if (method_exists($this->cloudFilesystem, $name)) { + return $this->cloudFilesystem->$name(...$arguments); + } - return $this; + throw new ConsoleMethodException(sprintf('Method %s::%s does not exist.', get_class($this->cloudFilesystem), $name)); } } diff --git a/src/Actions/Compiler.php b/src/Helpers/Compiler.php similarity index 62% rename from src/Actions/Compiler.php rename to src/Helpers/Compiler.php index dce149b..59e5850 100644 --- a/src/Actions/Compiler.php +++ b/src/Helpers/Compiler.php @@ -1,33 +1,35 @@ command) ->setTimeout($this->timeout) ->mustRun(function ($type, $line) use ($artisan) { - $artisan->compilerOutput($line); + $artisan->showCompilerOutput($line); }); $processTime = microtime(true) - $startTime; $artisan->compilerComplete(); - $this->setCompilationTime($processTime); + $this->compilationTime = round($processTime, 2); return $this; } - public function setCommand($command): self + /** + * Set the command for the compiler + */ + public function setCommand(string $command): self { $this->command = $command; return $this; } - public function setTimeout($timeout): self + /** + * Set the timeout for the compiler + */ + public function setTimeout(int $timeout): self { $this->timeout = $timeout; return $this; } - private function setCompilationTime(float $time): self - { - $this->compilationTime = round($time, 2); - - return $this; - } - + /** + * Get the compilation time + */ public function getCompilationTime(): float { return $this->compilationTime; diff --git a/src/Helpers/CompilerOutputFormatter.php b/src/Helpers/CompilerOutputFormatter.php index db5584c..db794d5 100644 --- a/src/Helpers/CompilerOutputFormatter.php +++ b/src/Helpers/CompilerOutputFormatter.php @@ -1,16 +1,18 @@ filesystem->exists($value) && $this->filesystem->isReadable($value) && $this->filesystem->isWritable($value); } /** - * @param $value - * @return bool + * Check disk exists */ - private function checkDiskExists($value): bool + private function checkDiskExists(mixed $value): bool { - return ! is_null(config('filesystems.disks.' . $value, null)); + return ! is_null(config('filesystems.disks.' . $value)); } /** - * @param $value - * @return bool + * Check bundle to keep count */ - private function checkBundleToKeepCount($value): bool + private function checkBundleToKeepCount(mixed $value): bool { return is_int($value) && $value > 0; } /** - * @throws ConfigFailedValidation - * @return void + * Validate config + * + * @throws ConfigFailedValidation|\Sammyjo20\Lasso\Exceptions\BaseException */ public function validate(): void { diff --git a/src/Helpers/FileLister.php b/src/Helpers/FileLister.php index 5fba6bd..44c2746 100644 --- a/src/Helpers/FileLister.php +++ b/src/Helpers/FileLister.php @@ -1,24 +1,27 @@ finder = (new Finder()) + $this->finder = (new Finder) ->in($directory) ->ignoreDotFiles(false) ->ignoreUnreadableDirs(true) @@ -27,9 +30,9 @@ public function __construct(string $directory) } /** - * @return Finder + * Return Finder */ - public function getFinder() + public function getFinder(): Finder { return $this->finder; } diff --git a/src/Helpers/Filesystem.php b/src/Helpers/Filesystem.php index 0591978..255a931 100644 --- a/src/Helpers/Filesystem.php +++ b/src/Helpers/Filesystem.php @@ -1,48 +1,49 @@ setLassoEnvironment($lassoEnvironment) - ->setCloudDisk($cloudDisk) - ->setPublicPath($publicPath); + $this->lassoEnvironment = config('lasso.storage.environment') ?? 'global'; + $this->cloudDisk = config('lasso.storage.disk', 'assets'); + $this->publicPath = config('lasso.public_path', public_path()); } /** - * @param $resource - * @param string $destination - * @return bool + * Store a file as a stream + * + * @param resource $resource */ - public function putStream($resource, string $destination): bool + public function putStream(mixed $resource, string $destination): bool { - $stream = fopen($destination, 'w+b'); + $stream = fopen($destination, 'wb+'); if (! $stream || stream_copy_to_stream($resource, $stream) === false || ! fclose($stream)) { return false; @@ -52,17 +53,19 @@ public function putStream($resource, string $destination): bool } /** - * @param array $bundle + * Create fresh from local bundle + * + * @param array $bundle */ public function createFreshLocalBundle(array $bundle): void { $this->deleteLocalBundle(); - $this->put(base_path('lasso-bundle.json'), json_encode($bundle)); + $this->put(base_path('lasso-bundle.json'), (string)json_encode($bundle)); } /** - * @return bool + * Delete local bundle */ public function deleteLocalBundle(): bool { @@ -71,8 +74,6 @@ public function deleteLocalBundle(): bool /** * Delete Lasso's base directory (.lasso) - * - * @return bool */ public function deleteBaseLassoDirectory(): bool { @@ -80,16 +81,7 @@ public function deleteBaseLassoDirectory(): bool } /** - * @return string - */ - public function getLassoEnvironment(): string - { - return $this->lassoEnvironment; - } - - /** - * @param string $environment - * @return $this + * Set the Lasso environment */ public function setLassoEnvironment(string $environment): self { @@ -99,40 +91,26 @@ public function setLassoEnvironment(string $environment): self } /** - * @return string + * Get the Lasso environment */ - public function getPublicPath(): string + public function getLassoEnvironment(): string { - return $this->publicPath; + return $this->lassoEnvironment; } /** - * @param string $publicPath - * @return $this + * Get the public path */ - public function setPublicPath(string $publicPath): self + public function getPublicPath(): string { - $this->publicPath = $publicPath; - - return $this; + return $this->publicPath; } /** - * @return string + * Get the cloud disk */ public function getCloudDisk(): string { return $this->cloudDisk; } - - /** - * @param string $disk - * @return $this - */ - public function setCloudDisk(string $disk): self - { - $this->cloudDisk = $disk; - - return $this; - } } diff --git a/src/Helpers/Git.php b/src/Helpers/Git.php index 6611389..13b38a3 100644 --- a/src/Helpers/Git.php +++ b/src/Helpers/Git.php @@ -1,28 +1,31 @@ getMessage(), $exception); - } - - if ($hash) { - return substr($hash, 0, 12); + } catch (Exception $exception) { + throw new GitHashException($exception->getMessage(), previous: $exception); } - return null; + return $hash ? mb_substr($hash, 0, 12) : null; } } diff --git a/src/Helpers/Unzipper.php b/src/Helpers/Unzipper.php index 844e0bc..c670303 100644 --- a/src/Helpers/Unzipper.php +++ b/src/Helpers/Unzipper.php @@ -1,93 +1,57 @@ setFilesystem() - ->createBaseZip($source) - ->setDestination($destination); + $this->filesystem = resolve(Filesystem::class); + $this->destination = $destination; + + $this->createBaseZip($source); } /** - * @return void + * Unzip the source into the destination */ public function run(): void { $this->filesystem->ensureDirectoryExists($this->destination); - $this->zip->extractTo($this->destination); - $this->closeZip(); - } - - /** - * @return $this - */ - private function setFilesystem(): self - { - $this->filesystem = resolve(Filesystem::class); + $this->zip->extractTo($this->destination); - return $this; + $this->zip->close(); } /** - * @param string $destination - * @return $this + * Create the base ZipArchive */ - private function createBaseZip(string $destination): self + private function createBaseZip(string $destination): void { - $this->zip = new ZipArchive(); + $this->zip = new ZipArchive; $this->zip->open($destination, ZipArchive::CREATE); - - return $this; - } - - /** - * @param string $destination - * @return $this - */ - private function setDestination(string $destination): self - { - $this->destination = $destination; - - return $this; - } - - /** - * @return void - */ - public function closeZip(): void - { - $this->zip->close(); } } diff --git a/src/Helpers/Webhook.php b/src/Helpers/Webhook.php new file mode 100644 index 0000000..a5a6432 --- /dev/null +++ b/src/Helpers/Webhook.php @@ -0,0 +1,29 @@ + $data + */ + public static function send(string $url, string $event, array $data = []): void + { + rescue( + callback: static function () use ($url, $event, $data) { + Http::post($url, array_merge(['event' => $event], $data)); + }, + rescue: false, + report: false + ); + } +} diff --git a/src/Helpers/Zip.php b/src/Helpers/Zip.php index b9f013b..28dbbbb 100644 --- a/src/Helpers/Zip.php +++ b/src/Helpers/Zip.php @@ -1,45 +1,47 @@ setFilesystem() - ->createBaseZip($destinationPath); + $this->filesystem = resolve(Filesystem::class); + + $this->createBaseZip($destinationPath); } /** - * @param string $directory - * @return $this + * Add files from a given directory */ public function addFilesFromDirectory(string $directory): self { - $files = (new FileLister($directory)) - ->getFinder(); + $files = (new FileLister($directory))->getFinder(); foreach ($files as $file) { $this->zip->addFile(str_replace('\\', '/', $file->getPathname()), str_replace('\\', '/', $file->getRelativePathname())); @@ -49,29 +51,16 @@ public function addFilesFromDirectory(string $directory): self } /** - * @return $this - */ - private function setFilesystem(): self - { - $this->filesystem = resolve(Filesystem::class); - - return $this; - } - - /** - * @param string $destination - * @return $this + * Create base ZipArchive */ - private function createBaseZip(string $destination): self + private function createBaseZip(string $destination): void { - $this->zip = new ZipArchive(); + $this->zip = (new ZipArchive); $this->zip->open($destination, ZipArchive::CREATE); - - return $this; } /** - * @return void + * Close the Zip */ public function closeZip(): void { diff --git a/src/Interfaces/JobInterface.php b/src/Interfaces/JobInterface.php deleted file mode 100644 index cc55c16..0000000 --- a/src/Interfaces/JobInterface.php +++ /dev/null @@ -1,11 +0,0 @@ -mergeConfigFrom( - __DIR__ . '/../config/lasso.php', - 'lasso' - ); - } - - public function boot(): void - { - if ($this->app->runningInConsole()) { - $this->registerCommands() - ->offerPublishing() - ->bindsServices(); - } - } - /** - * @return $this + * Register the Lasso service provider */ - protected function registerCommands(): self + public function register(): void { - $this->commands([ - PublishCommand::class, - PullCommand::class, - ]); - - return $this; + $this->mergeConfigFrom(__DIR__ . '/../config/lasso.php', 'lasso'); } /** - * @return $this + * Boot the Lasso service provider */ - protected function offerPublishing(): self + public function boot(): void { + if (! $this->app->runningInConsole()) { + return; + } + + // Publish the config + $this->publishes([ __DIR__ . '/../config/lasso.php' => config_path('lasso.php'), ], 'lasso-config'); - return $this; - } + // Register Lasso's commands - /** - * @return $this - */ - protected function bindsServices(): self - { - $this->app->singleton(Artisan::class, function () { - return new Artisan; - }); + $this->commands([ + PublishCommand::class, + PullCommand::class, + ]); - $this->app->singleton(Filesystem::class, function () { - return new Filesystem; - }); + // Bind Artisan and Filesystem to the container - return $this; + $this->app->singleton(Artisan::class, static fn () => new Artisan); + $this->app->singleton(Filesystem::class, static fn () => new Filesystem); } } diff --git a/src/Services/ArchiveService.php b/src/Services/ArchiveService.php index 6b33cb7..92753f0 100644 --- a/src/Services/ArchiveService.php +++ b/src/Services/ArchiveService.php @@ -1,5 +1,7 @@ run(); + (new Unzipper($source, $destination))->run(); } } diff --git a/src/Services/BackupService.php b/src/Services/BackupService.php index f57c00d..2fa6c24 100644 --- a/src/Services/BackupService.php +++ b/src/Services/BackupService.php @@ -1,5 +1,7 @@ setFilesystem($filesystem); + $this->filesystem = $filesystem; } /** * Copy a directory to a given location. - * - * @param string $sourceDirectory - * @param string $destinationDirectory - * @return bool */ public function createBackup(string $sourceDirectory, string $destinationDirectory): bool { @@ -49,8 +45,8 @@ public function createBackup(string $sourceDirectory, string $destinationDirecto } /** - * @param string $destinationDirectory - * @return bool + * Restore a backup + * * @throws \Sammyjo20\Lasso\Exceptions\BaseException */ public function restoreBackup(string $destinationDirectory): bool @@ -67,27 +63,7 @@ public function restoreBackup(string $destinationDirectory): bool } /** - * @param Filesystem $filesystem - * @return $this - */ - public function setFilesystem(Filesystem $filesystem): self - { - $this->filesystem = $filesystem; - - return $this; - } - - /** - * @return bool - */ - public function hasBackup(): bool - { - return ! is_null($this->backupPath); - } - - /** - * @param string $path - * @return $this + * Set a backup path */ public function setBackupPath(string $path): self { diff --git a/src/Services/VersioningService.php b/src/Services/VersioningService.php index a2e1044..98ebafb 100644 --- a/src/Services/VersioningService.php +++ b/src/Services/VersioningService.php @@ -1,25 +1,32 @@ * @throws \Sammyjo20\Lasso\Exceptions\BaseException */ private static function getHistoryFromDisk(): array { $disk = self::getDisk(); - $path = self::getFileDirectory('history.json'); + $path = self::getFileDirectory(); // If there is no history to be found in the Filesystem, // that's completely fine. Let's just return an empty @@ -60,7 +67,7 @@ private static function getHistoryFromDisk(): array ksort($history); return $history; - } catch (\Exception $ex) { + } catch (Exception $ex) { throw VersioningFailed::because( 'Lasso could not retrieve the history.json file from the Filesystem.' ); @@ -68,46 +75,49 @@ private static function getHistoryFromDisk(): array } /** - * @param array $bundles - * @return array + * Delete expired bundles + * + * @param array $bundles + * @return array */ private static function deleteExpiredBundles(array $bundles): array { - $bundle_limit = self::getMaxBundlesAllowed(); + $bundleLimit = config('lasso.storage.max_bundles'); - // If we haven't exceeded our bundle Limit, - // let's just return the bundles. There's nothing - // more we can do here. + // If we haven't exceeded our bundle Limit, let's just return the bundles. + // There's nothing more we can do here. - if (count($bundles) <= $bundle_limit) { + if (count($bundles) <= $bundleLimit) { return $bundles; } - // However, if there's a bundle to be removed - // we need to go Ghostbuster on that bundle. - $deletable_count = count($bundles) - $bundle_limit; + // However, if there's a bundle to be removed we need to go Ghostbuster on that bundle. + + $deletable_count = count($bundles) - $bundleLimit; $deletable = array_slice($bundles, 0, $deletable_count, true); // Now let's delete those bundles! + $deleted = self::deleteBundles(array_values($deletable)); - // Finally, we want to return a new array, with - // the bundles that have been deleted removed. + // Finally, we want to return a new array, with the bundles that have been deleted removed. + return array_diff($bundles, $deleted); } /** - * @param array $deletable - * @return array + * Delete bundles + * + * @param array $deletable + * @return array */ private static function deleteBundles(array $deletable): array { $disk = self::getDisk(); $deleted = []; - // Attempt to delete the bundle. If something - // goes wrong, Lasso isn't precious about it. - // we will simply try to delete the next directory and move on. + // Attempt to delete the bundle. If something goes wrong, Lasso isn't precious about it. + // We will simply try to delete the next directory and move on. foreach ($deletable as $bundle_key => $bundle) { try { @@ -116,7 +126,7 @@ private static function deleteBundles(array $deletable): array if ($success) { $deleted[$bundle_key] = $bundle; } - } catch (\Exception $ex) { + } catch (Exception) { continue; } } @@ -125,20 +135,16 @@ private static function deleteBundles(array $deletable): array } /** - * @param array $history - * @return bool + * Update history file + * + * @param array $history * @throws \Sammyjo20\Lasso\Exceptions\BaseException */ - private static function updateHistory(array $history): bool + private static function updateHistory(array $history): void { - $disk = self::getDisk(); - $path = self::getFileDirectory('history.json'); - try { - $encoded = json_encode($history); - - return Storage::disk($disk)->put($path, $encoded); - } catch (\Exception $ex) { + Storage::disk(self::getDisk())->put(self::getFileDirectory(), json_encode($history, JSON_THROW_ON_ERROR)); + } catch (Exception) { throw VersioningFailed::because( 'Lasso could not update the history.json on the Filesystem.' ); @@ -146,27 +152,18 @@ private static function updateHistory(array $history): bool } /** - * @param string $path - * @return string + * Get the file directory */ - private static function getFileDirectory(string $path): string + private static function getFileDirectory(): string { - return (new Cloud())->getUploadPath($path); + return (new Cloud)->getUploadPath('history.json'); } /** - * @return string + * Get the disk */ private static function getDisk(): string { return config('lasso.storage.disk'); } - - /** - * @return int - */ - private static function getMaxBundlesAllowed(): int - { - return config('lasso.storage.max_bundles'); - } } diff --git a/src/Tasks/BaseJob.php b/src/Tasks/BaseJob.php index 71ddcb0..ee78194 100644 --- a/src/Tasks/BaseJob.php +++ b/src/Tasks/BaseJob.php @@ -1,70 +1,37 @@ setArtisan() - ->setFilesystem(); - - // The Cloud class should be defined after the Filesystem as - // it depends on the Filesystem. - - $this->setCloud(); - } - - /** - * @return $this - */ - private function setArtisan(): self { $this->artisan = resolve(Artisan::class); - - return $this; - } - - /** - * @return $this - */ - private function setFilesystem(): self - { $this->filesystem = resolve(Filesystem::class); - - return $this; - } - - /** - * @return $this - */ - private function setCloud(): self - { $this->cloud = new Cloud; - - return $this; } } diff --git a/src/Tasks/Command.php b/src/Tasks/Command.php deleted file mode 100644 index e951991..0000000 --- a/src/Tasks/Command.php +++ /dev/null @@ -1,63 +0,0 @@ -script); - - $process->setTimeout($this->timeout) - ->run(); - - if (! $process->isSuccessful()) { - throw new ProcessFailedException($process); - } - } - - /** - * @param $script - * @return $this - */ - public function setScript($script): self - { - if (! is_array($script)) { - $script = explode(' ', $script); - } - - $this->script = $script; - - return $this; - } - - /** - * @param int $timeout - * @return $this - */ - public function setTimeout(int $timeout = 600): self - { - $this->timeout = $timeout; - - return $this; - } -} diff --git a/src/Tasks/Publish/BundleJob.php b/src/Tasks/Publish/BundleJob.php index d3a3193..97a5ba9 100644 --- a/src/Tasks/Publish/BundleJob.php +++ b/src/Tasks/Publish/BundleJob.php @@ -1,5 +1,7 @@ */ - protected $forbiddenFiles = [ + protected array $forbiddenFiles = [ '.htaccess', 'web.config', 'index.php', @@ -25,9 +29,11 @@ final class BundleJob extends BaseJob ]; /** - * @var array + * Forbidden Directories + * + * @var array */ - protected $forbiddenDirectories = [ + protected array $forbiddenDirectories = [ 'storage', 'hot', ]; @@ -42,15 +48,13 @@ public function __construct() $userForbiddenFiles = config('lasso.compiler.excluded_files', []); $userForbiddenDirs = config('lasso.compiler.excluded_directories', []); - $this->setForbiddenFiles( - array_merge($this->forbiddenFiles, $userForbiddenFiles) - ); - - $this->setForbiddenDirectories( - array_merge($this->forbiddenDirectories, $userForbiddenDirs) - ); + $this->forbiddenFiles = array_merge($this->forbiddenFiles, $userForbiddenFiles); + $this->forbiddenDirectories = array_merge($this->forbiddenDirectories, $userForbiddenDirs); } + /** + * Run the bundle job + */ public function run(): void { $filesystem = $this->filesystem; @@ -105,8 +109,7 @@ public function run(): void } /** - * @param string $bundleId - * @return $this + * Set the Bundle ID */ public function setBundleId(string $bundleId): self { @@ -114,26 +117,4 @@ public function setBundleId(string $bundleId): self return $this; } - - /** - * @param array $files - * @return $this - */ - private function setForbiddenFiles(array $files): self - { - $this->forbiddenFiles = $files; - - return $this; - } - - /** - * @param array $directories - * @return $this - */ - private function setForbiddenDirectories(array $directories): self - { - $this->forbiddenDirectories = $directories; - - return $this; - } } diff --git a/src/Tasks/Publish/PublishJob.php b/src/Tasks/Publish/PublishJob.php index 8a43a9c..1ac5e49 100644 --- a/src/Tasks/Publish/PublishJob.php +++ b/src/Tasks/Publish/PublishJob.php @@ -1,37 +1,39 @@ artisan; + $filesystem = $this->filesystem; + $cloud = $this->cloud; + try { $this->generateBundleId(); - $this->artisan->note('⏳ Compiling assets...'); + $artisan->note('⏳ Compiling assets...'); // Start with the compiler. This will run the "script" which // has been defined in the config file (e.g. npm run production). - $compiler = (new Compiler()) + $compiler = (new Compiler) ->setCommand(config('lasso.compiler.script')) ->setTimeout(config('lasso.compiler.timeout', 600)) ->execute(); - $this->artisan->note(sprintf( + $artisan->note(sprintf( '✅ Compiled assets in %s seconds.', $compiler->getCompilationTime() )); - $this->artisan->note('⏳ Copying and zipping compiled assets...'); + $artisan->note('⏳ Copying and zipping compiled assets...'); // Once we have compiled all of our assets, we need to "bundle" - // them up. Todo: Remove this step in the future. + // them up. - (new BundleJob()) + (new BundleJob) ->setBundleId($this->bundleId) ->run(); $zipBundlePath = base_path('.lasso/dist/' . $this->bundleId . '.zip'); - $this->artisan->note('✅ Successfully copied and zipped assets.'); + $artisan->note('✅ Successfully copied and zipped assets.'); // Now we want to create the data which will go inside the // "lasso-bundle.json" file. After that, we will create a Zip file // with all the assets inside. - $bundle = (new Bundle()) + $bundle = (new Bundle) ->setBundleId($this->bundleId) ->setZipPath($zipBundlePath) ->create(); - $this->artisan->note( - sprintf('⏳ Uploading asset bundle to "%s" filesystem...', $this->filesystem->getCloudDisk()) + $artisan->note( + sprintf('⏳ Uploading asset bundle to "%s" filesystem...', $filesystem->getCloudDisk()) ); $bundlePath = base_path('.lasso/dist/lasso-bundle.json'); @@ -99,20 +104,20 @@ public function run(): void // this uses stream to ensure we don't run out of memory // while uploading the file. - $this->cloud->uploadFile($zipBundlePath, $bundle['file']); + $cloud->uploadFile($zipBundlePath, $bundle['file']); // Has the user requested to use/not use Git? Here // we will create the lasso-bundle.json in the right // place. if ($this->usesGit) { - $this->filesystem->createFreshLocalBundle($bundle); + $filesystem->createFreshLocalBundle($bundle); } else { - $this->filesystem->deleteLocalBundle(); + $filesystem->deleteLocalBundle(); - $this->filesystem->put($bundlePath, json_encode($bundle)); + $filesystem->put($bundlePath, (string)json_encode($bundle)); - $this->cloud->uploadFile($bundlePath, config('lasso.storage.prefix') . 'lasso-bundle.json'); + $cloud->uploadFile($bundlePath, config('lasso.storage.prefix') . 'lasso-bundle.json'); } // Done! Let's run some cleanup, and dispatch all the @@ -126,8 +131,9 @@ public function run(): void } } + /** - * @return void + * Clean up the publish */ public function cleanUp(): void { @@ -135,10 +141,11 @@ public function cleanUp(): void } /** - * @param Exception $exception + * Roll back the publish + * * @throws Exception */ - private function rollBack(Exception $exception) + private function rollBack(Exception $exception): void { $this->deleteLassoDirectory(); @@ -146,7 +153,9 @@ private function rollBack(Exception $exception) } /** - * @param array $webhooks + * Dispatch the webhooks + * + * @param array $webhooks */ public function dispatchWebhooks(array $webhooks = []): void { @@ -164,10 +173,11 @@ public function dispatchWebhooks(array $webhooks = []): void } /** - * @return $this + * Generate the Bundle ID + * * @throws GitHashException */ - private function generateBundleId(): self + private function generateBundleId(): void { $id = Str::random(20); @@ -180,21 +190,19 @@ private function generateBundleId(): self } $this->bundleId = $id; - - return $this; } /** - * @return $this + * Delete the Lasso directory */ - private function deleteLassoDirectory(): self + private function deleteLassoDirectory(): void { $this->filesystem->deleteBaseLassoDirectory(); - - return $this; } /** + * Disable Git with Lasso + * * @return $this */ public function dontUseGit(): self @@ -205,6 +213,8 @@ public function dontUseGit(): self } /** + * Should Lasso use the latest commit from Git? + * * @return $this */ public function useCommit(): self @@ -214,6 +224,11 @@ public function useCommit(): self return $this; } + /** + * Specify a commit that is used for the bundle + * + * @return $this + */ public function withCommit(string $commitHash): self { $this->commit = $commitHash; diff --git a/src/Tasks/Pull/PullJob.php b/src/Tasks/Pull/PullJob.php index 5d63aaa..69bbb88 100644 --- a/src/Tasks/Pull/PullJob.php +++ b/src/Tasks/Pull/PullJob.php @@ -1,10 +1,13 @@ setBackup(); + $this->backup = new BackupService($this->filesystem); } /** - * @return void + * Run the "pull" job + * * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface * @throws \Sammyjo20\Lasso\Exceptions\BaseException - * @throws PullJobFailed */ public function run(): void { @@ -85,7 +90,7 @@ public function run(): void $this->filesystem ->copy($source, $destination); } - } catch (\Exception $ex) { + } catch (Exception $ex) { // If anything goes wrong inside this try block, // we will "roll back" which means we will restore // our backup. @@ -100,11 +105,12 @@ public function run(): void $this->cleanUp(); $webhooks = config('lasso.webhooks.pull', []); + $this->dispatchWebhooks($webhooks); } /** - * @return void + * Cleanup the command */ public function cleanUp(): void { @@ -112,7 +118,9 @@ public function cleanUp(): void } /** - * @param array $webhooks + * Dispatch the webhooks + * + * @param array $webhooks */ public function dispatchWebhooks(array $webhooks = []): void { @@ -130,16 +138,19 @@ public function dispatchWebhooks(array $webhooks = []): void } /** - * @return array + * Get the latest bundle info + * + * @return array * @throws \Illuminate\Contracts\Filesystem\FileNotFoundException + * @throws \Psr\Container\ContainerExceptionInterface + * @throws \Psr\Container\NotFoundExceptionInterface */ private function getLatestBundleInfo(): array { $localPath = base_path('lasso-bundle.json'); $cloudPath = $this->cloud->getUploadPath(config('lasso.storage.prefix') . 'lasso-bundle.json'); - // Firstly, let's check if the local filesystem has a "lasso-bundle.json" - // file in it's root directory. + // Firstly, let's check if the local filesystem has a "lasso-bundle.json" file in its root directory. if ($this->filesystem->exists($localPath)) { $file = $this->filesystem->get($localPath); @@ -150,10 +161,9 @@ private function getLatestBundleInfo(): array return $bundle; } - // If there isn't a "lasso-bundle.json" file in the root directory, - // that's okay - this means that the commit is in "non-git" mode. So - // let's just grab that file. If we don't have a file on the server - // however; we need to throw an exception. + // If there isn't a "lasso-bundle.json" file in the root directory, that's okay - this means + // that the commit is in "non-git" mode. So let's just grab that file. If we don't have a + // file on the server however; we need to throw an exception. if (! $this->cloud->exists($cloudPath)) { $this->rollBack( @@ -170,13 +180,14 @@ private function getLatestBundleInfo(): array } /** - * @param array $bundle - * @return bool + * Validate the bundle checksum + * + * @param array $bundle * @throws \Exception */ private function validateBundle(array $bundle): bool { - if (! isset($bundle['file']) || ! isset($bundle['checksum'])) { + if (! isset($bundle['file'], $bundle['checksum'])) { $this->rollBack( PullJobFailed::because('The bundle info was missing the required data.') ); @@ -186,10 +197,7 @@ private function validateBundle(array $bundle): bool } /** - * @param string $file - * @param string $checksum - * @return string - * @throws PullJobFailed|\Exception + * Download Zip bundle file */ private function downloadBundleZip(string $file, string $checksum): string { @@ -212,7 +220,7 @@ private function downloadBundleZip(string $file, string $checksum): string if (is_resource($bundleZip)) { fclose($bundleZip); } - } catch (\Exception $ex) { + } catch (Exception $ex) { $this->rollBack($ex); } @@ -236,18 +244,17 @@ private function downloadBundleZip(string $file, string $checksum): string } /** - * @param \Exception $exception + * Roll back and throw an exception + * * @throws \Exception */ - private function rollBack(\Exception $exception) + private function rollBack(Exception $exception): Exception { - if ($this->backup->hasBackup()) { - $this->artisan->note('⏳ Restoring backup...'); + $this->artisan->note('⏳ Restoring backup...'); - $this->backup->restoreBackup($this->filesystem->getPublicPath()); + $this->backup->restoreBackup($this->filesystem->getPublicPath()); - $this->artisan->note('✅ Successfully restored backup.'); - } + $this->artisan->note('✅ Successfully restored backup.'); $this->filesystem->deleteBaseLassoDirectory(); @@ -255,30 +262,15 @@ private function rollBack(\Exception $exception) } /** - * @return $this + * Backup the source data */ - private function runBackup(): self + private function runBackup(): void { - $this->backup->createBackup( - $this->filesystem->getPublicPath(), - base_path('.lasso/backup') - ); - - return $this; - } - - /** - * @return $this - */ - private function setBackup(): self - { - $this->backup = new BackupService($this->filesystem); - - return $this; + $this->backup->createBackup($this->filesystem->getPublicPath(), base_path('.lasso/backup')); } /** - * @return void + * Cleanup the Lasso directory */ private function cleanLassoDirectory(): void { @@ -288,8 +280,7 @@ private function cleanLassoDirectory(): void } /** - * @param string $file - * @return string + * Get the Lasso bundle path */ private function getBundlePath(string $file): string { @@ -305,7 +296,7 @@ private function getBundlePath(string $file): string } /** - * @return $this + * Should Lasso use the latest commit from Git? */ public function useCommit(): self { @@ -315,7 +306,7 @@ public function useCommit(): self } /** - * @return $this + * Specify a custom commit hash */ public function withCommit(string $commitHash): self { diff --git a/src/Tasks/Webhook.php b/src/Tasks/Webhook.php deleted file mode 100644 index a6f7795..0000000 --- a/src/Tasks/Webhook.php +++ /dev/null @@ -1,23 +0,0 @@ - $event], $data)); - }, false, false); - - return true; - } -} diff --git a/tests/ExampleTest.php b/tests/ExampleTest.php deleted file mode 100644 index 19eb2cf..0000000 --- a/tests/ExampleTest.php +++ /dev/null @@ -1,12 +0,0 @@ -assertTrue(true); - } -} diff --git a/tests/Feature/LassoTest.php b/tests/Feature/LassoTest.php new file mode 100644 index 0000000..fe17d8c --- /dev/null +++ b/tests/Feature/LassoTest.php @@ -0,0 +1,45 @@ +toBeFalse(); + expect(File::isEmptyDirectory('./tests/Fixtures/Cloud'))->toBeTrue(); + + Config::set('lasso', defaultConfig()); + + // Kick off the publish which will compile the asset + + $this->artisan('lasso:publish'); + + expect(File::exists('./lasso-bundle.json'))->toBeTrue(); + + $bundleName = json_decode(File::get('./lasso-bundle.json'), true, 512, JSON_THROW_ON_ERROR)['file']; + + // Check the bundle exists + + expect(File::exists('./tests/Fixtures/Cloud/lasso/global/' . $bundleName))->toBeTrue(); + + File::cleanDirectory('./tests/Fixtures/Public'); + + expect(File::isEmptyDirectory('./tests/Fixtures/Public'))->toBeTrue(); + + $this->artisan('lasso:pull'); + + // Now we'll ensure that the public directory has contents! + + expect(File::isEmptyDirectory('./tests/Fixtures/Public'))->toBeFalse(); + expect(File::exists('./tests/Fixtures/Public/app.css'))->toBeTrue(); + expect(File::exists('./tests/Fixtures/Public/lasso-logo.png'))->toBeTrue(); +}); diff --git a/tests/Fixtures/Local/app.css b/tests/Fixtures/Local/app.css new file mode 100644 index 0000000..7bb2d46 --- /dev/null +++ b/tests/Fixtures/Local/app.css @@ -0,0 +1,3 @@ +/*! tailwindcss v2.2.4 | MIT License | https://tailwindcss.com*/ + +/*! modern-normalize v1.1.0 | MIT License | https://github.com/sindresorhus/modern-normalize */html{-webkit-text-size-adjust:100%;line-height:1.15;-moz-tab-size:4;-o-tab-size:4;tab-size:4}body{font-family:system-ui,-apple-system,Segoe UI,Roboto,Helvetica,Arial,sans-serif,Apple Color Emoji,Segoe UI Emoji;margin:0}hr{color:inherit;height:0}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted}b,strong{font-weight:bolder}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Consolas,Liberation Mono,Menlo,monospace;font-size:1em}small{font-size:80%}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sub{bottom:-.25em}sup{top:-.5em}table{border-color:inherit;text-indent:0}button,input,optgroup,select,textarea{font-family:inherit;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}button{-webkit-appearance:button}legend{padding:0}progress{vertical-align:baseline}summary{display:list-item}blockquote,dd,dl,figure,h1,h2,h3,h4,h5,h6,hr,p,pre{margin:0}button{background-color:transparent;background-image:none}fieldset,ol,ul{margin:0;padding:0}ol,ul{list-style:none}html{font-family:ui-sans-serif,system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}body{font-family:inherit;line-height:inherit}*,:after,:before{border:0 solid;box-sizing:border-box}hr{border-top-width:1px}img{border-style:solid}textarea{resize:vertical}input::-moz-placeholder,textarea::-moz-placeholder{color:#9ca3af;opacity:1}input:-ms-input-placeholder,textarea:-ms-input-placeholder{color:#9ca3af;opacity:1}input::placeholder,textarea::placeholder{color:#9ca3af;opacity:1}[role=button],button{cursor:pointer}table{border-collapse:collapse}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;text-decoration:inherit}button,input,optgroup,select,textarea{color:inherit;line-height:inherit;padding:0}code,kbd,pre,samp{font-family:ui-monospace,SFMono-Regular,Menlo,Monaco,Consolas,Liberation Mono,Courier New,monospace}audio,canvas,embed,iframe,img,object,svg,video{display:block;vertical-align:middle}img,video{height:auto;max-width:100%}*,:after,:before{--tw-border-opacity:1;border-color:rgba(229,231,235,var(--tw-border-opacity))}.container{width:100%}@media (min-width:640px){.container{max-width:640px}}@media (min-width:768px){.container{max-width:768px}}@media (min-width:1024px){.container{max-width:1024px}}@media (min-width:1280px){.container{max-width:1280px}}@media (min-width:1536px){.container{max-width:1536px}}.absolute{position:absolute}.relative{position:relative}.top-0{top:0}.left-0{left:0}.z-0{z-index:0}.z-10{z-index:10}.mx-4{margin-left:1rem;margin-right:1rem}.mx-auto{margin-left:auto;margin-right:auto}.mt-2{margin-top:.5rem}.mt-6{margin-top:1.5rem}.mb-2{margin-bottom:.5rem}.mb-4{margin-bottom:1rem}.mb-6{margin-bottom:1.5rem}.mb-8{margin-bottom:2rem}.mb-12{margin-bottom:3rem}.block{display:block}.inline-block{display:inline-block}.flex{display:flex}.table{display:table}.hidden{display:none}.h-full{height:100%}.w-8{width:2rem}.w-12{width:3rem}.w-full{width:100%}.transform{--tw-translate-x:0;--tw-translate-y:0;--tw-rotate:0;--tw-skew-x:0;--tw-skew-y:0;transform:translateX(var(--tw-translate-x)) translateY(var(--tw-translate-y)) rotate(var(--tw-rotate)) skewX(var(--tw-skew-x)) skewY(var(--tw-skew-y)) scaleX(var(--tw-scale-x)) scaleY(var(--tw-scale-y))}.scale-100,.transform{--tw-scale-x:1;--tw-scale-y:1}.hover\:scale-105:hover{--tw-scale-x:1.05;--tw-scale-y:1.05}.active\:scale-95:active{--tw-scale-x:.95;--tw-scale-y:.95}@-webkit-keyframes spin{to{transform:rotate(1turn)}}@keyframes spin{to{transform:rotate(1turn)}}@-webkit-keyframes ping{75%,to{opacity:0;transform:scale(2)}}@keyframes ping{75%,to{opacity:0;transform:scale(2)}}@-webkit-keyframes pulse{50%{opacity:.5}}@keyframes pulse{50%{opacity:.5}}@-webkit-keyframes bounce{0%,to{-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1);transform:translateY(-25%)}50%{-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1);transform:none}}@keyframes bounce{0%,to{-webkit-animation-timing-function:cubic-bezier(.8,0,1,1);animation-timing-function:cubic-bezier(.8,0,1,1);transform:translateY(-25%)}50%{-webkit-animation-timing-function:cubic-bezier(0,0,.2,1);animation-timing-function:cubic-bezier(0,0,.2,1);transform:none}}.cursor-pointer{cursor:pointer}.flex-row{flex-direction:row}.items-center{align-items:center}.overflow-y-hidden{overflow-y:hidden}.truncate{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.rounded-lg{border-radius:.5rem}.bg-primary{--tw-bg-opacity:1;background-color:rgba(249,199,46,var(--tw-bg-opacity))}.bg-faded-leather{--tw-bg-opacity:1;background-color:rgba(130,95,77,var(--tw-bg-opacity))}.bg-burned-leather{--tw-bg-opacity:1;background-color:rgba(74,53,43,var(--tw-bg-opacity))}.p-4{padding:1rem}.px-3{padding-left:.75rem;padding-right:.75rem}.px-4{padding-left:1rem;padding-right:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.px-16{padding-left:4rem;padding-right:4rem}.py-1{padding-bottom:.25rem;padding-top:.25rem}.py-2{padding-bottom:.5rem;padding-top:.5rem}.py-8{padding-bottom:2rem;padding-top:2rem}.py-16{padding-bottom:4rem;padding-top:4rem}.pt-24{padding-top:6rem}.pb-16{padding-bottom:4rem}.text-center{text-align:center}.font-roboto{font-family:Roboto,sans-serif}.font-jetbrains-mono{font-family:JetBrains Mono,sans-serif}.text-sm{font-size:.875rem;line-height:1.25rem}.text-base{font-size:1rem;line-height:1.5rem}.text-lg{font-size:1.125rem}.text-lg,.text-xl{line-height:1.75rem}.text-xl{font-size:1.25rem}.text-4xl{font-size:2.25rem;line-height:2.5rem}.text-5xl{font-size:3rem;line-height:1}.leading-tight{line-height:1.25}.tracking-wide{letter-spacing:.025em}.text-white{--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity))}.text-primary{--tw-text-opacity:1;color:rgba(249,199,46,var(--tw-text-opacity))}.text-leather{--tw-text-opacity:1;color:rgba(99,71,57,var(--tw-text-opacity))}.opacity-25{opacity:.25}.opacity-75{opacity:.75}*,:after,:before{--tw-shadow:0 0 #0000}.shadow-md{--tw-shadow:0 4px 6px -1px rgba(0,0,0,0.1),0 2px 4px -1px rgba(0,0,0,0.06);box-shadow:var(--tw-ring-offset-shadow,0 0 #0000),var(--tw-ring-shadow,0 0 #0000),var(--tw-shadow)}.focus\:outline-none:focus,.outline-none{outline:2px solid transparent;outline-offset:2px}*,:after,:before{--tw-ring-inset:var(--tw-empty,/*!*/ /*!*/);--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-color:rgba(59,130,246,0.5);--tw-ring-offset-shadow:0 0 #0000;--tw-ring-shadow:0 0 #0000}.duration-200{transition-duration:.2s}@font-face{font-family:JetBrains Mono;font-style:normal;font-weight:400;src:url(../fonts/JetBrainsMono-Regular.eot) format("embedded-opentype"),url(../fonts/JetBrainsMono-Regular.woff2) format("woff2"),url(../fonts/JetBrainsMono-Regular.woff) format("woff"),url(../fonts/JetBrainsMono-Regular.ttf) format("truetype")}*{font-family:Yeseva One,sans-serif}body{--tw-bg-opacity:1;background-color:rgba(99,71,57,var(--tw-bg-opacity))}.github-icon{box-sizing:border-box;display:block;height:4rem;padding:1rem;position:absolute;right:0;top:0;width:4rem}.github-icon.docs{position:relative}@media (min-width:768px){.github-icon{height:5rem;width:5rem}}.kendrick-lamar{margin-left:auto;margin-right:auto;max-width:700px;width:100%}.svg-bg{background-color:#634739;background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='56' height='28'%3E%3Cpath fill='%23825f4d' fill-opacity='.4' d='M56 26v2h-7.75c2.3-1.27 4.94-2 7.75-2zm-26 2a2 2 0 1 0-4 0h-4.09A25.98 25.98 0 0 0 0 16v-2c.67 0 1.34.02 2 .07V14a2 2 0 0 0-2-2v-2a4 4 0 0 1 3.98 3.6 28.09 28.09 0 0 1 2.8-3.86A8 8 0 0 0 0 6V4a9.99 9.99 0 0 1 8.17 4.23c.94-.95 1.96-1.83 3.03-2.63A13.98 13.98 0 0 0 0 0h7.75c2 1.1 3.73 2.63 5.1 4.45 1.12-.72 2.3-1.37 3.53-1.93A20.1 20.1 0 0 0 14.28 0h2.7c.45.56.88 1.14 1.29 1.74 1.3-.48 2.63-.87 4-1.15-.11-.2-.23-.4-.36-.59H26v.07a28.4 28.4 0 0 1 4 0V0h4.09l-.37.59c1.38.28 2.72.67 4.01 1.15.4-.6.84-1.18 1.3-1.74h2.69a20.1 20.1 0 0 0-2.1 2.52c1.23.56 2.41 1.2 3.54 1.93A16.08 16.08 0 0 1 48.25 0H56c-4.58 0-8.65 2.2-11.2 5.6 1.07.8 2.09 1.68 3.03 2.63A9.99 9.99 0 0 1 56 4v2a8 8 0 0 0-6.77 3.74c1.03 1.2 1.97 2.5 2.79 3.86A4 4 0 0 1 56 10v2a2 2 0 0 0-2 2.07 28.4 28.4 0 0 1 2-.07v2c-9.2 0-17.3 4.78-21.91 12H30zM7.75 28H0v-2c2.81 0 5.46.73 7.75 2zM56 20v2c-5.6 0-10.65 2.3-14.28 6h-2.7c4.04-4.89 10.15-8 16.98-8zm-39.03 8h-2.69C10.65 24.3 5.6 22 0 22v-2c6.83 0 12.94 3.11 16.97 8zm15.01-.4a28.09 28.09 0 0 1 2.8-3.86 8 8 0 0 0-13.55 0c1.03 1.2 1.97 2.5 2.79 3.86a4 4 0 0 1 7.96 0zm14.29-11.86c1.3-.48 2.63-.87 4-1.15a25.99 25.99 0 0 0-44.55 0c1.38.28 2.72.67 4.01 1.15a21.98 21.98 0 0 1 36.54 0zm-5.43 2.71c1.13-.72 2.3-1.37 3.54-1.93a19.98 19.98 0 0 0-32.76 0c1.23.56 2.41 1.2 3.54 1.93a15.98 15.98 0 0 1 25.68 0zm-4.67 3.78c.94-.95 1.96-1.83 3.03-2.63a13.98 13.98 0 0 0-22.4 0c1.07.8 2.09 1.68 3.03 2.63a9.99 9.99 0 0 1 16.34 0z'/%3E%3C/svg%3E")}.header-bg .logo{display:block;margin-left:auto;margin-right:auto;pointer-events:none}.header-bg .logo,.header-bg .strapline{-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;width:100%}.header-bg .strapline{--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));font-size:1.25rem;line-height:1.75rem;margin-top:1.5rem;text-align:center}@media (min-width:640px){.header-bg .strapline{font-size:1.5rem;line-height:2rem}}@media (min-width:768px){.header-bg .strapline{bottom:16px;margin-top:0;position:absolute;right:-64px;width:auto}}.content{max-width:1000px}.flow{flex-direction:row;width:100%}.flow,.flow .step .circle{display:flex;justify-content:center}.flow .step .circle{--tw-bg-opacity:1;background-color:rgba(74,53,43,var(--tw-bg-opacity));border-radius:9999px;flex-direction:column;height:90px;text-align:center;width:90px}@media (min-width:768px){.flow .step .circle{height:140px;width:140px}}.flow .step .circle .icon{height:48px;margin-left:auto;margin-right:auto;width:48px}@media (min-width:768px){.flow .step .circle .icon{height:64px;width:64px}}.flow .breadcrumbs{align-items:center;display:flex;flex-direction:row;margin-left:.5rem;margin-right:.5rem}@media (min-width:768px){.flow .breadcrumbs{margin-left:1.5rem;margin-right:1.5rem}}.flow .breadcrumbs .arrow{--tw-text-opacity:1;color:rgba(255,255,255,var(--tw-text-opacity));font-size:1.875rem;line-height:2.25rem}@media (min-width:768px){.flow .breadcrumbs .arrow{display:none}}.flow .breadcrumbs .crumb{--tw-bg-opacity:1;background-color:rgba(255,255,255,var(--tw-bg-opacity));border-radius:9999px;display:none;height:16px;margin-left:.25rem;margin-right:.25rem;width:16px}@media (min-width:768px){.flow .breadcrumbs .crumb{display:block;margin-left:.75rem;margin-right:.75rem}}.flow .breadcrumbs .crumb:first-child{margin-left:0}.flow .breadcrumbs .crumb:last-child{margin-right:0}.cactus{bottom:0;display:none;opacity:.75;position:absolute;z-index:0}.cactus.cactus-one{left:0;margin-bottom:-5px;padding-left:8rem}.cactus.cactus-two{margin-bottom:-10px;padding-right:8rem;right:0}@media (min-width:1280px){.cactus{display:block}}.docs-font *{font-family:Lato,sans-serif}.check,.times{height:28px;width:28px}@media (min-width:768px){.md\:mx-12{margin-left:3rem;margin-right:3rem}.md\:mb-0{margin-bottom:0}.md\:mb-12{margin-bottom:3rem}.md\:mb-16{margin-bottom:4rem}.md\:ml-12{margin-left:3rem}.md\:flex{display:flex}.md\:grid{display:grid}.md\:hidden{display:none}.md\:w-1\/2{width:50%}.md\:grid-flow-col{grid-auto-flow:column}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:flex-col{flex-direction:column}.md\:justify-center{justify-content:center}.md\:gap-6{gap:1.5rem}.md\:bg-burned-leather{--tw-bg-opacity:1;background-color:rgba(74,53,43,var(--tw-bg-opacity))}.md\:px-6{padding-left:1.5rem;padding-right:1.5rem}.md\:px-8{padding-left:2rem;padding-right:2rem}.md\:px-12{padding-left:3rem;padding-right:3rem}.md\:py-2{padding-bottom:.5rem;padding-top:.5rem}.md\:py-3{padding-bottom:.75rem;padding-top:.75rem}.md\:py-4{padding-bottom:1rem;padding-top:1rem}.md\:py-24{padding-bottom:6rem;padding-top:6rem}.md\:py-32{padding-bottom:8rem;padding-top:8rem}.md\:text-center{text-align:center}.md\:text-lg{font-size:1.125rem;line-height:1.75rem}.md\:text-xl{font-size:1.25rem;line-height:1.75rem}.md\:text-2xl{font-size:1.5rem;line-height:2rem}.md\:text-5xl{font-size:3rem;line-height:1}} diff --git a/tests/Fixtures/Local/lasso-logo.png b/tests/Fixtures/Local/lasso-logo.png new file mode 100644 index 0000000..83bf8ca Binary files /dev/null and b/tests/Fixtures/Local/lasso-logo.png differ diff --git a/tests/Helpers/ConfigValidatorTest.php b/tests/Helpers/ConfigValidatorTest.php deleted file mode 100644 index 5b94056..0000000 --- a/tests/Helpers/ConfigValidatorTest.php +++ /dev/null @@ -1,97 +0,0 @@ -set([ - 'filesystems.disks' => [ - 'assets' => [ - 'driver' => 's3', - ], - ], - ]); - } - - /** @test */ - public function it_throws_exception_when_storage_push_to_git_is_set() - { - $this->expectException(ConfigFailedValidation::class); - $this->expectExceptionMessageMatches('/push_to_git/'); - config()->set(['lasso.storage.push_to_git' => true]); - (new ConfigValidator())->validate(); - } - - /** @test */ - public function it_throws_exception_when_compile_script_is_not_set() - { - $this->expectException(ConfigFailedValidation::class); - $this->expectExceptionMessageMatches('/npm run production/'); - - config()->set(['lasso.compiler.script' => null]); - - (new ConfigValidator())->validate(); - } - - /** @test */ - public function it_throws_exception_when_invalid_compiler_output_is_provided() - { - $this->expectException(ConfigFailedValidation::class); - $this->expectExceptionMessage('You must specify a valid output setting. Available options: all, progress, disable.'); - - config()->set(['lasso.compiler.output' => 'abc']); - - (new ConfigValidator())->validate(); - } - - /** @test */ - public function it_throws_exception_when_disk_does_not_exist() - { - $this->expectException(ConfigFailedValidation::class); - $this->expectExceptionMessageMatches('/not a valid disk/'); - - config()->set(['filesystems.disks' => null]); - - (new ConfigValidator())->validate(); - } - - /** @test */ - public function it_throws_exception_when_bundle_count_is_missing() - { - $this->expectException(ConfigFailedValidation::class); - $this->expectExceptionMessageMatches('/how many bundles/'); - - config()->set(['lasso.storage.max_bundles' => null]); - - (new ConfigValidator())->validate(); - } - - /** @test */ - public function it_throws_exception_when_bundle_count_is_less_than_one() - { - $this->expectException(ConfigFailedValidation::class); - $this->expectExceptionMessageMatches('/how many bundles/'); - - config()->set(['lasso.storage.max_bundles' => 0]); - - (new ConfigValidator())->validate(); - } - - /** @test */ - public function it_throws_exception_when_public_path_is_inaccessible() - { - $this->expectException(ConfigFailedValidation::class); - $this->expectExceptionMessageMatches('/accessible directory/'); - - config()->set(['lasso.public_path' => 'a_non_existing_path']); - - (new ConfigValidator())->validate(); - } -} diff --git a/tests/Helpers/ZipTest.php b/tests/Helpers/ZipTest.php deleted file mode 100644 index 364f593..0000000 --- a/tests/Helpers/ZipTest.php +++ /dev/null @@ -1,130 +0,0 @@ -sourceDirectory = __DIR__ . '/Support/Zip/Source'; - $this->destinationDirectory = __DIR__ . '/Support/Zip/Destination'; - - $this->cleanUpPreviousArtifacts(); - } - - /** @test */ - public function it_can_add_a_single_file_from_a_source_directory_to_a_zip_file(): void - { - $sourceFile = ['SingleFile/logo.png']; - $zipFile = $this->destinationDirectory . '/SingleFile.zip'; - - $this->assertFileDoesNotExist($zipFile); - - (new Zip($zipFile)) - ->addFilesFromDirectory($this->sourceDirectory .'/SingleFile') - ->closeZip(); - - $this->assertFileExists($zipFile); - - $this->assertZipFileContains($sourceFile, $zipFile); - } - - /** @test */ - public function it_adds_all_files_within_a_source_directory_to_a_zip_file(): void - { - $sourceFiles = [ - 'MultipleFiles/1.txt', - 'MultipleFiles/2.txt', - 'MultipleFiles/3.txt', - ]; - - $zipFile = $this->destinationDirectory . '/MultiFile.zip'; - - $this->assertFileDoesNotExist($zipFile); - - (new Zip($zipFile)) - ->addFilesFromDirectory($this->sourceDirectory .'/MultipleFiles') - ->closeZip(); - - $this->assertFileExists($zipFile); - - $this->assertZipFileContains($sourceFiles, $zipFile); - } - - /** @test */ - public function it_adds_all_files_within_a_source_directory_including_sub_folders_to_a_zip_file(): void - { - $sourceFiles = [ - 'FilesWithSubFolder/SubFolder/in_sub_folder.txt', - 'FilesWithSubFolder/SubFolder/logo_in_sub_folder.png', - 'FilesWithSubFolder/logo_in_root_folder.png', - 'FilesWithSubFolder/in_root_folder.txt', - ]; - - $zipFile = $this->destinationDirectory . '/WithSubFolder.zip'; - - $this->assertFileDoesNotExist($zipFile); - - (new Zip($zipFile)) - ->addFilesFromDirectory($this->sourceDirectory .'/FilesWithSubFolder') - ->closeZip(); - - $this->assertFileExists($zipFile); - - $this->assertZipFileContains($sourceFiles, $zipFile); - } - - private function assertZipFileContains(array $sourceFiles, string $destinationZipFile): void - { - $inspectZipFile = new ZipArchive(); - $inspectZipFile->open($destinationZipFile); - - collect($sourceFiles)->each(function (string $filePath) use ($inspectZipFile) { - $relativePath = $this->getRelativePath($filePath); - - $this->assertSame( - file_get_contents($this->sourceDirectory . '/' . $filePath), - $inspectZipFile->getFromName($relativePath) - ); - }); - } - - /** - * Clean working directory at the start by purging files generated in previous tests, - * to be able to inspect the files manually if needed after a specific test has run. - */ - private function cleanUpPreviousArtifacts(): void - { - File::cleanDirectory($this->destinationDirectory); - } - - /** - * @param string $filePath - * @return false|string - * - * Returns the relative path of source files, to be located in the zip file. - */ - private function getRelativePath(string $filePath): string - { - // Find the root directory (first directory listed) - $rootDirectory = explode('/', $filePath)[0]; - - // Remove the root directory from the given path - $normalizedPath = str_replace($rootDirectory, '', $filePath); - - // Remove preliminary forward slash "/Dir" -> "Dir" - $relativePath = substr($normalizedPath, 1); - - return $relativePath; - } -} diff --git a/tests/Pest.php b/tests/Pest.php index 7183e93..7c8afb0 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -1,5 +1,7 @@ in('Feature'); +uses(TestCase::class)->in(__DIR__); -/* -|-------------------------------------------------------------------------- -| Expectations -|-------------------------------------------------------------------------- -| -| When you're writing tests, you often need to check that values meet certain conditions. The -| "expect()" function gives you access to a set of "expectations" methods that you can use -| to assert different things. Of course, you may extend the Expectation API at any time. -| -*/ - -expect()->extend('toBeOne', function () { - return $this->toBe(1); -}); +function sourceDirectory(): string +{ + return __DIR__ . '/Unit/Support/Zip/Source'; +} -/* -|-------------------------------------------------------------------------- -| Functions -|-------------------------------------------------------------------------- -| -| While Pest is very powerful out-of-the-box, you may have some testing code specific to your -| project that you don't want to repeat in every file. Here you can also expose helpers as -| global functions to help you to reduce the number of lines of code in your test files. -| -*/ +function destinationDirectory(): string +{ + return __DIR__ . '/Unit/Support/Zip/Destination'; +} -function something() +function defaultConfig(): array { - // .. + return [ + 'compiler' => [ + + /* + * Configure which command Lasso should run in its deployment + * phase. This will most likely be "npm run production" but + * you may choose what you would like to execute. + */ + 'script' => 'npx mix --production', + + /* + * Configure the amount of time (in seconds) the compiler + * should run before it times out. By default, this is set + * to 600 seconds (10 minutes). + */ + 'timeout' => 600, + + /* + * Lasso will attempt to display the compilation progress + * from webpack. If your progress bar isn't incrementing, it's + * likely you have the `--no-progress` flag on your script + * (e.g npm run production). Change it to `--progress`. + * + * Available progress options are: + * + * - 'all': Display everything from the compiler. + * - 'progress': Display compilation progress. + * - 'disable': Disable the progress. + */ + 'output' => 'progress', + + /* + * If there are any directories/files you would like to Lasso to + * exclude when uploading to the Filesystem, specify them below. + */ + 'excluded_files' => [], + + 'excluded_directories' => [], + + ], + + 'storage' => [ + + /* + * Specify the filesystem Lasso should use to store + * and retrieve its files. + */ + 'disk' => 'assets', + + /* + * Specify the directory Lasso should store all of its + * files within. + * + * WARNING: If you have multiple projects all using Lasso, + * make sure this is unique for each project. + */ + 'upload_to' => 'lasso', + + /* + * Lasso can also create a separate directory containing + * the environment the files will be stored in. Specify this + * here. + */ + 'environment' => null, + + /* + * Lasso can add a prefix to the bundle file, in order to store + * multiple bundle files in the same filesystem for different + * environments + */ + 'prefix' => '', + + /* + * Lasso will automatically version the assets. This is useful if you + * suddenly need to roll back a deployment and use an older version + * of built files. You can set the maximum amount of files stored here. + */ + 'max_bundles' => 5, + + ], + + /* + * Lasso can also trigger Webhooks after its commands have been + * successfully executed. You may specify URLs that Lasso will POST + * to, for each of the commands. + */ + 'webhooks' => [ + + /* + * Specify which webhooks should be triggered after a successful + * "php artisan lasso:publish" command execution. + */ + 'publish' => [ + // + ], + + /* + * Specify which webhooks should be triggered after a successful + * "php artisan lasso:pull" command execution. + */ + 'pull' => [ + // + ], + + ], + + /* + * Where are your assets stored? Most of the time, they will + * be stored within the /public directory in Laravel - but if + * you have changed this - please specify it below. + */ + 'public_path' => __DIR__ . '/Fixtures/Public', + ]; } diff --git a/tests/TestCase.php b/tests/TestCase.php index aeb7399..298fd35 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -1,5 +1,7 @@ setBasePath(__DIR__ . '/../'); + + $app['config']->set('filesystems.disks.assets', [ + 'driver' => 'local', + 'root' => __DIR__ . '/Fixtures/Cloud', + 'throw' => false, + ]); + } } diff --git a/tests/Unit/ConfigValidatorPestTest.php b/tests/Unit/ConfigValidatorPestTest.php new file mode 100644 index 0000000..43a2e5c --- /dev/null +++ b/tests/Unit/ConfigValidatorPestTest.php @@ -0,0 +1,79 @@ +set([ + 'filesystems.disks' => [ + 'assets' => [ + 'driver' => 's3', + ], + ], + ]); +}); + +test('it throws exception when storage push to git is set', function () { + $this->expectException(ConfigFailedValidation::class); + $this->expectExceptionMessageMatches('/push_to_git/'); + + config()->set(['lasso.storage.push_to_git' => true]); + + (new ConfigValidator)->validate(); +}); + +test('it throws an exception when the compile script is not set', function () { + $this->expectException(ConfigFailedValidation::class); + $this->expectExceptionMessageMatches('/npm run production/'); + + config()->set(['lasso.compiler.script' => null]); + + (new ConfigValidator)->validate(); +}); + +test('it throws an exception when an invalid compiler output is provided', function () { + $this->expectException(ConfigFailedValidation::class); + $this->expectExceptionMessage('You must specify a valid output setting. Available options: all, progress, disable.'); + + config()->set(['lasso.compiler.output' => 'abc']); + + (new ConfigValidator)->validate(); +}); + +test('it throws exception when disk does not exist', function () { + $this->expectException(ConfigFailedValidation::class); + $this->expectExceptionMessageMatches('/not a valid disk/'); + + config()->set(['filesystems.disks' => null]); + + (new ConfigValidator)->validate(); +}); + +test('it throws exception when bundle count is missing', function () { + $this->expectException(ConfigFailedValidation::class); + $this->expectExceptionMessageMatches('/how many bundles/'); + + config()->set(['lasso.storage.max_bundles' => null]); + + (new ConfigValidator)->validate(); +}); + +test('it throws exception when bundle count is less than one', function () { + $this->expectException(ConfigFailedValidation::class); + $this->expectExceptionMessageMatches('/how many bundles/'); + + config()->set(['lasso.storage.max_bundles' => 0]); + + (new ConfigValidator)->validate(); +}); + +test('it throws exception when public path is inaccessible', function () { + $this->expectException(ConfigFailedValidation::class); + $this->expectExceptionMessageMatches('/accessible directory/'); + + config()->set(['lasso.public_path' => 'a_non_existing_path']); + + (new ConfigValidator)->validate(); +}); diff --git a/tests/Helpers/Support/Zip/Destination/WithSubFolder.zip b/tests/Unit/Support/Zip/Destination/WithSubFolder.zip similarity index 100% rename from tests/Helpers/Support/Zip/Destination/WithSubFolder.zip rename to tests/Unit/Support/Zip/Destination/WithSubFolder.zip diff --git a/tests/Helpers/Support/Zip/Source/FilesWithSubFolder/SubFolder/in_sub_folder.txt b/tests/Unit/Support/Zip/Source/FilesWithSubFolder/SubFolder/in_sub_folder.txt similarity index 100% rename from tests/Helpers/Support/Zip/Source/FilesWithSubFolder/SubFolder/in_sub_folder.txt rename to tests/Unit/Support/Zip/Source/FilesWithSubFolder/SubFolder/in_sub_folder.txt diff --git a/tests/Helpers/Support/Zip/Source/FilesWithSubFolder/SubFolder/logo_in_sub_folder.png b/tests/Unit/Support/Zip/Source/FilesWithSubFolder/SubFolder/logo_in_sub_folder.png similarity index 100% rename from tests/Helpers/Support/Zip/Source/FilesWithSubFolder/SubFolder/logo_in_sub_folder.png rename to tests/Unit/Support/Zip/Source/FilesWithSubFolder/SubFolder/logo_in_sub_folder.png diff --git a/tests/Helpers/Support/Zip/Source/FilesWithSubFolder/in_root_folder.txt b/tests/Unit/Support/Zip/Source/FilesWithSubFolder/in_root_folder.txt similarity index 100% rename from tests/Helpers/Support/Zip/Source/FilesWithSubFolder/in_root_folder.txt rename to tests/Unit/Support/Zip/Source/FilesWithSubFolder/in_root_folder.txt diff --git a/tests/Helpers/Support/Zip/Source/FilesWithSubFolder/logo_in_root_folder.png b/tests/Unit/Support/Zip/Source/FilesWithSubFolder/logo_in_root_folder.png similarity index 100% rename from tests/Helpers/Support/Zip/Source/FilesWithSubFolder/logo_in_root_folder.png rename to tests/Unit/Support/Zip/Source/FilesWithSubFolder/logo_in_root_folder.png diff --git a/tests/Helpers/Support/Zip/Source/MultipleFiles/1.txt b/tests/Unit/Support/Zip/Source/MultipleFiles/1.txt similarity index 100% rename from tests/Helpers/Support/Zip/Source/MultipleFiles/1.txt rename to tests/Unit/Support/Zip/Source/MultipleFiles/1.txt diff --git a/tests/Helpers/Support/Zip/Source/MultipleFiles/2.txt b/tests/Unit/Support/Zip/Source/MultipleFiles/2.txt similarity index 100% rename from tests/Helpers/Support/Zip/Source/MultipleFiles/2.txt rename to tests/Unit/Support/Zip/Source/MultipleFiles/2.txt diff --git a/tests/Helpers/Support/Zip/Source/MultipleFiles/3.txt b/tests/Unit/Support/Zip/Source/MultipleFiles/3.txt similarity index 100% rename from tests/Helpers/Support/Zip/Source/MultipleFiles/3.txt rename to tests/Unit/Support/Zip/Source/MultipleFiles/3.txt diff --git a/tests/Helpers/Support/Zip/Source/SingleFile/logo.png b/tests/Unit/Support/Zip/Source/SingleFile/logo.png similarity index 100% rename from tests/Helpers/Support/Zip/Source/SingleFile/logo.png rename to tests/Unit/Support/Zip/Source/SingleFile/logo.png diff --git a/tests/Unit/ZipPestTest.php b/tests/Unit/ZipPestTest.php new file mode 100644 index 0000000..e6d6216 --- /dev/null +++ b/tests/Unit/ZipPestTest.php @@ -0,0 +1,95 @@ +addFilesFromDirectory(sourceDirectory() . '/SingleFile') + ->closeZip(); + + assertFileExists($zipFile); + assertZipFileContains($sourceFile, $zipFile); +}); + +test('it adds all files within a source directory to a zip file', function () { + $sourceFiles = [ + 'MultipleFiles/1.txt', + 'MultipleFiles/2.txt', + 'MultipleFiles/3.txt', + ]; + + $zipFile = destinationDirectory() . '/MultiFile.zip'; + + assertFileDoesNotExist($zipFile); + + (new Zip($zipFile)) + ->addFilesFromDirectory(sourceDirectory() .'/MultipleFiles') + ->closeZip(); + + assertFileExists($zipFile); + + assertZipFileContains($sourceFiles, $zipFile); +}); + +test('it adds all files within a source directory including sub folders to a zip file', function () { + $sourceFiles = [ + 'FilesWithSubFolder/SubFolder/in_sub_folder.txt', + 'FilesWithSubFolder/SubFolder/logo_in_sub_folder.png', + 'FilesWithSubFolder/logo_in_root_folder.png', + 'FilesWithSubFolder/in_root_folder.txt', + ]; + + $zipFile = destinationDirectory() . '/WithSubFolder.zip'; + + assertFileDoesNotExist($zipFile); + + (new Zip($zipFile)) + ->addFilesFromDirectory(sourceDirectory() .'/FilesWithSubFolder') + ->closeZip(); + + assertFileExists($zipFile); + + assertZipFileContains($sourceFiles, $zipFile); +}); + +function assertZipFileContains(array $sourceFiles, string $destinationZipFile): void +{ + $inspectZipFile = new ZipArchive(); + $inspectZipFile->open($destinationZipFile); + + collect($sourceFiles)->each(function (string $filePath) use ($inspectZipFile) { + $relativePath = getRelativePath($filePath); + + assertStringEqualsFile( + sourceDirectory() . '/' . $filePath, + $inspectZipFile->getFromName($relativePath) + ); + }); +} + +function getRelativePath(string $filePath): string +{ + // Find the root directory (first directory listed) + $rootDirectory = explode('/', $filePath)[0]; + + // Remove the root directory from the given path + $normalizedPath = str_replace($rootDirectory, '', $filePath); + + // Remove preliminary forward slash "/Dir" -> "Dir" + return mb_substr($normalizedPath, 1); +} diff --git a/webpack.mix.js b/webpack.mix.js new file mode 100644 index 0000000..e94772e --- /dev/null +++ b/webpack.mix.js @@ -0,0 +1,3 @@ +let mix = require('laravel-mix'); + +mix.copyDirectory('tests/Fixtures/Local', './tests/Fixtures/Public');