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
133 changes: 90 additions & 43 deletions .dockerignore
Original file line number Diff line number Diff line change
@@ -1,61 +1,108 @@
# Git
# Include any files or directories that you don't want to be copied to your
# container here (e.g., local build artifacts, temporary files, etc.).
#
# For more help, visit the .dockerignore file reference guide at
# https://docs.docker.com/go/build-context-dockerignore/

**/.DS_Store
**/__pycache__
**/.venv
**/.classpath
**/.dockerignore
**/.env
**/.git
**/.gitignore
**/.project
**/.settings
**/.toolstarget
**/.vs
**/.vscode
**/*.*proj.user
**/*.dbmdl
**/*.jfm
**/bin
**/charts
**/docker-compose*
**/compose.y*ml
**/Dockerfile*
**/node_modules
**/npm-debug.log
**/obj
**/secrets.dev.yaml
**/values.dev.yaml
LICENSE
README.md
.git
.gitignore

# Python
__pycache__
*.pyc
*.pyo
*.pyd
.Python
env
venv
pip-log.txt
pip-delete-this-directory.txt
.tox
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
.venv/
__pycache__/
*.log
.python-version
data/


# Python-generated files
__pycache__/
.mypy_cache
.pytest_cache
.hypothesis
.ruff_cache
*.py[cod]
*$py.class
*.so
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
*.egg-info/
.installed.cfg
*.egg

# Environments
# Environment variables
.env
.venv
env/
venv/
env/
ENV/

# IDEs
.vscode
.idea
# IDEs and editors
.idea/
.vscode/
*.swp
*.swo
*~

# OS generated files
# Operating System Files
.DS_Store
.DS_Store?
._*
.Spotlight-V100
.Trashes
ehthumbs.db
Thumbs.db

# Project specific
tests/
*.md
LICENSE
.github/
docker-compose.yml

# DuckDB
*.duckdb

# Jupyter Notebook
.ipynb_checkpoints

# pytest
.pytest_cache/

# Coverage reports
htmlcov/
.coverage
.coverage.*
.cache

# Logs
*.log

# uv specific
.uv/
.uv/

# DuckDB
*.duckdb

# Scratch Pad
scratch.py
34 changes: 34 additions & 0 deletions .github/workflows/ci_cd.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
name: CI/CD Pipeline

on:
pull_request:
branches:
- main

jobs:
ci:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Build and Check (using multi-stage Dockerfile)
run: make docker-check-ci

cd:
needs: ci
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Build prod image
run: make docker-build-prod

- name: Login to Docker Hub
uses: docker/login-action@v3
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Push prod image
run: make docker-push
7 changes: 6 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Python-generated files
__pycache__/
.mypy_cache
.ruff_cache
*.py[cod]
*$py.class
*.so
Expand Down Expand Up @@ -57,4 +59,7 @@ htmlcov/
.uv/

# DuckDB
*.duckdb
*.duckdb

# Scratch Pad
scratch.py
6 changes: 6 additions & 0 deletions .streamlit/config.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
[theme]
base="dark"
backgroundColor="#F5F5F5"
secondaryBackgroundColor="#F0F2F6"
textColor="#333333"
font="sans serif"
67 changes: 67 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# ------------------------------- Builder Satge -------------------------------

# Build stage (with dev tools for linting, testing, etc.)
FROM python:3.13-slim-bookworm AS builder

# The installer requires curl (and certificates) to download the release archive
RUN apt-get update && apt-get install -y --no-install-recommends curl ca-certificates && \
apt-get clean && rm -rf /var/lib/apt/lists/*

# Download the latest installer, install it, and remove it
ADD https://astral.sh/uv/install.sh /uv-installer.sh
RUN chmod -R 655 /uv-installer.sh && /uv-installer.sh && rm /uv-installer.sh

# Set up uv environment PATH
ENV PATH="/root/.local/bin/:$PATH"

WORKDIR /app

# Prevent Python from writing pyc files and buffering output
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1

# Copy dependency files first for caching
COPY pyproject.toml /app/

# Sync dependencies (includes streamlit, ruff, pytest if in pyproject.toml)
RUN uv sync

# Copy the rest of the project
COPY . /app

# ------------------------- Production Stage -------------------------

# Final stage (production, lean image)
FROM python:3.13-slim-bookworm AS production
WORKDIR /app

# Prevent Python from writing pyc files and buffering output
ENV PYTHONDONTWRITEBYTECODE=1 \
PYTHONUNBUFFERED=1

# Create a non-privileged user
ARG UID=10001
RUN adduser \
--disabled-password \
--gecos "" \
--home "/nonexistent" \
--shell "/sbin/nologin" \
--no-create-home \
--uid "${UID}" \
appuser

# Copy the virtual env and project files
COPY --from=builder /app/.venv /app/.venv
COPY . /app

# Set PATH to use the virtual env
ENV PATH="/app/.venv/bin:$PATH"

# Switch to non-privileged user
USER appuser

# Expose Streamlit's default port
EXPOSE 8501

# Run Streamlit app
CMD ["streamlit", "run", "src/app.py"]
53 changes: 53 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
.PHONY: all deps check test docker-build-dev docker-check docker-test docker-check-ci docker-build-prod docker-run docker-push docker-clean

# Dependency management
deps:
uv sync

# Linting and formatting
check:
- uv run ruff check . --fix
- uv run ruff format .

# Testing
test:
uv run pytest -v

# Dev: Build the builder stage image
docker-build-dev:
docker build --target builder -t stockdataview:dev .

# Dev: Run linting in container
docker-check: docker-build-dev
docker run --rm -v $(PWD):/app stockdataview:dev uv run ruff check .

# Dev: Run tests in container
docker-test: docker-build-dev
docker run --rm stockdataview:dev uv run pytest -v

# CI specific target: Build up to the builder stage and run checks/tests
docker-check-ci: docker-build-dev
- docker run --rm -v $(PWD):/app stockdataview:dev uv run ruff check .
- docker run --rm -v $(PWD):/app stockdataview:dev uv run pytest -v


# Prod: Build the production stage image
docker-build-prod:
docker build --target production -t skytics/stockdataview:latest .

# Prod: Run the app locally
docker-run: docker-build-prod
docker run --rm -e FMP_API_KEY=${FMP_API_KEY} -p 8501:8501 skytics/stockdataview:latest

# Prod: Push to Docker Hub
docker-push: docker-build-prod
docker push skytics/stockdataview:latest

# Clean up images
docker-clean:
- docker image rm stockdataview:dev || true
- docker image rm skytics/stockdataview:latest || true

# All-in-one for local dev
all: check test docker-build-dev
@echo "All checks passed!"
Loading