diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 4eb838b8..4e43f9f4 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -11,10 +11,14 @@ on: description: Deployment target required: true type: environment + reset: + description: Reset database + default: false + type: boolean workflow_run: workflows: [Code Quality] types: [completed] - branches: [main] + branches: [main, 'feat/**'] concurrency: group: ${{ github.workflow }}-${{ github.ref }} @@ -76,8 +80,13 @@ jobs: - name: Deploy uses: deployphp/action@master + env: + DB_USERNAME: ${{ secrets.DB_USERNAME }} + DB_PASSWORD: ${{ secrets.DB_PASSWORD }} + DB_RESET: ${{ github.event_name == 'workflow_dispatch' && inputs.reset || false }} + DEPLOY_ARGS: --branch ${{ needs.prepare.outputs.target-branch }} --${{ env.DB_RESET == 'true' && 'no-reset' || 'no-reset' }} with: private-key: ${{ secrets.DEPLOY_SSH_RSAKEY }} ssh-config: ${{ secrets.DEPLOY_SSH_CONFIG }} verbosity: '' - dep: deploy -f scripts/deploy.php env=${{ env.APP_ENV }} --branch ${{ github.ref_name }} + dep: deploy -f scripts/deploy.php env=staging ${{ env.DEPLOY_ARGS }} diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d954cdca..54b401d7 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -27,6 +27,24 @@ env: SENTRY_DSN: ${{ secrets.SENTRY_DSN }} jobs: + labels: + name: Labels + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + + permissions: + contents: read + pull-requests: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Labeler + uses: actions/labeler@v5 + with: + dot: true + prepare: name: Prepare # uses: creasico/laravel-project/.github/workflows/prepare.yml@main diff --git a/.github/workflows/prepare.yml b/.github/workflows/prepare.yml index b7de7755..870d932e 100644 --- a/.github/workflows/prepare.yml +++ b/.github/workflows/prepare.yml @@ -11,43 +11,25 @@ on: outputs: composer-cache: value: ${{ jobs.build.outputs.composer-cache }} + should-reports: + value: ${{ jobs.build.outputs.should-reports }} + target-branch: + value: ${{ jobs.build.outputs.target-branch }} target-env: value: ${{ jobs.build.outputs.target-env }} target-url: value: ${{ jobs.build.outputs.target-url }} - should-reports: - value: ${{ jobs.build.outputs.should-reports }} jobs: - labels: - name: Labels - runs-on: ubuntu-latest - if: github.event_name == 'pull_request' - - permissions: - contents: read - pull-requests: write - - steps: - - name: Checkout - uses: actions/checkout@v4 - - - name: Labeler - uses: actions/labeler@v5 - with: - dot: true - build: runs-on: ubuntu-latest name: Build for `${{ inputs.target }}` outputs: - target-env: ${{ inputs.target }} - target-url: ${{ env.APP_URL }} composer-cache: ${{ steps.environments.outputs.composer-cache }} should-reports: ${{ steps.environments.outputs.should-reports }} - - environment: - name: ${{ inputs.target }} + target-branch: ${{ steps.environments.outputs.target-branch }} + target-env: ${{ env.APP_ENV }} + target-url: ${{ env.APP_URL }} env: APP_ENV: ${{ inputs.target }} @@ -85,6 +67,7 @@ jobs: CC_TEST_REPORTER_ID: ${{ secrets.CC_TEST_REPORTER_ID }} run: | git config user.name "Creasi.HQ" && git config user.email "developers@creasi.co" + echo "target-branch=${GITHUB_HEAD_REF:-${GITHUB_REF#refs/heads/}}" >> $GITHUB_OUTPUT echo "composer-cache=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT echo "should-reports=$([[ -n \"$CC_TEST_REPORTER_ID\" ]] && echo '1')" >> $GITHUB_OUTPUT @@ -98,6 +81,7 @@ jobs: - name: Install dependencies run: | composer install --prefer-dist --no-interaction --no-progress --ansi + composer dep deploy:info -- --branch ${{ steps.environments.outputs.target-branch }} composer ziggy:generate php artisan vendor:publish --tag creasi-assets --ansi diff --git a/app/Providers/AppServiceProvider.php b/app/Providers/AppServiceProvider.php index 02ed2aca..89a78011 100644 --- a/app/Providers/AppServiceProvider.php +++ b/app/Providers/AppServiceProvider.php @@ -15,9 +15,9 @@ class AppServiceProvider extends ServiceProvider */ public function register() { - if ($this->app->environment('local')) { - $this->app->register(\Laravel\Telescope\TelescopeServiceProvider::class); - // $this->app->register(TelescopeServiceProvider::class); + if (app()->environment('local') && \class_exists('\Laravel\Telescope\TelescopeServiceProvider')) { + app()->register(\Laravel\Telescope\TelescopeServiceProvider::class); + // app()->register(TelescopeServiceProvider::class); } // . } diff --git a/composer.json b/composer.json index 81fdbac5..6113b913 100644 --- a/composer.json +++ b/composer.json @@ -75,6 +75,7 @@ "@php artisan key:generate --ansi" ], "dep": [ + "Composer\\Config::disableProcessTimeout", "dep --ansi -f scripts/deploy.php" ], "fix": [ diff --git a/scripts/deploy.php b/scripts/deploy.php index 5113eb77..9aca263b 100644 --- a/scripts/deploy.php +++ b/scripts/deploy.php @@ -2,6 +2,9 @@ namespace Deployer; +use Illuminate\Support\Env; +use Symfony\Component\Console\Input\InputOption; + $rootDir = \dirname(__DIR__); require 'recipe/laravel.php'; @@ -9,12 +12,13 @@ // Config -set('repository', 'git@github.com:creasico/laravel-project.git'); -set('keep_releases', 3); - add('shared_files', []); add('shared_dirs', ['public/build', 'public/vendor']); -add('writable_dirs', ['storage']); +add('writable_dirs', []); + +set('keep_releases', 3); + +option('reset', null, InputOption::VALUE_NEGATABLE, 'Reset database', false); // Hosts @@ -24,18 +28,118 @@ ->setDeployPath('/var/www/skeleton') ->setLabels(['env' => 'staging']); +// Hooks + +after('deploy:failed', 'deploy:unlock'); +after('deploy:update_code', 'deploy:environment'); + // Tasks -task('deploy:assets', function () { - $config = ['flags' => '-zrtv']; +task('deploy:info', function () { + set('repository', $repository = runLocally('git remote get-url origin')); + set('pull_request', $pullRequest = null); + set('original_deploy_path', $deployPath = get('deploy_path')); + $env = get('labels')['env'] ?? null; + $host = currentHost(); $masterData = "storage/app/master-data.{$env}.xlsx"; + $appUrl = Env::get('APP_URL'); + $onGitHub = Env::get('GITHUB_ENV'); + + set('master_data', \file_exists($masterData) ? $masterData : 'storage/app/master-data.xlsx'); + + if (! $appUrl || \str_contains($appUrl, 'localhost')) { + $appUrl = 'https://'.$host->getAlias(); + } + + if ($ghRef = Env::get('GITHUB_REF')) { + [$_, $ref, $name] = explode('/', $ghRef); + if ($ref === 'pull' && \is_numeric($name)) { + $pullRequest = $name; + } + } + + $info = ['deploying {{target}}']; + + if ($pullRequest !== null) { + set('pull_request', (int) $pullRequest); + set('deploy_path', $deployPath.'-'.$pullRequest); + + if (! \str_contains($appUrl, $pullRequest)) { + $subdomain = \basename($deployPath); + $appUrl = \str_replace($subdomain, $subdomain.'-'.$pullRequest, $appUrl); + } + + $onGitHub && runLocally('echo "APP_ENV=preview-{{pull_request}}" >> $GITHUB_ENV'); + + $info[] = 'from PR #{{pull_request}}'; + } - if (! \file_exists($masterData)) { - $masterData = 'storage/app/master-data.xlsx'; + $onGitHub && runLocally('echo "APP_URL='.$appUrl.'" >> $GITHUB_ENV'); + $info[] = 'to '.$appUrl.''; + + info(\implode(' ', $info)); + info('repository: '.$repository.''); +})->desc('Check repository'); + +task('deploy:environment', function () { + $releasesList = get('releases_list'); + $isPullRequest = get('pull_request') !== null; + $isInitialDeploy = ((int) last($releasesList)) === \count($releasesList); + + if (! ($isInitialDeploy || $isPullRequest) || test("[ -s {{deploy_path}}/shared/.env ]")) { + info("Your deployment already configured! Skipping..."); + return; + } + + $dbName = str(get('deploy_path'))->basename()->slug('_'); + $envFile = $isPullRequest + ? '{{original_deploy_path}}/current/.env' + : '{{release_or_current_path}}/.env.example'; + + info('initialize environment file'); + run("cat {$envFile} > {{release_path}}/.env"); + run("sed -i 's~;APP_ENV=.*~APP_ENV=preview~' {{release_path}}/.env"); + run("sed -i 's~;DB_DATABASE=.*~DB_DATABASE={$dbName}~' {{release_path}}/.env"); + + if ($url = Env::get('APP_URL')) { + run("sed -i 's~;APP_URL=.*~APP_URL={$url}~' {{release_path}}/.env"); + } + + set('dotenv', '{{release_path}}/.env'); + run('createdb -h "$DB_HOST" -U "$DB_USERNAME" -O "$DB_USERNAME" '.$dbName, env: [ + 'DB_USERNAME' => Env::get('DB_USERNAME'), + 'PGPASSWORD' => Env::get('DB_PASSWORD'), + ]); + + info('database '.$dbName.' created'); +})->desc('Setup deployment environment'); + +task('deploy:database', function () { + if ($isInitialDeploy = (get('dotenv') !== false)) { + invoke('artisan:key:generate'); } - if (\file_exists($masterData)) { + if (! input()->getOption('reset')) { + invoke('artisan:migrate'); + + if ($isInitialDeploy) { + invoke('artisan:db:seed'); + } + + return; + } + + invoke('artisan:down'); + invoke('artisan:migrate:fresh'); + invoke('artisan:db:seed'); + invoke('artisan:up'); +})->desc('Deploy database'); + +task('deploy:assets', function () { + $config = ['flags' => '-zrtv']; + + if (\file_exists($masterData = get('master_data'))) { upload($masterData, '{{release_or_current_path}}/storage/app', $config); } @@ -45,19 +149,12 @@ ]); })->desc('Deploy static assets'); -// Hooks - -after('deploy:failed', 'deploy:unlock'); - task('deploy', [ 'deploy:prepare', 'deploy:vendors', 'deploy:assets', 'artisan:storage:link', 'artisan:optimize:clear', - 'artisan:down', - 'artisan:migrate:fresh', - 'artisan:db:seed', - 'artisan:up', + 'deploy:database', 'deploy:publish', ]);