<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Graham Patrick</title>
    <description>The latest articles on DEV Community by Graham Patrick (@skygreyai).</description>
    <link>https://dev.to/skygreyai</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3123080%2F2671a2d3-cc27-4e81-9dcd-216d16317513.jpg</url>
      <title>DEV Community: Graham Patrick</title>
      <link>https://dev.to/skygreyai</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/skygreyai"/>
    <language>en</language>
    <item>
      <title>The Age of the Smartphone Is Ending — And Most People Haven’t Noticed</title>
      <dc:creator>Graham Patrick</dc:creator>
      <pubDate>Wed, 07 May 2025 16:02:22 +0000</pubDate>
      <link>https://dev.to/skygreyai/the-age-of-the-smartphone-is-ending-and-most-people-havent-noticed-4pe3</link>
      <guid>https://dev.to/skygreyai/the-age-of-the-smartphone-is-ending-and-most-people-havent-noticed-4pe3</guid>
      <description>&lt;p&gt;For over 15 years, the smartphone has been the centre of our digital universe. It’s the screen we stare at, the tool we work on, the device we reach for instinctively. But look a little closer — and you’ll start to see the shift.&lt;/p&gt;

&lt;p&gt;We’re entering the post-smartphone era. Not with a bang, but through quiet disruption. And it’s already on your body.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9inkqgh6lb5j6tckna95.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9inkqgh6lb5j6tckna95.png" alt="And man using a Sky Puck" width="800" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Rise of the Wearable Web&lt;/strong&gt;&lt;br&gt;
I wear Ray-Ban Meta glasses. With a simple voice command, I can ask for directions, snap a photo, stream music, or get real-time translation.&lt;/p&gt;

&lt;p&gt;I wear a smartwatch that tracks my health, receives notifications, and lets me respond instantly.&lt;/p&gt;

&lt;p&gt;Samsung just launched a smart ring — one more layer of ambient computing wrapped around our daily lives.&lt;/p&gt;

&lt;p&gt;And of course, I can speak to an AI assistant and get intelligent, contextual responses without touching a screen.&lt;/p&gt;

&lt;p&gt;This is no longer sci-fi — this is now.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpqhlqet8snlqaxirbeaa.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fpqhlqet8snlqaxirbeaa.png" alt="A Hub for wearables" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;A New Hub: The Invisible Puck&lt;/strong&gt;&lt;br&gt;
The smartphone’s biggest strength was centralisation. But now, that central hub could shrink even further — into something like a “puck”: a small device in your pocket or bag that does nothing but supply data, compute, and connectivity.&lt;/p&gt;

&lt;p&gt;Imagine:&lt;/p&gt;

&lt;p&gt;A tiny 5G/6G puck lives in your backpack&lt;/p&gt;

&lt;p&gt;Your glasses are your screen&lt;/p&gt;

&lt;p&gt;Your ring is your security token&lt;/p&gt;

&lt;p&gt;Your watch handles notifications and health&lt;/p&gt;

&lt;p&gt;Voice becomes your primary interface&lt;/p&gt;

&lt;p&gt;AI becomes your operating system&lt;/p&gt;

&lt;p&gt;No more swiping. No more “app folders”. No more trying to type with your thumb while walking.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Screens Are Becoming Optional&lt;/strong&gt;&lt;br&gt;
We’re heading toward a world where the screen isn’t the experience — it’s a layer. Screens may still be there, but they’ll be optional, not essential.&lt;/p&gt;

&lt;p&gt;Even today, I can ask my glasses to play a song, call someone, or answer a question — all without touching a smartphone. I don’t need a “phone” to live connected. I just need smart inputs, wearable endpoints, and a cloud intelligence layer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;What This Means&lt;/strong&gt;&lt;br&gt;
The next generation won’t grow up “addicted to their phones” — they’ll talk to tech instead of tapping it.&lt;/p&gt;

&lt;p&gt;App developers will have to rethink interaction: conversational, contextual, multi-device.&lt;/p&gt;

&lt;p&gt;Privacy, identity, and edge computing will take centre stage — because the phone as a walled garden will be gone.&lt;/p&gt;

&lt;p&gt;We’ll be more present — or at least, less distracted by rectangles.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcdw76vdds50lhyb6v5u9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fcdw76vdds50lhyb6v5u9.png" alt="A woman lookign at her puck" width="800" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The Debate Begins&lt;/strong&gt;&lt;br&gt;
Is this the death of the smartphone — or just its evolution into something less visible?&lt;/p&gt;

