Skip to content

Commit

Permalink
feat: fix golang shared object in Docker
Browse files Browse the repository at this point in the history
  • Loading branch information
AiroPi committed Jan 7, 2025
1 parent c679692 commit 375bee4
Show file tree
Hide file tree
Showing 42 changed files with 155 additions and 83 deletions.
21 changes: 7 additions & 14 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -1,38 +1,33 @@
# syntax=docker/dockerfile-upstream:master-labs

FROM python:3.12-alpine AS build
FROM python:3.12-bookworm AS build
ARG UV_EXTRA_ARGS=""
COPY --from=ghcr.io/astral-sh/uv:latest /uv /bin/uv
WORKDIR /app
RUN --mount=type=cache,target=/var/cache/apk \
--mount=type=cache,target=/root/.cache/uv \
RUN --mount=type=cache,target=/root/.cache/uv \
--mount=type=bind,source=python/uv.lock,target=uv.lock \
--mount=type=bind,source=python/pyproject.toml,target=pyproject.toml \
: \
&& apk update && apk add gcc musl-dev linux-headers git \
&& uv sync --no-dev --locked $UV_EXTRA_ARGS \
&& :

FROM golang:1.23-alpine AS buildgo
FROM golang:1.23-bookworm AS buildgo
WORKDIR /app
RUN --mount=type=cache,target=/var/cache/apk \
--mount=type=cache,target=/go/pkg/mod/ \
RUN --mount=type=cache,target=/go/pkg/mod/ \
--mount=type=bind,source=golang/go.sum,target=go.sum \
--mount=type=bind,source=golang/go.mod,target=go.mod \
--mount=type=bind,source=golang/src,target=src \
: \
&& apk update && apk add gcc musl-dev \
&& go mod download -x \
&& CGO_ENABLED=1 go build -o connect4img.so -buildmode=c-shared src/connect4img.go \
&& :

FROM python:3.12-alpine AS base
FROM python:3.12-slim-bookworm AS base
# https://docs.docker.com/reference/dockerfile/#copy---parents
RUN apk add --no-cache musl-dev libc6-compat file
WORKDIR /app
COPY --from=buildgo /app/connect4img.so /app/shared/
COPY --parents --from=build /app/.venv /
COPY --parents ./resources ./
COPY --parents ./resources ./static ./
COPY ./python/src ./
ENV PATH="/app/.venv/bin:$PATH"
ENV PYTHONUNBUFFERED=0
Expand All @@ -45,6 +40,4 @@ COPY --parents ./scripts/readme_generator.py ./templates/README.md ./
RUN python3 scripts/readme_generator.py http://localhost/
ENV DEBUG=1
ENV LOG_LEVEL=DEBUG
RUN ls shared
RUN file shared/connect4img.so
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80", "--reload"]
CMD ["hypercorn", "main:app", "-b", "0.0.0.0:80", "--reload"]
11 changes: 6 additions & 5 deletions compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -8,17 +8,18 @@ services:
develop:
watch:
- action: sync
path: ./src
path: ./python/src
target: /app
- action: rebuild
path: ./golang/src
- action: sync
path: ./resources
target: /app/resources
- action: sync
path: ./readme.example.html
target: /app/readme.example.html
- action: rebuild
path: ./requirements.txt
path: uv.lock
ports:
- 80:80
volumes:
- ./data:/app/data
environment:
GITHUB_PROFILE_URL: "http://localhost/readme.md"
2 changes: 2 additions & 0 deletions deploy/compose.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
services:
readme-backend:
image: ghcr.io/airopi/readme-backend:master
volumes:
- data:/app/data
environment:
GITHUB_PROFILE_URL: "https://github.com/AiroPi"
1 change: 1 addition & 0 deletions golang/src/connect4img.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ var HIGHLIGHT_WIN_COLOR = color.RGBA{100, 255, 100, 255}
var FACE font.Face

