Skip to content

Commit

Permalink
The final build of the docker image in CI
Browse files Browse the repository at this point in the history
  • Loading branch information
antonko committed Mar 29, 2024
1 parent ee9f951 commit 2ddade6
Show file tree
Hide file tree
Showing 8 changed files with 109 additions and 20 deletions.
4 changes: 3 additions & 1 deletion .dockerignore
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
src/.pdm-python
**/.pdm-python
**/node_modules
**/.venv
23 changes: 16 additions & 7 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -76,16 +76,25 @@ jobs:
working-directory: ./frontend


backend-build:
needs: backend-check
docker-build:
needs: [frontend-build, backend-check]
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v4

- name: Setup PDM
uses: pdm-project/setup-pdm@v4
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1

- name: Install dependencies
working-directory: ./backend
run: pdm install
# - name: Login to DockerHub
# uses: docker/login-action@v1
# with:
# username: ${{ secrets.DOCKER_USERNAME }}
# password: ${{ secrets.DOCKER_PASSWORD }}

- name: Build and push Docker image
uses: docker/build-push-action@v2
with:
context: .
file: ./Dockerfile
push: false
36 changes: 36 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
FROM node:20 as frontend-builder

RUN corepack enable && yarn set version berry

WORKDIR /app/frontend

COPY ./frontend/package.json ./frontend/yarn.lock ./
COPY ./frontend/.yarn ./.yarn
COPY ./frontend/.yarnrc.yml ./

RUN yarn install

COPY ./frontend .

ENV NODE_ENV=production

RUN yarn build --mode production

FROM python:3.11-slim as backend-builder

RUN pip install pdm

WORKDIR /app

COPY ./backend/pyproject.toml ./backend/pdm.lock ./

RUN pdm install --prod

COPY ./backend/src ./src
COPY --from=frontend-builder /app/frontend/dist ./src/static

WORKDIR /app/src

EXPOSE 8000

ENTRYPOINT ["pdm", "run", "uvicorn", "main:app", "--host=0.0.0.0", "--port=8000"]
15 changes: 10 additions & 5 deletions backend/src/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,19 +6,24 @@

class Settings(BaseSettings):
"""Configuration settings for the application."""

debug: bool = False

root_path: str = "/arq"
docs_url: str = "api/docs"
openapi_url: str = "api/openapi.json"
redoc_url: str = "api/redoc"
api_prefix: str = "api"

environment: str = "production"
docs_url: str = "/arq/api/docs"
openapi_url: str = "/arq/api/openapi.json"
redoc_url: str = "/arq/api/redoc"

title: str = "Arq UI API"
version: str = "0.1.0"
summary: str = "Interface for Arq background jobs."
description: str = ""

cors_allowed_hosts: list[str] | None = ["http://localhost:5173"]
api_prefix: str = "/arq/api"


timezone: str = "UTC"

Expand Down
24 changes: 24 additions & 0 deletions backend/src/core/helpers.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
def join_paths_safely(base_path: str, relative_path: str) -> str:
"""
Joins a base path and a relative path safely, ensuring only one slash between them.
This function ensures that there's exactly one slash between the base and relative paths,
regardless of whether the base path ends with a slash or the relative path starts with one.
Parameters:
base_path (str): The base path to be joined.
relative_path (str): The relative path to append to the base path.
Returns:
str: The resulting path after safely joining the base and relative paths.
Examples:
>>> join_paths_safely('/base/path/', '/relative/path')
'/base/path/relative/path'
>>> join_paths_safely('/base/path', 'relative/path')
'/base/path/relative/path'
"""
# Remove the trailing slash from the base path if it exists, and add a leading slash to the relative path if it's missing
# Then concatenate the paths
return base_path.rstrip('/') + '/' + relative_path.lstrip('/')
14 changes: 10 additions & 4 deletions backend/src/main.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
import logging
from urllib.parse import urljoin

from fastapi.staticfiles import StaticFiles

