PullProductionDataCommand::importDatabaseBackup()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 37

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 37
rs 9.328
c 0
b 0
f 0
cc 1
nc 1
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 '.config('pull-production-data.paths.env', 'public_html/').'.env']);
108
        $process->setTimeout(config('pull-production-data.timeout'));
109
        $process->run();
110
111
        $env = $process->getOutput();
112
113
        preg_match('/(?:DB_DATABASE=)(.*)/', $env, $matches);
114
115
        $this->productionDatabaseName = $matches[1];
116
117
        preg_match('/(?:DB_USERNAME=)(.*)/', $env, $matches);
118
119
        $this->productionDatabaseUser = $matches[1];
120
121
        preg_match('/(?:DB_PASSWORD=)(.*)/', $env, $matches);
122
123
        $this->productionDatabasePassword = $matches[1];
124
125
        $this->info('Credentials fetched...');
126
    }
127
128
    private function fetchProductionDatabaseBackup()
129
    {
130
        // Create backup
131
        $this->info('Creating production database backup...');
132
133
        $command = sprintf(
134
            '%s --quick --compress -u%s -p%s %s > %s/database.sql',
135
            config('pull-production-data.paths.mysqldump'),
136
            $this->productionDatabaseUser,
137
            $this->productionDatabasePassword,
138
            $this->productionDatabaseName,
139
            $this->path
140
        );
141
142
        $process = new Process(['ssh', "{$this->user}@{$this->host}", "-p{$this->port}", $command]);
143
        $process->setTimeout(config('pull-production-data.timeout'));
144
        $process->run();
145
146
        $this->info('Backup created!');
147
148
        // Download backup
149
        $this->info('Downloading production database backup...');
150
151
        $source = sprintf('%s@%s:%s', $this->user, $this->host, $this->path.'/database.sql');
152
        $destination = base_path().'/database.sql';
153
154
        $process = new Process(['scp', '-P'.$this->port, $source, $destination]);
155
        $process->setTimeout(config('pull-production-data.timeout'));
156
        $process->run();
157
158
        $this->info('Backup downloaded!');
159
160
        // Remove backup from production machine
161
        $this->info('Remove database backup from production machine...');
162
163
        $command = sprintf('rm %s/database.sql', $this->path);
164
165
        $process = new Process(['ssh', "{$this->user}@{$this->host}", "-p{$this->port}", $command]);
166
        $process->setTimeout(config('pull-production-data.timeout'));
167
        $process->run();
168
169
        $this->info('Database removed!');
170
    }
171
172
    private function importDatabaseBackup()
173
    {
174
        // Remove all tables
175
        $this->info('Remove all local tables...');
176
177
        DB::getSchemaBuilder()->dropAllTables();
178
179
        $this->info('Tables removed!');
180
181
        // Import database backup
182
        $this->info('Importing database backup...');
183
184
        $config = config('database.connections.'.config('pull-production-data.database_connection'));
185
186
        $command = sprintf(
187
            '%s --user=%s --password=%s --host=%s %s < %s',
188
            config('pull-production-data.paths.mysql'),
189
            $config['username'],
190
            $config['password'],
191
            $config['host'],
192
            $config['database'],
193
            base_path().'/database.sql'
194
        );
195
196
        $process = Process::fromShellCommandline($command);
197
        $process->setTimeout(config('pull-production-data.timeout'));
198
        $process->run();
199
200
        $this->info('Database import ready!');
201
202
        // Remove database backup
203
        $this->info('Deleting database backup...');
204
205
        unlink(base_path().'/database.sql');
206
207
        $this->info('Database backup deleted!');
208
    }
209
210
    public function info($string, $verbosity = null)
211
    {
212
        parent::info('['.date('Y-m-d H:i:s').'] '.$string, $verbosity);
213
    }
214
}
215