func init() {
log.Print("Loaded!")
var err error
FACE, err = gg.LoadFontFace("./resources/fonts/Roboto-Regular.ttf", float64(FONT_SIZE))
if err != nil {
Expand Down
2 changes: 1 addition & 1 deletion python/pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ requires-python = ">=3.12"
dependencies = [
#
"fastapi",
"uvicorn",
"hypercorn",
"Pillow",
"connect4.py==1.0.3",
"minesweeper @ git+https://github.com/AiroPi/minesweeper",
Expand Down
2 changes: 2 additions & 0 deletions python/src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from fastapi import FastAPI
from fastapi.responses import HTMLResponse, RedirectResponse
from fastapi.staticfiles import StaticFiles

from routers import connect4_router, minesweeper_router

Expand All @@ -10,6 +11,7 @@
app = FastAPI()
app.include_router(connect4_router.router)
app.include_router(minesweeper_router.router)
app.mount("/static", StaticFiles(directory="static"), name="static")


@app.get("/")
Expand Down
6 changes: 3 additions & 3 deletions python/src/routers/connect4_router/go_library.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@

import os
from collections.abc import Sequence
from ctypes import CDLL, POINTER, Structure, c_longlong, c_uint8, c_void_p
from ctypes import POINTER, Structure, c_longlong, c_uint8, c_void_p, cdll
from typing import TYPE_CHECKING, ClassVar

if TYPE_CHECKING:
from connect4 import Connect4


_path = os.path.abspath("./golang/connect4img.so")
_path = os.path.abspath("./shared/connect4img.so")

try:
_lib = CDLL(_path, mode=0x8)
_lib = cdll.LoadLibrary(_path)
print("Library loaded successfully!")
except OSError as e:
print(f"Failed to load library: {e}")
Expand Down
82 changes: 51 additions & 31 deletions python/src/routers/minesweeper_router.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import hashlib
import os
from datetime import datetime
from pathlib import Path

import minesweeper
from fastapi import APIRouter
from fastapi import APIRouter, Response
from fastapi.responses import FileResponse, RedirectResponse

GITHUB_PROFILE_URL = os.environ["GITHUB_PROFILE_URL"]
Expand All @@ -16,87 +17,106 @@
flag_mode = False


def get_header():
def load_image(path: Path):
with path.open("rb") as f:
return f.read()


RED_MINE = load_image(BASE_PATH / "mine_red.png")
MINE = load_image(BASE_PATH / "mine.png")
FLAG = load_image(BASE_PATH / "flag.png")
DEACTIVATED_FLAG = load_image(BASE_PATH / "deactivated_flag.png")
CLOSED = load_image(BASE_PATH / "closed.png")
HEADER = load_image(BASE_PATH / "header.png")
UNDO = load_image(BASE_PATH / "undo.png")
FACE = load_image(BASE_PATH / "face.png")
FACE_LOSE = load_image(BASE_PATH / "face_lose.png")

DIGITS = {i: load_image(BASE_PATH / f"{i}.png") for i in range(9)}


def get_header(etag: str = ""):
return {
"Cache-Control": "no-cache, max-age=0",
"Last-Modified": datetime.now().strftime("%a, %d %b %Y %H:%M:%S GMT"),
# "Last-Modified": datetime.now().strftime("%a, %d %b %Y %H:%M:%S GMT"),
"ETag": etag,
}


def response_img(img: str):
return RedirectResponse(f"/static/ms/{img}.png")
# return Response("abc")
etag = hashlib.md5(img).hexdigest() # noqa: S324

return Response(img, media_type="image/png", headers=get_header(etag))


def redirect_to_github():
return RedirectResponse(GITHUB_PROFILE_URL)


@router.get("/img/{i}/{j}")
def get_board_img(i: int, j: int):
if (i, j) in game.revealed:
if game.board[i][j] == -1:
return FileResponse(
BASE_PATH / "mine_red.png",
headers=get_header(),
)
return FileResponse(
BASE_PATH / f"{game.board[i][j]}.png",
headers=get_header(),
)
return response_img("mine_red")
return response_img(str(game.board[i][j]))
if game.game_over and game.board[i][j] == -1:
return FileResponse(BASE_PATH / "mine.png", headers=get_header())
return response_img("mine")
if (i, j) in game.flags:
return FileResponse(BASE_PATH / "flag.png", headers=get_header())
return FileResponse(BASE_PATH / "closed.png", headers=get_header())
return response_img("flag")
return response_img("closed")


@router.get("/img/{text}")
def get_img(text: str):
match text:
case "flag-toggle":
if flag_mode:
return FileResponse(BASE_PATH / "flag.png", headers=get_header())
return response_img("flag.png")
else:
return FileResponse(BASE_PATH / "deactivated_flag.png", headers=get_header())
case "digit1":
return FileResponse(BASE_PATH / "dnull.png", headers=get_header())
case "digit2":
return FileResponse(BASE_PATH / "d3.png", headers=get_header())
case "digit3":
return FileResponse(BASE_PATH / "d5.png", headers=get_header())
return response_img("deactivated_flag.png")
case "header":
return FileResponse(BASE_PATH / "header.png", headers=get_header())
return response_img("header.png")
case "undo":
return FileResponse(BASE_PATH / "undo.png", headers=get_header())
return response_img("undo.png")
case "face":
if game.game_over:
return FileResponse(BASE_PATH / "face_lose.png", headers=get_header())
return response_img("face_lose.png")
else:
return FileResponse(BASE_PATH / "face.png", headers=get_header())
return response_img("face.png")
case _:
return 404


@router.get("/play/{i}/{j}")
def play(i: int, j: int):
if game.game_over:
return RedirectResponse(GITHUB_PROFILE_URL)
return redirect_to_github()

if flag_mode:
game.toggle_flag(i, j)
else:
if (i, j) not in game.flags:
game.play(i, j)
return RedirectResponse(GITHUB_PROFILE_URL)
return redirect_to_github()


@router.get("/toggle-flag")
def flag_toggle():
global flag_mode
flag_mode = not flag_mode
return RedirectResponse(GITHUB_PROFILE_URL)
return redirect_to_github()


@router.get("/reset")
def reset():
global game
game = minesweeper.Minesweeper(SIZE, BOMBS)
return RedirectResponse(GITHUB_PROFILE_URL)
return redirect_to_github()


@router.get("/undo")
def undo():
# TODO: Implement undo
return RedirectResponse(GITHUB_PROFILE_URL)
return redirect_to_github()
Loading

0 comments on commit 375bee4

Please sign in to comment.