Skip to content

Commit

Permalink
Updated application deployment via CI (on VDS) (gomzyakov#402)
Browse files Browse the repository at this point in the history
  • Loading branch information
gomzyakov authored Nov 15, 2022
1 parent 9b50cab commit e3edd3f
Show file tree
Hide file tree
Showing 6 changed files with 140 additions and 138 deletions.
2 changes: 1 addition & 1 deletion .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ LOG_DEPRECATIONS_CHANNEL=null
LOG_LEVEL=debug

DB_CONNECTION=mysql
# DB_HOST=mysql
# Replace to DB_HOST=mysql when run via `sail up`
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=secretic
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ jobs:
password: ${{ secrets.SSH_PASSWORD }}
script: |
whoami
cd /usr/secretic
cd /var/www/web/sites/secretic.app
git reset --hard HEAD
git clean -f -d
git pull origin main
git show --summary
# TODO ./vendor/bin/sail composer install
composer -V
composer install
123 changes: 64 additions & 59 deletions DEPLOY.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,47 @@

For simplicity, we will deploy the project to VDS. The deployment process consists of several steps:

## 1. Create a VDS on your preferred hosting
## Create a VDS on your preferred hosting

Create a VDS virtual machine for Docker, for example, at [NetAngels](https://panel.netangels.ru).
Create a VDS virtual machine for example at [NetAngels](https://panel.netangels.ru).

For [secretic.app](https://secretic.app) we used distribution `Docker` (Debian 11):
For [secretic.app](https://secretic.app) we used distribution `Ubuntu 20.04`.

![Docker VDS](vds-docker.png)
## Set up VDS

We will launch the project on [Laravel Sail](https://laravel.com/docs/9.x/sail) - yes, this is not ideal, but for non-production purposes it will do.
Login via SSH (as `root` user) and check PHP version (`php -v`).

If necessary, [upgrade to PHP 8](https://php.watch/articles/php-8.0-installation-update-guide-debian-ubuntu):

## 2. Generate RSA-key (for access to GitHub)
```bash
sudo apt update
sudo apt install software-properties-common
sudo apt update
sudo add-apt-repository ppa:ondrej/php
sudo apt update
sudo apt install php8.1-common php8.1-cli php8.1-mysql php8.1-xml php8.1-curl php8.1-bcmath php8.1-mbstring -y
```

Manually [install](https://getcomposer.org/download/) Composer:

```bash
php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php composer-setup.php
php -r "unlink('composer-setup.php');"
```

And make it available for calling through `composer`:

```bash
sudo mv composer.phar /usr/local/bin/composer
```

## CHANGE USER!

Next, we do everything from the user `web`, not `root`!!!


## Generate RSA-key (for access to GitHub)

First, generate SSH-keys with `ssh-keygen`:

Expand Down Expand Up @@ -46,10 +75,9 @@ The key's randomart image is:
+----[SHA256]-----+
```
## Add RSA-key to GitHub
## 3. Add RSA-key to GitHub
In the GitHub-repository, in the [Deploy Keys](https://github.com/gomzyakov/secretic/settings/keys) section, set the value of the public key from the virtual machine.
In the GitHub-repository, in the `Settings > Deploy Keys` section ([link](https://github.com/gomzyakov/secretic/settings/keys)), set the value of the public key from the virtual machine.
You can get it via:
Expand All @@ -59,77 +87,54 @@ cat ~/.ssh/id_rsa.pub
This will allow deployment via `git pull` from VDS.
## 4. Set secrets in the repository on GitHub
After that, in the GitHub repository, in the `Settings > Secrets > Actions` section, set the values:
## Set secrets on GitHub
After that, in the GitHub repository, in the `Settings > Secrets > Actions` section ([link](https://github.com/gomzyakov/secretic/settings/secrets/actions)), set the values:
- `SSH_HOST`: This is the IP address of the server.
- `SSH_USERNAME`: This is the server username.
- `SSH_USERNAME`: This is the `web` username.
- `SSH_PASSWORD`: This is the user password.
## 5. Clone repository from GitHub
## Clone repository from GitHub
Login to VDS via SSH as `root` user.
Go to path `/var/www/web/sites` and clone current repository (or your own fork). For example, use `secretic.app` folder name:
Go to path `/usr` and clone current repository (or your own fork). By default, Git clone to `secretic` folder:
```bash
git clone [email protected]:gomzyakov/secretic.git && cd secretic
``````
```bash
cd /var/www/web/sites
git clone [email protected]:gomzyakov/secretic.git secretic.app
cd secretic
```
## 6. Init & run Laravel Sail on VDS
## Set up Laravel
Run the command for the first time:
Then go to path `/var/www/web/sites/secretic.app` and run some commands:
```bash
docker run --rm \
-u "$(id -u):$(id -g)" \
-v $(pwd):/opt \
-w /opt \
laravelsail/php81-composer:latest \
composer install --ignore-platform-reqs
php -r "file_exists('.env') || copy('.env.example', '.env');"
composer install
chmod -R 777 storage bootstrap/cache
php artisan key:generate
```
Copy the environment settings:
- Write the correct database requisites in the `.env` file
- Create a `secretnotes` database via phpMyAdmin
- Run migrations `php artisan migrate:fresh --seed`
```bash
cp .env.example .env
```
And replace `DB_HOST` to `mysql` in `.env` (for local development).
Set permissions for some directories:
```bash
chmod -R 777 bootstrap/cache
chmod -R 777 ./storage/logs
chmod -R 777 ./storage/framework
```
Start containers with Sail:
Open assigned to VDS URL (like `dd`) in your favorite browser. Happy using Secretic!
```bash
./vendor/bin/sail up -d
```
After that go to shell:
```bash
./vendor/bin/sail shell
```
And run final commands:
## Optional: Make domain aliases (if needed)
```bash
./artisan key:generate
./artisan migrate:fresh --seed
cd /var/www/web/sites
ln -s secretic.app www.secretic.app
ln -s secretic.app secretic.ru
ln -s secretic.app secretnotes.ru
```
Open VDS IP-address in your favorite browser. Happy using Secretic!
## 7. Optional: Set up HTTPS
## Optional: Set up HTTPS
>No need if you are using mode `Flexible` on Cloudflare
Expand Down
22 changes: 10 additions & 12 deletions app/Http/Controllers/NoteController.php
Original file line number Diff line number Diff line change
Expand Up @@ -76,18 +76,16 @@ public function createNote(
NoteCreateRequest $request,
NotesRepository $notes_repository
): Application|View|ViewFactory {
abort(500);

// $note = $notes_repository->create(
// $request->getText(),
// $request->getPassword() ? Hash::make($request->getPassword()) : null,
// $this->getExpirationDate($request->getExpirationDate())
// );
//
// return view('note.show-link', [
// 'hide_footer' => true,
// 'note_url' => route('note.open_link', ['slug' => $note->slug]),
// ]);
$note = $notes_repository->create(
$request->getText(),
$request->getPassword() ? Hash::make($request->getPassword()) : null,
$this->getExpirationDate($request->getExpirationDate())
);

return view('note.show-link', [
'hide_footer' => true,
'note_url' => route('note.open_link', ['slug' => $note->slug]),
]);
}

/**
Expand Down
125 changes: 62 additions & 63 deletions tests/Feature/Note/CreateNoteTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,71 +18,70 @@ public function test_create_with_min_params(): void
route('note.create', ['text' => $text = $this->faker->text()])
);

$response->assertStatus(500);

// $response->assertSee('Secret note successfully created!');
// $response->assertSee('Copy note to clipboard');
//
// $note_slug = $this->getNoteSlugFromHTMLPage($response);
//
// $this->assertDatabaseHas('notes', ['slug' => $note_slug]);
//
// /** @var Note $note */
// $note = Note::where('slug', $note_slug)->first();
//
// $this->assertInstanceOf(Note::class, $note);
// $this->assertSame($text, Crypt::decryptString($note->text));

$response->assertSee('Secret note successfully created!');
$response->assertSee('Copy note to clipboard');

$note_slug = $this->getNoteSlugFromHTMLPage($response);

$this->assertDatabaseHas('notes', ['slug' => $note_slug]);

/** @var Note $note */
$note = Note::where('slug', $note_slug)->first();

$this->assertInstanceOf(Note::class, $note);
$this->assertSame($text, Crypt::decryptString($note->text));
}

// public function test_create_with_full_params(): void
// {
// $response = $this->post(
// route('note.create', [
// 'text' => $text = $this->faker->text(),
// 'encrypt_password' => 'foo_bar',
// 'expiration_date' => '1_week',
// ])
// );
//
// $response->assertStatus(200);
//
// $note_slug = $this->getNoteSlugFromHTMLPage($response);
//
// $this->assertDatabaseHas('notes', ['slug' => $note_slug]);
//
// /** @var Note $note */
// $note = Note::where('slug', $note_slug)->first();
//
// $this->assertInstanceOf(Note::class, $note);
// $this->assertSame($text, Crypt::decryptString($note->text));
// }
//
// public function test_empty_note(): void
// {
// $response = $this->post(
// route('note.create', ['text' => ''])
// );
//
// // TODO Is this the correct response status?
// $response->assertStatus(302);
// }
//
// /**
// * @param TestResponse $response
// *
// * @return string
// */
// private function getNoteSlugFromHTMLPage(TestResponse $response): string
// {
// // Get note URL from HTML page
// preg_match('/<input type="hidden" id="secret-note-url" value="(.+)">/iu', $response->content(), $matches);
// $note_url = $matches[1];
//
// // Get last word from URL after a slash
// preg_match('/[^\\/]+$/', $note_url, $matches);
//
// return $matches[0];
// }
public function test_create_with_full_params(): void
{
$response = $this->post(
route('note.create', [
'text' => $text = $this->faker->text(),
'encrypt_password' => 'foo_bar',
'expiration_date' => '1_week',
])
);

$response->assertStatus(200);

$note_slug = $this->getNoteSlugFromHTMLPage($response);

$this->assertDatabaseHas('notes', ['slug' => $note_slug]);

/** @var Note $note */
$note = Note::where('slug', $note_slug)->first();

$this->assertInstanceOf(Note::class, $note);
$this->assertSame($text, Crypt::decryptString($note->text));
}

public function test_empty_note(): void
{
$response = $this->post(
route('note.create', ['text' => ''])
);

// TODO Is this the correct response status?
$response->assertStatus(302);
}

/**
* @param TestResponse $response
*
* @return string
*/
private function getNoteSlugFromHTMLPage(TestResponse $response): string
{
// Get note URL from HTML page
preg_match('/<input type="hidden" id="secret-note-url" value="(.+)">/iu', $response->content(), $matches);
$note_url = $matches[1];

// Get last word from URL after a slash
preg_match('/[^\\/]+$/', $note_url, $matches);

return $matches[0];
}

// TODO test 65K+ characters
// TODO test_too_short_password
Expand Down
Binary file removed vds-docker.png
Binary file not shown.

0 comments on commit e3edd3f

Please sign in to comment.