&lt;p&gt;Will we embrace ambient computing — or push back against losing physical control?&lt;/p&gt;

&lt;p&gt;One thing’s clear: the era of staring at your hand all day is giving way to something new.&lt;/p&gt;

&lt;p&gt;So here’s the question:&lt;/p&gt;

&lt;p&gt;Would you give up your phone if you could wear the web instead?&lt;/p&gt;

</description>
      <category>watercooler</category>
    </item>
    <item>
      <title>Introducing Flaskion – A Lightweight MVC Framework for Flask</title>
      <dc:creator>Graham Patrick</dc:creator>
      <pubDate>Mon, 05 May 2025 16:28:41 +0000</pubDate>
      <link>https://dev.to/skygreyai/introducing-flaskion-a-lightweight-mvc-framework-for-flask-1en9</link>
      <guid>https://dev.to/skygreyai/introducing-flaskion-a-lightweight-mvc-framework-for-flask-1en9</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fabfsuh452cutk08ep3i0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fabfsuh452cutk08ep3i0.png" alt="flaskion logo" width="800" height="152"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;After 18 years in the web development world—first cutting my teeth in PHP and later exploring Node.js and Python—I’m excited to officially introduce &lt;strong&gt;Flaskion&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Flaskion is a modern, professional-grade MVC boilerplate for Flask that brings &lt;strong&gt;structure&lt;/strong&gt;, &lt;strong&gt;clarity&lt;/strong&gt;, and &lt;strong&gt;developer happiness&lt;/strong&gt; to building web apps in Python.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Flaskion?
&lt;/h2&gt;

&lt;h3&gt;
  
  
  My Journey
&lt;/h3&gt;

&lt;p&gt;Like many full-stack devs, I’ve always been on the lookout for tools that make development more intuitive. Years ago, after spending time with Laravel, I fell in love with its &lt;strong&gt;developer experience&lt;/strong&gt;, clarity, and powerful CLI tools.&lt;/p&gt;

&lt;p&gt;But when I transitioned to Flask, I quickly hit friction:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;❌ Inconsistent project structures
&lt;/li&gt;
&lt;li&gt;❌ No standard routing pattern
&lt;/li&gt;
&lt;li&gt;❌ Lack of built-in scaffolding
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Flask gives you freedom—but with freedom comes chaos.&lt;br&gt;&lt;br&gt;
That’s when &lt;strong&gt;Flaskion was born&lt;/strong&gt;.&lt;/p&gt;


&lt;h2&gt;
  
  
  What Is Flaskion?
&lt;/h2&gt;

&lt;p&gt;Flaskion is more than just a boilerplate—it's the foundation for your next Flask project.&lt;/p&gt;

&lt;p&gt;Think of it as &lt;strong&gt;Laravel meets Flask&lt;/strong&gt;, bringing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;✅ MVC structure
&lt;/li&gt;
&lt;li&gt;✅ Dynamic CLI generators
&lt;/li&gt;
&lt;li&gt;✅ Centralised routing
&lt;/li&gt;
&lt;li&gt;✅ Built-in support for SQLAlchemy, Marshmallow, Flask-Migrate
&lt;/li&gt;
&lt;li&gt;✅ Optional authentication scaffolding
&lt;/li&gt;
&lt;li&gt;✅ Beautiful templates (styled, not stock)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It’s clean, minimal, and perfect for both solo devs and professional teams.&lt;/p&gt;


&lt;h2&gt;
  
  
  Key Features
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;🏗️ &lt;code&gt;make:new&lt;/code&gt; – Scaffold a new Flaskion project with SQLite, MySQL or Postgres&lt;/li&gt;
&lt;li&gt;🧱 &lt;code&gt;make:model&lt;/code&gt; – Generate SQLAlchemy models instantly
&lt;/li&gt;
&lt;li&gt;📄 &lt;code&gt;make:controller&lt;/code&gt; – Class-based controller with web or API support
&lt;/li&gt;
&lt;li&gt;📦 &lt;code&gt;make:resource&lt;/code&gt; – Full MVC: model, schema, controller, and route
&lt;/li&gt;
&lt;li&gt;🔐 &lt;code&gt;make:auth&lt;/code&gt; – Instantly scaffold login, register, dashboard views
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;All CLI commands are designed to be intuitive, useful, and expandable.&lt;/p&gt;


&lt;h2&gt;
  
  
  Folder Structure
