Automating Deployment for Laravel Using Deployer and GitHub Actions

Overview

In this note, we outline how to automate the deployment process for a Laravel project using Deployer and GitHub Actions. The setup covers deploying to both staging and production environments, with different workflows for each environment. We also integrate necessary post-deployment tasks such as running migrations and restarting queues using Supervisor.

Prerequisites

Before setting up this automation, ensure the following:

  • Deployer is installed in your Laravel project (composer require deployer/deployer --dev).
  • The deploy.php configuration file is created in the root of your project.
  • SSH keys are set up correctly between GitHub Actions and your server for secure deployment.

Deployment Setup

Step 1: Creating the deploy.php File

The deploy.php file is essential for configuring Deployer. It defines the repository, shared directories, writable directories, and host settings for both staging and production environments. Below is an example.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?php
namespace Deployer;

require 'recipe/laravel.php';

// Project repository
set('repository', '[email protected]:your-username/your-repo.git');

// Shared files/dirs between deploys
add('shared_files', ['.env']);
add('shared_dirs', ['node_modules', 'vendor']);

// Writable dirs by web server
add('writable_dirs', []);

// Hosts
host('staging')
->set('hostname','IP')
->set('remote_user', 'deployer')
->set('branch', 'staging')
->set('deploy_path', '/var/www/staging');

host('production')
->set('hostname','IP')
->set('remote_user', 'deployer')
->set('branch', 'main')
->set('deploy_path', '/var/www/production');

// Tasks
desc('Restart PHP-FPM service');
task('php-fpm:restart', function () {
run('sudo systemctl restart php7.4-fpm');
});

desc('Restart supervisor service');
task('supervisor:restart', function () {
run('sudo supervisorctl reread');
run('sudo supervisorctl update');
run('sudo supervisorctl restart all');
});

// Hooks
after('deploy:symlink', 'php-fpm:restart');
after('deploy:symlink', 'artisan:migrate');
after('deploy:symlink', 'supervisor:restart');

Step 2: GitHub Actions Workflow

Staging Workflow (staging-deploy.yml)

The following is an example of a GitHub Actions workflow file for deploying to the staging environment.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
name: Deploy to Staging

on:
push:
branches:
- staging

jobs:
deploy:
name: Deploy to Staging
runs-on: ubuntu-latest

steps:
- name: Checkout Code
uses: actions/checkout@v4

- name: Install PHP
uses: shivammathur/setup-php@v2
with:
php-version: '8.1'

- name: Install Composer Dependencies
run: composer install --no-progress --no-interaction --prefer-dist --optimize-autoloader

- name: Deploy to Staging
uses: deployphp/action@v1
with:
private-key: ${{ secrets.SSH_KEY }}
dep: deploy staging

Production Workflow (production-deploy.yml)

The production workflow is similar but triggers on pushes to the main branch.

Step 3: Testing and Troubleshooting

Common Errors

  1. No host selected: This error occurs when Deployer cannot find the host based on the command selector. Make sure you pass the correct host (either staging or production) when deploying.

    1
    ./vendor/bin/dep deploy staging
  2. Call to undefined method: This error may appear if you’re using the wrong Deployer version or method. In our case, ensure you’re following the Deployer 7.x documentation.

Step 4: Post-Deployment Tasks

  1. Running Migrations: We use the artisan:migrate command to ensure that migrations are run automatically after deployment.

  2. Restarting Queues: If changes to the queue system are deployed, it’s essential to restart the queue workers. We achieve this by running artisan:queue:restart after deployment.

  3. Restarting Supervisor: If you’re managing workers using Supervisor, the deployment process includes commands to restart Supervisor with supervisorctl.


Summary

By combining Deployer and GitHub Actions, we’ve automated the deployment process for a Laravel project across both staging and production environments. This approach ensures the project is deployed and migrations are run automatically, and queue workers are restarted when necessary. The process is both scalable and adaptable for future projects.

Sources

  1. Deployer Documentation: Hosts in Deployer 7.x
    https://deployer.org/docs/7.x/hosts

  2. Deployer GitHub Action
    https://github.com/deployphp/action

  3. Deployer Official Documentation: Version 7.x
    https://deployer.org/docs/7.x/

  4. GitHub Actions Documentation: Workflow Syntax
    https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions

Masterclass Notes on Radio Advertising

Introduction

Companies buy reach instead of a number of spots. This reach represents the number of listeners at a given moment, such as during rush hour. The number of spots does not matter if the reach is low. You have gross reach, average contact frequency (GCF), and Net reach. Net reach refers to the number of unique listeners, while gross reach does not distinguish between them. The GCF measures how often a listener hears a spot.

Example Calculation

$Gross = 2.583.958$
$GCF = 4$
$Net = \frac{Gross}{GCF} = 645.989,50 \approx 645.990 \text{ Unique listeners}$


Understanding GRP (Gross Rating Point)

Radio stations use Gross Rating Point (GRP), which works with target groups and percentages. A GRP of 5 means that 5% of the target group was reached. This can be filtered by age, gender, country, etc., with age being the most common filter. GRP adds up, so Net listeners do not matter. For example, $2 \times 5%$ GRP simply becomes 10% GRP.

Important Considerations

  • Average Contact Frequency: Always pay attention to the GCF.
  • Target Groups: Some age groups are more difficult to reach.
  • Campaign Duration: Longer campaigns may be necessary for less popular stations.

Data Sources: National Listening Survey (NLO)

The National Listening Survey (NLO) provides data on the number of listeners to major radio stations. NLO participants maintain a diary every fifteen minutes over two months, noting which station they listen to.

Key Statistics

  • 15.000.000 listeners monthly to large radio stations.
  • Market share is recorded as a percentage of listeners aged ten years or older.

Choosing the Right Station

To advertise effectively, assess the most cost-efficient way to reach your target group. Many advertisers mistakenly choose stations based on assumptions rather than data.


Campaign Pricing Factors

Basic Annual Price (BJP)

The BJP represents how much you will pay per year for a basic number of spots. Discounts may apply for bulk spot purchases.

Spot Length

Radio stations base prices on a standard spot length of 20 seconds. Prices adjust in increments of 5 seconds for shorter or longer spots.

Monthly Rate Example

$BJP = 200 \text{ euros}$
$\text{Spot length} = 35 \text{ seconds}$
$Index = 175 \text{ euros}$
$\text{Monthly rate} = 175 \times 200 = 35.000 \text{ euros per month}$


Advertiser Packages

Advertiser packages offer different conditions such as priority in scarcity situations, and how quickly reach can be used (distribution).

Example: GRP Target

Suppose you want a GRP of 3.5 million people in a month on NPO Radio 2. With distribution, you can limit the campaign to stop once that GRP is reached, avoiding unnecessary extra costs.


Time Slots and Indexing

Time slots indicate when something is broadcast during the day (e.g., 6-24, 9-14). Some companies calculate these as a separate index.

Target groups also determine the price. Factors such as demand, supply, and size influence the cost.

Market Index

Radio stations use a market index to balance supply and demand. Prices may fluctuate based on demand and availability.

Monthly Index

The monthly index tracks how much spot time costs each month. For example, advertising costs may rise during holiday months due to increased demand.


Additional Options

Some packages include additional options, or they may be billed separately:

  • Preferred Positions: Your preference for spot position (e.g., last, first).
  • Roadblocks: A group of channels plays a spot simultaneously.

GRP Price Calculation

The GRP price is calculated by multiplying the following factors:

$\text{GRP price} = \text{BJP} \times \text{spot length index} \times \text{package index} \times \text{time slot index} \times \text{target group index} \times \text{market index} \times \text{month index}$


Conclusion

All radio stations use fixed costs per spot rather than GRPs, derived from second rates. For companies that prefer not to delve deeply into GRP mechanics, second rates may be the best choice for maximizing reach.

A small project: Short.moe

A small project: Short.moe

I’ve been recently working on a project with the domain short.moe creating a serverless URL shortening service.

Short.moe is a free URL shortener service that allows you to easily shorten long URLs into shorter, more manageable links. Built with NextJS 14, Clerk, Prisma, and PostgreSQL, hosted on serverless on Vercel, Short.moe is designed to be both easy to use and user-friendly.

Key Features

Easy URL Shortening

