Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .env
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
DATABASE_URL="postgres://root:password@localhost:5001/test?sslmode=disable"
105 changes: 20 additions & 85 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## test with act
# act push \
# -W .github/workflows/ci.yaml \
# --secret-file .env \
# -P ubuntu-22.04=catthehacker/ubuntu:act-22.04

name: CI/CD for sam-rust

on:
Expand All @@ -11,97 +17,26 @@ env:
CARGO_TERM_COLOR: always

jobs:
build-and-test:
deploy:
runs-on: ubuntu-22.04

services:
postgres:
image: postgres:14
env:
POSTGRES_USER: root
POSTGRES_PASSWORD: password
POSTGRES_DB: test
ports:
- 5432:5432
options: >-
--health-cmd "pg_isready -U root"
--health-interval 10s
--health-timeout 5s
--health-retries 5

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

- name: Set up Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
target: x86_64-unknown-linux-musl
profile: minimal
components: rustfmt, clippy
uses: actions/checkout@v3

- name: 📦 Cache Cargo registry and target
uses: actions/cache@v3
with:
path: |
~/.cargo/registry
~/.cargo/git
target
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}

- name: Install system dependencies
- name: Set up Docker-in-Docker permissions
run: |
sudo apt-get update
sudo apt-get install -y libssl-dev pkg-config zip make curl unzip python3-pip

- name: 🧹 Format and Lint Code (make pretty)
run: make pretty

- name: 🔬 Run Integration Tests
run: make test
env:
DATABASE_URL: postgres://root:password@localhost:5432/test

- name: Install AWS SAM CLI
run: pip3 install aws-sam-cli --upgrade
sudo chown -R $USER:$USER /var/run/docker.sock || true

- name: Validate SAM Template
run: sam validate --lint
- name: Make sure Docker is working
run: docker info

- name: Cache Cargo Binaries
id: cache-cargo-bin
uses: actions/cache@v3
with:
path: ~/.cargo/bin
key: ${{ runner.os }}-cargo-bin-cargo-lambda
- name: Build Rust Lambda with SAM (Docker-based)
run: make aws-build-sam

- name: Install cargo-lambda
if: steps.cache-cargo-bin.outputs.cache-hit != 'true'
run: cargo install cargo-lambda --locked

- name: Cache Zig
id: cache-zig
uses: actions/cache@v3
with:
path: ~/.zig
key: ${{ runner.os }}-zig-0.10.1