&lt;/h2&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flaskion/
├── app/
│   ├── controllers/
│   ├── models/
│   ├── schemas/
│   ├── routes/
│   ├── templates/
│   ├── static/
│   └── config.py
├── run.py
├── .env
├── requirements.txt
└── migrations/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;h2&gt;
  
  
  What’s Next?
&lt;/h2&gt;

&lt;p&gt;Flaskion is still growing, and this is just the beginning.&lt;/p&gt;

&lt;p&gt;I’m working on:&lt;br&gt;
    • Documentation &amp;amp; tutorials&lt;br&gt;
    • Pytest-ready testing layer&lt;br&gt;
    • More CLI tools (make:mail, make:form, etc.)&lt;br&gt;
    • Flaskion website &amp;amp; showcase&lt;/p&gt;
&lt;h2&gt;
  
  
  Get Started
&lt;/h2&gt;

&lt;p&gt;Install Flaskion CLI:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;pip &lt;span class="nb"&gt;install &lt;/span&gt;flaskion-cli
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Create a project:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flaskion make:new myapp &lt;span class="nt"&gt;--db&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;sqlite
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Flaskion is built with love by a developer who’s walked the same path as many of you—looking for simplicity, structure, and power in one place.&lt;/p&gt;

&lt;p&gt;Let’s make Flask development feel like Laravel—without losing the beauty of Python.&lt;/p&gt;

&lt;p&gt;check it out on &lt;a href="https://pypi.org/project/flaskion-cli/" rel="noopener noreferrer"&gt;pypi.org&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Deploying Flask with Laravel Forge + GitHub Actions</title>
      <dc:creator>Graham Patrick</dc:creator>
      <pubDate>Sun, 04 May 2025 18:25:08 +0000</pubDate>
      <link>https://dev.to/skygreyai/deploying-flask-with-laravel-forge-github-actions-5eio</link>
      <guid>https://dev.to/skygreyai/deploying-flask-with-laravel-forge-github-actions-5eio</guid>
      <description>&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj5u4aphrg9kseyqcy9zt.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fj5u4aphrg9kseyqcy9zt.png" alt="Deploy flask with Laravel Forge" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This guide walks you through deploying a production-ready Flask application using Laravel Forge, GitHub Actions, and Supervisor — with automatic database migrations, SSH deployment, and secure environment variable management.&lt;/p&gt;

&lt;p&gt;We’ll treat this as a fresh project with new migrations.&lt;/p&gt;

&lt;p&gt;1.Provision Your Server&lt;/p&gt;

&lt;p&gt;In Laravel Forge:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Create a new server (Ubuntu 22+ recommended)&lt;/li&gt;
&lt;li&gt;Enable SSH access for your GitHub Actions&lt;/li&gt;
&lt;li&gt;Add a site (e.g., api.yourapp.com)&lt;/li&gt;
&lt;li&gt;Clone your Git repo (use "Deploy Script" toggle OFF for now)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On the server:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;ssh forge@your-server-ip
&lt;span class="nb"&gt;cd&lt;/span&gt; /home/forge/api.yourapp.com
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv venv
&lt;span class="nb"&gt;source &lt;/span&gt;venv/bin/activate
pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;2.Project Structure &amp;amp; Entry Point&lt;/p&gt;

&lt;p&gt;Your main app should have a run.py file:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;create_app&lt;/span&gt;

&lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;create_app&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;__name__&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;__main__&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Use Flask-Migrate and SQLAlchemy in app/&lt;strong&gt;init&lt;/strong&gt;.py:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask_migrate&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Migrate&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;flask_sqlalchemy&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;SQLAlchemy&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;dotenv&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;load_dotenv&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;os&lt;/span&gt;

&lt;span class="nf"&gt;load_dotenv&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="n"&gt;db&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;SQLAlchemy&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;span class="n"&gt;migrate&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Migrate&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;create_app&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Flask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;__name__&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_object&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;config.Config&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init_app&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;migrate&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;init_app&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;app&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;db&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;app&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;GitHub Actions Deployment&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Create .github/workflows/deploy.yml:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;name: Deploy to Forge

on:
  push:
    branches: &lt;span class="o"&gt;[&lt;/span&gt;main]

