├── .gitignore
├── awes-io
├── LICENSE.md
├── composer.json
├── src
├── NewCommand.php
├── DemoCommand.php
├── KeyCommand.php
├── TokenCommand.php
└── BaseCommand.php
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | /vendor
2 | composer.lock
3 |
--------------------------------------------------------------------------------
/awes-io:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env php
2 | add(new AwesIO\Installer\Console\NewCommand);
12 | $app->add(new AwesIO\Installer\Console\DemoCommand);
13 | $app->add(new AwesIO\Installer\Console\KeyCommand);
14 | $app->add(new AwesIO\Installer\Console\TokenCommand);
15 |
16 | $app->run();
17 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) Taylor Otwell
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "awes-io/installer",
3 | "description": "AwesIO application installer.",
4 | "keywords": ["laravel", "awes-io"],
5 | "license": "MIT",
6 | "authors": [
7 | {
8 | "name": "Awescode GmbH",
9 | "email": "info@awescode.de",
10 | "homepage": "https://www.awescode.de"
11 | },
12 | {
13 | "name": "Ivan Slesarenko",
14 | "email": "info@boomdraw.com",
15 | "homepage": "https://boomdraw.com",
16 | "role": "Developer"
17 | },
18 | {
19 | "name": "Galymzhan Begimov",
20 | "email": "begimov@gmail.com",
21 | "homepage": "https://github.com/begimov"
22 | }
23 | ],
24 | "autoload": {
25 | "psr-4": {
26 | "AwesIO\\Installer\\Console\\": "src/"
27 | }
28 | },
29 | "require": {
30 | "ext-zip": "*",
31 | "guzzlehttp/guzzle": "~6.0",
32 | "symfony/console": "~3.0|~4.0",
33 | "symfony/filesystem": "~3.0|~4.0",
34 | "symfony/process": "~3.0|~4.0",
35 | "begimov/thanks": "^1.1"
36 | },
37 | "bin": [
38 | "awes-io"
39 | ]
40 | }
41 |
--------------------------------------------------------------------------------
/src/NewCommand.php:
--------------------------------------------------------------------------------
1 | setName('new')
20 | ->setDescription('Create a new AwesIO application')
21 | ->addArgument('name', InputArgument::OPTIONAL)
22 | ->addOption('key', 'k', InputOption::VALUE_OPTIONAL, 'Adds PackageKit CDN key to .env')
23 | ->addOption('token', 't', InputOption::VALUE_OPTIONAL, 'Adds PackageKit token to composer.json')
24 | ->addOption('force', 'f', InputOption::VALUE_NONE, 'Forces install even if the directory already exists');
25 | }
26 |
27 | /**
28 | * Download the temporary Zip to the given file.
29 | *
30 | * @param string $zipFile
31 | * @return $this
32 | */
33 | protected function download($zipFile)
34 | {
35 | $response = (new Client)->get('https://github.com/awes-io/awes-io/archive/master.zip');
36 |
37 | file_put_contents($zipFile, $response->getBody());
38 |
39 | return $this;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/DemoCommand.php:
--------------------------------------------------------------------------------
1 | setName('demo')
20 | ->setDescription('Create a new AwesIO demo application')
21 | ->addArgument('name', InputArgument::OPTIONAL)
22 | ->addOption('key', 'k', InputOption::VALUE_OPTIONAL, 'Adds PackageKit CDN key to .env')
23 | ->addOption('token', 't', InputOption::VALUE_OPTIONAL, 'Adds PackageKit token to composer.json')
24 | ->addOption('force', 'f', InputOption::VALUE_NONE, 'Forces install even if the directory already exists');
25 | }
26 |
27 | /**
28 | * Download the temporary Zip to the given file.
29 | *
30 | * @param string $zipFile
31 | * @return $this
32 | */
33 | protected function download($zipFile)
34 | {
35 | $response = (new Client)->get('https://github.com/awes-io/demo/archive/master.zip');
36 |
37 | file_put_contents($zipFile, $response->getBody());
38 |
39 | return $this;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/KeyCommand.php:
--------------------------------------------------------------------------------
1 | setName('key')
21 | ->setDescription('Add PackageKit CDN key to .env')
22 | ->addOption('path', 'p', InputOption::VALUE_OPTIONAL, 'Adds path to .env dir')
23 | ->addOption('key', 'k', InputOption::VALUE_OPTIONAL, 'Adds PackageKit CDN key to .env');
24 | }
25 |
26 | /**
27 | * Execute the command.
28 | *
29 | * @param \Symfony\Component\Console\Input\InputInterface $input
30 | * @param \Symfony\Component\Console\Output\OutputInterface $output
31 | * @return void
32 | */
33 | protected function execute(InputInterface $input, OutputInterface $output)
34 | {
35 | $this->input = $input;
36 | $this->output = $output;
37 |
38 | $directory = $input->getOption('path');
39 | if (empty($directory)) {
40 | $directory = getcwd();
41 | } elseif (substr($directory, 0, 1) !== '/') {
42 | $directory = getcwd() . '/' . $directory;
43 | }
44 |
45 | $output->writeln('Writing PackageKit cdn key to .env...');
46 |
47 | if (!$this->key = $input->getOption('key')) {
48 | $this->getKey();
49 | }
50 |
51 | $this->setKey($directory);
52 |
53 | $output->writeln('PackageKit cdn key is set.');
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Installation
2 |
3 | [](https://github.com/awes-io/installer)
4 |
5 | ## Server Requirements
6 |
7 | * PHP >= 7.1.3
8 | * OpenSSL PHP Extension
9 | * PDO PHP Extension
10 | * Mbstring PHP Extension
11 | * Tokenizer PHP Extension
12 | * XML PHP Extension
13 | * Ctype PHP Extension
14 | * JSON PHP Extension
15 | * BCMath PHP Extension
16 |
17 | ## Installing AwesIO
18 |
19 | [Awes.IO](https://www.awes.io) utilizes [Composer](https://getcomposer.org/) to manage its dependencies. So, before using [Awes.IO](https://www.awes.io), make sure you have Composer installed on your machine.
20 |
21 | First, download the [Awes.IO](https://www.awes.io) installer using Composer:
22 | ```bash
23 | composer global require awes-io/installer
24 | ```
25 |
26 | Make sure to place composer's system-wide vendor bin directory in your `$PATH` so the awes-io executable can be located by your system. This directory exists in different locations based on your operating system; however, some common locations include:
27 |
28 | - macOS: `$HOME/.composer/vendor/bin`, command: `export PATH=~/.composer/vendor/bin:$PATH`
29 | - GNU / Linux Distributions: `$HOME/.config/composer/vendor/bin`
30 | - Windows: `%USERPROFILE%\AppData\Roaming\Composer\vendor\bin`
31 |
32 | Once installed, the `awes-io new` command will create a fresh [Awes.IO](https://www.awes.io) installation in the directory you specify. For instance, `awes-io new crm` will create a directory named `crm` containing a fresh [Awes.IO](https://www.awes.io) installation with all of [Awes.IO](https://www.awes.io)'s dependencies already installed:
33 |
34 | ```bash
35 | awes-io new crm
36 | ```
37 |
38 | To create demo project use `awes-io demo` command
39 |
40 | ```bash
41 | awes-io demo crm
42 | ```
43 |
--------------------------------------------------------------------------------
/src/TokenCommand.php:
--------------------------------------------------------------------------------
1 | setName('token')
21 | ->setDescription('Add PackageKit token to composer.json')
22 | ->addOption('path', 'p', InputOption::VALUE_OPTIONAL, 'Adds path to composer.json dir')
23 | ->addOption('token', 't', InputOption::VALUE_OPTIONAL, 'Adds PackageKit token to composer.json');
24 | }
25 |
26 | /**
27 | * Execute the command.
28 | *
29 | * @param \Symfony\Component\Console\Input\InputInterface $input
30 | * @param \Symfony\Component\Console\Output\OutputInterface $output
31 | * @return void
32 | */
33 | protected function execute(InputInterface $input, OutputInterface $output)
34 | {
35 | $this->input = $input;
36 | $this->output = $output;
37 |
38 | $directory = $input->getOption('path');
39 | if (empty($directory)) {
40 | $directory = getcwd();
41 | } elseif (substr($directory, 0, 1) !== '/') {
42 | $directory = getcwd() . '/' . $directory;
43 | }
44 |
45 | $output->writeln('Writing PackageKit token to composer.json...');
46 |
47 | if (!$this->token = $input->getOption('token')) {
48 | $this->getToken();
49 | }
50 |
51 | $this->setToken($directory);
52 |
53 | $output->writeln('PackageKit token is set.');
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/src/BaseCommand.php:
--------------------------------------------------------------------------------
1 | client = new Client([
50 | 'base_uri' => 'https://repo.pkgkit.com',
51 | ]);
52 |
53 | parent::__construct();
54 | }
55 |
56 | /**
57 | * Execute the command.
58 | *
59 | * @param \Symfony\Component\Console\Input\InputInterface $input
60 | * @param \Symfony\Component\Console\Output\OutputInterface $output
61 | * @return void
62 | */
63 | protected function execute(InputInterface $input, OutputInterface $output)
64 | {
65 | $this->input = $input;
66 | $this->output = $output;
67 |
68 | if (!extension_loaded('zip')) {
69 | throw new RuntimeException('The Zip PHP extension is not installed. Please install it and try again.');
70 | }
71 |
72 | $directory = ($input->getArgument('name')) ? getcwd() . '/' . $input->getArgument('name') : getcwd();
73 |
74 | if (!$input->getOption('force')) {
75 | $this->verifyApplicationDoesntExist($directory);
76 | }
77 |
78 | $output->writeln('Crafting application...');
79 |
80 | $this->download($zipFile = $this->makeFilename())
81 | ->extract($zipFile, $directory)
82 | ->prepareWritableDirectories($directory, $output)
83 | ->cleanUp($zipFile);
84 |
85 | if (!$this->key = $input->getOption('key')) {
86 | $this->getKey();
87 | }
88 |
89 | if (!$this->token = $input->getOption('token')) {
90 | $this->getToken();
91 | }
92 |
93 | $this->setToken($directory);
94 |
95 | $composer = $this->findComposer();
96 |
97 | $commands = [
98 | $composer . ' install --no-scripts',
99 | $composer . ' run-script post-root-package-install',
100 | $composer . ' run-script post-create-project-cmd',
101 | $composer . ' run-script post-autoload-dump',
102 | ];
103 |
104 | if ($input->getOption('no-ansi')) {
105 | $commands = array_map(function ($value) {
106 | return $value . ' --no-ansi';
107 | }, $commands);
108 | }
109 |
110 | if ($input->getOption('quiet')) {
111 | $commands = array_map(function ($value) {
112 | return $value . ' --quiet';
113 | }, $commands);
114 | }
115 |
116 | $process = new Process(implode(' && ', $commands), $directory, null, null, null);
117 |
118 | if ('\\' !== DIRECTORY_SEPARATOR && file_exists('/dev/tty') && is_readable('/dev/tty')) {
119 | $process->setTty(true);
120 | }
121 |
122 | $process->run(function ($type, $line) use ($output) {
123 | $output->write($line);
124 | });
125 |
126 | $this->setKey($directory);
127 |
128 | $helper = $this->getHelper('question');
129 | $question = new ConfirmationQuestion('Do you want to support us by giving Github star? [Y/n]', true);
130 | if ($helper->ask($input, $output, $question)) {
131 | (new Process('composer thanks', $directory))->run();
132 | }
133 |
134 | $output->writeln('Application ready! Build something amazing.');
135 | }
136 |
137 | /**
138 | * Get PackageKit token
139 | */
140 | protected function getToken()
141 | {
142 | $helper = $this->getHelper('question');
143 | $question = new Question("Please enter your API-TOKEN from Package Kit \n");
144 | $this->token = $helper->ask($this->input, $this->output, $question);
145 | }
146 |
147 | /**
148 | * Get PackageKit key
149 | */
150 | protected function getKey()
151 | {
152 | $helper = $this->getHelper('question');
153 | $question = new Question("Please enter your PKGKIT_CDN_KEY. You can get it for free on https://www.pkgkit.com/awes-io/create \n");
154 | $this->key = $helper->ask($this->input, $this->output, $question);
155 | }
156 |
157 | /**
158 | * Check PackageKit composer token
159 | *
160 | * @return bool
161 | */
162 | protected function checkToken(): bool
163 | {
164 | if (empty($this->token)) {
165 | return false;
166 | }
167 |
168 | try {
169 | $response = $this->client->get('validate?token=' . $this->token);
170 | return $response->getStatusCode() == 200;
171 | } catch (RequestException $e) {
172 | if ($e->hasResponse() && $e->getResponse()->getStatusCode() == 403) {
173 | return false;
174 | }
175 | throw $e;
176 | }
177 | }
178 |
179 | /**
180 | * Check PackageKit cdn key
181 | *
182 | * @return bool
183 | */
184 | protected function checkKey(): bool
185 | {
186 | if (empty($this->key)) {
187 | return false;
188 | }
189 |
190 | try {
191 | $response = $this->client->get('check?key=' . $this->key);
192 | return $response->getStatusCode() == 200;
193 | } catch (RequestException $e) {
194 | if ($e->hasResponse() && $e->getResponse()->getStatusCode() == 403) {
195 | return false;
196 | }
197 | throw $e;
198 | }
199 | }
200 |
201 | /**
202 | * Set PackageKit token to composer.json
203 | *
204 | * @param string $directory
205 | */
206 | protected function setToken(string $directory)
207 | {
208 | while (!$this->checkToken()) {
209 | $this->output->writeln('Token is invalid.');
210 | $this->getToken();
211 | }
212 |
213 | $file = $directory . '/composer.json';
214 | $composer = file_get_contents($file);
215 | $composer = json_decode($composer, true);
216 |
217 | foreach ($composer['repositories'] as &$repository) {
218 | if ($repository['url'] === 'https://repo.pkgkit.com') {
219 | $repository['options']['http']['header'] = ['API-TOKEN: ' . $this->token];
220 | }
221 | }
222 |
223 | $composer = json_encode($composer, JSON_PRETTY_PRINT + JSON_UNESCAPED_SLASHES);
224 | file_put_contents($file, $composer);
225 | }
226 |
227 | /**
228 | * Set PackageKit key to .env
229 | *
230 | * @param string $directory
231 | */
232 | protected function setKey(string $directory)
233 | {
234 | $var = 'PKGKIT_CDN_KEY';
235 | while (!$this->checkKey()) {
236 | $this->output->writeln('Key is invalid.');
237 | $this->getKey();
238 | }
239 |
240 | $file = $directory . '/.env';
241 | $env = file_get_contents($file);
242 | if (strpos($env, $var) == false) {
243 | $env = $env . "{$var}={$this->key}\n";
244 | } else {
245 | $val = explode($var, $env, 2)[1];
246 | $val = explode("\n", $val, 2)[0];
247 | $val = $var . $val;
248 | $env = str_replace($val, "{$var}={$this->key}", $env);
249 | }
250 | file_put_contents($file, $env);
251 | }
252 |
253 | /**
254 | * Verify that the application does not already exist.
255 | *
256 | * @param string $directory
257 | * @return void
258 | */
259 | protected function verifyApplicationDoesntExist($directory)
260 | {
261 | if ((is_dir($directory) || is_file($directory)) && $directory != getcwd()) {
262 | throw new RuntimeException('Application already exists!');
263 | }
264 | }
265 |
266 | /**
267 | * Generate a random temporary filename.
268 | *
269 | * @return string
270 | */
271 | protected function makeFilename()
272 | {
273 | return getcwd() . '/awes-io_' . md5(time() . uniqid()) . '.zip';
274 | }
275 |
276 | /**
277 | * Extract the Zip file into the given directory.
278 | *
279 | * @param string $zipFile
280 | * @param string $directory
281 | * @return $this
282 | */
283 | protected function extract(string $zipFile, string $directory): self
284 | {
285 | $tmpdir = sys_get_temp_dir() . DIRECTORY_SEPARATOR . md5(time() . uniqid());
286 |
287 | $archive = new ZipArchive;
288 |
289 | $archive->open($zipFile);
290 |
291 | $archive->extractTo($tmpdir);
292 |
293 | $this->moveExtracted($tmpdir, $directory);
294 |
295 | $archive->close();
296 |
297 | return $this;
298 | }
299 |
300 | /**
301 | * Move extracted from temp dir to project dir
302 | *
303 | * @param string $directory
304 | */
305 | protected function moveExtracted(string $tmpdir, string $directory): void
306 | {
307 | $filesystem = new Filesystem;
308 |
309 | $subdir = array_diff(scandir($tmpdir), ['..', '.']);
310 | if (count($subdir) == 1) {
311 | $subdir = $tmpdir . DIRECTORY_SEPARATOR . array_shift($subdir);
312 | } else {
313 | $subdir = $tmpdir;
314 | }
315 |
316 | $filesystem->mirror($subdir, $directory, null, ['override' => true, 'delete' => false]);
317 | $filesystem->remove($tmpdir);
318 | }
319 |
320 | /**
321 | * Clean-up the Zip file.
322 | *
323 | * @param string $zipFile
324 | * @return $this
325 | */
326 | protected function cleanUp($zipFile): self
327 | {
328 | @chmod($zipFile, 0777);
329 |
330 | @unlink($zipFile);
331 |
332 | return $this;
333 | }
334 |
335 | /**
336 | * Make sure the storage and bootstrap cache directories are writable.
337 | *
338 | * @param string $appDirectory
339 | * @param \Symfony\Component\Console\Output\OutputInterface $output
340 | * @return $this
341 | */
342 | protected function prepareWritableDirectories($appDirectory, OutputInterface $output)
343 | {
344 | $filesystem = new Filesystem;
345 |
346 | try {
347 | $filesystem->chmod($appDirectory . DIRECTORY_SEPARATOR . "bootstrap/cache", 0755, 0000, true);
348 | $filesystem->chmod($appDirectory . DIRECTORY_SEPARATOR . "storage", 0755, 0000, true);
349 | } catch (IOExceptionInterface $e) {
350 | $output->writeln('You should verify that the "storage" and "bootstrap/cache" directories are writable.');
351 | }
352 |
353 | return $this;
354 | }
355 |
356 | /**
357 | * Get the composer command for the environment.
358 | *
359 | * @return string
360 | */
361 | protected function findComposer()
362 | {
363 | $composerPath = getcwd() . '/composer.phar';
364 |
365 | if (file_exists($composerPath)) {
366 | return '"' . PHP_BINARY . '" ' . $composerPath;
367 | }
368 |
369 | return 'composer';
370 | }
371 | }
372 |
--------------------------------------------------------------------------------