- name: Install Zig
if: steps.cache-zig.outputs.cache-hit != 'true'
run: |
ZIG_VERSION=0.10.1
mkdir -p ~/.zig/bin
wget https://ziglang.org/download/$ZIG_VERSION/zig-linux-x86_64-$ZIG_VERSION.tar.xz
tar -xf zig-linux-x86_64-$ZIG_VERSION.tar.xz
mv zig-linux-x86_64-$ZIG_VERSION/* ~/.zig/bin
echo "$HOME/.zig/bin" >> $GITHUB_PATH

- name: Add Zig to PATH
run: echo "$HOME/.zig/bin" >> $GITHUB_PATH

- name: 🛠️ Build Lambda with SAM
shell: bash
run: |
export PATH="$HOME/.cargo/bin:$HOME/.zig/bin:$PATH"
make sam-build
# - name: Deploy SAM application to AWS
# env:
# AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
# AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
# AWS_REGION: us-east-1
# run: make aws-deploy-sam
10 changes: 9 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,14 @@ $RECYCLE.BIN/

# End of https://www.toptal.com/developers/gitignore/api/rust,osx,linux,windows,pycharm,visualstudiocode

aws/.aws-sam/*
.aws-sam/*
libpq_layer.zip
postgresql-*.
postgresql-*.

aws/postgresql-10.23.tar.gz


/libpq_layer.zip
/postgresql-10.23/*
/postgresql-10.23.tar.gz
26 changes: 26 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Cargo.toml

[workspace]
resolver = "2"
members = [
"backend",
]

[workspace.dependencies]

tokio = { version = "1", features = ["macros"] }
tracing = { version = "0.1", features = ["log"] }
tracing-subscriber = { version = "0.3", default-features = false, features = ["fmt"] }

diesel = { version = "2.2.7", features = ["postgres", "r2d2"] }
serde = { version = "1", features = ["derive"] }
serde_json = "1"

warp = "0.3"
warp_lambda = "0.1.4"

openssl = { version = "0.10", features = ["vendored"] }
r2d2 = "0.8"
once_cell = "1.21.3"
lambda_http = { version = "0.13.0" }
lambda_runtime = { version = "0.13.0" }
112 changes: 32 additions & 80 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,98 +1,50 @@
# Makefile for building and deploying the Rust-based AWS SAM application
# Includes support for building a custom libpq layer for Diesel/PostgreSQL
# Makefile

BACKEND_STACK_NAME ?= warp-lambda-starter-stack

# Build the LibpqLayer using the SAM `makefile` build method.
# This is invoked by SAM when building the LibpqLayer defined in template.yaml.
# It copies prebuilt libpq shared objects and headers from our local `libpq_layer/` folder
# into the correct layer output directory (defined by $ARTIFACTS_DIR),
# where they will be placed in `/opt` at runtime in the Lambda environment.
build-LibpqLayer:
mkdir -p "$(ARTIFACTS_DIR)/lib"
mkdir -p "$(ARTIFACTS_DIR)/include/libpq"
cp -r libpq_layer/lib/* "$(ARTIFACTS_DIR)/lib/"
cp -r libpq_layer/include/libpq/* "$(ARTIFACTS_DIR)/include/libpq/"

# Build the full SAM application, including all Lambda functions and layers.
# This sets the required environment variables so `pq-sys` can link against the local libpq shared library.
# The paths are resolved to absolute paths to ensure the build works regardless of working directory.
sam-build:
@echo "🔧 Building with libpq from project-local layer..."

@test -f libpq_layer/lib/libpq.so || { echo "❌ libpq.so not found. Run 'make sh-libpq'"; exit 1; }

PQ_LIB_DIR=$(realpath libpq_layer/lib) \
PQ_INCLUDE_DIR=$(realpath libpq_layer/include/libpq) \
RUSTFLAGS="-C link-args=-Wl,-rpath=/opt/lib" \
RUST_LOG=debug \
sam build --beta-features

# Build the SAM project and run it locally via the SAM CLI.
# This allows you to test the Lambda functions using Docker on your machine.
sam-run:
@echo "Building SAM application..."
make sam-build
sam local start-api --docker-network sam-local --debug --env-vars env.json

# Build the libpq shared libraries and headers inside an Amazon Linux 2 container.
# This mimics the Lambda environment and ensures binary compatibility.
# Output is placed in `libpq_layer/lib` and `libpq_layer/include/libpq`.
sh-libpq:
sh ./build_libpq_layer_docker.sh

# Validate, build, and deploy the full SAM stack to AWS.
# This will upload the Lambda functions and layers, create/update resources,
# and deploy the API Gateway with no manual confirmations.
deploy-sam:
@echo "Validating SAM template..."
sam validate
# =======================================
# Root Makefile: Orchestrator
# =======================================

@echo "Building SAM project..."
make sam-build || { echo "Build failed"; exit 1; }
### Include submodule Makefiles
include backend/Makefile
include aws/Makefile

@echo "Deploying SAM stack: $(BACKEND_STACK_NAME)..."
sam deploy --stack-name $(BACKEND_STACK_NAME) \
--force-upload \
--no-confirm-changeset --no-fail-on-empty-changeset \
--capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM \
--resolve-s3 \
--debug || { echo "Deployment failed."; exit 1; }
# =======================================
# Directory-specific Commands
# =======================================

@echo "Deployment completed successfully for stack: $(BACKEND_STACK_NAME)."
AWS_MAKE = $(MAKE) -C aws
BACKEND_MAKE = $(MAKE) -C backend

# Delete the deployed SAM stack from AWS without prompting.
delete-sam:
sam delete --no-prompts --stack-name $(BACKEND_STACK_NAME)
# AWS commands delegation
aws-%:
@echo "Delegating to aws/$*..."
$(AWS_MAKE) $*

# # Backend commands delegation
be-%:
@echo "Delegating to backend/$*..."
$(BACKEND_MAKE) $*

# =======================================
# Utility Commands
# =======================================

# Format all code
# Format all code (including fixing simple style issues)
format:
@echo "Formatting all code..."
(cd rust_app && cargo fmt --all)
@echo "Formatting all code with cargo fmt..."
cargo fmt --all

# Lint all code
# Lint all code with clippy and suggest fixes where possible
lint:
@echo "Linting all code..."
(cd rust_app && cargo clippy --tests --all-features -- -D warnings)
@echo "Linting all code with cargo clippy..."
cargo clippy --workspace --all-targets --all-features --fix --allow-dirty --allow-staged -- -D warnings

# Format and lint code
# Format and lint
pretty: format lint

# =======================================
# Project Orchestration
# =======================================

test:
(cd rust_app && DATABASE_URL=$${DATABASE_URL:-postgres://root:password@localhost:5001/test} cargo test --all --all-features -- --nocapture)


help:
@echo "Available commands:"
@echo " make format - Run rustfmt on all code"
@echo " make lint - Run clippy and fail on warnings"
@echo " make test - Run unit and integration tests"
@echo " make sam-build - Build Lambda function using SAM"
@echo " make sam-run - Run API locally via SAM"
@echo " make deploy-sam - Deploy to AWS"
run-backend:
cargo watch -p backend -w backend/src -s 'make be-run'
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -199,3 +199,8 @@ Special thanks to the maintainers of:
- [`aws-lambda-rust-runtime`](https://github.com/awslabs/aws-lambda-rust-runtime)

---



cargo install cross --git https://github.com/cross-rs/cross --branch main --force
export CROSS_CONTAINER_ENGINE=docker
84 changes: 84 additions & 0 deletions aws/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# aws/Makefile
# ------------------------------------------------------------------------------
# Makefile for building and deploying the Rust-based AWS SAM application.
# This file provides targets for:
# - Building the Lambda binary using Docker with static linking
# - Compiling and packaging the libpq Lambda layer for Diesel
# - Running the Lambda API locally via SAM CLI
# - Deploying and deleting the AWS SAM stack
# ------------------------------------------------------------------------------

# Include Makefile from the docker/ subdirectory relative to this file
include $(dir $(lastword $(MAKEFILE_LIST)))docker/Makefile
DOCKER_MAKE = $(MAKE) -C docker

# Forward docker-* targets to the docker/Makefile
docker-%:
@echo "Delegating to docker/$*..."
$(DOCKER_MAKE) $*

# =======================================
# AWS SAM Build and Run Targets
# =======================================

# Build the Lambda binary using a Docker image
build-sam:
@echo "Building SAM project..."
rm -rf .aws-sam
mkdir -p .aws-sam
make docker-build-sam

# Run the Lambda locally using SAM CLI and the provided env.json config
run-sam:
@echo "Starting Local SAM application from aws/..."
sam local start-api \
--template .aws-sam/template.yaml \
--env-vars env.json \
--docker-network sam-local \
--debug \
--port 4040

# Validate the SAM template syntax and structure
validate-sam:
@echo "Validating SAM template..."
sam validate

# =======================================
# Deploy & Cleanup Targets
# =======================================

# Name of the AWS CloudFormation stack to create/update
BACKEND_STACK_NAME ?= warp-lambda-starter-stack

# Validate, build, and deploy the Lambda stack to AWS
deploy-sam: validate-sam build-sam
@echo "Deploying SAM stack: $(BACKEND_STACK_NAME)..."
sam deploy --stack-name $(BACKEND_STACK_NAME) \
--force-upload \
--no-confirm-changeset --no-fail-on-empty-changeset \
--capabilities CAPABILITY_IAM CAPABILITY_NAMED_IAM \
--resolve-s3 \
--debug || { echo "Deployment failed."; exit 1; }

@echo "Deployment completed successfully for stack: $(BACKEND_STACK_NAME)."

# Delete the deployed AWS stack without any confirmation prompts
delete-sam:
sam delete --no-prompts --stack-name $(BACKEND_STACK_NAME)

# =======================================
# Layer & Build Method Targets
# =======================================

# Copy the compiled backend binary into the Lambda-compatible bootstrap file
# Used by SAM's makefile-based build method
build-BackendFunction:
cp /app/target/x86_64-unknown-linux-musl/release/backend ${ARTIFACTS_DIR}/bootstrap

# Copy libpq static libraries and headers into the appropriate layer directories
# Used by SAM to package and attach the PostgreSQL C client libraries as a Lambda Layer
build-LibpqLayer:
mkdir -p "$(ARTIFACTS_DIR)/lib"
mkdir -p "$(ARTIFACTS_DIR)/include/libpq"
cp libpq_layer/lib/libpq.a "$(ARTIFACTS_DIR)/lib/"
cp -r libpq_layer/include/libpq/* "$(ARTIFACTS_DIR)/include/libpq/"
Loading