&lt;span class="nb"&gt;jobs&lt;/span&gt;:
  deploy:
    runs-on: ubuntu-latest

    steps:
    - name: Checkout code
      uses: actions/checkout@v3

    - name: Deploy over SSH
      uses: appleboy/ssh-action@v0.1.6
      with:
        host: &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="p"&gt;{ secrets.FORGE_HOST &lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
        username: forge
        key: &lt;span class="k"&gt;${&lt;/span&gt;&lt;span class="p"&gt;{ secrets.FORGE_SSH_KEY &lt;/span&gt;&lt;span class="k"&gt;}&lt;/span&gt;&lt;span class="o"&gt;}&lt;/span&gt;
        script: |
          &lt;span class="nb"&gt;cd&lt;/span&gt; /home/forge/api.yourapp.com
          git pull origin main
          &lt;span class="nb"&gt;source &lt;/span&gt;venv/bin/activate
          pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
          flask db upgrade
          &lt;span class="nb"&gt;sudo &lt;/span&gt;supervisorctl restart flask-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Important:&lt;/strong&gt; Add the Forge server's SSH private key (id_rsa) to GitHub Secrets as FORGE_SSH_KEY.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Managing Environment Variables&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;On the Forge server:&lt;/p&gt;

&lt;p&gt;Add a .env file in your project root (/home/forge/api.yourapp.com/.env):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="n"&gt;FLASK_ENV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;production&lt;/span&gt;
&lt;span class="n"&gt;SECRET_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;supersecret&lt;/span&gt;
&lt;span class="n"&gt;DATABASE_URL&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mysql&lt;/span&gt;&lt;span class="o"&gt;+&lt;/span&gt;&lt;span class="n"&gt;pymysql&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="o"&gt;//&lt;/span&gt;&lt;span class="n"&gt;user&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="k"&gt;pass&lt;/span&gt;&lt;span class="nd"&gt;@host&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="n"&gt;port&lt;/span&gt;&lt;span class="o"&gt;/&lt;/span&gt;&lt;span class="n"&gt;db&lt;/span&gt;
&lt;span class="n"&gt;MAILGUN_DOMAIN&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;mg&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;yourapp&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;com&lt;/span&gt;
&lt;span class="n"&gt;MAILGUN_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;your&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;mailgun&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;key&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In app/&lt;strong&gt;init&lt;/strong&gt;.py:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;from dotenv import load_dotenv
load_dotenv&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;dotenv_path&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;'/home/forge/api.yourapp.com/.env'&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Confirm values load with:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flask shell
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; import os
&lt;span class="o"&gt;&amp;gt;&amp;gt;&amp;gt;&lt;/span&gt; os.getenv&lt;span class="o"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;"MAILGUN_DOMAIN"&lt;/span&gt;&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;Running Migrations on Deploy&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;When you push code with new migrations:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;flask db migrate &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add new table"&lt;/span&gt;
git add migrations/
git commit &lt;span class="nt"&gt;-m&lt;/span&gt; &lt;span class="s2"&gt;"Add new table"&lt;/span&gt;
git push
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;GitHub Actions will:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pull the latest code&lt;/li&gt;
&lt;li&gt;Install dependencies&lt;/li&gt;
&lt;li&gt;Run flask db upgrade live&lt;/li&gt;
&lt;li&gt;Restart Gunicorn&lt;/li&gt;
&lt;/ul&gt;

&lt;ol&gt;
&lt;li&gt;Supervisor Configuration&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Set up supervisor to run your Flask app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nb"&gt;sudo &lt;/span&gt;nano /etc/supervisor/conf.d/flask-app.conf
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="o"&gt;[&lt;/span&gt;program:flask-app]
&lt;span class="nb"&gt;command&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/home/forge/api.yourapp.com/venv/bin/gunicorn run:app
&lt;span class="nv"&gt;user&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;forge
&lt;span class="nv"&gt;directory&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/home/forge/api.yourapp.com
&lt;span class="nv"&gt;autostart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true
&lt;/span&gt;&lt;span class="nv"&gt;autorestart&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nb"&gt;true
&lt;/span&gt;&lt;span class="nv"&gt;stdout_logfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/log/supervisor/flask-app.log
&lt;span class="nv"&gt;stderr_logfile&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;/var/log/supervisor/flask-app.err.log
&lt;span class="nv"&gt;environment&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="nv"&gt;FLASK_ENV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"production"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then reload:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;sudo supervisorctl reread
sudo supervisorctl update
sudo supervisorctl restart flask-app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You Did It&lt;/p&gt;

&lt;p&gt;You now have a full CI/CD pipeline:&lt;/p&gt;

&lt;p&gt;GitHub commit → GitHub Actions → SSH to Forge → Pull, Migrate, Restart&lt;/p&gt;

&lt;p&gt;New tables are deployed automatically&lt;/p&gt;

&lt;p&gt;Environment is secure&lt;/p&gt;

</description>
    </item>
  </channel>
</rss>
