diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..b8d4102 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,10 @@ +.git +.gitea +artifacts +bin +obj +.vs +.vscode +*.user +*.suo +TestResults diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..fdf1f04 --- /dev/null +++ b/.env.example @@ -0,0 +1,5 @@ +HUNTAPI_HTTP_PORT=8080 +HUNTAPI_CONNECTION_STRING=Host=pgsql.fshcomputer.dk;Port=5432;Database=hunting_app;Username=postgres;Password=change-me;Pooling=true;Maximum Pool Size=50; +HUNTAPI_JWT_SECRET=change-this-to-a-long-random-secret-min-32-chars +HUNTAPI_JWT_ISSUER=hunting-app-api +HUNTAPI_JWT_AUDIENCE=hunting-app diff --git a/.gitea/workflows/ci.yml b/.gitea/workflows/ci.yml index 5a4c88d..acd4cd5 100644 --- a/.gitea/workflows/ci.yml +++ b/.gitea/workflows/ci.yml @@ -37,3 +37,26 @@ jobs: with: name: huntapi path: ./artifacts/huntapi + + deploy: + name: Build and run Docker container + runs-on: docker + needs: build + if: github.event_name == 'push' && github.ref == 'refs/heads/master' + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Write environment file + run: | + cat > .env <<'EOF' + HUNTAPI_HTTP_PORT=${{ secrets.HUNTAPI_HTTP_PORT }} + HUNTAPI_CONNECTION_STRING=${{ secrets.HUNTAPI_CONNECTION_STRING }} + HUNTAPI_JWT_SECRET=${{ secrets.HUNTAPI_JWT_SECRET }} + HUNTAPI_JWT_ISSUER=${{ secrets.HUNTAPI_JWT_ISSUER }} + HUNTAPI_JWT_AUDIENCE=${{ secrets.HUNTAPI_JWT_AUDIENCE }} + EOF + + - name: Deploy + run: docker compose up -d --build --remove-orphans diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..83649e5 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,18 @@ +FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build +WORKDIR /src + +COPY NuGet.config . +COPY HuntingAppApi.csproj . +RUN dotnet restore HuntingAppApi.csproj + +COPY . . +RUN dotnet publish HuntingAppApi.csproj --configuration Release --no-restore --output /app/publish + +FROM mcr.microsoft.com/dotnet/aspnet:10.0 AS runtime +WORKDIR /app + +ENV ASPNETCORE_URLS=http://+:8080 +EXPOSE 8080 + +COPY --from=build /app/publish . +ENTRYPOINT ["dotnet", "HuntingAppApi.dll"] diff --git a/compose.yml b/compose.yml new file mode 100644 index 0000000..1079a1d --- /dev/null +++ b/compose.yml @@ -0,0 +1,16 @@ +services: + huntapi: + build: + context: . + dockerfile: Dockerfile + image: huntapi:latest + container_name: huntapi + restart: unless-stopped + ports: + - "${HUNTAPI_HTTP_PORT:-8080}:8080" + environment: + ASPNETCORE_ENVIRONMENT: Production + ConnectionStrings__Default: "${HUNTAPI_CONNECTION_STRING}" + Authentication__JwtSecret: "${HUNTAPI_JWT_SECRET}" + Authentication__Issuer: "${HUNTAPI_JWT_ISSUER:-hunting-app-api}" + Authentication__Audience: "${HUNTAPI_JWT_AUDIENCE:-hunting-app}" diff --git a/docs/gitea-actions-setup.md b/docs/gitea-actions-setup.md index 673e902..6cdbdfb 100644 --- a/docs/gitea-actions-setup.md +++ b/docs/gitea-actions-setup.md @@ -3,6 +3,8 @@ This project builds automatically with Gitea Actions when changes are pushed to the `master` branch. The workflow is defined in `.gitea/workflows/ci.yml`. +On pushes to `master`, the workflow also deploys the API with Docker Compose. + ## One-time Gitea server setup 1. Make sure the Gitea server is version 1.19.0 or newer. @@ -34,9 +36,9 @@ Example: ./act_runner daemon ``` -The workflow uses the `ubuntu-latest` runner label and installs .NET 10 with -`actions/setup-dotnet@v4`. The repository also contains a `NuGet.config` file so -the build uses `nuget.org` instead of machine-specific package feeds. +The build job uses the `ubuntu-latest` runner label and installs .NET 10 with +`actions/setup-dotnet@v4`. The deploy job uses a separate `deploy-host` runner +label so Docker Compose runs directly on the Ubuntu host. The runner must be able to reach `github.com`/`gitea.com` for actions and `api.nuget.org` for packages unless you configure a local action/package mirror. @@ -44,6 +46,40 @@ The runner must be able to reach `github.com`/`gitea.com` for actions and Gitea Actions currently treats `actions/upload-artifact@v4` as unsupported in this environment, so the workflow intentionally uses `actions/upload-artifact@v3`. +## Deployment setup + +The runner needs a host execution label for deployment: + +```bash +sudo systemctl stop act_runner +sudo -u act_runner /opt/act_runner/act_runner register \ + --no-interactive \ + --instance http://git.fshcomputer.dk:3000 \ + --token \ + --name ubuntu-runner \ + --labels ubuntu-latest:docker://node:20-bookworm,ubuntu-22.04:docker://node:20-bookworm,deploy-host:host +sudo systemctl start act_runner +``` + +The deploy host also needs Docker Compose: + +```bash +sudo apt update +sudo apt install -y docker-compose-plugin +docker compose version +``` + +Add these repository secrets in Gitea before the first deploy: + +- `HUNTAPI_HTTP_PORT`, for example `8080` +- `HUNTAPI_CONNECTION_STRING` +- `HUNTAPI_JWT_SECRET` +- `HUNTAPI_JWT_ISSUER`, for example `hunting-app-api` +- `HUNTAPI_JWT_AUDIENCE`, for example `hunting-app` + +Use the PostgreSQL server hostname in the connection string, for example +`Host=pgsql.fshcomputer.dk;Port=5432;Database=hunting_app;...`. + ## First push The local repository currently needs its first commit and a `master` branch for