Completed
Push — master ( 742044...61c5a7 )
by Mark
01:18 queued 10s
created

PullProductionDataCommand::handle()   A

Complexity

Conditions 5
Paths 3

Size

Total Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 17
rs 9.3888
c 0
b 0
f 0
cc 5
nc 3
nop 0
1
<?php
2
3
namespace DigiFactory\PullProductionData;
4
5
use Illuminate\Console\Command;
6
use Illuminate\Support\Facades\DB;
7
use Illuminate\Support\Facades\File;
8
use Symfony\Component\Process\Process;
9
10
class PullProductionDataCommand extends Command
11
{
12
    protected $signature = 'pull-production-data {--D|no-database : Whether the database should not be synced} {--S|no-storage-folder : Whether the storage folder should not be synced}';
13
    protected $description = 'Pull your production storage folder and database to your local environment';
14
15
    protected $user;
16
    protected $host;
17
    protected $port;
18
    protected $path;
19
20
    protected $productionDatabaseName;
21
    protected $productionDatabaseUser;
22
    protected $productionDatabasePassword;
23
24
    public function __construct()
25
    {
26
        parent::__construct();
27
28
        $deployServer = config('pull-production-data.deploy_server');
29
        $this->path = config('pull-production-data.deploy_path');
30
31
        preg_match('/(.*)@([^\s]+)(?:\s-p)?([0-9]*)/', $deployServer, $matches);
32
33
        if (count($matches) === 4) {
34
            $this->user = $matches[1];
35
            $this->host = $matches[2];
36
            $this->port = $matches[3] ? (int)$matches[3] : 22;
37
        }
38
    }
39
40
    /**
41
     * Execute the console command.
42
     *
43
     * @return mixed
44
     */
45
    public function handle()
46
    {
47
        if (!$this->user || !$this->host || !$this->path) {
48
            $this->error('Make sure DEPLOY_SERVER and DEPLOY_PATH are set in your .env file!');
49
50
            return;
51
        }
52
53
        if (!$this->confirm("Is it alright to sync production data from {$this->user} on {$this->host}?", false)) {
54
            $this->error('Aborted!');
55
56
            return;
57
        }
58
59
        $this->syncDatabase();
60
        $this->syncStorageFolder();
61
    }
62
63
    public function syncDatabase()
64
    {
65
        if ($this->option('no-database')) {
66
            $this->line('Skipping database...');
67
68
            return;
69
        }
70
71
        $this->fetchProductionDatabaseCredentials();
72
        $this->fetchProductionDatabaseBackup();
73
        $this->importDatabaseBackup();
74
    }
75
76
    public function syncStorageFolder()
77
    {
78
        if ($this->option('no-storage-folder')) {
79
            $this->line('Skipping storage folder...');
80
81
            return;
82
        }
83
84
        $this->info('Removing current storage folder...');
85
86
        File::deleteDirectory(storage_path().'/app');
87
88
        $this->info('Storage folder removed!');
89
90
        $source = sprintf('%s@%s:%s', $this->user, $this->host, $this->path.'/storage/app');
91
        $destination = storage_path();
92
93
        $process = new Process(['scp', '-r', '-P'.$this->port, $source, $destination]);
94
        $process->setTimeout(config('pull-production-data.timeout'));
95
96
        $this->info(sprintf('Syncing data from [%s] to [%s]...', $source, $destination));
97
98
        $process->run();
99
100
        $this->info('Data synced!');
101
    }
102
103
    private function fetchProductionDatabaseCredentials()
104
    {
105
        $this->info('Fetching production database credentials...');
106
107
        $process = new Process(['ssh', "{$this->user}@{$this->host}", "-p{$this->port}", 'cat public_html/.env']);
108
        $process->run();
109
110
        $env = $process->getOutput();
111
112
        preg_match('/(?:DB_DATABASE=)(.*)/', $env, $matches);
113
114
        $this->productionDatabaseName = $matches[1];
115
116
        preg_match('/(?:DB_USERNAME=)(.*)/', $env, $matches);
117
118
        $this->productionDatabaseUser = $matches[1];
119
120
        preg_match('/(?:DB_PASSWORD=)(.*)/', $env, $matches);
121
122
        $this->productionDatabasePassword = $matches[1];
123
124
        $this->info('Credentials fetched...');
125
    }
126
127
    private function fetchProductionDatabaseBackup()
128
    {
129
        // Create backup
130
        $this->info('Creating production database backup...');
131
132
        $command = sprintf('mysqldump -u%s -p%s %s > %s/database.sql', $this->productionDatabaseUser, $this->productionDatabasePassword, $this->productionDatabaseName, $this->path);
133
134
        $process = new Process(['ssh', "{$this->user}@{$this->host}", "-p{$this->port}", $command]);
135
        $process->run();
136
137
        $this->info('Backup created!');
138
139
        // Download backup
140
        $this->info('Downloading production database backup...');
141
142
        $source = sprintf('%s@%s:%s', $this->user, $this->host, $this->path.'/database.sql');
143
        $destination = base_path().'/database.sql';
144
145
        $process = new Process(['scp', '-P'.$this->port, $source, $destination]);
146
        $process->run();
147
148
        $this->info('Backup downloaded!');
149
150
        // Remove backup from production machine
151
        $this->info('Remove database backup from production machine...');
152
153
        $command = sprintf('rm %s/database.sql', $this->path);
154
155
        $process = new Process(['ssh', "{$this->user}@{$this->host}", "-p{$this->port}", $command]);
156
        $process->run();
157
158
        $this->info('Database removed!');
159
    }
160
161
    private function importDatabaseBackup()
162
    {
163
        // Remove all tables
164
        $this->info('Remove all local tables...');
165
166
        DB::getSchemaBuilder()->dropAllTables();
167
168
        $this->info('Tables removed!');
169
170
        // Import database backup
171
        $this->info('Importing database backup...');
172
173
        DB::unprepared(file_get_contents(base_path().'/database.sql'));
174
175
        $this->info('Database import ready!');
176
177
        // Remove database backup
178
        $this->info('Deleting database backup...');
179
180
        unlink(base_path().'/database.sql');
181
182
        $this->info('Database backup deleted!');
183
    }
184
185
    public function info($string, $verbosity = null)
186
    {
187
        parent::info('['.date('Y-m-d H:i:s').'] '.$string, $verbosity);
188
    }
189
}
190