from core.config import Settings, get_app_settings
from core.exception_handler import (
Expand All @@ -7,6 +10,7 @@
http_exception_handler,
starlette_http_exception_handler,
)
from core.helpers import join_paths_safely
from endpoints.api import routers
from fastapi import FastAPI
from fastapi.exceptions import HTTPException, RequestValidationError
Expand All @@ -25,9 +29,9 @@ def get_application() -> FastAPI:
title=settings.title,
version=settings.version,
description=settings.description,
redoc_url=settings.redoc_url,
docs_url=settings.docs_url,
openapi_url=settings.openapi_url,
redoc_url= join_paths_safely(settings.root_path, settings.redoc_url),
docs_url=join_paths_safely(settings.root_path, settings.docs_url),
openapi_url=join_paths_safely(settings.root_path, settings.openapi_url),
summary=settings.summary,
)

Expand All @@ -40,13 +44,15 @@ def get_application() -> FastAPI:
allow_headers=["*"],
)

application.include_router(routers, prefix=settings.api_prefix)
application.include_router(routers, prefix=join_paths_safely(settings.root_path, settings.api_prefix))

application.add_exception_handler(RequestValidationError, custom_validation_exception_handler) # type: ignore
application.add_exception_handler(HTTPException, http_exception_handler) # type: ignore
application.add_exception_handler(Exception, all_exception_handler) # type: ignore
application.add_exception_handler(StarletteHTTPException, starlette_http_exception_handler) # type: ignore

application.mount(join_paths_safely(settings.root_path, "ui"), StaticFiles(directory="static", html=True), name="static")

return application


Expand Down
Empty file added backend/src/static/.gitkeep
Empty file.
13 changes: 10 additions & 3 deletions frontend/src/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,17 @@
import { IFetchJobsParams, IJob, IJobsInfo } from "./types";

function joinPathsSafely(basePath: string, relativePath: string): string {
const trimmedBasePath = basePath.endsWith('/') ? basePath.slice(0, -1) : basePath;

Check failure on line 4 in frontend/src/api/index.ts

View workflow job for this annotation

GitHub Actions / frontend-check

Replace `'/')·?·basePath.slice(0,·-1)` with `"/")⏎····?·basePath.slice(0,·-1)⏎···`
const trimmedRelativePath = relativePath.startsWith('/') ? relativePath.slice(1) : relativePath;

Check failure on line 5 in frontend/src/api/index.ts

View workflow job for this annotation

GitHub Actions / frontend-check

Replace `'/')·?·relativePath.slice(1)` with `"/")⏎····?·relativePath.slice(1)⏎···`
return `${trimmedBasePath}/${trimmedRelativePath}`;
}

/**
* Fetches jobs from the API based on the specified parameters.
*/
export function fetchJobs(params: IFetchJobsParams = {}): Promise<IJobsInfo> {

Check failure on line 12 in frontend/src/api/index.ts

View workflow job for this annotation

GitHub Actions / frontend-check

Delete `⏎··`
const jobsUrl = new URL("jobs", import.meta.env.VITE_API_HOST).toString();

const jobsUrl = joinPathsSafely(import.meta.env.VITE_API_HOST, "jobs");

const queryParams = new URLSearchParams();

Expand Down Expand Up @@ -40,7 +47,7 @@ export function fetchJobs(params: IFetchJobsParams = {}): Promise<IJobsInfo> {
}

export function abortJob(jobId: string): Promise<void> {
const jobsUrl = new URL("jobs", import.meta.env.VITE_API_HOST).toString();
const jobsUrl = joinPathsSafely(import.meta.env.VITE_API_HOST, "jobs");
const url = `${jobsUrl}/${jobId}`;

return fetch(url, { method: "DELETE" }).then(async (response) => {
Expand All @@ -52,7 +59,7 @@ export function abortJob(jobId: string): Promise<void> {
}

export function fetchJob(jobId: string): Promise<IJob> {
const jobsUrl = new URL("jobs", import.meta.env.VITE_API_HOST).toString();
const jobsUrl = joinPathsSafely(import.meta.env.VITE_API_HOST, "jobs");
const url = `${jobsUrl}/${jobId}`;

return fetch(url).then(async (response) => {
Expand Down

0 comments on commit 2ddade6

Please sign in to comment.