With Short.moe, you can shorten URLs without the need to create an account. When shortening a URL without an account, the slug/alias (the unique part of the shortened URL) will be randomly generated using the nanoid package.

Account-Based Customization

For users who create an account, Short.moe offers the ability to set custom slugs. This means you can create memorable, aliased links that are easy to share and recall. Clerk handles authentication, making the process of signing up and managing your account straightforward. On the technical side, this was very easy to do.

In Short

Short.moe aims to be an easy option for anyone looking to shorten URLs quickly and easily, whether with or without a personalized alias/slug. Thank you for reading this short post.

Unleashing the Power of CloudFlare's AI Workers

Introduction

CloudFlare’s AI Workers allows developers to interact serverlessly with CloudFlare’s AI models, providing a simple way to integrate AI into your applications.

CloudFlare’s AI Workers Overview

CloudFlare’s AI Workers bring the capabilities of Artificial Intelligence (AI) directly into the realm of serverless computing.

Now, developers can use the power of AI models through CloudFlare’s infrastructure, opening new possibilities for their applications. The AI Workers come with a REST API, allowing for easy integration and communication with CloudFlare’s AI models. These include LLMs, Stable Diffusion, HuggingFace and more.

Python Example: Interacting with CloudFlare’s AI

This Python example that shows how to interact with CloudFlare’s AI models. This script utilizes CloudFlare’s REST API to run an AI model named “@cf/meta/llama-2-7b-chat-int8.”

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import requests

API_BASE_URL = "https://api.cloudflare.com/client/v4/accounts/ACCOUNT_ID/ai/run/"
headers = {"Authorization": "Bearer {API_TOKEN}"}

def run(model, inputs):
input = { "messages": inputs }
response = requests.post(f"{API_BASE_URL}{model}", headers=headers, json=input)
return response.json()

inputs = [
{ "role": "system", "content": "You are an AI" },
{ "role": "user", "content": "Write a short story about a llama that goes on a journey to find an orange cloud"}
]
output = run("@cf/meta/llama-2-7b-chat-int8", inputs)
print(output)

JavaScript Example: Deployment with CloudFlare’s AI

For developers looking to integrate CloudFlare’s AI into their web applications (e.g. customer service chat bots or SaaS), the following JavaScript example showcases the deployment of an AI-powered function using CloudFlare Workers:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import { Ai } from './vendor/@cloudflare/ai.js';

export default {
async fetch(request, env) {
const tasks = [];
const ai = new Ai(env.AI);

// Prompt - simple completion-style input
let simple = {
prompt: 'Tell me a joke about CloudFlare'
};
let response = await ai.run('@cf/meta/llama-2-7b-chat-int8', simple);
tasks.push({ inputs: simple, response });

// Messages - chat-style input
let chat = {
messages: [
{ role: 'system', content: 'You are a helpful assistant.' },
{ role: 'user', content: 'Who won the world series in 2020?' }
]
};
response = await ai.run('@cf/meta/llama-2-7b-chat-int8', chat);
tasks.push({ inputs: chat, response });

return Response.json(tasks);
}
};

Proof of Concept AI Worker

To see CloudFlare’s AI Workers in action, check out my AI Chat App. It’s a proof of concept that uses CloudFlare’s AI, providing an eye into the capabilities and potential applications. Try it here: ai.cvyl.me.

Harmony Within Us Development Update

Hey HWU community,

I hope this post finds you well. I wanted to provide a quick update on the development of Harmony Within Us (HWU). As many of you know, our shared journey toward creating a supportive and inclusive space is incredibly important to me.

Due to a heightened focus on my mental health and overall well-being, I’ve made the decision to temporarily pause the development of HWU. It’s crucial for me to take this time to ensure that I can continue to contribute positively to our community.

I want to reassure you that this pause is not a goodbye or an end. It’s a necessary step to prioritize self-care, and I believe it will ultimately lead to a more robust and impactful HWU in the long run.

I understand that many of you are excited about the project, and I truly appreciate your support and enthusiasm. Please don’t worry – Harmony Within Us is still very much alive, and I am committed to returning to development as soon as possible.

Thank you for your understanding and continued support. Let’s keep fostering harmony within ourselves and our community.

Stay tuned for more updates, and take care. <3