FastAPI Movie Theater is a RESTful API for managing a movie theater platform, built with Python and FastAPI. The service supports movie management, user interactions (comments, ratings, reactions, favourites) and an authentication system with role-based access control.
- JWT-based authentication
- Access & refresh tokens
- Role separation: users, moderators, admins
- Custom user model
- User registration
- Account activation
- Password change:
- old password
- refresh token
- Movie management
- Create, update, delete movies (staff/admin)
- Pagination & filtering
- User interactions
- Post comments on movies
- Add ratings (toggle / update)
- React to movies
- Add movies to favourites
- Swagger & ReDoc
- Auto-generated API documentation
- Production-ready architecture
- Async SQLAlchemy
- Alembic migrations
- Docker & Docker Compose support
- Python 3.12+
- FastAPI
- Pydantic v2
- SQLAlchemy (async)
- Alembic
- SQLite (local / development)
- PostgreSQL (production)
- Docker & Docker Compose
- Nginx (reverse proxy)
- AWS EC2
- GitHub Actions (CI)
Once the application is running, interactive documentation is available at:
- Swagger UI:
http://localhost:8000/docs - ReDoc:
http://localhost:8000/redoc
.
├── Dockerfile
├── README.MD
├── alembic.ini
├── commands
│ ├── run_migration.sh
│ ├── run_web_server_dev.sh
│ ├── run_web_server_prod.sh
│ ├── set_nginx_basic_auth.sh
│ ├── setup_mailhog_auth.sh
│ └── setup_minio.sh
├── configs
│ └── nginx
│ └── nginx.conf
├── docker
│ ├── mailhog
│ │ └── Dockerfile
│ ├── minio_mc
│ │ └── Dockerfile
│ ├── nginx
│ │ └── Dockerfile
│ └── tests
│ └── Dockerfile
├── docker-compose-dev.yml
├── docker-compose-prod.yml
├── docker-compose-tests.yml
├── init.sql
├── poetry.lock
├── pyproject.toml
├── pytest.ini
└── cinema
├── config
│ ├── __init__.py
│ ├── dependencies.py
│ └── settings.py
├── database
│ ├── __init__.py
│ ├── migrations
│ │ ├── README
│ │ ├── env.py
│ │ ├── script.py.mako
│ │ └── versions
│ │ ├── 2da0dc469be8_temp_migration.py
│ │ ├── 32b1054a69e3_initial_migration.py
│ │ └── 41cdafa531cf_temp_migration.py
│ ├── models
│ │ ├── __init__.py
│ │ ├── accounts.py
│ │ ├── base.py
│ │ └── movies.py
│ ├── populate.py
│ ├── seed_data
│ │ └── test_data.csv
│ ├── session_postgresql.py
│ ├── session_sqlite.py
│ └── validators
│ ├── __init__.py
│ └── accounts.py
├── exceptions
│ ├── __init__.py
│ ├── email.py
│ ├── security.py
│ └── storage.py
├── main.py
├── notifications
│ ├── __init__.py
│ ├── emails.py
│ ├── interfaces.py
│ └── templates
│ ├── activation_complete.html
│ ├── activation_request.html
│ ├── password_reset_complete.html
│ └── password_reset_request.html
├── routes
│ ├── __init__.py
│ ├── accounts.py
│ ├── genres.py
│ ├── movies
│ │ ├── movies.py
│ │ ├── movies_comments.py
│ │ ├── movies_favourites.py
│ │ ├── movies_ratings.py
│ │ ├── movies_reactions.py
│ └── profiles.py
├── schemas
│ ├── __init__.py
│ ├── accounts.py
│ ├── movies.py
│ └── profiles.py
├── security
│ ├── __init__.py
│ ├── http.py
│ ├── interfaces.py
│ ├── passwords.py
│ ├── token_manager.py
│ └── utils.py
├── storages
│ ├── __init__.py
│ ├── interfaces.py
│ └── s3.py
├── tests
│ ├── __init__.py
│ ├── conftest.py
│ ├── doubles
│ │ ├── __init__.py
│ │ ├── fakes
│ │ │ ├── __init__.py
│ │ │ └── storage.py
│ │ └── stubs
│ │ ├── __init__.py
│ │ └── emails.py
│ ├── test_e2e
│ │ ├── __init__.py
│ │ ├── test_email_notification.py
│ │ └── test_storage.py
│ └── test_integration
│ ├── __init__.py
│ ├── test_accounts.py
│ ├── test_movies.py
│ ├── test_movies_comments.py
│ ├── test_movies_favourites.py
│ ├── test_movies_ratings.py
│ ├── test_movies_reactions.py
│ └── test_profiles.py
└── validation
├── __init__.py
└── profile.py
Below is a detailed description of each directory and its contents to help you navigate and understand the project's structure.
Dockerfile: Defines the Docker image configuration for the application, including base image, dependencies, and startup commands.README.MD: The main documentation file providing an overview, setup instructions, and usage guidelines for the project.alembic.ini: Configuration file for Alembic, a database migration tool used with SQLAlchemy.init.sql: SQL script for initializing the database with necessary tables and data.
Contains shell scripts that automate various tasks related to the project.
run_migration.sh: Executes database migrations using Alembic to update the database schema.run_web_server_dev.sh: Starts the web server in development mode, typically with debugging enabled.run_web_server_prod.sh: Starts the web server in production mode, optimized for performance and security.set_nginx_basic_auth.sh: Configures Nginx with Basic Authentication to secure specific endpoints.setup_mailhog_auth.sh: Sets up authentication for MailHog, an email testing tool.setup_minio.sh: Configures MinIO, an object storage server compatible with Amazon S3 APIs.
Holds configuration files for Nginx, the web server used to serve the application.
nginx.conf: The main Nginx configuration file that sets up server blocks, proxy settings, and security configurations like Basic Authentication.
Contains Dockerfiles for various services used in the project, facilitating containerization and orchestration.
mailhog/Dockerfile: Dockerfile for setting up the MailHog email testing tool.minio_mc/Dockerfile: Dockerfile for configuring MinIO client tools.nginx/Dockerfile: Dockerfile for building the custom Nginx image with necessary configurations and scripts.tests/Dockerfile: Dockerfile for setting up the testing environment, ensuring consistency across different environments.
Manage multi-container Docker applications, defining services, networks, and volumes.
docker-compose-dev.yml: Configuration for the development environment, including services, volumes, and ports tailored for development workflows.docker-compose-prod.yml: Configuration for the production environment, optimized for performance, security, and scalability.docker-compose-tests.yml: Configuration for running tests within Docker containers, ensuring isolation and consistency during testing.
The core source code of the application, organized into various modules and components for maintainability and scalability.
Handles application configurations and dependencies.
__init__.py: Initializes theconfigmodule.dependencies.py: Defines dependencies for the application, often used with FastAPI for dependency injection.settings.py: Manages application settings, possibly using environment variables for configuration.
Manages database interactions, migrations, and models.
__init__.py: Initializes thedatabasemodule.models/: Defines the database models using SQLAlchemy.__init__.py: Initializes themodelsmodule.accounts.py: Defines theAccountmodel and related database structures.base.py: Base model definitions and common configurations.movies.py: Defines theMoviemodel and related database structures.
populate.py: Script to populate the database with initial data.seed_data/: Contains CSV files used for seeding the database.test_data.csv: Additional seed data for testing purposes.
session_postgresql.py: Manages PostgreSQL database sessions.session_sqlite.py: Manages SQLite database sessions for development or testing.validators/: Contains data validation logic.__init__.py: Initializes thevalidatorsmodule.accounts.py: Validation functions and classes for account-related data.
Defines custom exception classes to handle various error scenarios within the application.
__init__.py: Initializes theexceptionsmodule.email.py: Exceptions related to email operations.security.py: Exceptions related to security and authentication.storage.py: Exceptions related to storage and file handling.
The main entry point of the application, typically initializing the FastAPI app, including middleware, routers, and other configurations.
Handles email notifications and related functionalities.
__init__.py: Initializes thenotificationsmodule.emails.py: Functions and classes for sending emails.interfaces.py: Defines interfaces or abstract classes for notification services.templates/: HTML templates used for email notifications.activation_complete.html: Template for activation completion emails.activation_request.html: Template for activation request emails.password_reset_complete.html: Template for password reset completion emails.password_reset_request.html: Template for password reset request emails.
Defines the API endpoints and their respective handlers.
__init__.py: Initializes theroutesmodule.accounts.py: Routes related to user accounts (e.g., registration, login).movies/: Routes related to movies (e.g., registration, login).__init__.py: Initializes themoviespackage.movies.py: Routes related to movie data (e.g., listing, details).movies_comments.py: Routes related to movie comments (e.g., listing, details).movies_favourites.py: Routes related to movie comments (e.g., listing, details).movies_ratings.py: Routes related to movie ratings.movies_reactions.py: Routes related to movie reactions.
profiles.py: Routes related to user profiles.
Defines the data schemas using Pydantic for request validation and response models.
__init__.py: Initializes theschemasmodule.accounts.py: Schemas for account-related operations.movies.py: Schemas for movie-related operations.profiles.py: Schemas for profile-related operations.
Manages authentication, authorization, and security-related functionalities.
__init__.py: Initializes thesecuritymodule.http.py: Handles HTTP security configurations, possibly OAuth or JWT setups.interfaces.py: Defines interfaces for security components.passwords.py: Functions for hashing and verifying passwords.token_manager.py: Manages token creation, validation, and refreshing.utils.py: Utility functions related to security.
Handles file storage, interfacing with storage services like Amazon S3.
__init__.py: Initializes thestoragesmodule.interfaces.py: Defines interfaces for storage services.s3.py: Implements storage functionalities using Amazon S3 APIs.
Contains all test cases to ensure the application's reliability and correctness.
__init__.py: Initializes thetestsmodule.conftest.py: Configuration file for pytest, defining fixtures and plugins.doubles/: Contains test doubles like fakes and stubs for mocking dependencies.__init__.py: Initializes thedoublesmodule.fakes/: Implements fake objects for testing.__init__.py: Initializes thefakesmodule.storage.py: Fake storage implementations for tests.
stubs/: Implements stubs for testing.__init__.py: Initializes thestubsmodule.emails.py: Stub implementations for email functionalities.
test_e2e/: End-to-End test cases.__init__.py: Initializes thetest_e2emodule.test_email_notification.py: Tests for email notification flows.test_storage.py: Tests for storage functionalities.
test_integration/: Integration test cases.__init__.py: Initializes thetest_integrationmodule.test_accounts.py: Integration tests for account-related operations.test_movies.py: Integration tests for movie-related operations.test_movies_comments.py: Integration tests for movie comments operations.test_movies_favourites.py: Integration tests for movie favourites operations.test_movies_ratings.py: Integration tests for movie ratings operations.test_movies_reactions.py: Integration tests for movie reactions operations.test_profiles.py: Integration tests for profile-related operations.
Contains validation logic to ensure data integrity and correctness.
__init__.py: Initializes thevalidationmodule.profile.py: Validation functions and classes for profile data.
This directory structure is thoughtfully organized to promote maintainability, scalability, and clarity. Here's a quick overview:
-
Configuration and Commands:
commands/: Automates routine tasks like migrations and server setup.configs/nginx/: Houses Nginx configuration files.
-
Docker Setup:
docker/: Contains Dockerfiles for various services ensuring consistent containerization.docker-compose-*.yml: Manages multi-container Docker applications for different environments (development, production, testing).
-
Source Code (
cinema/):- Organized into submodules like
config,database,routes,schemas,security,storages,notifications,exceptions, andvalidationto separate concerns and enhance code readability.
- Organized into submodules like
-
Testing:
cinema/tests/: Structured to support both End-to-End and Integration testing, utilizing test doubles for isolated testing scenarios.
-
Dependencies and Setup:
poetry.lock&pyproject.toml: Manage Python dependencies using Poetry.alembic.ini: Configure database migrations with Alembic.
-
Miscellaneous:
init.sql: Initial SQL setup script.README.MD: Project documentation.
services:
db:
image: 'postgres:latest'
container_name: postgres_theater
env_file:
- .env
ports:
- "5432:5432"
volumes:
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
- postgres_theater_data:/var/lib/postgresql
networks:
- theater_network
healthcheck:
test: [ "CMD-SHELL", "pg_isready -U $POSTGRES_USER -d $POSTGRES_DB -h 127.0.0.1 || exit 1" ]
interval: 10s
timeout: 5s
retries: 5
start_period: 20s
pgadmin:
image: dpage/pgadmin4
container_name: pgadmin_theater
ports:
- "3333:80"
env_file:
- .env
depends_on:
db:
condition: service_healthy
volumes:
- pgadmin_theater_data:/var/lib/pgadmin
networks:
- theater_network
web:
restart: always
build: .
container_name: backend_theater
command: [ "/bin/bash", "/commands/run_web_server_prod.sh" ]
env_file:
- .env
environment:
- LOG_LEVEL=debug
- PYTHONPATH=/usr/src/fastapi
- WATCHFILES_FORCE_POLLING=true
ports:
- "8000:8000"
depends_on:
db:
condition: service_healthy
minio:
condition: service_healthy
volumes:
- ./src:/usr/src/fastapi
networks:
- theater_network
migrator:
build: .
container_name: alembic_migrator_theater
command: ["/bin/bash", "/commands/run_migration.sh"]
depends_on:
db:
condition: service_healthy
volumes:
- ./src:/usr/src/fastapi
env_file:
- .env
environment:
- PYTHONPATH=/usr/src/fastapi
networks:
- theater_network
mailhog:
restart: always
build:
context: .
dockerfile: ./docker/mailhog/Dockerfile
container_name: mailhog_theater
command: [ "/bin/bash", "-c", "/commands/setup_mailhog_auth.sh && MailHog" ]
ports:
- "8025:8025"
- "1025:1025"
env_file:
- .env
environment:
MH_AUTH_FILE: /mailhog.auth
networks:
- theater_network
minio:
image: minio/minio:latest
container_name: minio-theater
command: server --console-address ":9001" /data
ports:
- "9000:9000"
- "9001:9001"
env_file:
- .env
volumes:
- minio_data:/data
healthcheck:
test: [ "CMD", "curl", "-f", "http://localhost:9000/minio/health/live" ]
interval: 10s
timeout: 5s
retries: 5
networks:
- theater_network
minio_mc:
build:
context: .
dockerfile: docker/minio_mc/Dockerfile
container_name: minio_mc_theater
command: ["/bin/sh", "-c", "/commands/setup_minio.sh"]
depends_on:
minio:
condition: service_healthy
env_file:
- .env
networks:
- theater_network
nginx:
build:
context: .
dockerfile: docker/nginx/Dockerfile
container_name: nginx
restart: always
ports:
- "80:80"
volumes:
- ./configs/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
depends_on:
- web
env_file:
- ./docker/nginx/.env
networks:
- theater_network
volumes:
postgres_theater_data:
driver: local
pgadmin_theater_data:
driver: local
minio_data:
driver: local
networks:
theater_network:
driver: bridgeThis docker-compose.yml defines a production-ready FastAPI movie theater stack with database, migrations, object storage, email testing, and reverse proxy, all connected through a single isolated Docker network.
-
db (PostgreSQL) Primary relational database for the application.
- Initialized using
init.sql - Persistent storage via Docker volume
- Healthcheck ensures dependent services start only when DB is ready
- Initialized using
-
pgadmin Web-based PostgreSQL administration tool.
- Accessible on
http://localhost:3333 - Starts only after the database becomes healthy
- Accessible on
-
web (FastAPI backend) Main application container.
- Built from the project Dockerfile
- Runs the production startup script
- Mounts source code for flexibility
- Depends on PostgreSQL and MinIO healthchecks
-
migrator (Alembic) Dedicated container for database migrations.
- Runs Alembic migrations automatically
- Ensures schema is up to date before app usage
- Depends on healthy PostgreSQL service
-
mailhog Local SMTP testing service for email flows.
- Web UI on
http://localhost:8025 - SMTP server on port
1025 - Includes basic authentication setup
- Web UI on
-
minio S3-compatible object storage (used for media/files).
- API on port
9000 - Admin console on port
9001 - Persistent data stored in a Docker volume
- Healthcheck ensures readiness before setup
- API on port
-
minio_mc MinIO client container.
- Automatically configures buckets and policies
- Runs only after MinIO is healthy
-
nginx Reverse proxy for the FastAPI backend.
- Exposes application on port
80 - Uses a custom Nginx configuration
- Routes traffic to the
webservice
- Exposes application on port
postgres_theater_data– PostgreSQL persistent storagepgadmin_theater_data– pgAdmin configuration and stateminio_data– MinIO object storage data
- theater_network Custom bridge network that allows all services to communicate internally while keeping them isolated from other Docker stacks.
The Nginx Dockerfile customizes the Nginx image to include necessary packages and scripts for setting up Basic Authentication and other configurations.
# Use the official Nginx image as the base
FROM nginx:latest
# Install the necessary packages
RUN apt-get update && \
apt-get install -y --no-install-recommends \
apache2-utils \
dos2unix \
bash && \
rm -rf /var/lib/apt/lists/*
# Copy command scripts into the container
COPY ./commands/set_nginx_basic_auth.sh /commands/set_nginx_basic_auth.sh
# Ensure Unix-style line endings for scripts
RUN dos2unix /commands/*.sh
# Make the scripts executable
RUN chmod +x /commands/*.sh
# Set the entry point to the Basic Auth setup script
ENTRYPOINT ["/commands/set_nginx_basic_auth.sh"]
# Run Nginx in the foreground to keep the container running
CMD ["nginx", "-g", "daemon off;"]- Key Steps:
-
Base Image:
FROM nginx:latest: Starts with the latest official Nginx image.
-
Install Necessary Packages:
apache2-utils: Provides utilities likehtpasswdfor managing Basic Authentication.dos2unix: Converts DOS-style line endings to Unix-style, ensuring scripts run correctly.bash: Provides the Bash shell for executing scripts.
-
Copy Command Scripts:
COPY ./commands/set_nginx_basic_auth.sh /commands/set_nginx_basic_auth.sh: Adds theset_nginx_basic_auth.shscript to the container.
-
Convert Line Endings:
RUN dos2unix /commands/*.sh: Ensures all shell scripts have Unix-style line endings.
-
Make Scripts Executable:
RUN chmod +x /commands/*.sh: Grants execute permissions to the scripts.
-
Set Entry Point:
ENTRYPOINT ["/commands/set_nginx_basic_auth.sh"]: Defines the script to run when the container starts, setting up Basic Authentication.
-
Run Nginx in Foreground:
CMD ["nginx", "-g", "daemon off;"]: Starts Nginx in the foreground, ensuring the container remains active.
-
The Nginx configuration file sets up the server to handle HTTP requests, proxy them to the backend service, and secure specific endpoints with Basic Authentication.
events {}
http {
upstream backend_theater {
server web:8000;
}
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
location / {
proxy_pass http://web:8000;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /docs {
include /etc/nginx/conf.d/auth.conf;
proxy_pass http://web:8000/docs;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /redoc {
include /etc/nginx/conf.d/auth.conf;
proxy_pass http://web:8000/redoc;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
location /openapi.json {
include /etc/nginx/conf.d/auth.conf;
proxy_pass http://web:8000/openapi.json;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
}- Key Configurations:
-
Upstream Definition:
upstream backend_theater { server web:8000; }: Defines a group of backend servers. In this case, it points to thewebservice on port8000.
-
Server Block:
- Listening Ports:
listen 80 default_server;: Listens on port80for IPv4.listen [::]:80 default_server;: Listens on port80for IPv6.
- Server Name:
server_name _;: Catches all server names not explicitly defined elsewhere.
- Listening Ports:
-
Location Blocks:
-
Root Location (
/):- Purpose:
Proxies all incoming traffic to thewebservice. - Configurations:
proxy_pass http://web:8000;: Forwards requests to the backend.- Header Settings:
Ensures that the original request headers are preserved and forwarded to the backend.
- Purpose:
-
Secure Locations (
/docs,/redoc,/openapi.json):- Purpose:
Protects API documentation and specification endpoints with Basic Authentication. - Configurations:
include /etc/nginx/conf.d/auth.conf;: Includes authentication configurations.proxy_passand Header Settings:
Similar to the root location, proxies requests to the respective backend endpoints while preserving headers.
- Purpose:
-
-
The docker-compose-prod.yml orchestrates a multi-container Docker application with the following key components:
-
Database Layer:
- PostgreSQL (
db): Stores application data. - PgAdmin (
pgadmin): Provides a GUI for managing the PostgreSQL database.
- PostgreSQL (
-
Application Layer:
- Web Server (
web): Hosts the backend application, built from the project's source code. - Migrator (
migrator): Manages database schema migrations to keep the database in sync with the application.
- Web Server (
-
Auxiliary Services:
- MailHog (
mailhog): Captures outgoing emails for testing purposes without sending them to real recipients. - MinIO (
minio): Offers object storage capabilities, serving as an alternative to AWS S3. - MinIO Client (
minio_mc): Provides command-line tools to interact with the MinIO server.
- MailHog (
-
Reverse Proxy and Security:
- Nginx (
nginx): Acts as a reverse proxy, directing traffic to the appropriate backend services and securing specific endpoints with Basic Authentication.
- Nginx (
-
Data Persistence:
- Volumes: Ensure data persistence for PostgreSQL, PgAdmin, and MinIO across container restarts.
-
Networking:
- theater_network: A dedicated Docker network that isolates and facilitates communication between all services within the application stack.
GitHub Actions automates your software workflows, enabling continuous integration (CI) and continuous deployment (CD). This project utilizes GitHub Actions to ensure code quality through automated testing and to deploy the application seamlessly to AWS EC2.
The Continuous Integration (CI) pipeline is designed to automatically test your code whenever a pull request is made to the main branch. This ensures that new changes do not break existing functionality and adhere to code quality standards.
The CI pipeline performs the following tasks:
- Checkout Code: Retrieves the latest code from the repository.
- Set Up Python: Configures the Python environment.
- Install Dependencies: Installs project dependencies using Poetry.
- Run flake8: Checks the code for style and syntax errors.
- Run Tests: Executes integration tests for different components of the application.
name: CI
on:
push:
branches: ["**"]
pull_request:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
lint:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: "3.13"
- name: Install flake8
run: |
python -m pip install --upgrade pip
pip install flake8
- name: Run flake8
run: |
flake8 .
tests:
runs-on: ubuntu-latest
timeout-minutes: 30
needs: lint
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3
- name: Run tests via docker compose
run: |
docker compose -f docker-compose-tests.yml down -v --remove-orphans || true
docker compose -f docker-compose-tests.yml up --build --abort-on-container-exit --exit-code-from web
- name: Cleanup
if: always()
run: |
docker compose -f docker-compose-tests.yml down -v --remove-orphans || true
docker system prune -af || true
This GitHub Actions workflow runs automatic code quality checks and end-to-end/integration tests for the project on every push and pull request. It helps catch style issues early and ensures the application works correctly inside Docker (same way you run it locally).
The workflow runs when:
- Any push happens to any branch
- Any pull request is opened/updated
on:
push:
branches: ["**"]
pull_request:To avoid wasting CI minutes when multiple commits are pushed quickly, the workflow cancels older runs for the same branch/workflow:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true- Groups runs by workflow name + branch reference
- Cancels in-progress runs if a newer one starts
This workflow has two jobs:
- lint — runs
flake8(fast code style / lint checks) - tests — runs test suite using Docker Compose (only after lint passes)
Runs on ubuntu-latest with a 10 minute timeout:
runs-on: ubuntu-latest
timeout-minutes: 10Downloads the repo to the runner:
- uses: actions/checkout@v4Uses Python 3.13 for linting to match your project version:
- uses: actions/setup-python@v5
with:
python-version: "3.13"Upgrades pip and installs flake8:
python -m pip install --upgrade pip
pip install flake8Checks the whole project:
flake8 .If flake8 fails, CI stops and the tests job will not run.
Runs only if lint succeeds:
needs: lintRuns on ubuntu-latest with a 30 minute timeout:
timeout-minutes: 30Downloads the repo again for the test job:
- uses: actions/checkout@v4Enables Docker Buildx for building images efficiently:
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v3This job executes your test environment defined in docker-compose-tests.yml.
docker compose -f docker-compose-tests.yml down -v --remove-orphans || true
docker compose -f docker-compose-tests.yml up --build --abort-on-container-exit --exit-code-from webWhat this does:
- Cleans up any leftover containers/volumes first (safe because of
|| true) - Builds images (
--build) - Starts all services needed for tests (DB, Mailhog, MinIO, web, etc.)
- Stops everything when the test container finishes (
--abort-on-container-exit) - Uses the
webcontainer exit code as the final CI result (--exit-code-from web)
So CI passes/fails exactly based on the test run inside the web container.
Even if tests fail, the workflow ensures containers/volumes are removed and Docker cache is cleaned:
- name: Cleanup
if: always()
run: |
docker compose -f docker-compose-tests.yml down -v --remove-orphans || true
docker system prune -af || trueThis keeps GitHub runners from running out of disk space and prevents cross-run interference.
This CI workflow enforces two quality gates:
- Code style must pass (
flake8) - Tests must pass in Docker Compose environment
That gives you confidence that every PR and every push stays clean and deployable.
-
Create Workflow Directory:
Ensure that the
.github/workflows/directory exists in the root of your repository. If not, create it:mkdir -p .github/workflows
-
Add the CI Workflow File:
Create a file named
ci.ymlinside.github/workflows/and paste the CI pipeline YAML configuration provided above.touch .github/workflows/ci.yml
-
Commit and Push:
Commit the new workflow file and push it to the
mainbranch.git add .github/workflows/ci-pipeline.yml git commit -m "Add CI Pipeline for automated testing" git push origin main -
Triggering the CI Pipeline:
The CI pipeline will automatically run whenever a pull request is opened or updated against the
mainbranch. You can monitor the progress and results in the Actions tab of your GitHub repository.
The Continuous Deployment (CD) pipeline automates the deployment of your application to AWS EC2 instances after successful tests. This ensures that your latest changes are reflected in the production environment without manual intervention.
The CD pipeline performs the following tasks:
- Checkout Code: Retrieves the latest code from the repository.
- Set Up SSH Agent: Configures SSH for secure access to the EC2 instance.
- Add EC2 Host to known_hosts: Adds the EC2 host to known SSH hosts to prevent authenticity prompts.
- Execute Deployment Script on EC2: Connects to the EC2 instance and runs the
deploy.shscript to update the application.
This GitHub Actions workflow automates deployment of the FastAPI Movie Theater application to an AWS EC2 instance whenever changes are merged into the main branch.
name: Deploy to AWS EC2
on:
push:
branches: [main]
pull_request:
types: [closed]
branches: [main]
concurrency:
group: deploy-${{ github.ref }}
cancel-in-progress: true
jobs:
deploy:
if: github.event_name == 'push' || github.event.pull_request.merged == true
runs-on: ubuntu-latest
timeout-minutes: 30
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Add EC2 SSH key
uses: webfactory/ssh-agent@v0.9.0
with:
ssh-private-key: ${{ secrets.EC2_SSH_KEY }}
- name: Add EC2 host to known_hosts
run: |
mkdir -p ~/.ssh
ssh-keyscan -H "${{ secrets.EC2_HOST }}" >> ~/.ssh/known_hosts
- name: Deploy (run deploy script on EC2)
run: |
ssh "${{ secrets.EC2_USER }}@${{ secrets.EC2_HOST }}" "bash /home/ubuntu/src/FastAPI_movie_theater/commands/deploy.sh"The workflow runs when:
- A push is made directly to the
mainbranch - A pull request is merged into the
mainbranch
on:
push:
branches: [main]
pull_request:
types: [closed]
branches: [main]An additional condition ensures deployment only happens if:
- The event is a push, or
- The pull request was successfully merged
if: github.event_name == 'push' || github.event.pull_request.merged == trueTo prevent multiple deployments from running at the same time, the workflow uses concurrency locking:
concurrency:
group: deploy-${{ github.ref }}
cancel-in-progress: true- Only one deployment per branch can run at a time
- Any in-progress deployment is canceled if a new one starts
The workflow runs a single job named deploy on the latest Ubuntu runner:
runs-on: ubuntu-latest
timeout-minutes: 30Fetches the latest version of the repository so the workflow has access to the code and configuration files.
- name: Checkout
uses: actions/checkout@v4Loads the EC2 private SSH key from GitHub Secrets using an SSH agent, enabling secure remote access.
- name: Add EC2 SSH key
uses: webfactory/ssh-agent@v0.9.0
with:
ssh-private-key: ${{ secrets.EC2_SSH_KEY }}Required secret:
EC2_SSH_KEY– private SSH key for the EC2 instance
Adds the EC2 server to known_hosts to avoid SSH authenticity prompts during deployment.
- name: Add EC2 host to known_hosts
run: |
mkdir -p ~/.ssh
ssh-keyscan -H "${{ secrets.EC2_HOST }}" >> ~/.ssh/known_hostsRequired secret:
EC2_HOST– public IP or domain of the EC2 instance
Connects to the EC2 instance via SSH and executes the deployment script located on the server.
- name: Deploy (run deploy script on EC2)
run: |
ssh "${{ secrets.EC2_USER }}@${{ secrets.EC2_HOST }}" \
"bash /home/ubuntu/src/FastAPI_movie_theater/commands/deploy.sh"Required secrets:
EC2_USER– SSH username (e.g.ubuntu)EC2_HOST– EC2 public IP or hostname
The deploy.sh script typically:
- Pulls the latest code from GitHub
- Resets the local repository to
origin/main - Builds Docker images
- Starts or restarts services using
docker compose - Runs Alembic migrations (if configured)
To set up the CD pipeline, follow these steps:
-
Create Workflow Directory:
Ensure that the
.github/workflows/directory exists in the root of your repository. If not, create it:mkdir -p .github/workflows
-
Add the CD Workflow File:
Create a file named
cd-pipeline.ymlinside.github/workflows/and paste the CD pipeline YAML configuration provided above.touch .github/workflows/cd-pipeline.yml
-
Configure GitHub Secrets:
For the CD pipeline to securely access your EC2 instance, you need to add the following secrets to your GitHub repository:
EC2_SSH_KEY: Your private SSH key for accessing the EC2 instance.EC2_HOST: The public DNS or IP address of your EC2 instance.EC2_USER: The SSH username for your EC2 instance (e.g.,ubuntu).
How to Add Secrets:
- Navigate to your GitHub repository.
- Click on Settings > Secrets and variables > Actions.
- Click on New repository secret.
- Add each secret with its respective name and value.
-
Commit and Push:
Commit the new workflow file and push it to the
mainbranch.git add .github/workflows/cd-pipeline.yml git commit -m "Add CD Pipeline for automated deployment" git push origin main -
Triggering the CD Pipeline:
The CD pipeline will automatically run under the following conditions:
- Push to
mainbranch: When changes are pushed directly to themainbranch. - Merged Pull Request: When a pull request is merged into the
mainbranch.
Additionally, you can manually trigger the deployment:
- Navigate to the Actions tab in your GitHub repository.
- Select the Deploy to AWS EC2 workflow.
- Click on the Run workflow button.
- Push to